Merge "Use preview_sdk_int for version code checks"
diff --git a/Android.bp b/Android.bp
index e1f5abd9..477f027 100644
--- a/Android.bp
+++ b/Android.bp
@@ -262,12 +262,13 @@
name: "framework-updatable-sources",
srcs: [
":framework-appsearch-sources",
- ":framework-sdkext-sources",
+ ":framework-sdkextensions-sources",
":framework-statsd-sources",
":framework-tethering-srcs",
":updatable-media-srcs",
":framework-mediaprovider-sources",
":framework-wifi-updatable-sources",
+ ":ike-srcs",
]
}
@@ -284,7 +285,7 @@
name: "framework-aidl-export-defaults",
aidl: {
export_include_dirs: [
- "apex/media/java",
+ "apex/media/framework/java",
"core/java",
"drm/java",
"graphics/java",
@@ -432,6 +433,7 @@
// TODO(b/146167933): Use framework-statsd-stubs
"framework-statsd",
"framework-wifi-stubs",
+ "ike-stubs",
],
installable: true,
javac_shard_size: 150,
@@ -451,6 +453,16 @@
// For backwards compatibility.
stem: "framework",
apex_available: ["//apex_available:platform"],
+ visibility: [
+ "//frameworks/base",
+ // TODO(b/144149403) remove the below lines
+ "//frameworks/base/apex/appsearch/framework",
+ "//frameworks/base/apex/blobstore/framework",
+ "//frameworks/base/apex/jobscheduler/framework",
+ "//frameworks/base/apex/statsd/service",
+ "//frameworks/base/wifi",
+ "//frameworks/opt/net/wifi/service",
+ ],
}
// This "framework" module is NOT installed to the device. It's
@@ -471,11 +483,12 @@
"updatable_media_stubs",
"framework_mediaprovider_stubs",
"framework-appsearch", // TODO(b/146218515): should be framework-appsearch-stubs
- "framework-sdkext-stubs-systemapi",
+ "framework-sdkextensions-stubs-systemapi",
// TODO(b/146167933): Use framework-statsd-stubs instead.
"framework-statsd",
// TODO(b/140299412): should be framework-wifi-stubs
"framework-wifi",
+ "ike-stubs",
// TODO(jiyong): add more stubs for APEXes here
],
sdk_version: "core_platform",
@@ -622,6 +635,7 @@
"core/java/com/android/internal/util/StateMachine.java",
"core/java/com/android/internal/util/TrafficStatsConstants.java",
"core/java/com/android/internal/util/WakeupMessage.java",
+ "core/java/com/android/internal/util/TokenBucket.java",
"core/java/android/net/shared/*.java",
],
}
@@ -631,13 +645,13 @@
name: "framework-tethering-shared-srcs",
srcs: [
"core/java/android/util/LocalLog.java",
- "core/java/com/android/internal/util/BitUtils.java",
"core/java/com/android/internal/util/IndentingPrintWriter.java",
"core/java/com/android/internal/util/IState.java",
"core/java/com/android/internal/util/MessageUtils.java",
"core/java/com/android/internal/util/Preconditions.java",
"core/java/com/android/internal/util/State.java",
"core/java/com/android/internal/util/StateMachine.java",
+ "core/java/android/net/shared/Inet4AddressUtils.java",
],
}
@@ -1123,6 +1137,7 @@
srcs: [
":framework-annotations",
"core/java/android/net/InterfaceConfiguration.java",
+ "core/java/android/os/BasicShellCommandHandler.java",
"core/java/android/os/HandlerExecutor.java",
"core/java/android/util/BackupUtils.java",
"core/java/android/util/LocalLog.java",
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 78f1b9c..d195047 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -217,21 +217,6 @@
defaults: ["framework-stubs-default"],
}
-java_system_modules {
- name: "android_stubs_current_system_modules",
- libs: ["android_stubs_current"],
-}
-
-java_system_modules {
- name: "android_system_stubs_current_system_modules",
- libs: ["android_system_stubs_current"],
-}
-
-java_system_modules {
- name: "android_test_stubs_current_system_modules",
- libs: ["android_test_stubs_current"],
-}
-
/////////////////////////////////////////////////////////////////////
// hwbinder.stubs provides APIs required for building HIDL Java
// libraries.
diff --git a/apex/blobstore/OWNERS b/apex/blobstore/OWNERS
new file mode 100644
index 0000000..8e04399
--- /dev/null
+++ b/apex/blobstore/OWNERS
@@ -0,0 +1,4 @@
+set noparent
+
+sudheersai@google.com
+yamasani@google.com
diff --git a/apex/blobstore/TEST_MAPPING b/apex/blobstore/TEST_MAPPING
new file mode 100644
index 0000000..4dc0c49
--- /dev/null
+++ b/apex/blobstore/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsBlobStoreTestCases"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/apex/media/Android.bp b/apex/media/framework/Android.bp
similarity index 100%
rename from apex/media/Android.bp
rename to apex/media/framework/Android.bp
diff --git a/apex/media/jarjar_rules.txt b/apex/media/framework/jarjar_rules.txt
similarity index 100%
rename from apex/media/jarjar_rules.txt
rename to apex/media/framework/jarjar_rules.txt
diff --git a/apex/media/java/android/media/BufferingParams.java b/apex/media/framework/java/android/media/BufferingParams.java
similarity index 100%
rename from apex/media/java/android/media/BufferingParams.java
rename to apex/media/framework/java/android/media/BufferingParams.java
diff --git a/apex/media/java/android/media/Controller2Link.aidl b/apex/media/framework/java/android/media/Controller2Link.aidl
similarity index 100%
rename from apex/media/java/android/media/Controller2Link.aidl
rename to apex/media/framework/java/android/media/Controller2Link.aidl
diff --git a/apex/media/java/android/media/Controller2Link.java b/apex/media/framework/java/android/media/Controller2Link.java
similarity index 100%
rename from apex/media/java/android/media/Controller2Link.java
rename to apex/media/framework/java/android/media/Controller2Link.java
diff --git a/apex/media/java/android/media/DataSourceCallback.java b/apex/media/framework/java/android/media/DataSourceCallback.java
similarity index 100%
rename from apex/media/java/android/media/DataSourceCallback.java
rename to apex/media/framework/java/android/media/DataSourceCallback.java
diff --git a/apex/media/java/android/media/IMediaController2.aidl b/apex/media/framework/java/android/media/IMediaController2.aidl
similarity index 100%
rename from apex/media/java/android/media/IMediaController2.aidl
rename to apex/media/framework/java/android/media/IMediaController2.aidl
diff --git a/apex/media/java/android/media/IMediaSession2.aidl b/apex/media/framework/java/android/media/IMediaSession2.aidl
similarity index 100%
rename from apex/media/java/android/media/IMediaSession2.aidl
rename to apex/media/framework/java/android/media/IMediaSession2.aidl
diff --git a/apex/media/java/android/media/IMediaSession2Service.aidl b/apex/media/framework/java/android/media/IMediaSession2Service.aidl
similarity index 100%
rename from apex/media/java/android/media/IMediaSession2Service.aidl
rename to apex/media/framework/java/android/media/IMediaSession2Service.aidl
diff --git a/apex/media/java/android/media/MediaConstants.java b/apex/media/framework/java/android/media/MediaConstants.java
similarity index 100%
rename from apex/media/java/android/media/MediaConstants.java
rename to apex/media/framework/java/android/media/MediaConstants.java
diff --git a/apex/media/java/android/media/MediaController2.java b/apex/media/framework/java/android/media/MediaController2.java
similarity index 100%
rename from apex/media/java/android/media/MediaController2.java
rename to apex/media/framework/java/android/media/MediaController2.java
diff --git a/apex/media/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
similarity index 100%
rename from apex/media/java/android/media/MediaParser.java
rename to apex/media/framework/java/android/media/MediaParser.java
diff --git a/apex/media/java/android/media/MediaSession2.java b/apex/media/framework/java/android/media/MediaSession2.java
similarity index 100%
rename from apex/media/java/android/media/MediaSession2.java
rename to apex/media/framework/java/android/media/MediaSession2.java
diff --git a/apex/media/java/android/media/MediaSession2Service.java b/apex/media/framework/java/android/media/MediaSession2Service.java
similarity index 100%
rename from apex/media/java/android/media/MediaSession2Service.java
rename to apex/media/framework/java/android/media/MediaSession2Service.java
diff --git a/apex/media/java/android/media/ProxyDataSourceCallback.java b/apex/media/framework/java/android/media/ProxyDataSourceCallback.java
similarity index 100%
rename from apex/media/java/android/media/ProxyDataSourceCallback.java
rename to apex/media/framework/java/android/media/ProxyDataSourceCallback.java
diff --git a/apex/media/java/android/media/RoutingDelegate.java b/apex/media/framework/java/android/media/RoutingDelegate.java
similarity index 100%
rename from apex/media/java/android/media/RoutingDelegate.java
rename to apex/media/framework/java/android/media/RoutingDelegate.java
diff --git a/apex/media/java/android/media/Session2Command.aidl b/apex/media/framework/java/android/media/Session2Command.aidl
similarity index 100%
rename from apex/media/java/android/media/Session2Command.aidl
rename to apex/media/framework/java/android/media/Session2Command.aidl
diff --git a/apex/media/java/android/media/Session2Command.java b/apex/media/framework/java/android/media/Session2Command.java
similarity index 100%
rename from apex/media/java/android/media/Session2Command.java
rename to apex/media/framework/java/android/media/Session2Command.java
diff --git a/apex/media/java/android/media/Session2CommandGroup.java b/apex/media/framework/java/android/media/Session2CommandGroup.java
similarity index 100%
rename from apex/media/java/android/media/Session2CommandGroup.java
rename to apex/media/framework/java/android/media/Session2CommandGroup.java
diff --git a/apex/media/java/android/media/Session2Link.java b/apex/media/framework/java/android/media/Session2Link.java
similarity index 100%
rename from apex/media/java/android/media/Session2Link.java
rename to apex/media/framework/java/android/media/Session2Link.java
diff --git a/apex/media/java/android/media/Session2Token.aidl b/apex/media/framework/java/android/media/Session2Token.aidl
similarity index 100%
rename from apex/media/java/android/media/Session2Token.aidl
rename to apex/media/framework/java/android/media/Session2Token.aidl
diff --git a/apex/media/java/android/media/Session2Token.java b/apex/media/framework/java/android/media/Session2Token.java
similarity index 100%
rename from apex/media/java/android/media/Session2Token.java
rename to apex/media/framework/java/android/media/Session2Token.java
diff --git a/apex/sdkext/TEST_MAPPING b/apex/sdkext/TEST_MAPPING
deleted file mode 100644
index 91947f3..0000000
--- a/apex/sdkext/TEST_MAPPING
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "presubmit": [
- {
- "name": "CtsSdkExtTestCases"
- }
- ]
-}
diff --git a/apex/sdkext/framework/Android.bp b/apex/sdkext/framework/Android.bp
deleted file mode 100644
index a50dc3d..0000000
--- a/apex/sdkext/framework/Android.bp
+++ /dev/null
@@ -1,71 +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 {
- default_visibility: [ ":__pkg__" ]
-}
-
-filegroup {
- name: "framework-sdkext-sources",
- srcs: [
- "java/**/*.java",
- ],
- path: "java",
- visibility: [ "//frameworks/base:__pkg__" ] // For the "global" stubs.
-}
-
-java_library {
- name: "framework-sdkext",
- srcs: [ ":framework-sdkext-sources" ],
- sdk_version: "system_current",
- libs: [ "framework-annotations-lib" ],
- permitted_packages: [ "android.os.ext" ],
- installable: true,
- visibility: [ "//frameworks/base/apex/sdkext:__pkg__" ],
-}
-
-droidstubs {
- name: "framework-sdkext-droidstubs-publicapi",
- defaults: [
- "framework-sdkext-stubs-defaults",
- "framework-module-stubs-defaults-publicapi",
- ]
-}
-
-droidstubs {
- name: "framework-sdkext-droidstubs-systemapi",
- defaults: [
- "framework-sdkext-stubs-defaults",
- "framework-module-stubs-defaults-systemapi",
- ]
-}
-
-stubs_defaults {
- name: "framework-sdkext-stubs-defaults",
- srcs: [
- ":framework-sdkext-sources",
- ":framework-annotations",
- ],
- sdk_version: "system_current",
-}
-
-java_library {
- name: "framework-sdkext-stubs-systemapi",
- srcs: [":framework-sdkext-droidstubs-systemapi"],
- sdk_version: "system_current",
- visibility: [
- "//frameworks/base:__pkg__", // Framework
- "//frameworks/base/apex/sdkext:__pkg__", // sdkext SDK
- ]
-}
diff --git a/apex/sdkext/Android.bp b/apex/sdkextensions/Android.bp
similarity index 83%
rename from apex/sdkext/Android.bp
rename to apex/sdkextensions/Android.bp
index f62f167..4c5c2b2 100644
--- a/apex/sdkext/Android.bp
+++ b/apex/sdkextensions/Android.bp
@@ -18,21 +18,26 @@
apex {
name: "com.android.sdkext",
- manifest: "manifest.json",
+ defaults: [ "com.android.sdkext-defaults" ],
binaries: [ "derive_sdk" ],
- java_libs: [ "framework-sdkext" ],
+ prebuilts: [ "cur_sdkinfo" ],
+ manifest: "manifest.json",
+}
+
+apex_defaults {
+ name: "com.android.sdkext-defaults",
+ java_libs: [ "framework-sdkextensions" ],
prebuilts: [
- "com.android.sdkext.ldconfig",
- "cur_sdkinfo",
- "derive_sdk.rc",
+ "com.android.sdkext.ldconfig",
+ "derive_sdk.rc",
],
key: "com.android.sdkext.key",
certificate: ":com.android.sdkext.certificate",
}
sdk {
- name: "sdkext-sdk",
- java_header_libs: [ "framework-sdkext-stubs-systemapi" ],
+ name: "sdkextensions-sdk",
+ java_header_libs: [ "framework-sdkextensions-stubs-systemapi" ],
}
apex_key {
diff --git a/apex/sdkext/OWNERS b/apex/sdkextensions/OWNERS
similarity index 100%
rename from apex/sdkext/OWNERS
rename to apex/sdkextensions/OWNERS
diff --git a/apex/sdkextensions/TEST_MAPPING b/apex/sdkextensions/TEST_MAPPING
new file mode 100644
index 0000000..7e77623
--- /dev/null
+++ b/apex/sdkextensions/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsSdkExtTestCases"
+ },
+ {
+ "name": "apiextensions_e2e_tests"
+ }
+ ]
+}
diff --git a/apex/sdkext/com.android.sdkext.avbpubkey b/apex/sdkextensions/com.android.sdkext.avbpubkey
similarity index 100%
rename from apex/sdkext/com.android.sdkext.avbpubkey
rename to apex/sdkextensions/com.android.sdkext.avbpubkey
Binary files differ
diff --git a/apex/sdkext/com.android.sdkext.pem b/apex/sdkextensions/com.android.sdkext.pem
similarity index 100%
rename from apex/sdkext/com.android.sdkext.pem
rename to apex/sdkextensions/com.android.sdkext.pem
diff --git a/apex/sdkext/com.android.sdkext.pk8 b/apex/sdkextensions/com.android.sdkext.pk8
similarity index 100%
rename from apex/sdkext/com.android.sdkext.pk8
rename to apex/sdkextensions/com.android.sdkext.pk8
Binary files differ
diff --git a/apex/sdkext/com.android.sdkext.x509.pem b/apex/sdkextensions/com.android.sdkext.x509.pem
similarity index 100%
rename from apex/sdkext/com.android.sdkext.x509.pem
rename to apex/sdkextensions/com.android.sdkext.x509.pem
diff --git a/apex/sdkext/derive_sdk/Android.bp b/apex/sdkextensions/derive_sdk/Android.bp
similarity index 63%
rename from apex/sdkext/derive_sdk/Android.bp
rename to apex/sdkextensions/derive_sdk/Android.bp
index c4e3c29..cf49902 100644
--- a/apex/sdkext/derive_sdk/Android.bp
+++ b/apex/sdkextensions/derive_sdk/Android.bp
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-cc_binary {
- name: "derive_sdk",
+cc_defaults {
+ name: "derive_sdk-defaults",
srcs: [
"derive_sdk.cpp",
"sdk.proto",
@@ -30,6 +30,24 @@
],
}
+cc_binary {
+ name: "derive_sdk",
+ defaults: [ "derive_sdk-defaults" ],
+ apex_available: [ "com.android.sdkext" ],
+ visibility: [ "//frameworks/base/apex/sdkextensions" ]
+}
+
+// Work around testing using a 64-bit test suite on 32-bit test device by
+// using a prefer32 version of derive_sdk in testing.
+cc_binary {
+ name: "derive_sdk_prefer32",
+ defaults: [ "derive_sdk-defaults" ],
+ compile_multilib: "prefer32",
+ stem: "derive_sdk",
+ apex_available: [ "test_com.android.sdkext" ],
+ visibility: [ "//frameworks/base/apex/sdkextensions/testing" ]
+}
+
prebuilt_etc {
name: "derive_sdk.rc",
src: "derive_sdk.rc",
diff --git a/apex/sdkext/derive_sdk/derive_sdk.cpp b/apex/sdkextensions/derive_sdk/derive_sdk.cpp
similarity index 97%
rename from apex/sdkext/derive_sdk/derive_sdk.cpp
rename to apex/sdkextensions/derive_sdk/derive_sdk.cpp
index 0a97116..6fb7ef4 100644
--- a/apex/sdkext/derive_sdk/derive_sdk.cpp
+++ b/apex/sdkextensions/derive_sdk/derive_sdk.cpp
@@ -26,7 +26,7 @@
#include <android-base/logging.h>
#include <android-base/properties.h>
-#include "frameworks/base/apex/sdkext/derive_sdk/sdk.pb.h"
+#include "frameworks/base/apex/sdkextensions/derive_sdk/sdk.pb.h"
using com::android::sdkext::proto::SdkVersion;
diff --git a/apex/sdkext/derive_sdk/derive_sdk.rc b/apex/sdkextensions/derive_sdk/derive_sdk.rc
similarity index 100%
rename from apex/sdkext/derive_sdk/derive_sdk.rc
rename to apex/sdkextensions/derive_sdk/derive_sdk.rc
diff --git a/apex/sdkext/derive_sdk/sdk.proto b/apex/sdkextensions/derive_sdk/sdk.proto
similarity index 100%
rename from apex/sdkext/derive_sdk/sdk.proto
rename to apex/sdkextensions/derive_sdk/sdk.proto
diff --git a/apex/sdkextensions/framework/Android.bp b/apex/sdkextensions/framework/Android.bp
new file mode 100644
index 0000000..5504f4e
--- /dev/null
+++ b/apex/sdkextensions/framework/Android.bp
@@ -0,0 +1,74 @@
+// 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 {
+ default_visibility: [ ":__pkg__" ]
+}
+
+filegroup {
+ name: "framework-sdkextensions-sources",
+ srcs: [
+ "java/**/*.java",
+ ],
+ path: "java",
+ visibility: [ "//frameworks/base" ] // For the "global" stubs.
+}
+
+java_library {
+ name: "framework-sdkextensions",
+ srcs: [ ":framework-sdkextensions-sources" ],
+ sdk_version: "system_current",
+ libs: [ "framework-annotations-lib" ],
+ permitted_packages: [ "android.os.ext" ],
+ installable: true,
+ visibility: [
+ "//frameworks/base/apex/sdkextensions",
+ "//frameworks/base/apex/sdkextensions/testing",
+ ],
+}
+
+droidstubs {
+ name: "framework-sdkextensions-droidstubs-publicapi",
+ defaults: [
+ "framework-sdkextensions-stubs-defaults",
+ "framework-module-stubs-defaults-publicapi",
+ ]
+}
+
+droidstubs {
+ name: "framework-sdkextensions-droidstubs-systemapi",
+ defaults: [
+ "framework-sdkextensions-stubs-defaults",
+ "framework-module-stubs-defaults-systemapi",
+ ]
+}
+
+stubs_defaults {
+ name: "framework-sdkextensions-stubs-defaults",
+ srcs: [
+ ":framework-sdkextensions-sources",
+ ":framework-annotations",
+ ],
+ sdk_version: "system_current",
+}
+
+java_library {
+ name: "framework-sdkextensions-stubs-systemapi",
+ srcs: [":framework-sdkextensions-droidstubs-systemapi"],
+ sdk_version: "system_current",
+ visibility: [
+ "//frameworks/base", // Framework
+ "//frameworks/base/apex/sdkextensions", // sdkextensions SDK
+ ]
+}
diff --git a/apex/sdkext/framework/java/android/os/ext/SdkExtensions.java b/apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java
similarity index 100%
rename from apex/sdkext/framework/java/android/os/ext/SdkExtensions.java
rename to apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java
diff --git a/apex/sdkext/framework/java/android/os/ext/package.html b/apex/sdkextensions/framework/java/android/os/ext/package.html
similarity index 100%
rename from apex/sdkext/framework/java/android/os/ext/package.html
rename to apex/sdkextensions/framework/java/android/os/ext/package.html
diff --git a/apex/sdkext/gen_sdkinfo.py b/apex/sdkextensions/gen_sdkinfo.py
similarity index 100%
rename from apex/sdkext/gen_sdkinfo.py
rename to apex/sdkextensions/gen_sdkinfo.py
diff --git a/apex/sdkext/ld.config.txt b/apex/sdkextensions/ld.config.txt
similarity index 91%
rename from apex/sdkext/ld.config.txt
rename to apex/sdkextensions/ld.config.txt
index b447068..dcc69b8 100644
--- a/apex/sdkext/ld.config.txt
+++ b/apex/sdkextensions/ld.config.txt
@@ -1,10 +1,10 @@
# Copyright (C) 2019 The Android Open Source Project
#
-# Bionic loader config file for the sdkext apex.
+# Bionic loader config file for the sdkextensions apex.
-dir.sdkext = /apex/com.android.sdkext/bin/
+dir.sdkextensions = /apex/com.android.sdkext/bin/
-[sdkext]
+[sdkextensions]
additional.namespaces = platform
namespace.default.isolated = true
diff --git a/apex/sdkext/manifest.json b/apex/sdkextensions/manifest.json
similarity index 100%
rename from apex/sdkext/manifest.json
rename to apex/sdkextensions/manifest.json
diff --git a/apex/sdkext/sdk.proto b/apex/sdkextensions/sdk.proto
similarity index 100%
rename from apex/sdkext/sdk.proto
rename to apex/sdkextensions/sdk.proto
diff --git a/apex/sdkextensions/testing/Android.bp b/apex/sdkextensions/testing/Android.bp
new file mode 100644
index 0000000..e6451cc
--- /dev/null
+++ b/apex/sdkextensions/testing/Android.bp
@@ -0,0 +1,46 @@
+// 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.
+
+apex {
+ name: "test_com.android.sdkext",
+ visibility: [ "//system/apex/tests" ],
+ defaults: ["com.android.sdkext-defaults"],
+ manifest: "test_manifest.json",
+ prebuilts: [ "sdkinfo_45" ],
+ file_contexts: ":com.android.sdkext-file_contexts",
+ installable: false, // Should never be installed on the systemimage
+ multilib: {
+ prefer32: {
+ binaries: ["derive_sdk_prefer32"],
+ },
+ },
+ // The automated test infra ends up building this apex for 64+32-bit and
+ // then installs it on a 32-bit-only device. Work around this weirdness
+ // by preferring 32-bit.
+ compile_multilib: "prefer32",
+}
+
+genrule {
+ name: "sdkinfo_45_src",
+ out: [ "sdkinfo.binarypb" ],
+ tools: [ "gen_sdkinfo" ],
+ cmd: "$(location) -v 45 -o $(out)",
+}
+
+prebuilt_etc {
+ name: "sdkinfo_45",
+ src: ":sdkinfo_45_src",
+ filename: "sdkinfo.binarypb",
+ installable: false,
+}
diff --git a/apex/sdkextensions/testing/test_manifest.json b/apex/sdkextensions/testing/test_manifest.json
new file mode 100644
index 0000000..1b4a2b0
--- /dev/null
+++ b/apex/sdkextensions/testing/test_manifest.json
@@ -0,0 +1,4 @@
+{
+ "name": "com.android.sdkext",
+ "version": 2147483647
+}
diff --git a/apex/statsd/aidl/Android.bp b/apex/statsd/aidl/Android.bp
index aed6ad9..f8325d4 100644
--- a/apex/statsd/aidl/Android.bp
+++ b/apex/statsd/aidl/Android.bp
@@ -18,6 +18,7 @@
filegroup {
name: "statsd_aidl",
srcs: [
+ "android/os/IPendingIntentRef.aidl",
"android/os/IPullAtomCallback.aidl",
"android/os/IPullAtomResultReceiver.aidl",
"android/os/IStatsCompanionService.aidl",
diff --git a/apex/statsd/aidl/android/os/IPendingIntentRef.aidl b/apex/statsd/aidl/android/os/IPendingIntentRef.aidl
new file mode 100644
index 0000000..6b9e467
--- /dev/null
+++ b/apex/statsd/aidl/android/os/IPendingIntentRef.aidl
@@ -0,0 +1,46 @@
+/*
+ * 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;
+
+import android.os.StatsDimensionsValue;
+
+/**
+ * Binder interface to hold a PendingIntent for StatsCompanionService.
+ * {@hide}
+ */
+interface IPendingIntentRef {
+
+ /**
+ * Sends a broadcast to the specified PendingIntent that it should getData now.
+ * This should be only called from StatsCompanionService.
+ */
+ oneway void sendDataBroadcast(long lastReportTimeNs);
+
+ /**
+ * Send a broadcast to the specified PendingIntent notifying it that the list of active configs
+ * has changed. This should be only called from StatsCompanionService.
+ */
+ oneway void sendActiveConfigsChangedBroadcast(in long[] configIds);
+
+ /**
+ * Send a broadcast to the specified PendingIntent, along with the other information
+ * specified. This should only be called from StatsCompanionService.
+ */
+ oneway void sendSubscriberBroadcast(long configUid, long configId, long subscriptionId,
+ long subscriptionRuleId, in String[] cookies,
+ in StatsDimensionsValue dimensionsValue);
+}
\ No newline at end of file
diff --git a/apex/statsd/aidl/android/os/IStatsCompanionService.aidl b/apex/statsd/aidl/android/os/IStatsCompanionService.aidl
index 5a6118e..21b7767 100644
--- a/apex/statsd/aidl/android/os/IStatsCompanionService.aidl
+++ b/apex/statsd/aidl/android/os/IStatsCompanionService.aidl
@@ -17,7 +17,6 @@
package android.os;
import android.os.IPullAtomCallback;
-import android.os.StatsDimensionsValue;
import android.os.StatsLogEventWrapper;
/**
@@ -66,24 +65,6 @@
/** Pull the specified data. Results will be sent to statsd when complete. */
StatsLogEventWrapper[] pullData(int pullCode);
- /** Send a broadcast to the specified PendingIntent's as IBinder that it should getData now. */
- oneway void sendDataBroadcast(in IBinder intentSender, long lastReportTimeNs);
-
- /**
- * Send a broadcast to the specified PendingIntent's as IBinder notifying it that the list
- * of active configs has changed.
- */
- oneway void sendActiveConfigsChangedBroadcast(in IBinder intentSender, in long[] configIds);
-
- /**
- * Requests StatsCompanionService to send a broadcast using the given intentSender
- * (which should cast to an IIntentSender), along with the other information specified.
- */
- oneway void sendSubscriberBroadcast(in IBinder intentSender, long configUid, long configId,
- long subscriptionId, long subscriptionRuleId,
- in String[] cookies,
- in StatsDimensionsValue dimensionsValue);
-
/** Tells StatsCompaionService to grab the uid map snapshot and send it to statsd. */
oneway void triggerUidSnapshot();
diff --git a/apex/statsd/aidl/android/os/IStatsManagerService.aidl b/apex/statsd/aidl/android/os/IStatsManagerService.aidl
index 45ba3a2..dec5634 100644
--- a/apex/statsd/aidl/android/os/IStatsManagerService.aidl
+++ b/apex/statsd/aidl/android/os/IStatsManagerService.aidl
@@ -30,12 +30,19 @@
* memory consumed by the metrics for this configuration approach the pre-defined limits. There
* can be at most one listener per config key.
*
- * Requires Manifest.permission.DUMP.
+ * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS.
*/
- void setDataFetchOperation(long configKey, in PendingIntent pendingIntent,
+ void setDataFetchOperation(long configId, in PendingIntent pendingIntent,
in String packageName);
/**
+ * Removes the data fetch operation for the specified configuration.
+ *
+ * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS.
+ */
+ void removeDataFetchOperation(long configId, in String packageName);
+
+ /**
* Registers the given pending intent for this packagename. This intent is invoked when the
* active status of any of the configs sent by this package changes and will contain a list of
* config ids that are currently active. It also returns the list of configs that are currently
@@ -46,6 +53,13 @@
long[] setActiveConfigsChangedOperation(in PendingIntent pendingIntent, in String packageName);
/**
+ * Removes the active configs changed operation for the specified package name.
+ *
+ * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS.
+ */
+ void removeActiveConfigsChangedOperation(in String packageName);
+
+ /**
* Set the PendingIntent to be used when broadcasting subscriber
* information to the given subscriberId within the given config.
*
@@ -58,8 +72,57 @@
* This function can only be called by the owner (uid) of the config. It must be called each
* time statsd starts. Later calls overwrite previous calls; only one PendingIntent is stored.
*
- * Requires Manifest.permission.DUMP.
+ * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS.
*/
void setBroadcastSubscriber(long configKey, long subscriberId, in PendingIntent pendingIntent,
in String packageName);
+
+ /**
+ * Undoes setBroadcastSubscriber() for the (configKey, subscriberId) pair.
+ * Any broadcasts associated with subscriberId will henceforth not be sent.
+ * No-op if this (configKey, subscriberId) pair was not associated with an PendingIntent.
+ *
+ * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS.
+ */
+ void unsetBroadcastSubscriber(long configKey, long subscriberId, in String packageName);
+
+ /**
+ * Returns the most recently registered experiment IDs.
+ *
+ * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS.
+ */
+ long[] getRegisteredExperimentIds();
+
+ /**
+ * Fetches metadata across statsd. Returns byte array representing wire-encoded proto.
+ *
+ * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS.
+ */
+ byte[] getMetadata(in String packageName);
+
+ /**
+ * Fetches data for the specified configuration key. Returns a byte array representing proto
+ * wire-encoded of ConfigMetricsReportList.
+ *
+ * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS.
+ */
+ byte[] getData(in long key, in String packageName);
+
+ /**
+ * Sets a configuration with the specified config id and subscribes to updates for this
+ * configuration id. Broadcasts will be sent if this configuration needs to be collected.
+ * The configuration must be a wire-encoded StatsdConfig. The receiver for this data is
+ * registered in a separate function.
+ *
+ * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS.
+ */
+ void addConfiguration(in long configId, in byte[] config, in String packageName);
+
+ /**
+ * Removes the configuration with the matching config id. No-op if this config id does not
+ * exist.
+ *
+ * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS.
+ */
+ void removeConfiguration(in long configId, in String packageName);
}
\ No newline at end of file
diff --git a/apex/statsd/aidl/android/os/IStatsd.aidl b/apex/statsd/aidl/android/os/IStatsd.aidl
index cce79fa..c409f51 100644
--- a/apex/statsd/aidl/android/os/IStatsd.aidl
+++ b/apex/statsd/aidl/android/os/IStatsd.aidl
@@ -17,6 +17,7 @@
package android.os;
import android.os.IStatsPullerCallback;
+import android.os.IPendingIntentRef;
import android.os.IPullAtomCallback;
import android.os.ParcelFileDescriptor;
@@ -88,24 +89,24 @@
*
* Requires Manifest.permission.DUMP.
*/
- byte[] getData(in long key, in String packageName);
+ byte[] getData(in long key, int callingUid);
/**
* Fetches metadata across statsd. Returns byte array representing wire-encoded proto.
*
* Requires Manifest.permission.DUMP.
*/
- byte[] getMetadata(in String packageName);
+ byte[] getMetadata();
/**
- * Sets a configuration with the specified config key and subscribes to updates for this
+ * Sets a configuration with the specified config id and subscribes to updates for this
* configuration key. Broadcasts will be sent if this configuration needs to be collected.
* The configuration must be a wire-encoded StatsdConfig. The receiver for this data is
* registered in a separate function.
*
* Requires Manifest.permission.DUMP.
*/
- void addConfiguration(in long configKey, in byte[] config, in String packageName);
+ void addConfiguration(in long configId, in byte[] config, in int callingUid);
/**
* Registers the given pending intent for this config key. This intent is invoked when the
@@ -114,14 +115,15 @@
*
* Requires Manifest.permission.DUMP.
*/
- void setDataFetchOperation(long configKey, in IBinder intentSender, in String packageName);
+ void setDataFetchOperation(long configId, in IPendingIntentRef pendingIntentRef,
+ int callingUid);
/**
* Removes the data fetch operation for the specified configuration.
*
* Requires Manifest.permission.DUMP.
*/
- void removeDataFetchOperation(long configKey, in String packageName);
+ void removeDataFetchOperation(long configId, int callingUid);
/**
* Registers the given pending intent for this packagename. This intent is invoked when the
@@ -131,52 +133,49 @@
*
* Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS.
*/
- long[] setActiveConfigsChangedOperation(in IBinder intentSender, in String packageName);
+ long[] setActiveConfigsChangedOperation(in IPendingIntentRef pendingIntentRef, int callingUid);
/**
* Removes the active configs changed operation for the specified package name.
*
* Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS.
*/
- void removeActiveConfigsChangedOperation(in String packageName);
+ void removeActiveConfigsChangedOperation(int callingUid);
/**
- * Removes the configuration with the matching config key. No-op if this config key does not
+ * Removes the configuration with the matching config id. No-op if this config id does not
* exist.
*
* Requires Manifest.permission.DUMP.
*/
- void removeConfiguration(in long configKey, in String packageName);
+ void removeConfiguration(in long configId, in int callingUid);
/**
- * Set the IIntentSender (i.e. PendingIntent) to be used when broadcasting subscriber
+ * Set the PendingIntentRef to be used when broadcasting subscriber
* information to the given subscriberId within the given config.
*
- * Suppose that the calling uid has added a config with key configKey, and that in this config
+ * Suppose that the calling uid has added a config with key configId, and that in this config
* it is specified that when a particular anomaly is detected, a broadcast should be sent to
- * a BroadcastSubscriber with id subscriberId. This function links the given intentSender with
- * that subscriberId (for that config), so that this intentSender is used to send the broadcast
+ * a BroadcastSubscriber with id subscriberId. This function links the given pendingIntent with
+ * that subscriberId (for that config), so that this pendingIntent is used to send the broadcast
* when the anomaly is detected.
*
* This function can only be called by the owner (uid) of the config. It must be called each
- * time statsd starts. Later calls overwrite previous calls; only one intentSender is stored.
- *
- * intentSender must be convertible into an IntentSender using IntentSender(IBinder)
- * and cannot be null.
+ * time statsd starts. Later calls overwrite previous calls; only one pendingIntent is stored.
*
* Requires Manifest.permission.DUMP.
*/
- void setBroadcastSubscriber(long configKey, long subscriberId, in IBinder intentSender,
- in String packageName);
+ void setBroadcastSubscriber(long configId, long subscriberId, in IPendingIntentRef pir,
+ int callingUid);
/**
- * Undoes setBroadcastSubscriber() for the (configKey, subscriberId) pair.
+ * Undoes setBroadcastSubscriber() for the (configId, subscriberId) pair.
* Any broadcasts associated with subscriberId will henceforth not be sent.
- * No-op if this (configKey, subsriberId) pair was not associated with an IntentSender.
+ * No-op if this (configKey, subscriberId) pair was not associated with an PendingIntentRef.
*
* Requires Manifest.permission.DUMP.
*/
- void unsetBroadcastSubscriber(long configKey, long subscriberId, in String packageName);
+ void unsetBroadcastSubscriber(long configId, long subscriberId, int callingUid);
/**
* Apps can send an atom via this application breadcrumb with the specified label and state for
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanion.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanion.java
index 71b52e2..4383b50 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanion.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanion.java
@@ -16,11 +16,21 @@
package com.android.server.stats;
+import android.app.PendingIntent;
+import android.app.StatsManager;
import android.content.Context;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IPendingIntentRef;
+import android.os.Process;
+import android.os.StatsDimensionsValue;
import android.util.Slog;
import com.android.server.SystemService;
+import java.util.ArrayList;
+import java.util.Arrays;
+
/**
* @hide
*/
@@ -28,6 +38,13 @@
private static final String TAG = "StatsCompanion";
private static final boolean DEBUG = false;
+ static void enforceStatsCompanionPermission(Context context) {
+ if (Binder.getCallingPid() == Process.myPid()) {
+ return;
+ }
+ context.enforceCallingPermission(android.Manifest.permission.STATSCOMPANION, null);
+ }
+
/**
* Lifecycle class for both {@link StatsCompanionService} and {@link StatsManagerService}.
*/
@@ -63,7 +80,97 @@
super.onBootPhase(phase);
if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
mStatsCompanionService.systemReady();
- mStatsManagerService.systemReady();
+ }
+ }
+ }
+
+ /**
+ * Wrapper for {@link PendingIntent}. Allows Statsd to send PendingIntents.
+ */
+ public static class PendingIntentRef extends IPendingIntentRef.Stub {
+
+ private static final String TAG = "PendingIntentRef";
+
+ /**
+ * The last report time is provided with each intent registered to
+ * StatsManager#setFetchReportsOperation. This allows easy de-duping in the receiver if
+ * statsd is requesting the client to retrieve the same statsd data. The last report time
+ * corresponds to the last_report_elapsed_nanos that will provided in the current
+ * ConfigMetricsReport, and this timestamp also corresponds to the
+ * current_report_elapsed_nanos of the most recently obtained ConfigMetricsReport.
+ */
+ private static final String EXTRA_LAST_REPORT_TIME = "android.app.extra.LAST_REPORT_TIME";
+ private static final int CODE_DATA_BROADCAST = 1;
+ private static final int CODE_ACTIVE_CONFIGS_BROADCAST = 1;
+ private static final int CODE_SUBSCRIBER_BROADCAST = 1;
+
+ private final PendingIntent mPendingIntent;
+ private final Context mContext;
+
+ public PendingIntentRef(PendingIntent pendingIntent, Context context) {
+ mPendingIntent = pendingIntent;
+ mContext = context;
+ }
+
+ @Override
+ public void sendDataBroadcast(long lastReportTimeNs) {
+ enforceStatsCompanionPermission(mContext);
+ Intent intent = new Intent();
+ intent.putExtra(EXTRA_LAST_REPORT_TIME, lastReportTimeNs);
+ try {
+ mPendingIntent.send(mContext, CODE_DATA_BROADCAST, intent, null, null);
+ } catch (PendingIntent.CanceledException e) {
+ Slog.w(TAG, "Unable to send PendingIntent");
+ }
+ }
+
+ @Override
+ public void sendActiveConfigsChangedBroadcast(long[] configIds) {
+ enforceStatsCompanionPermission(mContext);
+ Intent intent = new Intent();
+ intent.putExtra(StatsManager.EXTRA_STATS_ACTIVE_CONFIG_KEYS, configIds);
+ try {
+ mPendingIntent.send(mContext, CODE_ACTIVE_CONFIGS_BROADCAST, intent, null, null);
+ if (DEBUG) {
+ Slog.d(TAG, "Sent broadcast with config ids " + Arrays.toString(configIds));
+ }
+ } catch (PendingIntent.CanceledException e) {
+ Slog.w(TAG, "Unable to send active configs changed broadcast using PendingIntent");
+ }
+ }
+
+ @Override
+ public void sendSubscriberBroadcast(long configUid, long configId, long subscriptionId,
+ long subscriptionRuleId, String[] cookies, StatsDimensionsValue dimensionsValue) {
+ enforceStatsCompanionPermission(mContext);
+ Intent intent =
+ new Intent()
+ .putExtra(StatsManager.EXTRA_STATS_CONFIG_UID, configUid)
+ .putExtra(StatsManager.EXTRA_STATS_CONFIG_KEY, configId)
+ .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_ID, subscriptionId)
+ .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_RULE_ID,
+ subscriptionRuleId)
+ .putExtra(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE, dimensionsValue);
+
+ ArrayList<String> cookieList = new ArrayList<>(cookies.length);
+ cookieList.addAll(Arrays.asList(cookies));
+ intent.putStringArrayListExtra(
+ StatsManager.EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES, cookieList);
+
+ if (DEBUG) {
+ Slog.d(TAG,
+ String.format(
+ "Statsd sendSubscriberBroadcast with params {%d %d %d %d %s %s}",
+ configUid, configId, subscriptionId, subscriptionRuleId,
+ Arrays.toString(cookies),
+ dimensionsValue));
+ }
+ try {
+ mPendingIntent.send(mContext, CODE_SUBSCRIBER_BROADCAST, intent, null, null);
+ } catch (PendingIntent.CanceledException e) {
+ Slog.w(TAG,
+ "Unable to send using PendingIntent from uid " + configUid
+ + "; presumably it had been cancelled.");
}
}
}
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
index e4f7300..d57afee 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
@@ -50,7 +50,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -85,11 +84,9 @@
import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
-import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StatFs;
-import android.os.StatsDimensionsValue;
import android.os.StatsLogEventWrapper;
import android.os.SynchronousResultReceiver;
import android.os.SystemClock;
@@ -203,18 +200,6 @@
private static final int PACKAGE_NAME_FIELD_ID = 4;
private static final int INSTALLER_FIELD_ID = 5;
- public static final int CODE_DATA_BROADCAST = 1;
- public static final int CODE_SUBSCRIBER_BROADCAST = 1;
- public static final int CODE_ACTIVE_CONFIGS_BROADCAST = 1;
- /**
- * The last report time is provided with each intent registered to
- * StatsManager#setFetchReportsOperation. This allows easy de-duping in the receiver if
- * statsd is requesting the client to retrieve the same statsd data. The last report time
- * corresponds to the last_report_elapsed_nanos that will provided in the current
- * ConfigMetricsReport, and this timestamp also corresponds to the
- * current_report_elapsed_nanos of the most recently obtained ConfigMetricsReport.
- */
- public static final String EXTRA_LAST_REPORT_TIME = "android.app.extra.LAST_REPORT_TIME";
public static final int DEATH_THRESHOLD = 10;
/**
* Which native processes to snapshot memory for.
@@ -454,72 +439,6 @@
KernelCpuThreadReaderSettingsObserver.getSettingsModifiedReader(mContext);
}
- @Override
- public void sendDataBroadcast(IBinder intentSenderBinder, long lastReportTimeNs) {
- enforceCallingPermission();
- IntentSender intentSender = new IntentSender(intentSenderBinder);
- Intent intent = new Intent();
- intent.putExtra(EXTRA_LAST_REPORT_TIME, lastReportTimeNs);
- try {
- intentSender.sendIntent(mContext, CODE_DATA_BROADCAST, intent, null, null);
- } catch (IntentSender.SendIntentException e) {
- Slog.w(TAG, "Unable to send using IntentSender");
- }
- }
-
- @Override
- public void sendActiveConfigsChangedBroadcast(IBinder intentSenderBinder, long[] configIds) {
- enforceCallingPermission();
- IntentSender intentSender = new IntentSender(intentSenderBinder);
- Intent intent = new Intent();
- intent.putExtra(StatsManager.EXTRA_STATS_ACTIVE_CONFIG_KEYS, configIds);
- try {
- intentSender.sendIntent(mContext, CODE_ACTIVE_CONFIGS_BROADCAST, intent, null, null);
- if (DEBUG) {
- Slog.d(TAG, "Sent broadcast with config ids " + Arrays.toString(configIds));
- }
- } catch (IntentSender.SendIntentException e) {
- Slog.w(TAG, "Unable to send active configs changed broadcast using IntentSender");
- }
- }
-
- @Override
- public void sendSubscriberBroadcast(IBinder intentSenderBinder, long configUid, long configKey,
- long subscriptionId, long subscriptionRuleId, String[] cookies,
- StatsDimensionsValue dimensionsValue) {
- enforceCallingPermission();
- IntentSender intentSender = new IntentSender(intentSenderBinder);
- Intent intent =
- new Intent()
- .putExtra(StatsManager.EXTRA_STATS_CONFIG_UID, configUid)
- .putExtra(StatsManager.EXTRA_STATS_CONFIG_KEY, configKey)
- .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_ID, subscriptionId)
- .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_RULE_ID, subscriptionRuleId)
- .putExtra(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE, dimensionsValue);
-
- ArrayList<String> cookieList = new ArrayList<>(cookies.length);
- for (String cookie : cookies) {
- cookieList.add(cookie);
- }
- intent.putStringArrayListExtra(
- StatsManager.EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES, cookieList);
-
- if (DEBUG) {
- Slog.d(TAG,
- String.format("Statsd sendSubscriberBroadcast with params {%d %d %d %d %s %s}",
- configUid, configKey, subscriptionId, subscriptionRuleId,
- Arrays.toString(cookies),
- dimensionsValue));
- }
- try {
- intentSender.sendIntent(mContext, CODE_SUBSCRIBER_BROADCAST, intent, null, null);
- } catch (IntentSender.SendIntentException e) {
- Slog.w(TAG,
- "Unable to send using IntentSender from uid " + configUid
- + "; presumably it had been cancelled.");
- }
- }
-
private final static int[] toIntArray(List<Integer> list) {
int[] ret = new int[list.size()];
for (int i = 0; i < ret.length; i++) {
@@ -770,7 +689,7 @@
@Override // Binder call
public void setAnomalyAlarm(long timestampMs) {
- enforceCallingPermission();
+ StatsCompanion.enforceStatsCompanionPermission(mContext);
if (DEBUG) Slog.d(TAG, "Setting anomaly alarm for " + timestampMs);
final long callingToken = Binder.clearCallingIdentity();
try {
@@ -786,7 +705,7 @@
@Override // Binder call
public void cancelAnomalyAlarm() {
- enforceCallingPermission();
+ StatsCompanion.enforceStatsCompanionPermission(mContext);
if (DEBUG) Slog.d(TAG, "Cancelling anomaly alarm");
final long callingToken = Binder.clearCallingIdentity();
try {
@@ -798,7 +717,7 @@
@Override // Binder call
public void setAlarmForSubscriberTriggering(long timestampMs) {
- enforceCallingPermission();
+ StatsCompanion.enforceStatsCompanionPermission(mContext);
if (DEBUG) {
Slog.d(TAG,
"Setting periodic alarm in about " + (timestampMs
@@ -817,7 +736,7 @@
@Override // Binder call
public void cancelAlarmForSubscriberTriggering() {
- enforceCallingPermission();
+ StatsCompanion.enforceStatsCompanionPermission(mContext);
if (DEBUG) {
Slog.d(TAG, "Cancelling periodic alarm");
}
@@ -831,7 +750,7 @@
@Override // Binder call
public void setPullingAlarm(long nextPullTimeMs) {
- enforceCallingPermission();
+ StatsCompanion.enforceStatsCompanionPermission(mContext);
if (DEBUG) {
Slog.d(TAG, "Setting pulling alarm in about "
+ (nextPullTimeMs - SystemClock.elapsedRealtime()));
@@ -849,7 +768,7 @@
@Override // Binder call
public void cancelPullingAlarm() {
- enforceCallingPermission();
+ StatsCompanion.enforceStatsCompanionPermission(mContext);
if (DEBUG) {
Slog.d(TAG, "Cancelling pulling alarm");
}
@@ -2455,7 +2374,7 @@
*/
@Override // Binder call
public StatsLogEventWrapper[] pullData(int tagId) {
- enforceCallingPermission();
+ StatsCompanion.enforceStatsCompanionPermission(mContext);
if (DEBUG) {
Slog.d(TAG, "Pulling " + tagId);
}
@@ -2690,12 +2609,11 @@
@Override // Binder call
public void statsdReady() {
- enforceCallingPermission();
+ StatsCompanion.enforceStatsCompanionPermission(mContext);
if (DEBUG) {
Slog.d(TAG, "learned that statsdReady");
}
sayHiToStatsd(); // tell statsd that we're ready too and link to it
- mStatsManagerService.systemReady();
mContext.sendBroadcastAsUser(new Intent(StatsManager.ACTION_STATSD_STARTED)
.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND),
UserHandle.SYSTEM, android.Manifest.permission.DUMP);
@@ -2703,7 +2621,7 @@
@Override
public void triggerUidSnapshot() {
- enforceCallingPermission();
+ StatsCompanion.enforceStatsCompanionPermission(mContext);
synchronized (sStatsdLock) {
final long token = Binder.clearCallingIdentity();
try {
@@ -2716,13 +2634,6 @@
}
}
- private void enforceCallingPermission() {
- if (Binder.getCallingPid() == Process.myPid()) {
- return;
- }
- mContext.enforceCallingPermission(android.Manifest.permission.STATSCOMPANION, null);
- }
-
@Override
public void registerPullAtomCallback(int atomTag, long coolDownNs, long timeoutNs,
int[] additiveFields, IPullAtomCallback pullerCallback) {
@@ -2817,6 +2728,7 @@
+ "alive.");
return;
}
+ mStatsManagerService.statsdReady(sStatsd);
if (DEBUG) Slog.d(TAG, "Saying hi to statsd");
try {
sStatsd.statsCompanionReady();
@@ -2928,6 +2840,7 @@
if (looperStats != null) {
looperStats.reset();
}
+ mStatsManagerService.statsdNotReady();
}
@Override
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
index 24b7978..b27d0f7 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
@@ -16,18 +16,29 @@
package com.android.server.stats;
+import static com.android.server.stats.StatsCompanion.PendingIntentRef;
+
+import android.Manifest;
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.content.Context;
-import android.os.IBinder;
+import android.os.Binder;
import android.os.IStatsManagerService;
import android.os.IStatsd;
+import android.os.Process;
import android.os.RemoteException;
-import android.os.ServiceManager;
+import android.util.ArrayMap;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import java.util.Map;
+import java.util.Objects;
+
/**
+ * Service for {@link android.app.StatsManager}.
+ *
* @hide
*/
public class StatsManagerService extends IStatsManagerService.Stub {
@@ -35,79 +46,441 @@
private static final String TAG = "StatsManagerService";
private static final boolean DEBUG = false;
- @GuardedBy("sStatsdLock")
- private static IStatsd sStatsd;
- private static final Object sStatsdLock = new Object();
+ private static final int STATSD_TIMEOUT_MILLIS = 5000;
+
+ private static final String USAGE_STATS_PERMISSION_OPS = "android:get_usage_stats";
+
+ @GuardedBy("mLock")
+ private IStatsd mStatsd;
+ private final Object mLock = new Object();
private StatsCompanionService mStatsCompanionService;
+ private Context mContext;
+
+ @GuardedBy("mLock")
+ private ArrayMap<ConfigKey, PendingIntentRef> mDataFetchPirMap = new ArrayMap<>();
+ @GuardedBy("mLock")
+ private ArrayMap<Integer, PendingIntentRef> mActiveConfigsPirMap =
+ new ArrayMap<>();
+ @GuardedBy("mLock")
+ private ArrayMap<ConfigKey, ArrayMap<Long, PendingIntentRef>> mBroadcastSubscriberPirMap =
+ new ArrayMap<>();
public StatsManagerService(Context context) {
super();
+ mContext = context;
+ }
+
+ private static class ConfigKey {
+ private int mUid;
+ private long mConfigId;
+
+ ConfigKey(int uid, long configId) {
+ mUid = uid;
+ mConfigId = configId;
+ }
+
+ public int getUid() {
+ return mUid;
+ }
+
+ public long getConfigId() {
+ return mConfigId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mUid, mConfigId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof ConfigKey) {
+ ConfigKey other = (ConfigKey) obj;
+ return this.mUid == other.getUid() && this.mConfigId == other.getConfigId();
+ }
+ return false;
+ }
}
@Override
- public void setDataFetchOperation(long configKey, PendingIntent pendingIntent,
+ public void setDataFetchOperation(long configId, PendingIntent pendingIntent,
String packageName) {
- // no-op
- if (DEBUG) {
- Slog.d(TAG, "setDataFetchOperation");
+ enforceDumpAndUsageStatsPermission(packageName);
+ int callingUid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+ PendingIntentRef pir = new PendingIntentRef(pendingIntent, mContext);
+ ConfigKey key = new ConfigKey(callingUid, configId);
+ // We add the PIR to a map so we can reregister if statsd is unavailable.
+ synchronized (mLock) {
+ mDataFetchPirMap.put(key, pir);
+ }
+ try {
+ IStatsd statsd = getStatsdNonblocking();
+ if (statsd != null) {
+ statsd.setDataFetchOperation(configId, pir, callingUid);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to setDataFetchOperation with statsd");
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void removeDataFetchOperation(long configId, String packageName) {
+ enforceDumpAndUsageStatsPermission(packageName);
+ int callingUid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+ ConfigKey key = new ConfigKey(callingUid, configId);
+ synchronized (mLock) {
+ mDataFetchPirMap.remove(key);
+ }
+ try {
+ IStatsd statsd = getStatsdNonblocking();
+ if (statsd != null) {
+ statsd.removeDataFetchOperation(configId, callingUid);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to removeDataFetchOperation with statsd");
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
@Override
public long[] setActiveConfigsChangedOperation(PendingIntent pendingIntent,
String packageName) {
- // no-op
- if (DEBUG) {
- Slog.d(TAG, "setActiveConfigsChangedOperation");
+ enforceDumpAndUsageStatsPermission(packageName);
+ int callingUid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+ PendingIntentRef pir = new PendingIntentRef(pendingIntent, mContext);
+ // We add the PIR to a map so we can reregister if statsd is unavailable.
+ synchronized (mLock) {
+ mActiveConfigsPirMap.put(callingUid, pir);
}
- return new long[]{};
+ try {
+ IStatsd statsd = getStatsdNonblocking();
+ if (statsd != null) {
+ return statsd.setActiveConfigsChangedOperation(pir, callingUid);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to setActiveConfigsChangedOperation with statsd");
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return new long[] {};
}
@Override
- public void setBroadcastSubscriber(long configKey, long subscriberId,
- PendingIntent pendingIntent, String packageName) {
- //no-op
- if (DEBUG) {
- Slog.d(TAG, "setBroadcastSubscriber");
+ public void removeActiveConfigsChangedOperation(String packageName) {
+ enforceDumpAndUsageStatsPermission(packageName);
+ int callingUid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+ synchronized (mLock) {
+ mActiveConfigsPirMap.remove(callingUid);
}
+ try {
+ IStatsd statsd = getStatsdNonblocking();
+ if (statsd != null) {
+ statsd.removeActiveConfigsChangedOperation(callingUid);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to removeActiveConfigsChangedOperation with statsd");
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void setBroadcastSubscriber(long configId, long subscriberId,
+ PendingIntent pendingIntent, String packageName) {
+ enforceDumpAndUsageStatsPermission(packageName);
+ int callingUid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+ PendingIntentRef pir = new PendingIntentRef(pendingIntent, mContext);
+ ConfigKey key = new ConfigKey(callingUid, configId);
+ // We add the PIR to a map so we can reregister if statsd is unavailable.
+ synchronized (mLock) {
+ ArrayMap<Long, PendingIntentRef> innerMap = mBroadcastSubscriberPirMap
+ .getOrDefault(key, new ArrayMap<>());
+ innerMap.put(subscriberId, pir);
+ mBroadcastSubscriberPirMap.put(key, innerMap);
+ }
+ try {
+ IStatsd statsd = getStatsdNonblocking();
+ if (statsd != null) {
+ statsd.setBroadcastSubscriber(
+ configId, subscriberId, pir, callingUid);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to setBroadcastSubscriber with statsd");
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void unsetBroadcastSubscriber(long configId, long subscriberId, String packageName) {
+ enforceDumpAndUsageStatsPermission(packageName);
+ int callingUid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+ ConfigKey key = new ConfigKey(callingUid, configId);
+ synchronized (mLock) {
+ ArrayMap<Long, PendingIntentRef> innerMap = mBroadcastSubscriberPirMap
+ .getOrDefault(key, new ArrayMap<>());
+ innerMap.remove(subscriberId);
+ if (innerMap.isEmpty()) {
+ mBroadcastSubscriberPirMap.remove(key);
+ }
+ }
+ try {
+ IStatsd statsd = getStatsdNonblocking();
+ if (statsd != null) {
+ statsd.unsetBroadcastSubscriber(configId, subscriberId, callingUid);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to unsetBroadcastSubscriber with statsd");
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public long[] getRegisteredExperimentIds() throws IllegalStateException {
+ enforceDumpAndUsageStatsPermission(null);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ IStatsd statsd = waitForStatsd();
+ if (statsd != null) {
+ return statsd.getRegisteredExperimentIds();
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to getRegisteredExperimentIds with statsd");
+ throw new IllegalStateException(e.getMessage(), e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ throw new IllegalStateException("Failed to connect to statsd to registerExperimentIds");
+ }
+
+ @Override
+ public byte[] getMetadata(String packageName) throws IllegalStateException {
+ enforceDumpAndUsageStatsPermission(packageName);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ IStatsd statsd = waitForStatsd();
+ if (statsd != null) {
+ return statsd.getMetadata();
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to getMetadata with statsd");
+ throw new IllegalStateException(e.getMessage(), e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ throw new IllegalStateException("Failed to connect to statsd to getMetadata");
+ }
+
+ @Override
+ public byte[] getData(long key, String packageName) throws IllegalStateException {
+ enforceDumpAndUsageStatsPermission(packageName);
+ int callingUid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ IStatsd statsd = waitForStatsd();
+ if (statsd != null) {
+ return statsd.getData(key, callingUid);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to getData with statsd");
+ throw new IllegalStateException(e.getMessage(), e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ throw new IllegalStateException("Failed to connect to statsd to getData");
+ }
+
+ @Override
+ public void addConfiguration(long configId, byte[] config, String packageName)
+ throws IllegalStateException {
+ enforceDumpAndUsageStatsPermission(packageName);
+ int callingUid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ IStatsd statsd = waitForStatsd();
+ if (statsd != null) {
+ statsd.addConfiguration(configId, config, callingUid);
+ return;
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to addConfiguration with statsd");
+ throw new IllegalStateException(e.getMessage(), e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ throw new IllegalStateException("Failed to connect to statsd to addConfig");
+ }
+
+ @Override
+ public void removeConfiguration(long configId, String packageName)
+ throws IllegalStateException {
+ enforceDumpAndUsageStatsPermission(packageName);
+ int callingUid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ IStatsd statsd = waitForStatsd();
+ if (statsd != null) {
+ statsd.removeConfiguration(configId, callingUid);
+ return;
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to removeConfiguration with statsd");
+ throw new IllegalStateException(e.getMessage(), e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ throw new IllegalStateException("Failed to connect to statsd to removeConfig");
}
void setStatsCompanionService(StatsCompanionService statsCompanionService) {
mStatsCompanionService = statsCompanionService;
}
- void systemReady() {
- if (DEBUG) {
- Slog.d(TAG, "statsdReady");
+ /**
+ * Checks that the caller has both DUMP and PACKAGE_USAGE_STATS permissions. Also checks that
+ * the caller has USAGE_STATS_PERMISSION_OPS for the specified packageName if it is not null.
+ *
+ * @param packageName The packageName to check USAGE_STATS_PERMISSION_OPS.
+ */
+ private void enforceDumpAndUsageStatsPermission(@Nullable String packageName) {
+ int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
+
+ if (callingPid == Process.myPid()) {
+ return;
}
- setupStatsManagerService();
+
+ mContext.enforceCallingPermission(Manifest.permission.DUMP, null);
+ mContext.enforceCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS, null);
+
+ if (packageName == null) {
+ return;
+ }
+ AppOpsManager appOpsManager = (AppOpsManager) mContext
+ .getSystemService(Context.APP_OPS_SERVICE);
+ switch (appOpsManager.noteOp(USAGE_STATS_PERMISSION_OPS,
+ Binder.getCallingUid(), packageName, null, null)) {
+ case AppOpsManager.MODE_ALLOWED:
+ case AppOpsManager.MODE_DEFAULT:
+ break;
+ default:
+ throw new SecurityException(
+ String.format("UID %d / PID %d lacks app-op %s",
+ callingUid, callingPid, USAGE_STATS_PERMISSION_OPS)
+ );
+ }
}
- private void setupStatsManagerService() {
- synchronized (sStatsdLock) {
- if (sStatsd != null) {
- if (DEBUG) {
- Slog.e(TAG, "Trying to fetch statsd, but it was already fetched",
- new IllegalStateException(
- "sStatsd is not null when being fetched"));
+ /**
+ * Clients should call this if blocking until statsd to be ready is desired
+ *
+ * @return IStatsd object if statsd becomes ready within the timeout, null otherwise.
+ */
+ private IStatsd waitForStatsd() {
+ synchronized (mLock) {
+ if (mStatsd == null) {
+ try {
+ mLock.wait(STATSD_TIMEOUT_MILLIS);
+ } catch (InterruptedException e) {
+ Slog.e(TAG, "wait for statsd interrupted");
}
- return;
}
- sStatsd = IStatsd.Stub.asInterface(ServiceManager.getService("stats"));
- if (sStatsd == null) {
- if (DEBUG) {
- Slog.d(TAG, "Failed to get stats service.");
- }
- return;
+ return mStatsd;
+ }
+ }
+
+ /**
+ * Clients should call this to receive a reference to statsd.
+ *
+ * @return IStatsd object if statsd is ready, null otherwise.
+ */
+ private IStatsd getStatsdNonblocking() {
+ synchronized (mLock) {
+ return mStatsd;
+ }
+ }
+
+ /**
+ * Called from {@link StatsCompanionService}.
+ *
+ * Tells StatsManagerService that Statsd is ready and updates
+ * Statsd with the contents of our local cache.
+ */
+ void statsdReady(IStatsd statsd) {
+ synchronized (mLock) {
+ mStatsd = statsd;
+ mLock.notify();
+ }
+ sayHiToStatsd(statsd);
+ }
+
+ /**
+ * Called from {@link StatsCompanionService}.
+ *
+ * Tells StatsManagerService that Statsd is no longer ready
+ * and we should no longer make binder calls with statsd.
+ */
+ void statsdNotReady() {
+ synchronized (mLock) {
+ mStatsd = null;
+ }
+ }
+
+ private void sayHiToStatsd(IStatsd statsd) {
+ if (statsd == null) {
+ return;
+ }
+ // Since we do not want to make an IPC with the a lock held, we first create local deep
+ // copies of the data with the lock held before iterating through the maps.
+ ArrayMap<ConfigKey, PendingIntentRef> dataFetchCopy;
+ ArrayMap<Integer, PendingIntentRef> activeConfigsChangedCopy;
+ ArrayMap<ConfigKey, ArrayMap<Long, PendingIntentRef>> broadcastSubscriberCopy;
+ synchronized (mLock) {
+ dataFetchCopy = new ArrayMap<>(mDataFetchPirMap);
+ activeConfigsChangedCopy = new ArrayMap<>(mActiveConfigsPirMap);
+ broadcastSubscriberCopy = new ArrayMap<>();
+ for (Map.Entry<ConfigKey, ArrayMap<Long, PendingIntentRef>> entry
+ : mBroadcastSubscriberPirMap.entrySet()) {
+ broadcastSubscriberCopy.put(entry.getKey(), new ArrayMap<>(entry.getValue()));
}
- // Assume statsd is ready since this is called form statscompanion, link to statsd.
+ }
+ for (Map.Entry<ConfigKey, PendingIntentRef> entry : dataFetchCopy.entrySet()) {
+ ConfigKey key = entry.getKey();
try {
- sStatsd.asBinder().linkToDeath((IBinder.DeathRecipient) () -> {
- sStatsd = null;
- }, 0);
+ statsd.setDataFetchOperation(key.getConfigId(), entry.getValue(), key.getUid());
} catch (RemoteException e) {
- Slog.e(TAG, "linkToDeath(StatsdDeathRecipient) failed", e);
+ Slog.e(TAG, "Failed to setDataFetchOperation from pirMap");
+ }
+ }
+ for (Map.Entry<Integer, PendingIntentRef> entry
+ : activeConfigsChangedCopy.entrySet()) {
+ try {
+ statsd.setActiveConfigsChangedOperation(entry.getValue(), entry.getKey());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to setActiveConfigsChangedOperation from pirMap");
+ }
+ }
+ for (Map.Entry<ConfigKey, ArrayMap<Long, PendingIntentRef>> entry
+ : broadcastSubscriberCopy.entrySet()) {
+ for (Map.Entry<Long, PendingIntentRef> subscriberEntry : entry.getValue().entrySet()) {
+ ConfigKey configKey = entry.getKey();
+ try {
+ statsd.setBroadcastSubscriber(configKey.getConfigId(), subscriberEntry.getKey(),
+ subscriberEntry.getValue(), configKey.getUid());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to setBroadcastSubscriber from pirMap");
+ }
}
}
}
diff --git a/api/current.txt b/api/current.txt
index fca72b1..956a892 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -87,10 +87,12 @@
field public static final String INSTALL_PACKAGES = "android.permission.INSTALL_PACKAGES";
field public static final String INSTALL_SHORTCUT = "com.android.launcher.permission.INSTALL_SHORTCUT";
field public static final String INSTANT_APP_FOREGROUND_SERVICE = "android.permission.INSTANT_APP_FOREGROUND_SERVICE";
+ field public static final String INTERACT_ACROSS_PROFILES = "android.permission.INTERACT_ACROSS_PROFILES";
field public static final String INTERNET = "android.permission.INTERNET";
field public static final String KILL_BACKGROUND_PROCESSES = "android.permission.KILL_BACKGROUND_PROCESSES";
field public static final String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
field public static final String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS";
+ field public static final String MANAGE_EXTERNAL_STORAGE = "android.permission.MANAGE_EXTERNAL_STORAGE";
field public static final String MANAGE_OWN_CALLS = "android.permission.MANAGE_OWN_CALLS";
field public static final String MASTER_CLEAR = "android.permission.MASTER_CLEAR";
field public static final String MEDIA_CONTENT_CONTROL = "android.permission.MEDIA_CONTENT_CONTROL";
@@ -477,6 +479,7 @@
field public static final int countDown = 16844059; // 0x101051b
field public static final int country = 16843962; // 0x10104ba
field public static final int cropToPadding = 16843043; // 0x1010123
+ field public static final int crossProfile = 16844303; // 0x101060f
field public static final int cursorVisible = 16843090; // 0x1010152
field public static final int customNavigationLayout = 16843474; // 0x10102d2
field public static final int customTokens = 16843579; // 0x101033b
@@ -6787,6 +6790,7 @@
method @Nullable public java.util.List<java.lang.String> getPermittedAccessibilityServices(@NonNull android.content.ComponentName);
method @Nullable public java.util.List<java.lang.String> getPermittedCrossProfileNotificationListeners(@NonNull android.content.ComponentName);
method @Nullable public java.util.List<java.lang.String> getPermittedInputMethods(@NonNull android.content.ComponentName);
+ method @NonNull public java.util.List<java.lang.String> getProtectedPackages(@NonNull android.content.ComponentName);
method public long getRequiredStrongAuthTimeout(@Nullable android.content.ComponentName);
method public boolean getScreenCaptureDisabled(@Nullable android.content.ComponentName);
method public java.util.List<android.os.UserHandle> getSecondaryUsers(@NonNull android.content.ComponentName);
@@ -6908,6 +6912,7 @@
method public boolean setPermittedInputMethods(@NonNull android.content.ComponentName, java.util.List<java.lang.String>);
method public void setProfileEnabled(@NonNull android.content.ComponentName);
method public void setProfileName(@NonNull android.content.ComponentName, String);
+ method public void setProtectedPackages(@NonNull android.content.ComponentName, @NonNull java.util.List<java.lang.String>);
method public void setRecommendedGlobalProxy(@NonNull android.content.ComponentName, @Nullable android.net.ProxyInfo);
method public void setRequiredStrongAuthTimeout(@NonNull android.content.ComponentName, long);
method public boolean setResetPasswordToken(android.content.ComponentName, byte[]);
@@ -7125,6 +7130,7 @@
field public static final int TAG_ADB_SHELL_CMD = 210002; // 0x33452
field public static final int TAG_ADB_SHELL_INTERACTIVE = 210001; // 0x33451
field public static final int TAG_APP_PROCESS_START = 210005; // 0x33455
+ field public static final int TAG_CAMERA_POLICY_SET = 210034; // 0x33472
field public static final int TAG_CERT_AUTHORITY_INSTALLED = 210029; // 0x3346d
field public static final int TAG_CERT_AUTHORITY_REMOVED = 210030; // 0x3346e
field public static final int TAG_CERT_VALIDATION_FAILURE = 210033; // 0x33471
@@ -25775,8 +25781,8 @@
method @Nullable public android.graphics.Bitmap getImageAtIndex(int);
method @Nullable public android.graphics.Bitmap getPrimaryImage(@NonNull android.media.MediaMetadataRetriever.BitmapParams);
method @Nullable public android.graphics.Bitmap getPrimaryImage();
- method @Nullable public android.graphics.Bitmap getScaledFrameAtTime(long, int, int, int);
- method @Nullable public android.graphics.Bitmap getScaledFrameAtTime(long, int, int, int, @NonNull android.media.MediaMetadataRetriever.BitmapParams);
+ method @Nullable public android.graphics.Bitmap getScaledFrameAtTime(long, int, @IntRange(from=1) int, @IntRange(from=1) int);
+ method @Nullable public android.graphics.Bitmap getScaledFrameAtTime(long, int, @IntRange(from=1) int, @IntRange(from=1) int, @NonNull android.media.MediaMetadataRetriever.BitmapParams);
method public void release();
method public void setDataSource(String) throws java.lang.IllegalArgumentException;
method public void setDataSource(String, java.util.Map<java.lang.String,java.lang.String>) throws java.lang.IllegalArgumentException;
@@ -28675,7 +28681,9 @@
method public int getVideoHeight();
method public float getVideoPixelAspectRatio();
method public int getVideoWidth();
+ method public boolean isAudioDescription();
method public boolean isEncrypted();
+ method public boolean isHardOfHearing();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.TvTrackInfo> CREATOR;
field public static final int TYPE_AUDIO = 0; // 0x0
@@ -28687,10 +28695,12 @@
ctor public TvTrackInfo.Builder(int, @NonNull String);
method public android.media.tv.TvTrackInfo build();
method public android.media.tv.TvTrackInfo.Builder setAudioChannelCount(int);
+ method @NonNull public android.media.tv.TvTrackInfo.Builder setAudioDescription(boolean);
method public android.media.tv.TvTrackInfo.Builder setAudioSampleRate(int);
method public android.media.tv.TvTrackInfo.Builder setDescription(CharSequence);
method @NonNull public android.media.tv.TvTrackInfo.Builder setEncrypted(boolean);
method public android.media.tv.TvTrackInfo.Builder setExtra(android.os.Bundle);
+ method @NonNull public android.media.tv.TvTrackInfo.Builder setHardOfHearing(boolean);
method public android.media.tv.TvTrackInfo.Builder setLanguage(String);
method public android.media.tv.TvTrackInfo.Builder setVideoActiveFormatDescription(byte);
method public android.media.tv.TvTrackInfo.Builder setVideoFrameRate(float);
@@ -29003,6 +29013,37 @@
field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortal> CREATOR;
}
+ public class ConnectivityDiagnosticsManager {
+ method public void registerConnectivityDiagnosticsCallback(@NonNull android.net.NetworkRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback);
+ method public void unregisterConnectivityDiagnosticsCallback(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback);
+ field public static final int DETECTION_METHOD_DNS_EVENTS = 1; // 0x1
+ field public static final int DETECTION_METHOD_TCP_METRICS = 2; // 0x2
+ }
+
+ public abstract static class ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback {
+ ctor public ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback();
+ method public void onConnectivityReport(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityReport);
+ method public void onDataStallSuspected(@NonNull android.net.ConnectivityDiagnosticsManager.DataStallReport);
+ method public void onNetworkConnectivityReported(@NonNull android.net.Network, boolean);
+ }
+
+ public static class ConnectivityDiagnosticsManager.ConnectivityReport {
+ ctor public ConnectivityDiagnosticsManager.ConnectivityReport(@NonNull android.net.Network, long, @NonNull android.net.LinkProperties, @NonNull android.net.NetworkCapabilities, @NonNull android.os.PersistableBundle);
+ field @NonNull public final android.os.PersistableBundle additionalInfo;
+ field @NonNull public final android.net.LinkProperties linkProperties;
+ field @NonNull public final android.net.Network network;
+ field @NonNull public final android.net.NetworkCapabilities networkCapabilities;
+ field public final long reportTimestamp;
+ }
+
+ public static class ConnectivityDiagnosticsManager.DataStallReport {
+ ctor public ConnectivityDiagnosticsManager.DataStallReport(@NonNull android.net.Network, long, int, @NonNull android.os.PersistableBundle);
+ field public final int detectionMethod;
+ field @NonNull public final android.net.Network network;
+ field public final long reportTimestamp;
+ field @NonNull public final android.os.PersistableBundle stallDetails;
+ }
+
public class ConnectivityManager {
method public void addDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
method public boolean bindProcessToNetwork(@Nullable android.net.Network);
@@ -29236,6 +29277,7 @@
method public boolean addRoute(@NonNull android.net.RouteInfo);
method public void clear();
method public int describeContents();
+ method @Nullable public java.net.Inet4Address getDhcpServerAddress();
method @NonNull public java.util.List<java.net.InetAddress> getDnsServers();
method @Nullable public String getDomains();
method @Nullable public android.net.ProxyInfo getHttpProxy();
@@ -29247,6 +29289,7 @@
method @NonNull public java.util.List<android.net.RouteInfo> getRoutes();
method public boolean isPrivateDnsActive();
method public boolean isWakeOnLanSupported();
+ method public void setDhcpServerAddress(@Nullable java.net.Inet4Address);
method public void setDnsServers(@NonNull java.util.Collection<java.net.InetAddress>);
method public void setDomains(@Nullable String);
method public void setHttpProxy(@Nullable android.net.ProxyInfo);
@@ -29471,6 +29514,7 @@
method public android.net.NetworkRequest.Builder addCapability(int);
method public android.net.NetworkRequest.Builder addTransportType(int);
method public android.net.NetworkRequest build();
+ method @NonNull public android.net.NetworkRequest.Builder clearCapabilities();
method public android.net.NetworkRequest.Builder removeCapability(int);
method public android.net.NetworkRequest.Builder removeTransportType(int);
method public android.net.NetworkRequest.Builder setNetworkSpecifier(String);
@@ -30235,6 +30279,7 @@
@Deprecated public class WifiConfiguration implements android.os.Parcelable {
ctor @Deprecated public WifiConfiguration();
+ ctor @Deprecated public WifiConfiguration(@NonNull android.net.wifi.WifiConfiguration);
method public int describeContents();
method @Deprecated public android.net.ProxyInfo getHttpProxy();
method @Deprecated @NonNull public String getKey();
@@ -30268,6 +30313,7 @@
@Deprecated public static class WifiConfiguration.AuthAlgorithm {
field @Deprecated public static final int LEAP = 2; // 0x2
field @Deprecated public static final int OPEN = 0; // 0x0
+ field @Deprecated public static final int SAE = 3; // 0x3
field @Deprecated public static final int SHARED = 1; // 0x1
field @Deprecated public static final String[] strings;
field @Deprecated public static final String varName = "auth_alg";
@@ -30345,6 +30391,7 @@
method public String getPlmn();
method public String getRealm();
method @Deprecated public String getSubjectMatch();
+ method public boolean isAuthenticationSimBased();
method public void setAltSubjectMatch(String);
method public void setAnonymousIdentity(String);
method public void setCaCertificate(@Nullable java.security.cert.X509Certificate);
@@ -35669,7 +35716,9 @@
method public int describeContents();
method @Nullable public android.os.PersistableBundle getPersistableBundle(@Nullable String);
method public void putPersistableBundle(@Nullable String, @Nullable android.os.PersistableBundle);
+ method @NonNull public static android.os.PersistableBundle readFromStream(@NonNull java.io.InputStream) throws java.io.IOException;
method public void writeToParcel(android.os.Parcel, int);
+ method public void writeToStream(@NonNull java.io.OutputStream) throws java.io.IOException;
field @NonNull public static final android.os.Parcelable.Creator<android.os.PersistableBundle> CREATOR;
field public static final android.os.PersistableBundle EMPTY;
}
@@ -36089,6 +36138,37 @@
method public int getUserOperationResult();
}
+ public final class VibrationAttributes implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getFlags();
+ method public int getUsage();
+ method public int getUsageClass();
+ method public boolean isFlagSet(int);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.os.VibrationAttributes> CREATOR;
+ field public static final int FLAG_BYPASS_INTERRUPTION_POLICY = 1; // 0x1
+ field public static final int USAGE_ALARM = 17; // 0x11
+ field public static final int USAGE_CLASS_ALARM = 1; // 0x1
+ field public static final int USAGE_CLASS_FEEDBACK = 2; // 0x2
+ field public static final int USAGE_CLASS_MASK = 15; // 0xf
+ field public static final int USAGE_CLASS_UNKNOWN = 0; // 0x0
+ field public static final int USAGE_COMMUNICATION_REQUEST = 65; // 0x41
+ field public static final int USAGE_HARDWARE_FEEDBACK = 50; // 0x32
+ field public static final int USAGE_NOTIFICATION = 49; // 0x31
+ field public static final int USAGE_PHYSICAL_EMULATION = 34; // 0x22
+ field public static final int USAGE_RINGTONE = 33; // 0x21
+ field public static final int USAGE_TOUCH = 18; // 0x12
+ field public static final int USAGE_UNKNOWN = 0; // 0x0
+ }
+
+ public static final class VibrationAttributes.Builder {
+ ctor public VibrationAttributes.Builder();
+ ctor public VibrationAttributes.Builder(@Nullable android.os.VibrationAttributes);
+ method @NonNull public android.os.VibrationAttributes build();
+ method @NonNull public android.os.VibrationAttributes.Builder replaceFlags(int);
+ method @NonNull public android.os.VibrationAttributes.Builder setUsage(int);
+ }
+
public abstract class VibrationEffect implements android.os.Parcelable {
method public static android.os.VibrationEffect createOneShot(long, int);
method @NonNull public static android.os.VibrationEffect createPredefined(int);
@@ -41782,6 +41862,7 @@
}
public static final class Dataset.Builder {
+ ctor public Dataset.Builder(@NonNull android.widget.RemoteViews, @NonNull android.service.autofill.InlinePresentation);
ctor public Dataset.Builder(@NonNull android.widget.RemoteViews);
ctor public Dataset.Builder();
method @NonNull public android.service.autofill.Dataset build();
@@ -41791,6 +41872,8 @@
method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @NonNull android.widget.RemoteViews);
method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern);
method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.widget.RemoteViews);
+ method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @NonNull android.widget.RemoteViews, @NonNull android.service.autofill.InlinePresentation);
+ method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.widget.RemoteViews, @NonNull android.service.autofill.InlinePresentation);
}
public final class DateTransformation implements android.os.Parcelable android.service.autofill.Transformation {
@@ -41879,7 +41962,6 @@
public static final class FillResponse.Builder {
ctor public FillResponse.Builder();
method @NonNull public android.service.autofill.FillResponse.Builder addDataset(@Nullable android.service.autofill.Dataset);
- method @NonNull public android.service.autofill.FillResponse.Builder addInlineSuggestionSlice(@NonNull android.app.slice.Slice);
method @NonNull public android.service.autofill.FillResponse build();
method @NonNull public android.service.autofill.FillResponse.Builder disableAutofill(long);
method @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.widget.RemoteViews);
@@ -41908,6 +41990,15 @@
method public android.service.autofill.ImageTransformation build();
}
+ public final class InlinePresentation implements android.os.Parcelable {
+ ctor public InlinePresentation(@NonNull android.app.slice.Slice, @NonNull android.view.inline.InlinePresentationSpec);
+ method public int describeContents();
+ method @NonNull public android.view.inline.InlinePresentationSpec getInlinePresentationSpec();
+ method @NonNull public android.app.slice.Slice getSlice();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.service.autofill.InlinePresentation> CREATOR;
+ }
+
public final class LuhnChecksumValidator implements android.os.Parcelable android.service.autofill.Validator {
ctor public LuhnChecksumValidator(@NonNull android.view.autofill.AutofillId...);
method public int describeContents();
@@ -43123,6 +43214,7 @@
method public static void execve(String, String[], String[]) throws android.system.ErrnoException;
method public static void fchmod(java.io.FileDescriptor, int) throws android.system.ErrnoException;
method public static void fchown(java.io.FileDescriptor, int, int) throws android.system.ErrnoException;
+ method public static int fcntlInt(@NonNull java.io.FileDescriptor, int, int) throws android.system.ErrnoException;
method public static void fdatasync(java.io.FileDescriptor) throws android.system.ErrnoException;
method public static android.system.StructStat fstat(java.io.FileDescriptor) throws android.system.ErrnoException;
method public static android.system.StructStatVfs fstatvfs(java.io.FileDescriptor) throws android.system.ErrnoException;
@@ -45543,6 +45635,7 @@
method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo);
method public void onMessageWaitingIndicatorChanged(boolean);
method @RequiresPermission("android.permission.MODIFY_PHONE_STATE") public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState);
+ method public void onRegistrationFailed(@NonNull android.telephony.CellIdentity, @NonNull String, int, int, int);
method public void onServiceStateChanged(android.telephony.ServiceState);
method @Deprecated public void onSignalStrengthChanged(int);
method public void onSignalStrengthsChanged(android.telephony.SignalStrength);
@@ -45560,6 +45653,7 @@
field public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 4; // 0x4
field public static final int LISTEN_NONE = 0; // 0x0
field @RequiresPermission("android.permission.MODIFY_PHONE_STATE") public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000
+ field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int LISTEN_REGISTRATION_FAILURE = 1073741824; // 0x40000000
field public static final int LISTEN_SERVICE_STATE = 1; // 0x1
field @Deprecated public static final int LISTEN_SIGNAL_STRENGTH = 2; // 0x2
field public static final int LISTEN_SIGNAL_STRENGTHS = 256; // 0x100
@@ -45635,6 +45729,7 @@
method public int getLevel();
method @Deprecated public boolean isGsm();
method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.SignalStrength> CREATOR;
field public static final int INVALID = 2147483647; // 0x7fffffff
}
@@ -45643,7 +45738,7 @@
method @Nullable public String createAppSpecificSmsTokenWithPackageInfo(@Nullable String, @NonNull android.app.PendingIntent);
method public java.util.ArrayList<java.lang.String> divideMessage(String);
method public void downloadMultimediaMessage(android.content.Context, String, android.net.Uri, android.os.Bundle, android.app.PendingIntent);
- method public android.os.Bundle getCarrierConfigValues();
+ method @Nullable public android.os.Bundle getCarrierConfigValues();
method public static android.telephony.SmsManager getDefault();
method public static int getDefaultSmsSubscriptionId();
method public static android.telephony.SmsManager getSmsManagerForSubscriptionId(int);
diff --git a/api/system-current.txt b/api/system-current.txt
index 2edef24..4f26261 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -90,7 +90,6 @@
field public static final String INSTALL_PACKAGE_UPDATES = "android.permission.INSTALL_PACKAGE_UPDATES";
field public static final String INSTALL_SELF_UPDATES = "android.permission.INSTALL_SELF_UPDATES";
field public static final String INTENT_FILTER_VERIFICATION_AGENT = "android.permission.INTENT_FILTER_VERIFICATION_AGENT";
- field public static final String INTERACT_ACROSS_PROFILES = "android.permission.INTERACT_ACROSS_PROFILES";
field public static final String INTERACT_ACROSS_USERS = "android.permission.INTERACT_ACROSS_USERS";
field public static final String INTERACT_ACROSS_USERS_FULL = "android.permission.INTERACT_ACROSS_USERS_FULL";
field public static final String INTERNAL_SYSTEM_WINDOW = "android.permission.INTERNAL_SYSTEM_WINDOW";
@@ -128,6 +127,7 @@
field public static final String MODIFY_PARENTAL_CONTROLS = "android.permission.MODIFY_PARENTAL_CONTROLS";
field public static final String MODIFY_QUIET_MODE = "android.permission.MODIFY_QUIET_MODE";
field public static final String MOVE_PACKAGE = "android.permission.MOVE_PACKAGE";
+ field public static final String NETWORK_AIRPLANE_MODE = "android.permission.NETWORK_AIRPLANE_MODE";
field public static final String NETWORK_CARRIER_PROVISIONING = "android.permission.NETWORK_CARRIER_PROVISIONING";
field public static final String NETWORK_FACTORY = "android.permission.NETWORK_FACTORY";
field public static final String NETWORK_MANAGED_PROVISIONING = "android.permission.NETWORK_MANAGED_PROVISIONING";
@@ -363,6 +363,7 @@
field public static final String OPSTR_GET_ACCOUNTS = "android:get_accounts";
field public static final String OPSTR_GPS = "android:gps";
field public static final String OPSTR_INSTANT_APP_START_FOREGROUND = "android:instant_app_start_foreground";
+ field public static final String OPSTR_INTERACT_ACROSS_PROFILES = "android:interact_across_profiles";
field public static final String OPSTR_LEGACY_STORAGE = "android:legacy_storage";
field public static final String OPSTR_MANAGE_EXTERNAL_STORAGE = "android:manage_external_storage";
field public static final String OPSTR_MANAGE_IPSEC_TUNNELS = "android:manage_ipsec_tunnels";
@@ -412,6 +413,16 @@
field public static final int UID_STATE_TOP = 200; // 0xc8
}
+ public static final class AppOpsManager.HistoricalFeatureOps implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public String getFeatureId();
+ method @Nullable public android.app.AppOpsManager.HistoricalOp getOp(@NonNull String);
+ method @NonNull public android.app.AppOpsManager.HistoricalOp getOpAt(@IntRange(from=0) int);
+ method @IntRange(from=0) public int getOpCount();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalFeatureOps> CREATOR;
+ }
+
public static final class AppOpsManager.HistoricalOp implements android.os.Parcelable {
method public int describeContents();
method public long getAccessCount(int, int, int);
@@ -445,6 +456,7 @@
public static final class AppOpsManager.HistoricalOpsRequest.Builder {
ctor public AppOpsManager.HistoricalOpsRequest.Builder(long, long);
method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest build();
+ method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setFeatureId(@Nullable String);
method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setFlags(int);
method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setOpNames(@Nullable java.util.List<java.lang.String>);
method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setPackageName(@Nullable String);
@@ -453,6 +465,9 @@
public static final class AppOpsManager.HistoricalPackageOps implements android.os.Parcelable {
method public int describeContents();
+ method @IntRange(from=0) public int getFeatureCount();
+ method @Nullable public android.app.AppOpsManager.HistoricalFeatureOps getFeatureOps(@NonNull String);
+ method @NonNull public android.app.AppOpsManager.HistoricalFeatureOps getFeatureOpsAt(@IntRange(from=0) int);
method @Nullable public android.app.AppOpsManager.HistoricalOp getOp(@NonNull String);
method @NonNull public android.app.AppOpsManager.HistoricalOp getOpAt(@IntRange(from=0) int);
method @IntRange(from=0) public int getOpCount();
@@ -662,6 +677,7 @@
public class StatusBarManager {
method @NonNull @RequiresPermission(android.Manifest.permission.STATUS_BAR) public android.app.StatusBarManager.DisableInfo getDisableInfo();
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSetup(boolean);
+ method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSimNetworkLock(boolean);
}
public static final class StatusBarManager.DisableInfo {
@@ -784,6 +800,7 @@
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioned();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioningConfigApplied();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isManagedKiosk();
+ method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isOrganizationOwnedDeviceWithManagedProfile();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isUnattendedManagedKiosk();
method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long);
method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long, boolean);
@@ -1267,6 +1284,7 @@
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void addRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean addRoleHolderFromController(@NonNull String, @NonNull String);
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void clearRoleHoldersAsUser(@NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method @Nullable public String getDefaultSmsPackage(int);
method @NonNull @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public java.util.List<java.lang.String> getHeldRolesFromController(@NonNull String);
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHolders(@NonNull String);
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle);
@@ -1516,6 +1534,14 @@
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
}
+ public final class BluetoothHidHost implements android.bluetooth.BluetoothProfile {
+ method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice);
+ method public int getConnectionState(@Nullable android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice, int);
+ field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED";
+ }
+
public final class BluetoothPan implements android.bluetooth.BluetoothProfile {
method protected void finalize();
method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
@@ -1533,6 +1559,7 @@
public class BluetoothPbap implements android.bluetooth.BluetoothProfile {
method public int getConnectionState(@Nullable android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED";
}
@@ -1658,6 +1685,7 @@
field public static final String EUICC_CARD_SERVICE = "euicc_card";
field public static final String HDMI_CONTROL_SERVICE = "hdmi_control";
field public static final String NETD_SERVICE = "netd";
+ field public static final String NETWORK_POLICY_SERVICE = "netpolicy";
field public static final String NETWORK_SCORE_SERVICE = "network_score";
field public static final String OEM_LOCK_SERVICE = "oem_lock";
field public static final String PERMISSION_SERVICE = "permission";
@@ -1671,6 +1699,7 @@
field public static final String TELEPHONY_REGISTRY_SERVICE = "telephony_registry";
field public static final String TETHERING_SERVICE = "tethering";
field public static final String VR_SERVICE = "vrmanager";
+ field public static final String WIFI_COND_SERVICE = "wificond";
field @Deprecated public static final String WIFI_RTT_SERVICE = "rttmanager";
field public static final String WIFI_SCANNING_SERVICE = "wifiscanner";
}
@@ -1696,6 +1725,7 @@
field public static final String ACTION_INSTALL_INSTANT_APP_PACKAGE = "android.intent.action.INSTALL_INSTANT_APP_PACKAGE";
field public static final String ACTION_INSTANT_APP_RESOLVER_SETTINGS = "android.intent.action.INSTANT_APP_RESOLVER_SETTINGS";
field public static final String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION";
+ field public static final String ACTION_LOAD_DATA = "android.intent.action.LOAD_DATA";
field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_MANAGE_APP_PERMISSION = "android.intent.action.MANAGE_APP_PERMISSION";
field public static final String ACTION_MANAGE_APP_PERMISSIONS = "android.intent.action.MANAGE_APP_PERMISSIONS";
field @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public static final String ACTION_MANAGE_DEFAULT_APP = "android.intent.action.MANAGE_DEFAULT_APP";
@@ -2114,6 +2144,7 @@
field public static final String EXTRA_REQUEST_PERMISSIONS_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES";
field public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS";
field public static final String FEATURE_BROADCAST_RADIO = "android.hardware.broadcastradio";
+ field public static final String FEATURE_REBOOT_ESCROW = "android.hardware.reboot_escrow";
field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock";
field public static final int FLAG_PERMISSION_APPLY_RESTRICTION = 16384; // 0x4000
field public static final int FLAG_PERMISSION_GRANTED_BY_DEFAULT = 32; // 0x20
@@ -4138,6 +4169,14 @@
}
+package android.media.audiofx {
+
+ public class AudioEffect {
+ ctor @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public AudioEffect(@NonNull java.util.UUID, @NonNull android.media.AudioDeviceAddress);
+ }
+
+}
+
package android.media.audiopolicy {
public class AudioMix {
@@ -4330,6 +4369,15 @@
package android.media.tv {
+ public final class DvbDeviceInfo implements android.os.Parcelable {
+ ctor public DvbDeviceInfo(int, int);
+ method public int describeContents();
+ method public int getAdapterId();
+ method public int getDeviceId();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.DvbDeviceInfo> CREATOR;
+ }
+
public final class TvContentRatingSystemInfo implements android.os.Parcelable {
method public static android.media.tv.TvContentRatingSystemInfo createTvContentRatingSystemInfo(int, android.content.pm.ApplicationInfo);
method public int describeContents();
@@ -4444,12 +4492,14 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) public void addBlockedRating(@NonNull android.media.tv.TvContentRating);
method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public boolean captureFrame(String, android.view.Surface, android.media.tv.TvStreamConfig);
method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public java.util.List<android.media.tv.TvStreamConfig> getAvailableTvStreamConfigList(String);
+ method @NonNull @RequiresPermission("android.permission.DVB_DEVICE") public java.util.List<android.media.tv.DvbDeviceInfo> getDvbDeviceList();
method @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) public java.util.List<android.media.tv.TvInputHardwareInfo> getHardwareList();
method @RequiresPermission(android.Manifest.permission.READ_CONTENT_RATING_SYSTEMS) public java.util.List<android.media.tv.TvContentRatingSystemInfo> getTvContentRatingSystemList();
method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public boolean isSingleSessionActive();
method @RequiresPermission(android.Manifest.permission.NOTIFY_TV_INPUTS) public void notifyPreviewProgramAddedToWatchNext(String, long, long);
method @RequiresPermission(android.Manifest.permission.NOTIFY_TV_INPUTS) public void notifyPreviewProgramBrowsableDisabled(String, long);
method @RequiresPermission(android.Manifest.permission.NOTIFY_TV_INPUTS) public void notifyWatchNextProgramBrowsableDisabled(String, long);
+ method @Nullable @RequiresPermission("android.permission.DVB_DEVICE") public android.os.ParcelFileDescriptor openDvbDevice(@NonNull android.media.tv.DvbDeviceInfo, int);
method @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) public void releaseTvInputHardware(int, android.media.tv.TvInputManager.Hardware);
method @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) public void removeBlockedRating(@NonNull android.media.tv.TvContentRating);
method @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) public void setParentalControlsEnabled(boolean);
@@ -4538,6 +4588,36 @@
public class Tuner.Descrambler {
}
+ public class Tuner.Filter {
+ }
+
+ public static interface Tuner.FilterCallback {
+ method public void onFilterEvent(@NonNull android.media.tv.tuner.Tuner.Filter, @NonNull android.media.tv.tuner.filter.FilterEvent[]);
+ method public void onFilterStatusChanged(@NonNull android.media.tv.tuner.Tuner.Filter, int);
+ }
+
+ public final class TunerConstants {
+ field public static final int FILTER_STATUS_DATA_READY = 1; // 0x1
+ field public static final int FILTER_STATUS_HIGH_WATER = 4; // 0x4
+ field public static final int FILTER_STATUS_LOW_WATER = 2; // 0x2
+ field public static final int FILTER_STATUS_OVERFLOW = 8; // 0x8
+ }
+
+}
+
+package android.media.tv.tuner.filter {
+
+ public abstract class FilterEvent {
+ ctor public FilterEvent();
+ }
+
+ public class SectionEvent extends android.media.tv.tuner.filter.FilterEvent {
+ method public int getDataLength();
+ method public int getSectionNumber();
+ method public int getTableId();
+ method public int getVersion();
+ }
+
}
package android.metrics {
@@ -4601,7 +4681,7 @@
method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEntitlementResultListener);
method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
- method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_AIRPLANE_MODE, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean);
method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public boolean shouldAvoidBadWifi();
method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull android.os.Bundle);
method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
@@ -4746,6 +4826,7 @@
public final class MatchAllNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
ctor public MatchAllNetworkSpecifier();
method public int describeContents();
+ method public boolean satisfiedBy(android.net.NetworkSpecifier);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.MatchAllNetworkSpecifier> CREATOR;
}
@@ -4809,6 +4890,12 @@
method public void updateScores(@NonNull java.util.List<android.net.ScoredNetwork>);
}
+ public abstract class NetworkSpecifier {
+ method public void assertValidFromUid(int);
+ method @Nullable public android.net.NetworkSpecifier redact();
+ method public abstract boolean satisfiedBy(@Nullable android.net.NetworkSpecifier);
+ }
+
public class NetworkStack {
field public static final String PERMISSION_MAINLINE_NETWORK_STACK = "android.permission.MAINLINE_NETWORK_STACK";
}
@@ -4879,6 +4966,7 @@
public final class StringNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
ctor public StringNetworkSpecifier(@NonNull String);
method public int describeContents();
+ method public boolean satisfiedBy(android.net.NetworkSpecifier);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.StringNetworkSpecifier> CREATOR;
field @NonNull public final String specifier;
@@ -5626,8 +5714,32 @@
}
public class ScanResult implements android.os.Parcelable {
+ field public static final int CIPHER_CCMP = 3; // 0x3
+ field public static final int CIPHER_GCMP_256 = 4; // 0x4
+ field public static final int CIPHER_NONE = 0; // 0x0
+ field public static final int CIPHER_NO_GROUP_ADDRESSED = 1; // 0x1
+ field public static final int CIPHER_SMS4 = 5; // 0x5
+ field public static final int CIPHER_TKIP = 2; // 0x2
+ field public static final int KEY_MGMT_EAP = 2; // 0x2
+ field public static final int KEY_MGMT_EAP_SHA256 = 6; // 0x6
+ field public static final int KEY_MGMT_EAP_SUITE_B_192 = 10; // 0xa
+ field public static final int KEY_MGMT_FT_EAP = 4; // 0x4
+ field public static final int KEY_MGMT_FT_PSK = 3; // 0x3
+ field public static final int KEY_MGMT_FT_SAE = 11; // 0xb
+ field public static final int KEY_MGMT_NONE = 0; // 0x0
+ field public static final int KEY_MGMT_OSEN = 7; // 0x7
+ field public static final int KEY_MGMT_OWE = 9; // 0x9
+ field public static final int KEY_MGMT_OWE_TRANSITION = 12; // 0xc
+ field public static final int KEY_MGMT_PSK = 1; // 0x1
+ field public static final int KEY_MGMT_PSK_SHA256 = 5; // 0x5
+ field public static final int KEY_MGMT_SAE = 8; // 0x8
field public static final int KEY_MGMT_WAPI_CERT = 14; // 0xe
field public static final int KEY_MGMT_WAPI_PSK = 13; // 0xd
+ field public static final int PROTOCOL_NONE = 0; // 0x0
+ field public static final int PROTOCOL_OSEN = 3; // 0x3
+ field public static final int PROTOCOL_RSN = 2; // 0x2
+ field public static final int PROTOCOL_WAPI = 4; // 0x4
+ field public static final int PROTOCOL_WPA = 1; // 0x1
}
public final class SoftApCapability implements android.os.Parcelable {
@@ -5638,6 +5750,7 @@
field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.SoftApCapability> CREATOR;
field public static final int SOFTAP_FEATURE_ACS_OFFLOAD = 1; // 0x1
field public static final int SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT = 2; // 0x2
+ field public static final int SOFTAP_FEATURE_WPA3_SAE = 4; // 0x4
}
public final class SoftApConfiguration implements android.os.Parcelable {
@@ -5646,6 +5759,7 @@
method @Nullable public android.net.MacAddress getBssid();
method public int getChannel();
method public int getMaxNumberOfClients();
+ method @Nullable public String getPassphrase();
method public int getSecurityType();
method @Nullable public String getSsid();
method @Nullable public String getWpa2Passphrase();
@@ -5658,6 +5772,8 @@
field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.SoftApConfiguration> CREATOR;
field public static final int SECURITY_TYPE_OPEN = 0; // 0x0
field public static final int SECURITY_TYPE_WPA2_PSK = 1; // 0x1
+ field public static final int SECURITY_TYPE_WPA3_SAE = 3; // 0x3
+ field public static final int SECURITY_TYPE_WPA3_SAE_TRANSITION = 2; // 0x2
}
public static final class SoftApConfiguration.Builder {
@@ -5669,6 +5785,7 @@
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setChannel(int, int);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setHiddenSsid(boolean);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setMaxNumberOfClients(int);
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setPassphrase(@Nullable String, int);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setSsid(@Nullable String);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setWpa2Passphrase(@Nullable String);
}
@@ -5813,6 +5930,7 @@
public class WifiManager {
method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void addOnWifiUsabilityStatsListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void allowAutojoin(int, boolean);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void allowAutojoinPasspoint(@NonNull String, boolean);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void connect(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void connect(int, @Nullable android.net.wifi.WifiManager.ActionListener);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void disable(int, @Nullable android.net.wifi.WifiManager.ActionListener);
@@ -5889,6 +6007,7 @@
field public static final String EXTRA_OSU_NETWORK = "android.net.wifi.extra.OSU_NETWORK";
field public static final String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state";
field public static final String EXTRA_URL = "android.net.wifi.extra.URL";
+ field public static final String EXTRA_WIFI_AP_FAILURE_REASON = "android.net.wifi.extra.WIFI_AP_FAILURE_REASON";
field public static final String EXTRA_WIFI_AP_INTERFACE_NAME = "android.net.wifi.extra.WIFI_AP_INTERFACE_NAME";
field public static final String EXTRA_WIFI_AP_MODE = "android.net.wifi.extra.WIFI_AP_MODE";
field public static final String EXTRA_WIFI_AP_STATE = "wifi_state";
@@ -5967,6 +6086,10 @@
field public int numUsage;
}
+ public final class WifiNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
+ method public boolean satisfiedBy(android.net.NetworkSpecifier);
+ }
+
public static final class WifiNetworkSuggestion.Builder {
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_CARRIER_PROVISIONING) public android.net.wifi.WifiNetworkSuggestion.Builder setCarrierId(int);
}
@@ -6153,6 +6276,10 @@
method @Deprecated public android.net.NetworkSpecifier createNetworkSpecifierPmk(@NonNull android.net.wifi.aware.PeerHandle, @NonNull byte[]);
}
+ public final class WifiAwareNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
+ method public boolean satisfiedBy(android.net.NetworkSpecifier);
+ }
+
public class WifiAwareSession implements java.lang.AutoCloseable {
method public android.net.NetworkSpecifier createNetworkSpecifierPmk(int, @NonNull byte[], @NonNull byte[]);
}
@@ -6169,6 +6296,10 @@
field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.OsuProvider> CREATOR;
}
+ public final class PasspointConfiguration implements android.os.Parcelable {
+ method public boolean isAutoJoinEnabled();
+ }
+
public abstract class ProvisioningCallback {
ctor public ProvisioningCallback();
method public abstract void onProvisioningComplete();
@@ -6367,6 +6498,7 @@
method @NonNull public java.util.List<android.net.wifi.wificond.NativeScanResult> getScanResults(@NonNull String, int);
method @Nullable public android.net.wifi.wificond.WifiCondManager.TxPacketCounters getTxPacketCounters(@NonNull String);
method public boolean initialize(@NonNull Runnable);
+ method @Nullable public static android.net.wifi.wificond.WifiCondManager.OemSecurityType parseOemSecurityTypeElement(int, int, @NonNull byte[]);
method public boolean registerApCallback(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiCondManager.SoftApCallback);
method public void sendMgmtFrame(@NonNull String, @NonNull byte[], int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiCondManager.SendMgmtFrameCallback);
method public boolean setupInterfaceForClientMode(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiCondManager.ScanEventCallback, @NonNull android.net.wifi.wificond.WifiCondManager.ScanEventCallback);
@@ -6387,6 +6519,14 @@
field public static final int SEND_MGMT_FRAME_ERROR_UNKNOWN = 1; // 0x1
}
+ public static class WifiCondManager.OemSecurityType {
+ ctor public WifiCondManager.OemSecurityType(int, @NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<java.lang.Integer>, int);
+ field public final int groupCipher;
+ field @NonNull public final java.util.List<java.lang.Integer> keyManagement;
+ field @NonNull public final java.util.List<java.lang.Integer> pairwiseCipher;
+ field public final int protocol;
+ }
+
public static interface WifiCondManager.PnoScanRequestCallback {
method public void onPnoRequestFailed();
method public void onPnoRequestSucceeded();
@@ -6858,9 +6998,12 @@
public class RecoverySystem {
method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void cancelScheduledUpdate(android.content.Context) throws java.io.IOException;
+ method @RequiresPermission(android.Manifest.permission.RECOVERY) public static boolean clearPrepareForUnattendedUpdate(@NonNull android.content.Context) throws java.io.IOException;
method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void installPackage(android.content.Context, java.io.File, boolean) throws java.io.IOException;
+ method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void prepareForUnattendedUpdate(@NonNull android.content.Context, @NonNull String, @Nullable android.content.IntentSender) throws java.io.IOException;
method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void processPackage(android.content.Context, java.io.File, android.os.RecoverySystem.ProgressListener, android.os.Handler) throws java.io.IOException;
method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void processPackage(android.content.Context, java.io.File, android.os.RecoverySystem.ProgressListener) throws java.io.IOException;
+ method @RequiresPermission(android.Manifest.permission.RECOVERY) public static boolean rebootAndApply(@NonNull android.content.Context, @NonNull String, @NonNull String) throws java.io.IOException;
method @RequiresPermission(allOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static void rebootWipeAb(android.content.Context, java.io.File, String) throws java.io.IOException;
method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void scheduleUpdateOnBoot(android.content.Context, java.io.File) throws java.io.IOException;
method public static boolean verifyPackageCompatibility(java.io.File) throws java.io.IOException;
@@ -6931,7 +7074,22 @@
}
public class TelephonyServiceManager {
+ method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getCarrierConfigServiceRegisterer();
+ method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getEuiccCardControllerServiceRegisterer();
+ method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getEuiccControllerService();
+ method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getIccPhoneBookServiceRegisterer();
+ method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getNetworkPolicyServiceRegisterer();
+ method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getOpportunisticNetworkServiceRegisterer();
+ method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getPackageManagerServiceRegisterer();
+ method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getPermissionManagerServiceRegisterer();
+ method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getPhoneSubServiceRegisterer();
+ method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getSmsServiceRegisterer();
+ method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getSubscriptionServiceRegisterer();
+ method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getTelephonyImsServiceRegisterer();
+ method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getTelephonyRcsMessageServiceRegisterer();
+ method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getTelephonyRegistryServiceRegisterer();
method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getTelephonyServiceRegisterer();
+ method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getWindowServiceRegisterer();
}
public static class TelephonyServiceManager.ServiceNotFoundException extends java.lang.Exception {
@@ -6947,11 +7105,13 @@
public class UpdateEngine {
ctor public UpdateEngine();
+ method @NonNull public android.os.UpdateEngine.AllocateSpaceResult allocateSpace(@NonNull String, @NonNull String[]);
method public void applyPayload(String, long, long, String[]);
method public void applyPayload(@NonNull android.os.ParcelFileDescriptor, long, long, @NonNull String[]);
method public boolean bind(android.os.UpdateEngineCallback, android.os.Handler);
method public boolean bind(android.os.UpdateEngineCallback);
method public void cancel();
+ method public int cleanupAppliedPayload();
method public void resetStatus();
method public void resume();
method public void suspend();
@@ -6959,14 +7119,21 @@
method public boolean verifyPayloadMetadata(String);
}
+ public static final class UpdateEngine.AllocateSpaceResult {
+ method public int errorCode();
+ method public long freeSpaceRequired();
+ }
+
public static final class UpdateEngine.ErrorCodeConstants {
ctor public UpdateEngine.ErrorCodeConstants();
+ field public static final int DEVICE_CORRUPTED = 61; // 0x3d
field public static final int DOWNLOAD_PAYLOAD_VERIFICATION_ERROR = 12; // 0xc
field public static final int DOWNLOAD_TRANSFER_ERROR = 9; // 0x9
field public static final int ERROR = 1; // 0x1
field public static final int FILESYSTEM_COPIER_ERROR = 4; // 0x4
field public static final int INSTALL_DEVICE_OPEN_ERROR = 7; // 0x7
field public static final int KERNEL_DEVICE_OPEN_ERROR = 8; // 0x8
+ field public static final int NOT_ENOUGH_SPACE = 60; // 0x3c
field public static final int PAYLOAD_HASH_MISMATCH_ERROR = 10; // 0xa
field public static final int PAYLOAD_MISMATCHED_TYPE_ERROR = 6; // 0x6
field public static final int PAYLOAD_SIZE_MISMATCH_ERROR = 11; // 0xb
@@ -7713,7 +7880,9 @@
public static final class Telephony.Carriers implements android.provider.BaseColumns {
field public static final String APN_SET_ID = "apn_set_id";
field public static final int CARRIER_EDITED = 4; // 0x4
+ field @NonNull public static final android.net.Uri DPC_URI;
field public static final String EDITED_STATUS = "edited";
+ field public static final int INVALID_APN_ID = -1; // 0xffffffff
field public static final String MAX_CONNECTIONS = "max_conns";
field public static final String MODEM_PERSIST = "modem_cognitive";
field public static final String MTU = "mtu";
@@ -7728,6 +7897,9 @@
}
public static final class Telephony.CellBroadcasts implements android.provider.BaseColumns {
+ field @NonNull public static final String AUTHORITY_LEGACY = "cellbroadcast-legacy";
+ field @NonNull public static final android.net.Uri AUTHORITY_LEGACY_URI;
+ field @NonNull public static final String CALL_METHOD_GET_PREFERENCE = "get_preference";
field public static final String CID = "cid";
field public static final String CMAS_CATEGORY = "cmas_category";
field public static final String CMAS_CERTAINTY = "cmas_certainty";
@@ -7755,11 +7927,90 @@
field public static final String SERIAL_NUMBER = "serial_number";
field public static final String SERVICE_CATEGORY = "service_category";
field public static final String SLOT_INDEX = "slot_index";
- field public static final String SUB_ID = "sub_id";
+ field public static final String SUBSCRIPTION_ID = "sub_id";
+ }
+
+ public static final class Telephony.CellBroadcasts.Preference {
+ field @NonNull public static final String ENABLE_ALERT_VIBRATION_PREF = "enable_alert_vibrate";
+ field @NonNull public static final String ENABLE_AREA_UPDATE_INFO_PREF = "enable_area_update_info_alerts";
+ field @NonNull public static final String ENABLE_CMAS_AMBER_PREF = "enable_cmas_amber_alerts";
+ field @NonNull public static final String ENABLE_CMAS_EXTREME_THREAT_PREF = "enable_cmas_extreme_threat_alerts";
+ field @NonNull public static final String ENABLE_CMAS_IN_SECOND_LANGUAGE_PREF = "receive_cmas_in_second_language";
+ field @NonNull public static final String ENABLE_CMAS_PRESIDENTIAL_PREF = "enable_cmas_presidential_alerts";
+ field @NonNull public static final String ENABLE_CMAS_SEVERE_THREAT_PREF = "enable_cmas_severe_threat_alerts";
+ field @NonNull public static final String ENABLE_EMERGENCY_PERF = "enable_emergency_alerts";
+ field @NonNull public static final String ENABLE_FULL_VOLUME_PREF = "use_full_volume";
+ field @NonNull public static final String ENABLE_PUBLIC_SAFETY_PREF = "enable_public_safety_messages";
+ field @NonNull public static final String ENABLE_STATE_LOCAL_TEST_PREF = "enable_state_local_test_alerts";
+ field @NonNull public static final String ENABLE_TEST_ALERT_PREF = "enable_test_alerts";
}
public static final class Telephony.SimInfo {
+ field public static final String ACCESS_RULES = "access_rules";
+ field public static final String ACCESS_RULES_FROM_CARRIER_CONFIGS = "access_rules_from_carrier_configs";
+ field public static final String CARD_ID = "card_id";
+ field public static final String CARRIER_ID = "carrier_id";
+ field public static final String CARRIER_NAME = "carrier_name";
+ field public static final String CB_ALERT_REMINDER_INTERVAL = "alert_reminder_interval";
+ field public static final String CB_ALERT_SOUND_DURATION = "alert_sound_duration";
+ field public static final String CB_ALERT_SPEECH = "enable_alert_speech";
+ field public static final String CB_ALERT_VIBRATE = "enable_alert_vibrate";
+ field public static final String CB_AMBER_ALERT = "enable_cmas_amber_alerts";
+ field public static final String CB_CHANNEL_50_ALERT = "enable_channel_50_alerts";
+ field public static final String CB_CMAS_TEST_ALERT = "enable_cmas_test_alerts";
+ field public static final String CB_EMERGENCY_ALERT = "enable_emergency_alerts";
+ field public static final String CB_ETWS_TEST_ALERT = "enable_etws_test_alerts";
+ field public static final String CB_EXTREME_THREAT_ALERT = "enable_cmas_extreme_threat_alerts";
+ field public static final String CB_OPT_OUT_DIALOG = "show_cmas_opt_out_dialog";
+ field public static final String CB_SEVERE_THREAT_ALERT = "enable_cmas_severe_threat_alerts";
+ field public static final String COLOR = "color";
field @NonNull public static final android.net.Uri CONTENT_URI;
+ field public static final String DATA_ENABLED_OVERRIDE_RULES = "data_enabled_override_rules";
+ field public static final String DATA_ROAMING = "data_roaming";
+ field public static final int DATA_ROAMING_DEFAULT = 0; // 0x0
+ field public static final int DATA_ROAMING_DISABLE = 0; // 0x0
+ field public static final int DATA_ROAMING_ENABLE = 1; // 0x1
+ field public static final String DISPLAY_NAME = "display_name";
+ field public static final String EHPLMNS = "ehplmns";
+ field public static final String ENHANCED_4G_MODE_ENABLED = "volte_vt_enabled";
+ field public static final String GROUP_OWNER = "group_owner";
+ field public static final String GROUP_UUID = "group_uuid";
+ field public static final String HPLMNS = "hplmns";
+ field public static final String ICC_ID = "icc_id";
+ field public static final String IMSI = "imsi";
+ field public static final String ISO_COUNTRY_CODE = "iso_country_code";
+ field public static final String IS_EMBEDDED = "is_embedded";
+ field public static final String IS_OPPORTUNISTIC = "is_opportunistic";
+ field public static final String IS_REMOVABLE = "is_removable";
+ field public static final String MCC = "mcc";
+ field public static final String MCC_STRING = "mcc_string";
+ field public static final String MNC = "mnc";
+ field public static final String MNC_STRING = "mnc_string";
+ field public static final String NAME_SOURCE = "name_source";
+ field public static final int NAME_SOURCE_CARRIER = 3; // 0x3
+ field public static final int NAME_SOURCE_DEFAULT = 0; // 0x0
+ field public static final int NAME_SOURCE_SIM_PNN = 4; // 0x4
+ field public static final int NAME_SOURCE_SIM_SPN = 1; // 0x1
+ field public static final int NAME_SOURCE_USER_INPUT = 2; // 0x2
+ field public static final String NUMBER = "number";
+ field public static final String PROFILE_CLASS = "profile_class";
+ field public static final int PROFILE_CLASS_DEFAULT = -1; // 0xffffffff
+ field public static final int PROFILE_CLASS_OPERATIONAL = 2; // 0x2
+ field public static final int PROFILE_CLASS_PROVISIONING = 1; // 0x1
+ field public static final int PROFILE_CLASS_TESTING = 0; // 0x0
+ field public static final int PROFILE_CLASS_UNSET = -1; // 0xffffffff
+ field public static final int SIM_NOT_INSERTED = -1; // 0xffffffff
+ field public static final String SIM_SLOT_INDEX = "sim_id";
+ field public static final String SUBSCRIPTION_TYPE = "subscription_type";
+ field public static final int SUBSCRIPTION_TYPE_LOCAL_SIM = 0; // 0x0
+ field public static final int SUBSCRIPTION_TYPE_REMOTE_SIM = 1; // 0x1
+ field public static final String UICC_APPLICATIONS_ENABLED = "uicc_applications_enabled";
+ field public static final String UNIQUE_KEY_SUBSCRIPTION_ID = "_id";
+ field public static final String VT_IMS_ENABLED = "vt_ims_enabled";
+ field public static final String WFC_IMS_ENABLED = "wfc_ims_enabled";
+ field public static final String WFC_IMS_MODE = "wfc_ims_mode";
+ field public static final String WFC_IMS_ROAMING_ENABLED = "wfc_ims_roaming_enabled";
+ field public static final String WFC_IMS_ROAMING_MODE = "wfc_ims_roaming_mode";
}
public static final class Telephony.Sms.Intents {
@@ -7986,6 +8237,11 @@
field public static final String SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM = "android.autofill.field_classification.default_algorithm";
}
+ public static final class Dataset.Builder {
+ ctor public Dataset.Builder(@NonNull android.service.autofill.InlinePresentation);
+ method @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.service.autofill.InlinePresentation);
+ }
+
}
package android.service.autofill.augmented {
@@ -9021,30 +9277,37 @@
public abstract class CellIdentity implements android.os.Parcelable {
method @NonNull public abstract android.telephony.CellLocation asCellLocation();
+ method @NonNull public abstract android.telephony.CellIdentity sanitizeLocationInfo();
}
public final class CellIdentityCdma extends android.telephony.CellIdentity {
method @NonNull public android.telephony.cdma.CdmaCellLocation asCellLocation();
+ method @NonNull public android.telephony.CellIdentityCdma sanitizeLocationInfo();
}
public final class CellIdentityGsm extends android.telephony.CellIdentity {
method @NonNull public android.telephony.gsm.GsmCellLocation asCellLocation();
+ method @NonNull public android.telephony.CellIdentityGsm sanitizeLocationInfo();
}
public final class CellIdentityLte extends android.telephony.CellIdentity {
method @NonNull public android.telephony.gsm.GsmCellLocation asCellLocation();
+ method @NonNull public android.telephony.CellIdentityLte sanitizeLocationInfo();
}
public final class CellIdentityNr extends android.telephony.CellIdentity {
method @NonNull public android.telephony.CellLocation asCellLocation();
+ method @NonNull public android.telephony.CellIdentityNr sanitizeLocationInfo();
}
public final class CellIdentityTdscdma extends android.telephony.CellIdentity {
method @NonNull public android.telephony.gsm.GsmCellLocation asCellLocation();
+ method @NonNull public android.telephony.CellIdentityTdscdma sanitizeLocationInfo();
}
public final class CellIdentityWcdma extends android.telephony.CellIdentity {
method @NonNull public android.telephony.gsm.GsmCellLocation asCellLocation();
+ method @NonNull public android.telephony.CellIdentityWcdma sanitizeLocationInfo();
}
public final class DataFailCause {
@@ -9444,6 +9707,7 @@
field public static final int IMS_ACCESS_BLOCKED = 60; // 0x3c
field public static final int IMS_MERGED_SUCCESSFULLY = 45; // 0x2d
field public static final int IMS_SIP_ALTERNATE_EMERGENCY_CALL = 71; // 0x47
+ field public static final int INCOMING_AUTO_REJECTED = 81; // 0x51
field public static final int INCOMING_MISSED = 1; // 0x1
field public static final int INCOMING_REJECTED = 16; // 0x10
field public static final int INVALID_CREDENTIALS = 10; // 0xa
@@ -9545,7 +9809,9 @@
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.NetworkRegistrationInfo> CREATOR;
field public static final int DOMAIN_CS = 1; // 0x1
+ field public static final int DOMAIN_CS_PS = 3; // 0x3
field public static final int DOMAIN_PS = 2; // 0x2
+ field public static final int DOMAIN_UNKNOWN = 0; // 0x0
field public static final int REGISTRATION_STATE_DENIED = 3; // 0x3
field public static final int REGISTRATION_STATE_HOME = 1; // 0x1
field public static final int REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING = 0; // 0x0
@@ -9786,6 +10052,10 @@
field public static final int ROAMING_TYPE_UNKNOWN = 1; // 0x1
}
+ public class SignalStrength implements android.os.Parcelable {
+ ctor public SignalStrength(@NonNull android.telephony.SignalStrength);
+ }
+
public final class SmsCbCmasInfo implements android.os.Parcelable {
ctor public SmsCbCmasInfo(int, int, int, int, int, int);
method public int describeContents();
@@ -9928,6 +10198,9 @@
public class SubscriptionManager {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean canDisablePhysicalSubscription();
method public boolean canManageSubscription(@Nullable android.telephony.SubscriptionInfo, @Nullable String);
+ method @NonNull public int[] getActiveAndHiddenSubscriptionIdList();
+ method @NonNull public int[] getActiveSubscriptionIdList();
+ method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.SubscriptionInfo getActiveSubscriptionInfoForIcc(@NonNull String);
method public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEnabledSubscriptionId(int);
method @NonNull public static android.content.res.Resources getResourcesForSubId(@NonNull android.content.Context, int);
@@ -9988,6 +10261,7 @@
}
public class TelephonyManager {
+ method public int addDevicePolicyOverrideApn(@NonNull android.content.Context, @NonNull android.telephony.data.ApnSetting);
method @Deprecated @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void call(String, String);
method public int checkCarrierPrivilegesForPackage(String);
method public int checkCarrierPrivilegesForPackageAnyPhone(String);
@@ -10016,6 +10290,7 @@
method @Deprecated public boolean getDataEnabled(int);
method @Nullable public static android.content.ComponentName getDefaultRespondViaMessageApplication(@NonNull android.content.Context, boolean);
method @NonNull public static String getDefaultSimCountryIso();
+ method @NonNull public java.util.List<android.telephony.data.ApnSetting> getDevicePolicyOverrideApns(@NonNull android.content.Context);
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDeviceSoftwareVersion(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getEmergencyCallbackMode();
method public int getEmergencyNumberDbVersion();
@@ -10053,15 +10328,17 @@
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isEmergencyAssistanceEnabled();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isInEmergencySmsMode();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isManualNetworkSelectionAllowed();
method public boolean isModemEnabledForSlot(int);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isOpportunisticNetworkEnabled();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isPotentialEmergencyNumber(@NonNull String);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRadioOn();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isTetheringApnRequired();
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isTetheringApnRequired();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isVideoCallingEnabled();
method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle);
+ method public boolean modifyDevicePolicyOverrideApn(@NonNull android.content.Context, int, @NonNull android.telephony.data.ApnSetting);
method public boolean needsOtaServiceProvisioning();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyOtaEmergencyNumberDbInstalled();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio();
@@ -10074,6 +10351,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean resetRadioConfig();
method @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL) public void resetSettings();
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setAlwaysAllowMmsData(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCarrierDataEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setCarrierRestrictionRules(@NonNull android.telephony.CarrierRestrictionRules);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int);
@@ -10081,6 +10359,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setPolicyDataEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setPreferredNetworkTypeBitmask(long);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setRadio(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setRadioEnabled(boolean);
@@ -10099,10 +10378,13 @@
method public void updateServiceLocation();
method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void updateTestOtaEmergencyNumberDbFilePath(@NonNull String);
field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final String ACTION_ANOMALY_REPORTED = "android.telephony.action.ANOMALY_REPORTED";
+ field public static final String ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED = "android.intent.action.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED";
+ field public static final String ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED = "android.intent.action.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED";
field public static final String ACTION_EMERGENCY_ASSISTANCE = "android.telephony.action.EMERGENCY_ASSISTANCE";
field public static final String ACTION_EMERGENCY_CALLBACK_MODE_CHANGED = "android.intent.action.EMERGENCY_CALLBACK_MODE_CHANGED";
field public static final String ACTION_EMERGENCY_CALL_STATE_CHANGED = "android.intent.action.EMERGENCY_CALL_STATE_CHANGED";
field public static final String ACTION_NETWORK_SET_TIME = "android.telephony.action.NETWORK_SET_TIME";
+ field public static final String ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE = "com.android.omadm.service.CONFIGURATION_UPDATE";
field public static final String ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS = "android.telephony.action.SHOW_NOTICE_ECM_BLOCK_OTHERS";
field public static final String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
field public static final String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED";
@@ -10164,6 +10446,7 @@
method public void addOnSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener, @NonNull java.util.concurrent.Executor);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyCallStateChangedForAllSubscriptions(int, @Nullable String);
method public void notifyCarrierNetworkChange(boolean);
+ method public void notifyRegistrationFailed(int, int, @NonNull android.telephony.CellIdentity, @NonNull String, int, int, int);
method public void removeOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
method public void removeOnSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
}
@@ -10202,6 +10485,20 @@
method public static final void setSmsFilterSettings(android.content.Context, android.telecom.PhoneAccountHandle, android.telephony.VisualVoicemailSmsFilterSettings);
}
+ public final class WapPushManagerConnector {
+ ctor public WapPushManagerConnector(@NonNull android.content.Context);
+ method public boolean bindToWapPushManagerService();
+ method @Nullable public String getConnectedWapPushManagerServicePackage();
+ method public int processMessage(@NonNull String, @NonNull String, @NonNull android.content.Intent);
+ method public void unbindWapPushManagerService();
+ field public static final int RESULT_APP_QUERY_FAILED = 2; // 0x2
+ field public static final int RESULT_EXCEPTION_CAUGHT = 16; // 0x10
+ field public static final int RESULT_FURTHER_PROCESSING = 32768; // 0x8000
+ field public static final int RESULT_INVALID_RECEIVER_NAME = 8; // 0x8
+ field public static final int RESULT_MESSAGE_HANDLED = 1; // 0x1
+ field public static final int RESULT_SIGNATURE_NO_MATCH = 4; // 0x4
+ }
+
}
package android.telephony.cdma {
@@ -10582,6 +10879,7 @@
field public static final int DIALSTRING_USSD = 2; // 0x2
field public static final String EXTRA_ADDITIONAL_CALL_INFO = "AdditionalCallInfo";
field public static final String EXTRA_ADDITIONAL_SIP_INVITE_FIELDS = "android.telephony.ims.extra.ADDITIONAL_SIP_INVITE_FIELDS";
+ field public static final String EXTRA_CALL_DISCONNECT_CAUSE = "android.telephony.ims.extra.CALL_DISCONNECT_CAUSE";
field public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech";
field public static final String EXTRA_CHILD_NUMBER = "ChildNum";
field public static final String EXTRA_CNA = "cna";
diff --git a/api/test-current.txt b/api/test-current.txt
index e7153e6..e64cbdb 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -244,6 +244,16 @@
field public static final int UID_STATE_TOP = 200; // 0xc8
}
+ public static final class AppOpsManager.HistoricalFeatureOps implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public String getFeatureId();
+ method @Nullable public android.app.AppOpsManager.HistoricalOp getOp(@NonNull String);
+ method @NonNull public android.app.AppOpsManager.HistoricalOp getOpAt(@IntRange(from=0) int);
+ method @IntRange(from=0) public int getOpCount();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalFeatureOps> CREATOR;
+ }
+
public static final class AppOpsManager.HistoricalOp implements android.os.Parcelable {
method public int describeContents();
method public long getAccessCount(int, int, int);
@@ -268,9 +278,9 @@
method @IntRange(from=0) public int getUidCount();
method @Nullable public android.app.AppOpsManager.HistoricalUidOps getUidOps(int);
method @NonNull public android.app.AppOpsManager.HistoricalUidOps getUidOpsAt(@IntRange(from=0) int);
- method public void increaseAccessCount(int, int, @NonNull String, int, int, long);
- method public void increaseAccessDuration(int, int, @NonNull String, int, int, long);
- method public void increaseRejectCount(int, int, @NonNull String, int, int, long);
+ method public void increaseAccessCount(int, int, @NonNull String, @Nullable String, int, int, long);
+ method public void increaseAccessDuration(int, int, @NonNull String, @Nullable String, int, int, long);
+ method public void increaseRejectCount(int, int, @NonNull String, @Nullable String, int, int, long);
method public void offsetBeginAndEndTime(long);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalOps> CREATOR;
@@ -282,6 +292,7 @@
public static final class AppOpsManager.HistoricalOpsRequest.Builder {
ctor public AppOpsManager.HistoricalOpsRequest.Builder(long, long);
method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest build();
+ method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setFeatureId(@Nullable String);
method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setFlags(int);
method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setOpNames(@Nullable java.util.List<java.lang.String>);
method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setPackageName(@Nullable String);
@@ -290,6 +301,9 @@
public static final class AppOpsManager.HistoricalPackageOps implements android.os.Parcelable {
method public int describeContents();
+ method @IntRange(from=0) public int getFeatureCount();
+ method @Nullable public android.app.AppOpsManager.HistoricalFeatureOps getFeatureOps(@NonNull String);
+ method @NonNull public android.app.AppOpsManager.HistoricalFeatureOps getFeatureOpsAt(@IntRange(from=0) int);
method @Nullable public android.app.AppOpsManager.HistoricalOp getOp(@NonNull String);
method @NonNull public android.app.AppOpsManager.HistoricalOp getOpAt(@IntRange(from=0) int);
method @IntRange(from=0) public int getOpCount();
@@ -423,6 +437,7 @@
public class StatusBarManager {
method @NonNull @RequiresPermission(android.Manifest.permission.STATUS_BAR) public android.app.StatusBarManager.DisableInfo getDisableInfo();
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSetup(boolean);
+ method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSimNetworkLock(boolean);
}
public static final class StatusBarManager.DisableInfo {
@@ -2183,6 +2198,14 @@
field public static final String ACTION_USER_RESTRICTIONS_CHANGED = "android.os.action.USER_RESTRICTIONS_CHANGED";
}
+ public final class VibrationAttributes implements android.os.Parcelable {
+ method @Deprecated @NonNull public android.media.AudioAttributes getAudioAttributes();
+ }
+
+ public static final class VibrationAttributes.Builder {
+ ctor public VibrationAttributes.Builder(@NonNull android.media.AudioAttributes, @Nullable android.os.VibrationEffect);
+ }
+
public abstract class VibrationEffect implements android.os.Parcelable {
method public static android.os.VibrationEffect get(int);
method public static android.os.VibrationEffect get(int, boolean);
@@ -2602,7 +2625,7 @@
field public static final String SERIAL_NUMBER = "serial_number";
field public static final String SERVICE_CATEGORY = "service_category";
field public static final String SLOT_INDEX = "slot_index";
- field public static final String SUB_ID = "sub_id";
+ field public static final String SUBSCRIPTION_ID = "sub_id";
}
public static final class Telephony.Sms.Intents {
@@ -3170,7 +3193,9 @@
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.NetworkRegistrationInfo> CREATOR;
field public static final int DOMAIN_CS = 1; // 0x1
+ field public static final int DOMAIN_CS_PS = 3; // 0x3
field public static final int DOMAIN_PS = 2; // 0x2
+ field public static final int DOMAIN_UNKNOWN = 0; // 0x0
field public static final int REGISTRATION_STATE_DENIED = 3; // 0x3
field public static final int REGISTRATION_STATE_HOME = 1; // 0x1
field public static final int REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING = 0; // 0x0
@@ -3249,14 +3274,17 @@
}
public class TelephonyManager {
+ method public int addDevicePolicyOverrideApn(@NonNull android.content.Context, @NonNull android.telephony.data.ApnSetting);
method public int checkCarrierPrivilegesForPackage(String);
method public int getCarrierIdListVersion();
method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
method @Nullable public static android.content.ComponentName getDefaultRespondViaMessageApplication(@NonNull android.content.Context, boolean);
+ method @NonNull public java.util.List<android.telephony.data.ApnSetting> getDevicePolicyOverrideApns(@NonNull android.content.Context);
method public int getEmergencyNumberDbVersion();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getLine1AlphaTag();
method @NonNull @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getNetworkCountryIso(int);
method public android.util.Pair<java.lang.Integer,java.lang.Integer> getRadioHalVersion();
+ method public boolean modifyDevicePolicyOverrideApn(@NonNull android.content.Context, int, @NonNull android.telephony.data.ApnSetting);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void refreshUiccProfile();
method @Deprecated public void setCarrierTestOverride(String, String, String, String, String, String, String);
method public void setCarrierTestOverride(String, String, String, String, String, String, String, String, String);
@@ -3367,6 +3395,7 @@
field public static final int DIALSTRING_USSD = 2; // 0x2
field public static final String EXTRA_ADDITIONAL_CALL_INFO = "AdditionalCallInfo";
field public static final String EXTRA_ADDITIONAL_SIP_INVITE_FIELDS = "android.telephony.ims.extra.ADDITIONAL_SIP_INVITE_FIELDS";
+ field public static final String EXTRA_CALL_DISCONNECT_CAUSE = "android.telephony.ims.extra.CALL_DISCONNECT_CAUSE";
field public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech";
field public static final String EXTRA_CHILD_NUMBER = "ChildNum";
field public static final String EXTRA_CNA = "cna";
diff --git a/cmds/input/src/com/android/commands/input/Input.java b/cmds/input/src/com/android/commands/input/Input.java
index a077745..08216d9 100644
--- a/cmds/input/src/com/android/commands/input/Input.java
+++ b/cmds/input/src/com/android/commands/input/Input.java
@@ -430,6 +430,6 @@
+ " (Default: touchscreen)");
out.println(" press (Default: trackball)");
out.println(" roll <dx> <dy> (Default: trackball)");
- out.println(" event <DOWN|UP|MOVE> <x> <y> (Default: touchscreen)");
+ out.println(" motionevent <DOWN|UP|MOVE> <x> <y> (Default: touchscreen)");
}
}
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 7b96ce9..118a508 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -126,29 +126,28 @@
],
static_libs: [
- "libhealthhalutils",
- "libplatformprotos",
- ],
-
- shared_libs: [
"android.frameworks.stats@1.0",
- "android.hardware.health@2.0",
"android.hardware.power.stats@1.0",
"android.hardware.power@1.0",
"android.hardware.power@1.1",
"libbase",
- "libbinder",
"libcutils",
+ "libhealthhalutils",
+ "liblog",
+ "libplatformprotos",
+ "libprotoutil",
+ "libstatslog",
+ "libstatssocket",
+ "libsysutils",
+ ],
+ shared_libs: [
+ "android.hardware.health@2.0",
+ "libbinder",
"libgraphicsenv",
"libhidlbase",
"libincident",
- "liblog",
- "libprotoutil",
"libservices",
- "libstatslog",
"libstatsmetadata",
- "libstatssocket",
- "libsysutils",
"libtimestats_proto",
"libutils",
],
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index bb3a094..1ca19c34 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -170,33 +170,32 @@
mUidMap, mPullerManager, mAnomalyAlarmMonitor, mPeriodicAlarmMonitor,
getElapsedRealtimeNs(),
[this](const ConfigKey& key) {
- sp<IStatsCompanionService> sc = getStatsCompanionService();
- auto receiver = mConfigManager->GetConfigReceiver(key);
- if (sc == nullptr) {
- VLOG("Could not find StatsCompanionService");
+ sp<IPendingIntentRef> receiver = mConfigManager->GetConfigReceiver(key);
+ if (receiver == nullptr) {
+ VLOG("Could not find a broadcast receiver for %s",
+ key.ToString().c_str());
return false;
- } else if (receiver == nullptr) {
- VLOG("Statscompanion could not find a broadcast receiver for %s",
- key.ToString().c_str());
- return false;
- } else {
- sc->sendDataBroadcast(receiver, mProcessor->getLastReportTimeNs(key));
+ } else if (receiver->sendDataBroadcast(
+ mProcessor->getLastReportTimeNs(key)).isOk()) {
return true;
+ } else {
+ VLOG("Failed to send a broadcast for receiver %s",
+ key.ToString().c_str());
+ return false;
}
},
[this](const int& uid, const vector<int64_t>& activeConfigs) {
- auto receiver = mConfigManager->GetActiveConfigsChangedReceiver(uid);
- sp<IStatsCompanionService> sc = getStatsCompanionService();
- if (sc == nullptr) {
- VLOG("Could not access statsCompanion");
- return false;
- } else if (receiver == nullptr) {
+ sp<IPendingIntentRef> receiver =
+ mConfigManager->GetActiveConfigsChangedReceiver(uid);
+ if (receiver == nullptr) {
VLOG("Could not find receiver for uid %d", uid);
return false;
- } else {
- sc->sendActiveConfigsChangedBroadcast(receiver, activeConfigs);
+ } else if (receiver->sendActiveConfigsChangedBroadcast(activeConfigs).isOk()) {
VLOG("StatsService::active configs broadcast succeeded for uid %d" , uid);
return true;
+ } else {
+ VLOG("StatsService::active configs broadcast failed for uid %d" , uid);
+ return false;
}
});
@@ -574,18 +573,19 @@
return UNKNOWN_ERROR;
}
ConfigKey key(uid, StrToInt64(name));
- auto receiver = mConfigManager->GetConfigReceiver(key);
- sp<IStatsCompanionService> sc = getStatsCompanionService();
- if (sc == nullptr) {
- VLOG("Could not access statsCompanion");
- } else if (receiver == nullptr) {
- VLOG("Could not find receiver for %s, %s", args[1].c_str(), args[2].c_str())
- } else {
- sc->sendDataBroadcast(receiver, mProcessor->getLastReportTimeNs(key));
+ sp<IPendingIntentRef> receiver = mConfigManager->GetConfigReceiver(key);
+ if (receiver == nullptr) {
+ VLOG("Could not find receiver for %s, %s", args[1].c_str(), args[2].c_str());
+ return UNKNOWN_ERROR;
+ } else if (receiver->sendDataBroadcast(
+ mProcessor->getLastReportTimeNs(key)).isOk()) {
VLOG("StatsService::trigger broadcast succeeded to %s, %s", args[1].c_str(),
args[2].c_str());
+ } else {
+ VLOG("StatsService::trigger broadcast failed to %s, %s", args[1].c_str(),
+ args[2].c_str());
+ return UNKNOWN_ERROR;
}
-
return NO_ERROR;
}
@@ -629,15 +629,15 @@
}
}
}
- auto receiver = mConfigManager->GetActiveConfigsChangedReceiver(uid);
- sp<IStatsCompanionService> sc = getStatsCompanionService();
- if (sc == nullptr) {
- VLOG("Could not access statsCompanion");
- } else if (receiver == nullptr) {
+ sp<IPendingIntentRef> receiver = mConfigManager->GetActiveConfigsChangedReceiver(uid);
+ if (receiver == nullptr) {
VLOG("Could not find receiver for uid %d", uid);
- } else {
- sc->sendActiveConfigsChangedBroadcast(receiver, configIds);
+ return UNKNOWN_ERROR;
+ } else if (receiver->sendActiveConfigsChangedBroadcast(configIds).isOk()) {
VLOG("StatsService::trigger active configs changed broadcast succeeded for uid %d" , uid);
+ } else {
+ VLOG("StatsService::trigger active configs changed broadcast failed for uid %d", uid);
+ return UNKNOWN_ERROR;
}
return NO_ERROR;
}
@@ -1111,7 +1111,6 @@
mPullerManager->SetStatsCompanionService(statsCompanion);
mAnomalyAlarmMonitor->setStatsCompanionService(statsCompanion);
mPeriodicAlarmMonitor->setStatsCompanionService(statsCompanion);
- SubscriberReporter::getInstance().setStatsCompanionService(statsCompanion);
return Status::ok();
}
@@ -1136,12 +1135,11 @@
}
}
-Status StatsService::getData(int64_t key, const String16& packageName, vector<uint8_t>* output) {
- ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+Status StatsService::getData(int64_t key, const int32_t callingUid, vector<uint8_t>* output) {
+ ENFORCE_UID(AID_SYSTEM);
- IPCThreadState* ipc = IPCThreadState::self();
- VLOG("StatsService::getData with Pid %i, Uid %i", ipc->getCallingPid(), ipc->getCallingUid());
- ConfigKey configKey(ipc->getCallingUid(), key);
+ VLOG("StatsService::getData with Uid %i", callingUid);
+ ConfigKey configKey(callingUid, key);
// The dump latency does not matter here since we do not include the current bucket, we do not
// need to pull any new data anyhow.
mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(), false /* include_current_bucket*/,
@@ -1149,22 +1147,18 @@
return Status::ok();
}
-Status StatsService::getMetadata(const String16& packageName, vector<uint8_t>* output) {
- ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+Status StatsService::getMetadata(vector<uint8_t>* output) {
+ ENFORCE_UID(AID_SYSTEM);
- IPCThreadState* ipc = IPCThreadState::self();
- VLOG("StatsService::getMetadata with Pid %i, Uid %i", ipc->getCallingPid(),
- ipc->getCallingUid());
StatsdStats::getInstance().dumpStats(output, false); // Don't reset the counters.
return Status::ok();
}
Status StatsService::addConfiguration(int64_t key, const vector <uint8_t>& config,
- const String16& packageName) {
- ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+ const int32_t callingUid) {
+ ENFORCE_UID(AID_SYSTEM);
- IPCThreadState* ipc = IPCThreadState::self();
- if (addConfigurationChecked(ipc->getCallingUid(), key, config)) {
+ if (addConfigurationChecked(callingUid, key, config)) {
return Status::ok();
} else {
ALOGE("Could not parse malformatted StatsdConfig");
@@ -1185,23 +1179,21 @@
return true;
}
-Status StatsService::removeDataFetchOperation(int64_t key, const String16& packageName) {
- ENFORCE_DUMP_AND_USAGE_STATS(packageName);
-
- IPCThreadState* ipc = IPCThreadState::self();
- ConfigKey configKey(ipc->getCallingUid(), key);
+Status StatsService::removeDataFetchOperation(int64_t key,
+ const int32_t callingUid) {
+ ENFORCE_UID(AID_SYSTEM);
+ ConfigKey configKey(callingUid, key);
mConfigManager->RemoveConfigReceiver(configKey);
return Status::ok();
}
Status StatsService::setDataFetchOperation(int64_t key,
- const sp<android::IBinder>& intentSender,
- const String16& packageName) {
- ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+ const sp<IPendingIntentRef>& pir,
+ const int32_t callingUid) {
+ ENFORCE_UID(AID_SYSTEM);
- IPCThreadState* ipc = IPCThreadState::self();
- ConfigKey configKey(ipc->getCallingUid(), key);
- mConfigManager->SetConfigReceiver(configKey, intentSender);
+ ConfigKey configKey(callingUid, key);
+ mConfigManager->SetConfigReceiver(configKey, pir);
if (StorageManager::hasConfigMetricsReport(configKey)) {
VLOG("StatsService::setDataFetchOperation marking configKey %s to dump reports on disk",
configKey.ToString().c_str());
@@ -1210,62 +1202,55 @@
return Status::ok();
}
-Status StatsService::setActiveConfigsChangedOperation(const sp<android::IBinder>& intentSender,
- const String16& packageName,
+Status StatsService::setActiveConfigsChangedOperation(const sp<IPendingIntentRef>& pir,
+ const int32_t callingUid,
vector<int64_t>* output) {
- ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+ ENFORCE_UID(AID_SYSTEM);
- IPCThreadState* ipc = IPCThreadState::self();
- int uid = ipc->getCallingUid();
- mConfigManager->SetActiveConfigsChangedReceiver(uid, intentSender);
+ mConfigManager->SetActiveConfigsChangedReceiver(callingUid, pir);
if (output != nullptr) {
- mProcessor->GetActiveConfigs(uid, *output);
+ mProcessor->GetActiveConfigs(callingUid, *output);
} else {
ALOGW("StatsService::setActiveConfigsChanged output was nullptr");
}
return Status::ok();
}
-Status StatsService::removeActiveConfigsChangedOperation(const String16& packageName) {
- ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+Status StatsService::removeActiveConfigsChangedOperation(const int32_t callingUid) {
+ ENFORCE_UID(AID_SYSTEM);
- IPCThreadState* ipc = IPCThreadState::self();
- mConfigManager->RemoveActiveConfigsChangedReceiver(ipc->getCallingUid());
+ mConfigManager->RemoveActiveConfigsChangedReceiver(callingUid);
return Status::ok();
}
-Status StatsService::removeConfiguration(int64_t key, const String16& packageName) {
- ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+Status StatsService::removeConfiguration(int64_t key, const int32_t callingUid) {
+ ENFORCE_UID(AID_SYSTEM);
- IPCThreadState* ipc = IPCThreadState::self();
- ConfigKey configKey(ipc->getCallingUid(), key);
+ ConfigKey configKey(callingUid, key);
mConfigManager->RemoveConfig(configKey);
- SubscriberReporter::getInstance().removeConfig(configKey);
return Status::ok();
}
Status StatsService::setBroadcastSubscriber(int64_t configId,
int64_t subscriberId,
- const sp<android::IBinder>& intentSender,
- const String16& packageName) {
- ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+ const sp<IPendingIntentRef>& pir,
+ const int32_t callingUid) {
+ ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::setBroadcastSubscriber called.");
- IPCThreadState* ipc = IPCThreadState::self();
- ConfigKey configKey(ipc->getCallingUid(), configId);
+ ConfigKey configKey(callingUid, configId);
SubscriberReporter::getInstance()
- .setBroadcastSubscriber(configKey, subscriberId, intentSender);
+ .setBroadcastSubscriber(configKey, subscriberId, pir);
return Status::ok();
}
Status StatsService::unsetBroadcastSubscriber(int64_t configId,
int64_t subscriberId,
- const String16& packageName) {
- ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+ const int32_t callingUid) {
+ ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::unsetBroadcastSubscriber called.");
- IPCThreadState* ipc = IPCThreadState::self();
- ConfigKey configKey(ipc->getCallingUid(), configId);
+ ConfigKey configKey(callingUid, configId);
SubscriberReporter::getInstance()
.unsetBroadcastSubscriber(configKey, subscriberId);
return Status::ok();
@@ -1485,17 +1470,7 @@
Status StatsService::getRegisteredExperimentIds(std::vector<int64_t>* experimentIdsOut) {
- uid_t uid = IPCThreadState::self()->getCallingUid();
-
- // Caller must be granted these permissions
- if (!checkCallingPermission(String16(kPermissionDump))) {
- return exception(binder::Status::EX_SECURITY,
- StringPrintf("UID %d lacks permission %s", uid, kPermissionDump));
- }
- if (!checkCallingPermission(String16(kPermissionUsage))) {
- return exception(binder::Status::EX_SECURITY,
- StringPrintf("UID %d lacks permission %s", uid, kPermissionUsage));
- }
+ ENFORCE_UID(AID_SYSTEM);
// TODO: add verifier permission
// Read the latest train info
@@ -1631,7 +1606,6 @@
}
mAnomalyAlarmMonitor->setStatsCompanionService(nullptr);
mPeriodicAlarmMonitor->setStatsCompanionService(nullptr);
- SubscriberReporter::getInstance().setStatsCompanionService(nullptr);
mPullerManager->SetStatsCompanionService(nullptr);
}
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index de55ca9..c9a9072 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -30,6 +30,7 @@
#include <android/frameworks/stats/1.0/IStats.h>
#include <android/frameworks/stats/1.0/types.h>
#include <android/os/BnStatsd.h>
+#include <android/os/IPendingIntentRef.h>
#include <android/os/IStatsCompanionService.h>
#include <android/os/IStatsd.h>
#include <binder/IResultReceiver.h>
@@ -98,15 +99,14 @@
* Binder call for clients to request data for this configuration key.
*/
virtual Status getData(int64_t key,
- const String16& packageName,
+ const int32_t callingUid,
vector<uint8_t>* output) override;
/**
* Binder call for clients to get metadata across all configs in statsd.
*/
- virtual Status getMetadata(const String16& packageName,
- vector<uint8_t>* output) override;
+ virtual Status getMetadata(vector<uint8_t>* output) override;
/**
@@ -115,53 +115,52 @@
*/
virtual Status addConfiguration(int64_t key,
const vector<uint8_t>& config,
- const String16& packageName) override;
+ const int32_t callingUid) override;
/**
* Binder call to let clients register the data fetch operation for a configuration.
*/
virtual Status setDataFetchOperation(int64_t key,
- const sp<android::IBinder>& intentSender,
- const String16& packageName) override;
+ const sp<IPendingIntentRef>& pir,
+ const int32_t callingUid) override;
/**
* Binder call to remove the data fetch operation for the specified config key.
*/
virtual Status removeDataFetchOperation(int64_t key,
- const String16& packageName) override;
+ const int32_t callingUid) override;
/**
* Binder call to let clients register the active configs changed operation.
*/
- virtual Status setActiveConfigsChangedOperation(const sp<android::IBinder>& intentSender,
- const String16& packageName,
+ virtual Status setActiveConfigsChangedOperation(const sp<IPendingIntentRef>& pir,
+ const int32_t callingUid,
vector<int64_t>* output) override;
/**
* Binder call to remove the active configs changed operation for the specified package..
*/
- virtual Status removeActiveConfigsChangedOperation(const String16& packageName) override;
+ virtual Status removeActiveConfigsChangedOperation(const int32_t callingUid) override;
/**
* Binder call to allow clients to remove the specified configuration.
*/
virtual Status removeConfiguration(int64_t key,
- const String16& packageName) override;
+ const int32_t callingUid) override;
/**
- * Binder call to associate the given config's subscriberId with the given intentSender.
- * intentSender must be convertible into an IntentSender (in Java) using IntentSender(IBinder).
+ * Binder call to associate the given config's subscriberId with the given pendingIntentRef.
*/
virtual Status setBroadcastSubscriber(int64_t configId,
int64_t subscriberId,
- const sp<android::IBinder>& intentSender,
- const String16& packageName) override;
+ const sp<IPendingIntentRef>& pir,
+ const int32_t callingUid) override;
/**
- * Binder call to unassociate the given config's subscriberId with any intentSender.
+ * Binder call to unassociate the given config's subscriberId with any pendingIntentRef.
*/
virtual Status unsetBroadcastSubscriber(int64_t configId,
int64_t subscriberId,
- const String16& packageName) override;
+ const int32_t callingUid) override;
/** Inform statsCompanion that statsd is ready. */
virtual void sayHiToStatsCompanion();
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index fc949b4..972adf7 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -46,6 +46,41 @@
using android::base::StringPrintf;
using std::unique_ptr;
+class ConfigReceiverDeathRecipient : public android::IBinder::DeathRecipient {
+ public:
+ ConfigReceiverDeathRecipient(sp<ConfigManager> configManager, const ConfigKey& configKey):
+ mConfigManager(configManager),
+ mConfigKey(configKey) {}
+ ~ConfigReceiverDeathRecipient() override = default;
+ private:
+ sp<ConfigManager> mConfigManager;
+ ConfigKey mConfigKey;
+
+ void binderDied(const android::wp<android::IBinder>& who) override {
+ if (IInterface::asBinder(mConfigManager->GetConfigReceiver(mConfigKey)) == who.promote()) {
+ mConfigManager->RemoveConfigReceiver(mConfigKey);
+ }
+ }
+};
+
+class ActiveConfigChangedReceiverDeathRecipient : public android::IBinder::DeathRecipient {
+ public:
+ ActiveConfigChangedReceiverDeathRecipient(sp<ConfigManager> configManager, const int uid):
+ mConfigManager(configManager),
+ mUid(uid) {}
+ ~ActiveConfigChangedReceiverDeathRecipient() override = default;
+ private:
+ sp<ConfigManager> mConfigManager;
+ int mUid;
+
+ void binderDied(const android::wp<android::IBinder>& who) override {
+ if (IInterface::asBinder(mConfigManager->GetActiveConfigsChangedReceiver(mUid))
+ == who.promote()) {
+ mConfigManager->RemoveActiveConfigsChangedReceiver(mUid);
+ }
+ }
+};
+
ConfigManager::ConfigManager() {
}
@@ -118,9 +153,11 @@
}
}
-void ConfigManager::SetConfigReceiver(const ConfigKey& key, const sp<IBinder>& intentSender) {
+void ConfigManager::SetConfigReceiver(const ConfigKey& key,
+ const sp<IPendingIntentRef>& pir) {
lock_guard<mutex> lock(mMutex);
- mConfigReceivers[key] = intentSender;
+ mConfigReceivers[key] = pir;
+ IInterface::asBinder(pir)->linkToDeath(new ConfigReceiverDeathRecipient(this, key));
}
void ConfigManager::RemoveConfigReceiver(const ConfigKey& key) {
@@ -129,9 +166,11 @@
}
void ConfigManager::SetActiveConfigsChangedReceiver(const int uid,
- const sp<IBinder>& intentSender) {
+ const sp<IPendingIntentRef>& pir) {
lock_guard<mutex> lock(mMutex);
- mActiveConfigsChangedReceivers[uid] = intentSender;
+ mActiveConfigsChangedReceivers[uid] = pir;
+ IInterface::asBinder(pir)->linkToDeath(
+ new ActiveConfigChangedReceiverDeathRecipient(this, uid));
}
void ConfigManager::RemoveActiveConfigsChangedReceiver(const int uid) {
@@ -150,25 +189,11 @@
// Remove from map
uidIt->second.erase(key);
- // No more configs for this uid, lets remove the active configs callback.
- if (uidIt->second.empty()) {
- auto itActiveConfigsChangedReceiver = mActiveConfigsChangedReceivers.find(uid);
- if (itActiveConfigsChangedReceiver != mActiveConfigsChangedReceivers.end()) {
- mActiveConfigsChangedReceivers.erase(itActiveConfigsChangedReceiver);
- }
- }
-
for (const sp<ConfigListener>& listener : mListeners) {
broadcastList.push_back(listener);
}
}
- auto itReceiver = mConfigReceivers.find(key);
- if (itReceiver != mConfigReceivers.end()) {
- // Remove from map
- mConfigReceivers.erase(itReceiver);
- }
-
// Remove from disk. There can still be a lingering file on disk so we check
// whether or not the config was on memory.
remove_saved_configs(key);
@@ -199,12 +224,6 @@
// Remove from map
remove_saved_configs(*it);
removed.push_back(*it);
- mConfigReceivers.erase(*it);
- }
-
- auto itActiveConfigsChangedReceiver = mActiveConfigsChangedReceivers.find(uid);
- if (itActiveConfigsChangedReceiver != mActiveConfigsChangedReceivers.end()) {
- mActiveConfigsChangedReceivers.erase(itActiveConfigsChangedReceiver);
}
mConfigs.erase(uidIt);
@@ -238,8 +257,6 @@
uidIt = mConfigs.erase(uidIt);
}
- mConfigReceivers.clear();
- mActiveConfigsChangedReceivers.clear();
for (const sp<ConfigListener>& listener : mListeners) {
broadcastList.push_back(listener);
}
@@ -266,7 +283,7 @@
return ret;
}
-const sp<android::IBinder> ConfigManager::GetConfigReceiver(const ConfigKey& key) const {
+const sp<IPendingIntentRef> ConfigManager::GetConfigReceiver(const ConfigKey& key) const {
lock_guard<mutex> lock(mMutex);
auto it = mConfigReceivers.find(key);
@@ -277,7 +294,7 @@
}
}
-const sp<android::IBinder> ConfigManager::GetActiveConfigsChangedReceiver(const int uid) const {
+const sp<IPendingIntentRef> ConfigManager::GetActiveConfigsChangedReceiver(const int uid) const {
lock_guard<mutex> lock(mMutex);
auto it = mActiveConfigsChangedReceivers.find(uid);
diff --git a/cmds/statsd/src/config/ConfigManager.h b/cmds/statsd/src/config/ConfigManager.h
index c064a51..88e864a 100644
--- a/cmds/statsd/src/config/ConfigManager.h
+++ b/cmds/statsd/src/config/ConfigManager.h
@@ -16,10 +16,10 @@
#pragma once
-#include "binder/IBinder.h"
#include "config/ConfigKey.h"
#include "config/ConfigListener.h"
+#include <android/os/IPendingIntentRef.h>
#include <map>
#include <mutex>
#include <set>
@@ -64,12 +64,12 @@
/**
* Sets the broadcast receiver for a configuration key.
*/
- void SetConfigReceiver(const ConfigKey& key, const sp<IBinder>& intentSender);
+ void SetConfigReceiver(const ConfigKey& key, const sp<IPendingIntentRef>& pir);
/**
* Returns the package name and class name representing the broadcast receiver for this config.
*/
- const sp<android::IBinder> GetConfigReceiver(const ConfigKey& key) const;
+ const sp<IPendingIntentRef> GetConfigReceiver(const ConfigKey& key) const;
/**
* Returns all config keys registered.
@@ -85,13 +85,13 @@
* Sets the broadcast receiver that is notified whenever the list of active configs
* changes for this uid.
*/
- void SetActiveConfigsChangedReceiver(const int uid, const sp<IBinder>& intentSender);
+ void SetActiveConfigsChangedReceiver(const int uid, const sp<IPendingIntentRef>& pir);
/**
* Returns the broadcast receiver for active configs changed for this uid.
*/
- const sp<IBinder> GetActiveConfigsChangedReceiver(const int uid) const;
+ const sp<IPendingIntentRef> GetActiveConfigsChangedReceiver(const int uid) const;
/**
* Erase any active configs changed broadcast receiver associated with this uid.
@@ -141,16 +141,15 @@
std::map<int, std::set<ConfigKey>> mConfigs;
/**
- * Each config key can be subscribed by up to one receiver, specified as IBinder from
- * PendingIntent.
+ * Each config key can be subscribed by up to one receiver, specified as IPendingIntentRef.
*/
- std::map<ConfigKey, sp<android::IBinder>> mConfigReceivers;
+ std::map<ConfigKey, sp<IPendingIntentRef>> mConfigReceivers;
/**
* Each uid can be subscribed by up to one receiver to notify that the list of active configs
- * for this uid has changed. The receiver is specified as IBinder from PendingIntent.
+ * for this uid has changed. The receiver is specified as IPendingIntentRef.
*/
- std::map<int, sp<android::IBinder>> mActiveConfigsChangedReceivers;
+ std::map<int, sp<IPendingIntentRef>> mActiveConfigsChangedReceivers;
/**
* The ConfigListeners that will be told about changes.
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 35c6d37..e85b975 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -207,10 +207,7 @@
&linkedConditionDimensionKey);
if (trueConditionDimensions.find(linkedConditionDimensionKey) !=
trueConditionDimensions.end()) {
- for (auto& condIt : whatIt.second) {
- condIt.second->onConditionChanged(
- currentUnSlicedPartCondition, eventTime);
- }
+ whatIt.second->onConditionChanged(currentUnSlicedPartCondition, eventTime);
}
}
} else {
@@ -222,15 +219,11 @@
&linkedConditionDimensionKey);
if (dimensionsChangedToTrue->find(linkedConditionDimensionKey) !=
dimensionsChangedToTrue->end()) {
- for (auto& condIt : whatIt.second) {
- condIt.second->onConditionChanged(true, eventTime);
- }
+ whatIt.second->onConditionChanged(true, eventTime);
}
if (dimensionsChangedToFalse->find(linkedConditionDimensionKey) !=
dimensionsChangedToFalse->end()) {
- for (auto& condIt : whatIt.second) {
- condIt.second->onConditionChanged(false, eventTime);
- }
+ whatIt.second->onConditionChanged(false, eventTime);
}
}
}
@@ -247,9 +240,7 @@
// Now for each of the on-going event, check if the condition has changed for them.
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- for (auto& pair : whatIt.second) {
- pair.second->onSlicedConditionMayChange(overallCondition, eventTimeNs);
- }
+ whatIt.second->onSlicedConditionMayChange(overallCondition, eventTimeNs);
}
}
@@ -283,18 +274,14 @@
}
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- for (auto& pair : whatIt.second) {
- pair.second->onConditionChanged(mIsActive, eventTimeNs);
- }
+ whatIt.second->onConditionChanged(mIsActive, eventTimeNs);
}
} else if (mIsActive) {
flushIfNeededLocked(eventTimeNs);
onSlicedConditionMayChangeInternalLocked(mIsActive, eventTimeNs);
} else { // mConditionSliced == true && !mIsActive
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- for (auto& pair : whatIt.second) {
- pair.second->onConditionChanged(mIsActive, eventTimeNs);
- }
+ whatIt.second->onConditionChanged(mIsActive, eventTimeNs);
}
}
}
@@ -310,9 +297,7 @@
flushIfNeededLocked(eventTime);
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- for (auto& pair : whatIt.second) {
- pair.second->onConditionChanged(conditionMet, eventTime);
- }
+ whatIt.second->onConditionChanged(conditionMet, eventTime);
}
}
@@ -425,19 +410,11 @@
const int64_t& nextBucketStartTimeNs) {
for (auto whatIt = mCurrentSlicedDurationTrackerMap.begin();
whatIt != mCurrentSlicedDurationTrackerMap.end();) {
- for (auto it = whatIt->second.begin(); it != whatIt->second.end();) {
- if (it->second->flushCurrentBucket(eventTimeNs, &mPastBuckets)) {
- VLOG("erase bucket for key %s %s", whatIt->first.toString().c_str(),
- it->first.toString().c_str());
- it = whatIt->second.erase(it);
- } else {
- ++it;
- }
- }
- if (whatIt->second.empty()) {
+ if (whatIt->second->flushCurrentBucket(eventTimeNs, &mPastBuckets)) {
+ VLOG("erase bucket for key %s", whatIt->first.toString().c_str());
whatIt = mCurrentSlicedDurationTrackerMap.erase(whatIt);
} else {
- whatIt++;
+ ++whatIt;
}
}
StatsdStats::getInstance().noteBucketCount(mMetricId);
@@ -453,35 +430,15 @@
(unsigned long)mCurrentSlicedDurationTrackerMap.size());
if (verbose) {
for (const auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- for (const auto& slice : whatIt.second) {
- fprintf(out, "\t(what)%s\t(states)%s\n", whatIt.first.toString().c_str(),
- slice.first.toString().c_str());
- slice.second->dumpStates(out, verbose);
- }
+ fprintf(out, "\t(what)%s\n", whatIt.first.toString().c_str());
+ whatIt.second->dumpStates(out, verbose);
}
}
}
bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
auto whatIt = mCurrentSlicedDurationTrackerMap.find(newKey.getDimensionKeyInWhat());
- if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
- auto stateIt = whatIt->second.find(newKey.getStateValuesKey());
- if (stateIt != whatIt->second.end()) {
- return false;
- }
- if (whatIt->second.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
- size_t newTupleCount = whatIt->second.size() + 1;
- StatsdStats::getInstance().noteMetricDimensionInConditionSize(
- mConfigKey, mMetricId, newTupleCount);
- // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
- if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
- ALOGE("DurationMetric %lld dropping data for state values key %s",
- (long long)mMetricId, newKey.getStateValuesKey().toString().c_str());
- StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId);
- return true;
- }
- }
- } else {
+ if (whatIt == mCurrentSlicedDurationTrackerMap.end()) {
// 1. Report the tuple count if the tuple count > soft limit
if (mCurrentSlicedDurationTrackerMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
size_t newTupleCount = mCurrentSlicedDurationTrackerMap.size() + 1;
@@ -503,24 +460,16 @@
const ConditionKey& conditionKeys,
bool condition, const LogEvent& event) {
const auto& whatKey = eventKey.getDimensionKeyInWhat();
- const auto& stateKey = eventKey.getStateValuesKey();
auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatKey);
if (whatIt == mCurrentSlicedDurationTrackerMap.end()) {
if (hitGuardRailLocked(eventKey)) {
return;
}
- mCurrentSlicedDurationTrackerMap[whatKey][stateKey] = createDurationTracker(eventKey);
- } else {
- if (whatIt->second.find(stateKey) == whatIt->second.end()) {
- if (hitGuardRailLocked(eventKey)) {
- return;
- }
- mCurrentSlicedDurationTrackerMap[whatKey][stateKey] = createDurationTracker(eventKey);
- }
+ mCurrentSlicedDurationTrackerMap[whatKey] = createDurationTracker(eventKey);
}
- auto it = mCurrentSlicedDurationTrackerMap.find(whatKey)->second.find(stateKey);
+ auto it = mCurrentSlicedDurationTrackerMap.find(whatKey);
if (mUseWhatDimensionAsInternalDimension) {
it->second->noteStart(whatKey, condition,
event.GetElapsedTimestampNs(), conditionKeys);
@@ -560,18 +509,14 @@
// Handles Stopall events.
if (matcherIndex == mStopAllIndex) {
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- for (auto& pair : whatIt.second) {
- pair.second->noteStopAll(event.GetElapsedTimestampNs());
- }
+ whatIt.second->noteStopAll(event.GetElapsedTimestampNs());
}
return;
}
- HashableDimensionKey dimensionInWhat;
+ HashableDimensionKey dimensionInWhat = DEFAULT_DIMENSION_KEY;
if (!mDimensionsInWhat.empty()) {
filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat);
- } else {
- dimensionInWhat = DEFAULT_DIMENSION_KEY;
}
// Handles Stop events.
@@ -579,9 +524,7 @@
if (mUseWhatDimensionAsInternalDimension) {
auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat);
if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
- for (const auto& stateIt : whatIt->second) {
- stateIt.second->noteStop(dimensionInWhat, event.GetElapsedTimestampNs(), false);
- }
+ whatIt->second->noteStop(dimensionInWhat, event.GetElapsedTimestampNs(), false);
}
return;
}
@@ -593,10 +536,7 @@
auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat);
if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
- for (const auto& stateIt : whatIt->second) {
- stateIt.second->noteStop(internalDimensionKey, event.GetElapsedTimestampNs(),
- false);
- }
+ whatIt->second->noteStop(internalDimensionKey, event.GetElapsedTimestampNs(), false);
}
return;
}
@@ -619,8 +559,8 @@
condition = condition && mIsActive;
- handleStartEvent(MetricDimensionKey(dimensionInWhat, DEFAULT_DIMENSION_KEY),
- conditionKey, condition, event);
+ handleStartEvent(MetricDimensionKey(dimensionInWhat, DEFAULT_DIMENSION_KEY), conditionKey,
+ condition, event);
}
size_t DurationMetricProducer::byteSizeLocked() const {
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 45908fb..06da0f6 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -132,8 +132,7 @@
std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>> mPastBuckets;
// The duration trackers in the current bucket.
- std::unordered_map<HashableDimensionKey,
- std::unordered_map<HashableDimensionKey, std::unique_ptr<DurationTracker>>>
+ std::unordered_map<HashableDimensionKey, std::unique_ptr<DurationTracker>>
mCurrentSlicedDurationTrackerMap;
// Helper function to create a duration tracker given the metric aggregation type.
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index 6b5c299..afe93d4 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -82,8 +82,6 @@
virtual ~DurationTracker(){};
- virtual unique_ptr<DurationTracker> clone(const int64_t eventTime) = 0;
-
virtual void noteStart(const HashableDimensionKey& key, bool condition,
const int64_t eventTime, const ConditionKey& conditionKey) = 0;
virtual void noteStop(const HashableDimensionKey& key, const int64_t eventTime,
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
index df66cb0..2be5855 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -37,24 +37,6 @@
conditionSliced, fullLink, anomalyTrackers) {
}
-unique_ptr<DurationTracker> MaxDurationTracker::clone(const int64_t eventTime) {
- auto clonedTracker = make_unique<MaxDurationTracker>(*this);
- for (auto it = clonedTracker->mInfos.begin(); it != clonedTracker->mInfos.end();) {
- if (it->second.state != kStopped) {
- it->second.lastStartTime = eventTime;
- it->second.lastDuration = 0;
- it++;
- } else {
- it = clonedTracker->mInfos.erase(it);
- }
- }
- if (clonedTracker->mInfos.empty()) {
- return nullptr;
- } else {
- return clonedTracker;
- }
-}
-
bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
// ===========GuardRail==============
if (mInfos.find(newKey) != mInfos.end()) {
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
index d0371da..efb8dc7 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
@@ -38,8 +38,6 @@
MaxDurationTracker(const MaxDurationTracker& tracker) = default;
- unique_ptr<DurationTracker> clone(const int64_t eventTime) override;
-
void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime,
const ConditionKey& conditionKey) override;
void noteStop(const HashableDimensionKey& key, const int64_t eventTime,
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
index b0fd975..57f3965 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -38,13 +38,6 @@
mLastStartTime = 0;
}
-unique_ptr<DurationTracker> OringDurationTracker::clone(const int64_t eventTime) {
- auto clonedTracker = make_unique<OringDurationTracker>(*this);
- clonedTracker->mLastStartTime = eventTime;
- clonedTracker->mDuration = 0;
- return clonedTracker;
-}
-
bool OringDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
// ===========GuardRail==============
// 1. Report the tuple count if the tuple count > soft limit
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
index 43c48d5..c3aad66 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
@@ -37,8 +37,6 @@
OringDurationTracker(const OringDurationTracker& tracker) = default;
- unique_ptr<DurationTracker> clone(const int64_t eventTime) override;
-
void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime,
const ConditionKey& conditionKey) override;
void noteStop(const HashableDimensionKey& key, const int64_t eventTime,
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.cpp b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
index 25d2257..a37cad1 100644
--- a/cmds/statsd/src/subscriber/SubscriberReporter.cpp
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
@@ -19,7 +19,6 @@
#include "SubscriberReporter.h"
-using android::IBinder;
using std::lock_guard;
using std::unordered_map;
@@ -29,12 +28,32 @@
using std::vector;
+class BroadcastSubscriberDeathRecipient : public android::IBinder::DeathRecipient {
+ public:
+ BroadcastSubscriberDeathRecipient(const ConfigKey& configKey, int64_t subscriberId):
+ mConfigKey(configKey),
+ mSubscriberId(subscriberId) {}
+ ~BroadcastSubscriberDeathRecipient() override = default;
+ private:
+ ConfigKey mConfigKey;
+ int64_t mSubscriberId;
+
+ void binderDied(const android::wp<android::IBinder>& who) override {
+ if (IInterface::asBinder(SubscriberReporter::getInstance().getBroadcastSubscriber(
+ mConfigKey, mSubscriberId)) == who.promote()) {
+ SubscriberReporter::getInstance().unsetBroadcastSubscriber(mConfigKey, mSubscriberId);
+ }
+ }
+};
+
void SubscriberReporter::setBroadcastSubscriber(const ConfigKey& configKey,
int64_t subscriberId,
- const sp<IBinder>& intentSender) {
+ const sp<IPendingIntentRef>& pir) {
VLOG("SubscriberReporter::setBroadcastSubscriber called.");
lock_guard<std::mutex> lock(mLock);
- mIntentMap[configKey][subscriberId] = intentSender;
+ mIntentMap[configKey][subscriberId] = pir;
+ IInterface::asBinder(pir)->linkToDeath(
+ new BroadcastSubscriberDeathRecipient(configKey, subscriberId));
}
void SubscriberReporter::unsetBroadcastSubscriber(const ConfigKey& configKey,
@@ -50,12 +69,6 @@
}
}
-void SubscriberReporter::removeConfig(const ConfigKey& configKey) {
- VLOG("SubscriberReporter::removeConfig called.");
- lock_guard<std::mutex> lock(mLock);
- mIntentMap.erase(configKey);
-}
-
void SubscriberReporter::alertBroadcastSubscriber(const ConfigKey& configKey,
const Subscription& subscription,
const MetricDimensionKey& dimKey) const {
@@ -97,18 +110,13 @@
sendBroadcastLocked(it2->second, configKey, subscription, cookies, dimKey);
}
-void SubscriberReporter::sendBroadcastLocked(const sp<IBinder>& intentSender,
+void SubscriberReporter::sendBroadcastLocked(const sp<IPendingIntentRef>& pir,
const ConfigKey& configKey,
const Subscription& subscription,
const vector<String16>& cookies,
const MetricDimensionKey& dimKey) const {
VLOG("SubscriberReporter::sendBroadcastLocked called.");
- if (mStatsCompanionService == nullptr) {
- ALOGW("Failed to send subscriber broadcast: could not access StatsCompanionService.");
- return;
- }
- mStatsCompanionService->sendSubscriberBroadcast(
- intentSender,
+ pir->sendSubscriberBroadcast(
configKey.GetUid(),
configKey.GetId(),
subscription.id(),
@@ -117,6 +125,20 @@
getStatsDimensionsValue(dimKey.getDimensionKeyInWhat()));
}
+sp<IPendingIntentRef> SubscriberReporter::getBroadcastSubscriber(const ConfigKey& configKey,
+ int64_t subscriberId) {
+ lock_guard<std::mutex> lock(mLock);
+ auto subscriberMapIt = mIntentMap.find(configKey);
+ if (subscriberMapIt == mIntentMap.end()) {
+ return nullptr;
+ }
+ auto pirMapIt = subscriberMapIt->second.find(subscriberId);
+ if (pirMapIt == subscriberMapIt->second.end()) {
+ return nullptr;
+ }
+ return pirMapIt->second;
+}
+
void getStatsDimensionsValueHelper(const vector<FieldValue>& dims, size_t* index, int depth,
int prefix, vector<StatsDimensionsValue>* output) {
size_t count = dims.size();
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.h b/cmds/statsd/src/subscriber/SubscriberReporter.h
index 2a7f771..087a1b8 100644
--- a/cmds/statsd/src/subscriber/SubscriberReporter.h
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.h
@@ -16,6 +16,7 @@
#pragma once
+#include <android/os/IPendingIntentRef.h>
#include <android/os/IStatsCompanionService.h>
#include <utils/RefBase.h>
@@ -47,32 +48,17 @@
void operator=(SubscriberReporter const&) = delete;
/**
- * Tells SubscriberReporter what IStatsCompanionService to use.
- * May be nullptr, but SubscriberReporter will not send broadcasts for any calls
- * to alertBroadcastSubscriber that occur while nullptr.
- */
- void setStatsCompanionService(sp<IStatsCompanionService> statsCompanionService) {
- std::lock_guard<std::mutex> lock(mLock);
- sp<IStatsCompanionService> tmpForLock = mStatsCompanionService;
- mStatsCompanionService = statsCompanionService;
- }
-
- /**
* Stores the given intentSender, associating it with the given (configKey, subscriberId) pair.
- * intentSender must be convertible into an IntentSender (in Java) using IntentSender(IBinder).
*/
void setBroadcastSubscriber(const ConfigKey& configKey,
int64_t subscriberId,
- const sp<android::IBinder>& intentSender);
+ const sp<IPendingIntentRef>& pir);
/**
* Erases any intentSender information from the given (configKey, subscriberId) pair.
*/
void unsetBroadcastSubscriber(const ConfigKey& configKey, int64_t subscriberId);
- /** Remove all information stored by SubscriberReporter about the given config. */
- void removeConfig(const ConfigKey& configKey);
-
/**
* Sends a broadcast via the intentSender previously stored for the
* given (configKey, subscriberId) pair by setBroadcastSubscriber.
@@ -82,6 +68,8 @@
const Subscription& subscription,
const MetricDimensionKey& dimKey) const;
+ sp<IPendingIntentRef> getBroadcastSubscriber(const ConfigKey& configKey, int64_t subscriberId);
+
static StatsDimensionsValue getStatsDimensionsValue(const HashableDimensionKey& dim);
private:
@@ -92,15 +80,15 @@
/** Binder interface for communicating with StatsCompanionService. */
sp<IStatsCompanionService> mStatsCompanionService = nullptr;
- /** Maps <ConfigKey, SubscriberId> -> IBinder (which represents an IIntentSender). */
+ /** Maps <ConfigKey, SubscriberId> -> IPendingIntentRef (which represents a PendingIntent). */
std::unordered_map<ConfigKey,
- std::unordered_map<int64_t, sp<android::IBinder>>> mIntentMap;
+ std::unordered_map<int64_t, sp<IPendingIntentRef>>> mIntentMap;
/**
* Sends a broadcast via the given intentSender (using mStatsCompanionService), along
* with the information in the other parameters.
*/
- void sendBroadcastLocked(const sp<android::IBinder>& intentSender,
+ void sendBroadcastLocked(const sp<IPendingIntentRef>& pir,
const ConfigKey& configKey,
const Subscription& subscription,
const std::vector<String16>& cookies,
diff --git a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
index 0bc3ebb..16b51d9 100644
--- a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
@@ -28,16 +28,16 @@
#ifdef __ANDROID__
-const string kAndroid = "android";
const string kApp1 = "app1.sharing.1";
const int kConfigKey = 789130123; // Randomly chosen to avoid collisions with existing configs.
+const int kCallingUid = 0; // Randomly chosen
void SendConfig(StatsService& service, const StatsdConfig& config) {
string str;
config.SerializeToString(&str);
std::vector<uint8_t> configAsVec(str.begin(), str.end());
bool success;
- service.addConfiguration(kConfigKey, configAsVec, String16(kAndroid.c_str()));
+ service.addConfiguration(kConfigKey, configAsVec, kCallingUid);
}
ConfigMetricsReport GetReports(sp<StatsLogProcessor> processor, int64_t timestamp,
@@ -50,7 +50,7 @@
ConfigMetricsReportList reports;
reports.ParseFromArray(output.data(), output.size());
EXPECT_EQ(1, reports.reports_size());
- return reports.reports(0);
+ return reports.reports(kCallingUid);
}
StatsdConfig MakeConfig() {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 3f9f7fb..9aef20b 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -3107,7 +3107,7 @@
public static final int IMPORTANCE_CANT_SAVE_STATE_PRE_26 = 170;
/**
- * Constant for {@link #importance}: This process is contains services
+ * Constant for {@link #importance}: This process contains services
* that should remain running. These are background services apps have
* started, not something the user is aware of, so they may be killed by
* the system relatively freely (though it is generally desired that they
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index de7cc9d..032e824 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -278,6 +278,9 @@
public abstract boolean isBackgroundActivityStartsEnabled();
public abstract void reportCurKeyguardUsageEvent(boolean keyguardShowing);
+ /** @see com.android.server.am.ActivityManagerService#monitor */
+ public abstract void monitor();
+
/** Input dispatch timeout to a window, start the ANR process. */
public abstract long inputDispatchingTimedOut(int pid, boolean aboveSystem, String reason);
public abstract boolean inputDispatchingTimedOut(Object proc, String activityShortComponentName,
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index b5ac37b..4a8e4e2 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -850,10 +850,12 @@
public static final int OP_QUERY_ALL_PACKAGES = 91;
/** @hide Access all external storage */
public static final int OP_MANAGE_EXTERNAL_STORAGE = 92;
+ /** @hide Communicate cross-profile within the same profile group. */
+ public static final int OP_INTERACT_ACROSS_PROFILES = 93;
/** @hide */
@UnsupportedAppUsage
- public static final int _NUM_OP = 93;
+ public static final int _NUM_OP = 94;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1144,6 +1146,10 @@
public static final String OPSTR_MANAGE_EXTERNAL_STORAGE =
"android:manage_external_storage";
+ /** @hide Communicate cross-profile within the same profile group. */
+ @SystemApi
+ public static final String OPSTR_INTERACT_ACROSS_PROFILES = "android:interact_across_profiles";
+
/** {@link #sAppOpsToNote} not initialized yet for this op */
private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
@@ -1219,7 +1225,9 @@
OP_START_FOREGROUND,
OP_SMS_FINANCIAL_TRANSACTIONS,
OP_MANAGE_IPSEC_TUNNELS,
- OP_INSTANT_APP_START_FOREGROUND
+ OP_INSTANT_APP_START_FOREGROUND,
+ OP_MANAGE_EXTERNAL_STORAGE,
+ OP_INTERACT_ACROSS_PROFILES
};
/**
@@ -1324,6 +1332,7 @@
OP_ACCESS_MEDIA_LOCATION, // ACCESS_MEDIA_LOCATION
OP_QUERY_ALL_PACKAGES, // QUERY_ALL_PACKAGES
OP_MANAGE_EXTERNAL_STORAGE, // MANAGE_EXTERNAL_STORAGE
+ OP_INTERACT_ACROSS_PROFILES, //INTERACT_ACROSS_PROFILES
};
/**
@@ -1423,6 +1432,7 @@
OPSTR_ACCESS_MEDIA_LOCATION,
OPSTR_QUERY_ALL_PACKAGES,
OPSTR_MANAGE_EXTERNAL_STORAGE,
+ OPSTR_INTERACT_ACROSS_PROFILES,
};
/**
@@ -1522,7 +1532,8 @@
"READ_DEVICE_IDENTIFIERS",
"ACCESS_MEDIA_LOCATION",
"QUERY_ALL_PACKAGES",
- "MANAGE_EXTERNAL_STORAGE"
+ "MANAGE_EXTERNAL_STORAGE",
+ "INTERACT_ACROSS_PROFILES"
};
/**
@@ -1623,7 +1634,8 @@
null, // no direct permission for OP_READ_DEVICE_IDENTIFIERS
Manifest.permission.ACCESS_MEDIA_LOCATION,
null, // no permission for OP_QUERY_ALL_PACKAGES
- null, // no permission for OP_MANAGE_EXTERNAL_STORAGE
+ Manifest.permission.MANAGE_EXTERNAL_STORAGE,
+ android.Manifest.permission.INTERACT_ACROSS_PROFILES,
};
/**
@@ -1725,6 +1737,7 @@
null, // ACCESS_MEDIA_LOCATION
null, // QUERY_ALL_PACKAGES
null, // MANAGE_EXTERNAL_STORAGE
+ null, // INTERACT_ACROSS_PROFILES
};
/**
@@ -1825,6 +1838,7 @@
false, // ACCESS_MEDIA_LOCATION
false, // QUERY_ALL_PACKAGES
false, // MANAGE_EXTERNAL_STORAGE
+ false, // INTERACT_ACROSS_PROFILES
};
/**
@@ -1924,6 +1938,7 @@
AppOpsManager.MODE_ALLOWED, // ALLOW_MEDIA_LOCATION
AppOpsManager.MODE_DEFAULT, // QUERY_ALL_PACKAGES
AppOpsManager.MODE_DEFAULT, // MANAGE_EXTERNAL_STORAGE
+ AppOpsManager.MODE_DEFAULT, // INTERACT_ACROSS_PROFILES
};
/**
@@ -2027,6 +2042,7 @@
false, // ACCESS_MEDIA_LOCATION
false, // QUERY_ALL_PACKAGES
false, // MANAGE_EXTERNAL_STORAGE
+ false, // INTERACT_ACROSS_PROFILES
};
/**
@@ -3901,10 +3917,53 @@
void visitHistoricalOps(@NonNull HistoricalOps ops);
void visitHistoricalUidOps(@NonNull HistoricalUidOps ops);
void visitHistoricalPackageOps(@NonNull HistoricalPackageOps ops);
+ void visitHistoricalFeatureOps(@NonNull HistoricalFeatureOps ops);
void visitHistoricalOp(@NonNull HistoricalOp ops);
}
/**
+ * Specifies what parameters to filter historical appop requests for
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, prefix = { "FILTER_BY_" }, value = {
+ FILTER_BY_UID,
+ FILTER_BY_PACKAGE_NAME,
+ FILTER_BY_FEATURE_ID,
+ FILTER_BY_OP_NAMES
+ })
+ public @interface HistoricalOpsRequestFilter {}
+
+ /**
+ * Filter historical appop request by uid.
+ *
+ * @hide
+ */
+ public static final int FILTER_BY_UID = 1<<0;
+
+ /**
+ * Filter historical appop request by package name.
+ *
+ * @hide
+ */
+ public static final int FILTER_BY_PACKAGE_NAME = 1<<1;
+
+ /**
+ * Filter historical appop request by feature id.
+ *
+ * @hide
+ */
+ public static final int FILTER_BY_FEATURE_ID = 1<<2;
+
+ /**
+ * Filter historical appop request by op names.
+ *
+ * @hide
+ */
+ public static final int FILTER_BY_OP_NAMES = 1<<3;
+
+ /**
* Request for getting historical app op usage. The request acts
* as a filtering criteria when querying historical op usage.
*
@@ -3916,17 +3975,22 @@
public static final class HistoricalOpsRequest {
private final int mUid;
private final @Nullable String mPackageName;
+ private final @Nullable String mFeatureId;
private final @Nullable List<String> mOpNames;
+ private final @HistoricalOpsRequestFilter int mFilter;
private final long mBeginTimeMillis;
private final long mEndTimeMillis;
private final @OpFlags int mFlags;
private HistoricalOpsRequest(int uid, @Nullable String packageName,
- @Nullable List<String> opNames, long beginTimeMillis, long endTimeMillis,
- @OpFlags int flags) {
+ @Nullable String featureId, @Nullable List<String> opNames,
+ @HistoricalOpsRequestFilter int filter, long beginTimeMillis,
+ long endTimeMillis, @OpFlags int flags) {
mUid = uid;
mPackageName = packageName;
+ mFeatureId = featureId;
mOpNames = opNames;
+ mFilter = filter;
mBeginTimeMillis = beginTimeMillis;
mEndTimeMillis = endTimeMillis;
mFlags = flags;
@@ -3942,7 +4006,9 @@
public static final class Builder {
private int mUid = Process.INVALID_UID;
private @Nullable String mPackageName;
+ private @Nullable String mFeatureId;
private @Nullable List<String> mOpNames;
+ private @HistoricalOpsRequestFilter int mFilter;
private final long mBeginTimeMillis;
private final long mEndTimeMillis;
private @OpFlags int mFlags = OP_FLAGS_ALL;
@@ -3975,6 +4041,13 @@
Preconditions.checkArgument(uid == Process.INVALID_UID || uid >= 0,
"uid must be " + Process.INVALID_UID + " or non negative");
mUid = uid;
+
+ if (uid == Process.INVALID_UID) {
+ mFilter &= ~FILTER_BY_UID;
+ } else {
+ mFilter |= FILTER_BY_UID;
+ }
+
return this;
}
@@ -3986,6 +4059,26 @@
*/
public @NonNull Builder setPackageName(@Nullable String packageName) {
mPackageName = packageName;
+
+ if (packageName == null) {
+ mFilter &= ~FILTER_BY_PACKAGE_NAME;
+ } else {
+ mFilter |= FILTER_BY_PACKAGE_NAME;
+ }
+
+ return this;
+ }
+
+ /**
+ * Sets the feature id to query for.
+ *
+ * @param featureId The id of the feature.
+ * @return This builder.
+ */
+ public @NonNull Builder setFeatureId(@Nullable String featureId) {
+ mFeatureId = featureId;
+ mFilter |= FILTER_BY_FEATURE_ID;
+
return this;
}
@@ -4004,6 +4097,13 @@
}
}
mOpNames = opNames;
+
+ if (mOpNames == null) {
+ mFilter &= ~FILTER_BY_OP_NAMES;
+ } else {
+ mFilter |= FILTER_BY_OP_NAMES;
+ }
+
return this;
}
@@ -4028,8 +4128,8 @@
* @return a new {@link HistoricalOpsRequest}.
*/
public @NonNull HistoricalOpsRequest build() {
- return new HistoricalOpsRequest(mUid, mPackageName, mOpNames,
- mBeginTimeMillis, mEndTimeMillis, mFlags);
+ return new HistoricalOpsRequest(mUid, mPackageName, mFeatureId, mOpNames,
+ mFilter, mBeginTimeMillis, mEndTimeMillis, mFlags);
}
}
}
@@ -4186,15 +4286,18 @@
/**
* AppPermissionUsage the ops to leave only the data we filter for.
*
- * @param uid Uid to filter for or {@link android.os.Process#INCIDENTD_UID} for all.
- * @param packageName Package to filter for or null for all.
- * @param opNames Ops to filter for or null for all.
+ * @param uid Uid to filter for.
+ * @param packageName Package to filter for.
+ * @param featureId Package to filter for.
+ * @param opNames Ops to filter for.
+ * @param filter Which parameters to filter on.
* @param beginTimeMillis The begin time to filter for or {@link Long#MIN_VALUE} for all.
* @param endTimeMillis The end time to filter for or {@link Long#MAX_VALUE} for all.
*
* @hide
*/
- public void filter(int uid, @Nullable String packageName, @Nullable String[] opNames,
+ public void filter(int uid, @Nullable String packageName, @Nullable String featureId,
+ @Nullable String[] opNames, @HistoricalOpsRequestFilter int filter,
long beginTimeMillis, long endTimeMillis) {
final long durationMillis = getDurationMillis();
mBeginTimeMillis = Math.max(mBeginTimeMillis, beginTimeMillis);
@@ -4204,10 +4307,10 @@
final int uidCount = getUidCount();
for (int i = uidCount - 1; i >= 0; i--) {
final HistoricalUidOps uidOp = mHistoricalUidOps.valueAt(i);
- if (uid != Process.INVALID_UID && uid != uidOp.getUid()) {
+ if ((filter & FILTER_BY_UID) != 0 && uid != uidOp.getUid()) {
mHistoricalUidOps.removeAt(i);
} else {
- uidOp.filter(packageName, opNames, scaleFactor);
+ uidOp.filter(packageName, featureId, opNames, filter, scaleFactor);
}
}
}
@@ -4235,25 +4338,28 @@
/** @hide */
@TestApi
public void increaseAccessCount(int opCode, int uid, @NonNull String packageName,
- @UidState int uidState, @OpFlags int flags, long increment) {
+ @Nullable String featureId, @UidState int uidState, @OpFlags int flags,
+ long increment) {
getOrCreateHistoricalUidOps(uid).increaseAccessCount(opCode,
- packageName, uidState, flags, increment);
+ packageName, featureId, uidState, flags, increment);
}
/** @hide */
@TestApi
public void increaseRejectCount(int opCode, int uid, @NonNull String packageName,
- @UidState int uidState, @OpFlags int flags, long increment) {
+ @Nullable String featureId, @UidState int uidState, @OpFlags int flags,
+ long increment) {
getOrCreateHistoricalUidOps(uid).increaseRejectCount(opCode,
- packageName, uidState, flags, increment);
+ packageName, featureId, uidState, flags, increment);
}
/** @hide */
@TestApi
public void increaseAccessDuration(int opCode, int uid, @NonNull String packageName,
- @UidState int uidState, @OpFlags int flags, long increment) {
+ @Nullable String featureId, @UidState int uidState, @OpFlags int flags,
+ long increment) {
getOrCreateHistoricalUidOps(uid).increaseAccessDuration(opCode,
- packageName, uidState, flags, increment);
+ packageName, featureId, uidState, flags, increment);
}
/** @hide */
@@ -4533,15 +4639,17 @@
}
}
- private void filter(@Nullable String packageName, @Nullable String[] opNames,
+ private void filter(@Nullable String packageName, @Nullable String featureId,
+ @Nullable String[] opNames, @HistoricalOpsRequestFilter int filter,
double fractionToRemove) {
final int packageCount = getPackageCount();
for (int i = packageCount - 1; i >= 0; i--) {
final HistoricalPackageOps packageOps = getPackageOpsAt(i);
- if (packageName != null && !packageName.equals(packageOps.getPackageName())) {
+ if ((filter & FILTER_BY_PACKAGE_NAME) != 0 && !packageName.equals(
+ packageOps.getPackageName())) {
mHistoricalPackageOps.removeAt(i);
} else {
- packageOps.filter(opNames, fractionToRemove);
+ packageOps.filter(featureId, opNames, filter, fractionToRemove);
}
}
}
@@ -4558,21 +4666,24 @@
}
private void increaseAccessCount(int opCode, @NonNull String packageName,
- @UidState int uidState, @OpFlags int flags, long increment) {
+ @Nullable String featureId, @UidState int uidState, @OpFlags int flags,
+ long increment) {
getOrCreateHistoricalPackageOps(packageName).increaseAccessCount(
- opCode, uidState, flags, increment);
+ opCode, featureId, uidState, flags, increment);
}
private void increaseRejectCount(int opCode, @NonNull String packageName,
- @UidState int uidState, @OpFlags int flags, long increment) {
+ @Nullable String featureId, @UidState int uidState, @OpFlags int flags,
+ long increment) {
getOrCreateHistoricalPackageOps(packageName).increaseRejectCount(
- opCode, uidState, flags, increment);
+ opCode, featureId, uidState, flags, increment);
}
private void increaseAccessDuration(int opCode, @NonNull String packageName,
- @UidState int uidState, @OpFlags int flags, long increment) {
+ @Nullable String featureId, @UidState int uidState, @OpFlags int flags,
+ long increment) {
getOrCreateHistoricalPackageOps(packageName).increaseAccessDuration(
- opCode, uidState, flags, increment);
+ opCode, featureId, uidState, flags, increment);
}
/**
@@ -4717,7 +4828,7 @@
@SystemApi
public static final class HistoricalPackageOps implements Parcelable {
private final @NonNull String mPackageName;
- private @Nullable ArrayMap<String, HistoricalOp> mHistoricalOps;
+ private @Nullable ArrayMap<String, HistoricalFeatureOps> mHistoricalFeatureOps;
/** @hide */
public HistoricalPackageOps(@NonNull String packageName) {
@@ -4726,6 +4837,339 @@
private HistoricalPackageOps(@NonNull HistoricalPackageOps other) {
mPackageName = other.mPackageName;
+ final int opCount = other.getFeatureCount();
+ for (int i = 0; i < opCount; i++) {
+ final HistoricalFeatureOps origOps = other.getFeatureOpsAt(i);
+ final HistoricalFeatureOps cloneOps = new HistoricalFeatureOps(origOps);
+ if (mHistoricalFeatureOps == null) {
+ mHistoricalFeatureOps = new ArrayMap<>(opCount);
+ }
+ mHistoricalFeatureOps.put(cloneOps.getFeatureId(), cloneOps);
+ }
+ }
+
+ private HistoricalPackageOps(@NonNull Parcel parcel) {
+ mPackageName = parcel.readString();
+ mHistoricalFeatureOps = parcel.createTypedArrayMap(HistoricalFeatureOps.CREATOR);
+ }
+
+ private @Nullable HistoricalPackageOps splice(double fractionToRemove) {
+ HistoricalPackageOps splice = null;
+ final int featureCount = getFeatureCount();
+ for (int i = 0; i < featureCount; i++) {
+ final HistoricalFeatureOps origOps = getFeatureOpsAt(i);
+ final HistoricalFeatureOps spliceOps = origOps.splice(fractionToRemove);
+ if (spliceOps != null) {
+ if (splice == null) {
+ splice = new HistoricalPackageOps(mPackageName);
+ }
+ if (splice.mHistoricalFeatureOps == null) {
+ splice.mHistoricalFeatureOps = new ArrayMap<>();
+ }
+ splice.mHistoricalFeatureOps.put(spliceOps.getFeatureId(), spliceOps);
+ }
+ }
+ return splice;
+ }
+
+ private void merge(@NonNull HistoricalPackageOps other) {
+ final int featureCount = other.getFeatureCount();
+ for (int i = 0; i < featureCount; i++) {
+ final HistoricalFeatureOps otherFeatureOps = other.getFeatureOpsAt(i);
+ final HistoricalFeatureOps thisFeatureOps = getFeatureOps(
+ otherFeatureOps.getFeatureId());
+ if (thisFeatureOps != null) {
+ thisFeatureOps.merge(otherFeatureOps);
+ } else {
+ if (mHistoricalFeatureOps == null) {
+ mHistoricalFeatureOps = new ArrayMap<>();
+ }
+ mHistoricalFeatureOps.put(otherFeatureOps.getFeatureId(), otherFeatureOps);
+ }
+ }
+ }
+
+ private void filter(@Nullable String featureId, @Nullable String[] opNames,
+ @HistoricalOpsRequestFilter int filter, double fractionToRemove) {
+ final int featureCount = getFeatureCount();
+ for (int i = featureCount - 1; i >= 0; i--) {
+ final HistoricalFeatureOps featureOps = getFeatureOpsAt(i);
+ if ((filter & FILTER_BY_FEATURE_ID) != 0 && !Objects.equals(featureId,
+ featureOps.getFeatureId())) {
+ mHistoricalFeatureOps.removeAt(i);
+ } else {
+ featureOps.filter(opNames, filter, fractionToRemove);
+ }
+ }
+ }
+
+ private void accept(@NonNull HistoricalOpsVisitor visitor) {
+ visitor.visitHistoricalPackageOps(this);
+ final int featureCount = getFeatureCount();
+ for (int i = 0; i < featureCount; i++) {
+ getFeatureOpsAt(i).accept(visitor);
+ }
+ }
+
+ private boolean isEmpty() {
+ final int featureCount = getFeatureCount();
+ for (int i = featureCount - 1; i >= 0; i--) {
+ final HistoricalFeatureOps featureOps = mHistoricalFeatureOps.valueAt(i);
+ if (!featureOps.isEmpty()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void increaseAccessCount(int opCode, @Nullable String featureId,
+ @UidState int uidState, @OpFlags int flags, long increment) {
+ getOrCreateHistoricalFeatureOps(featureId).increaseAccessCount(
+ opCode, uidState, flags, increment);
+ }
+
+ private void increaseRejectCount(int opCode, @Nullable String featureId,
+ @UidState int uidState, @OpFlags int flags, long increment) {
+ getOrCreateHistoricalFeatureOps(featureId).increaseRejectCount(
+ opCode, uidState, flags, increment);
+ }
+
+ private void increaseAccessDuration(int opCode, @Nullable String featureId,
+ @UidState int uidState, @OpFlags int flags, long increment) {
+ getOrCreateHistoricalFeatureOps(featureId).increaseAccessDuration(
+ opCode, uidState, flags, increment);
+ }
+
+ /**
+ * Gets the package name which the data represents.
+ *
+ * @return The package name which the data represents.
+ */
+ public @NonNull String getPackageName() {
+ return mPackageName;
+ }
+
+ private @NonNull HistoricalFeatureOps getOrCreateHistoricalFeatureOps(
+ @Nullable String featureId) {
+ if (mHistoricalFeatureOps == null) {
+ mHistoricalFeatureOps = new ArrayMap<>();
+ }
+ HistoricalFeatureOps historicalFeatureOp = mHistoricalFeatureOps.get(featureId);
+ if (historicalFeatureOp == null) {
+ historicalFeatureOp = new HistoricalFeatureOps(featureId);
+ mHistoricalFeatureOps.put(featureId, historicalFeatureOp);
+ }
+ return historicalFeatureOp;
+ }
+
+ /**
+ * Gets number historical app ops.
+ *
+ * @return The number historical app ops.
+ * @see #getOpAt(int)
+ */
+ public @IntRange(from = 0) int getOpCount() {
+ int numOps = 0;
+ int numFeatures = getFeatureCount();
+
+ for (int code = 0; code < _NUM_OP; code++) {
+ String opName = opToPublicName(code);
+
+ for (int featureNum = 0; featureNum < numFeatures; featureNum++) {
+ if (getFeatureOpsAt(featureNum).getOp(opName) != null) {
+ numOps++;
+ break;
+ }
+ }
+ }
+
+ return numOps;
+ }
+
+ /**
+ * Gets the historical op at a given index.
+ *
+ * <p>This combines the counts from all features.
+ *
+ * @param index The index to lookup.
+ * @return The op at the given index.
+ * @see #getOpCount()
+ */
+ public @NonNull HistoricalOp getOpAt(@IntRange(from = 0) int index) {
+ int numOpsFound = 0;
+ int numFeatures = getFeatureCount();
+
+ for (int code = 0; code < _NUM_OP; code++) {
+ String opName = opToPublicName(code);
+
+ for (int featureNum = 0; featureNum < numFeatures; featureNum++) {
+ if (getFeatureOpsAt(featureNum).getOp(opName) != null) {
+ if (numOpsFound == index) {
+ return getOp(opName);
+ } else {
+ numOpsFound++;
+ break;
+ }
+ }
+ }
+ }
+
+ throw new IndexOutOfBoundsException();
+ }
+
+ /**
+ * Gets the historical entry for a given op name.
+ *
+ * <p>This combines the counts from all features.
+ *
+ * @param opName The op name.
+ * @return The historical entry for that op name.
+ */
+ public @Nullable HistoricalOp getOp(@NonNull String opName) {
+ if (mHistoricalFeatureOps == null) {
+ return null;
+ }
+
+ HistoricalOp combinedOp = null;
+ int numFeatures = getFeatureCount();
+ for (int i = 0; i < numFeatures; i++) {
+ HistoricalOp featureOp = getFeatureOpsAt(i).getOp(opName);
+ if (featureOp != null) {
+ if (combinedOp == null) {
+ combinedOp = new HistoricalOp(featureOp);
+ } else {
+ combinedOp.merge(featureOp);
+ }
+ }
+ }
+
+ return combinedOp;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeString(mPackageName);
+ parcel.writeTypedArrayMap(mHistoricalFeatureOps, flags);
+ }
+
+ public static final @android.annotation.NonNull Creator<HistoricalPackageOps> CREATOR =
+ new Creator<HistoricalPackageOps>() {
+ @Override
+ public @NonNull HistoricalPackageOps createFromParcel(@NonNull Parcel parcel) {
+ return new HistoricalPackageOps(parcel);
+ }
+
+ @Override
+ public @NonNull HistoricalPackageOps[] newArray(int size) {
+ return new HistoricalPackageOps[size];
+ }
+ };
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ final HistoricalPackageOps other = (HistoricalPackageOps) obj;
+ if (!mPackageName.equals(other.mPackageName)) {
+ return false;
+ }
+ if (mHistoricalFeatureOps == null) {
+ if (other.mHistoricalFeatureOps != null) {
+ return false;
+ }
+ } else if (!mHistoricalFeatureOps.equals(other.mHistoricalFeatureOps)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mPackageName != null ? mPackageName.hashCode() : 0;
+ result = 31 * result + (mHistoricalFeatureOps != null ? mHistoricalFeatureOps.hashCode()
+ : 0);
+ return result;
+ }
+
+ /**
+ * Gets number of feature with historical ops.
+ *
+ * @return The number of feature with historical ops.
+ *
+ * @see #getFeatureOpsAt(int)
+ */
+ public @IntRange(from = 0) int getFeatureCount() {
+ if (mHistoricalFeatureOps == null) {
+ return 0;
+ }
+ return mHistoricalFeatureOps.size();
+ }
+
+ /**
+ * Gets the historical feature ops at a given index.
+ *
+ * @param index The index.
+ *
+ * @return The historical feature ops at the given index.
+ *
+ * @see #getFeatureCount()
+ */
+ public @NonNull HistoricalFeatureOps getFeatureOpsAt(@IntRange(from = 0) int index) {
+ if (mHistoricalFeatureOps == null) {
+ throw new IndexOutOfBoundsException();
+ }
+ return mHistoricalFeatureOps.valueAt(index);
+ }
+
+ /**
+ * Gets the historical feature ops for a given feature.
+ *
+ * @param featureId The feature id.
+ *
+ * @return The historical ops for the feature.
+ */
+ public @Nullable HistoricalFeatureOps getFeatureOps(@NonNull String featureId) {
+ if (mHistoricalFeatureOps == null) {
+ return null;
+ }
+ return mHistoricalFeatureOps.get(featureId);
+ }
+ }
+
+ /**
+ * This class represents historical app op information about a feature in a package.
+ *
+ * @hide
+ */
+ @TestApi
+ @SystemApi
+ /* codegen verifier cannot deal with nested class parameters
+ @DataClass(genHiddenConstructor = true,
+ genEqualsHashCode = true, genHiddenCopyConstructor = true) */
+ @DataClass.Suppress("getHistoricalOps")
+ public static final class HistoricalFeatureOps implements Parcelable {
+ /** Id of the {@link Context#createFeatureContext feature} in the package */
+ private final @Nullable String mFeatureId;
+
+ /** Ops for this feature */
+ private @Nullable ArrayMap<String, HistoricalOp> mHistoricalOps;
+
+ /** @hide */
+ public HistoricalFeatureOps(@NonNull String featureId) {
+ mFeatureId = featureId;
+ }
+
+ private HistoricalFeatureOps(@NonNull HistoricalFeatureOps other) {
+ mFeatureId = other.mFeatureId;
final int opCount = other.getOpCount();
for (int i = 0; i < opCount; i++) {
final HistoricalOp origOp = other.getOpAt(i);
@@ -4737,20 +5181,15 @@
}
}
- private HistoricalPackageOps(@NonNull Parcel parcel) {
- mPackageName = parcel.readString();
- mHistoricalOps = parcel.createTypedArrayMap(HistoricalOp.CREATOR);
- }
-
- private @Nullable HistoricalPackageOps splice(double fractionToRemove) {
- HistoricalPackageOps splice = null;
+ private @Nullable HistoricalFeatureOps splice(double fractionToRemove) {
+ HistoricalFeatureOps splice = null;
final int opCount = getOpCount();
for (int i = 0; i < opCount; i++) {
final HistoricalOp origOps = getOpAt(i);
final HistoricalOp spliceOps = origOps.splice(fractionToRemove);
if (spliceOps != null) {
if (splice == null) {
- splice = new HistoricalPackageOps(mPackageName);
+ splice = new HistoricalFeatureOps(mFeatureId, null);
}
if (splice.mHistoricalOps == null) {
splice.mHistoricalOps = new ArrayMap<>();
@@ -4761,7 +5200,7 @@
return splice;
}
- private void merge(@NonNull HistoricalPackageOps other) {
+ private void merge(@NonNull HistoricalFeatureOps other) {
final int opCount = other.getOpCount();
for (int i = 0; i < opCount; i++) {
final HistoricalOp otherOp = other.getOpAt(i);
@@ -4777,11 +5216,13 @@
}
}
- private void filter(@Nullable String[] opNames, double scaleFactor) {
+ private void filter(@Nullable String[] opNames, @HistoricalOpsRequestFilter int filter,
+ double scaleFactor) {
final int opCount = getOpCount();
for (int i = opCount - 1; i >= 0; i--) {
final HistoricalOp op = mHistoricalOps.valueAt(i);
- if (opNames != null && !ArrayUtils.contains(opNames, op.getOpName())) {
+ if ((filter & FILTER_BY_OP_NAMES) != 0 && !ArrayUtils.contains(opNames,
+ op.getOpName())) {
mHistoricalOps.removeAt(i);
} else {
op.filter(scaleFactor);
@@ -4816,15 +5257,6 @@
}
/**
- * Gets the package name which the data represents.
- *
- * @return The package name which the data represents.
- */
- public @NonNull String getPackageName() {
- return mPackageName;
- }
-
- /**
* Gets number historical app ops.
*
* @return The number historical app ops.
@@ -4864,19 +5296,8 @@
return mHistoricalOps.get(opName);
}
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel parcel, int flags) {
- parcel.writeString(mPackageName);
- parcel.writeTypedArrayMap(mHistoricalOps, flags);
- }
-
private void accept(@NonNull HistoricalOpsVisitor visitor) {
- visitor.visitHistoricalPackageOps(this);
+ visitor.visitHistoricalFeatureOps(this);
final int opCount = getOpCount();
for (int i = 0; i < opCount; i++) {
getOpAt(i).accept(visitor);
@@ -4896,47 +5317,143 @@
return op;
}
- public static final @android.annotation.NonNull Creator<HistoricalPackageOps> CREATOR =
- new Creator<HistoricalPackageOps>() {
+
+
+ // Code below generated by codegen v1.0.14.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/app/AppOpsManager.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * Creates a new HistoricalFeatureOps.
+ *
+ * @param featureId
+ * Id of the {@link Context#createFeatureContext feature} in the package
+ * @param historicalOps
+ * Ops for this feature
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public HistoricalFeatureOps(
+ @Nullable String featureId,
+ @Nullable ArrayMap<String,HistoricalOp> historicalOps) {
+ this.mFeatureId = featureId;
+ this.mHistoricalOps = historicalOps;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * Id of the {@link Context#createFeatureContext feature} in the package
+ */
+ @DataClass.Generated.Member
+ public @Nullable String getFeatureId() {
+ return mFeatureId;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(HistoricalFeatureOps other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ HistoricalFeatureOps that = (HistoricalFeatureOps) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && Objects.equals(mFeatureId, that.mFeatureId)
+ && Objects.equals(mHistoricalOps, that.mHistoricalOps);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + Objects.hashCode(mFeatureId);
+ _hash = 31 * _hash + Objects.hashCode(mHistoricalOps);
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ byte flg = 0;
+ if (mFeatureId != null) flg |= 0x1;
+ if (mHistoricalOps != null) flg |= 0x2;
+ dest.writeByte(flg);
+ if (mFeatureId != null) dest.writeString(mFeatureId);
+ if (mHistoricalOps != null) dest.writeMap(mHistoricalOps);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ HistoricalFeatureOps(@NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ byte flg = in.readByte();
+ String featureId = (flg & 0x1) == 0 ? null : in.readString();
+ ArrayMap<String,HistoricalOp> historicalOps = null;
+ if ((flg & 0x2) != 0) {
+ historicalOps = new ArrayMap();
+ in.readMap(historicalOps, HistoricalOp.class.getClassLoader());
+ }
+
+ this.mFeatureId = featureId;
+ this.mHistoricalOps = historicalOps;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<HistoricalFeatureOps> CREATOR
+ = new Parcelable.Creator<HistoricalFeatureOps>() {
@Override
- public @NonNull HistoricalPackageOps createFromParcel(@NonNull Parcel parcel) {
- return new HistoricalPackageOps(parcel);
+ public HistoricalFeatureOps[] newArray(int size) {
+ return new HistoricalFeatureOps[size];
}
@Override
- public @NonNull HistoricalPackageOps[] newArray(int size) {
- return new HistoricalPackageOps[size];
+ public HistoricalFeatureOps createFromParcel(@NonNull Parcel in) {
+ return new HistoricalFeatureOps(in);
}
};
- @Override
- public boolean equals(@Nullable Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null || getClass() != obj.getClass()) {
- return false;
- }
- final HistoricalPackageOps other = (HistoricalPackageOps) obj;
- if (!mPackageName.equals(other.mPackageName)) {
- return false;
- }
- if (mHistoricalOps == null) {
- if (other.mHistoricalOps != null) {
- return false;
- }
- } else if (!mHistoricalOps.equals(other.mHistoricalOps)) {
- return false;
- }
- return true;
- }
+ /*
+ @DataClass.Generated(
+ time = 1578113234821L,
+ codegenVersion = "1.0.14",
+ sourceFile = "frameworks/base/core/java/android/app/AppOpsManager.java",
+ inputSignatures = "private final @android.annotation.Nullable java.lang.String mFeatureId\nprivate @android.annotation.Nullable android.util.ArrayMap<java.lang.String,android.app.HistoricalOp> mHistoricalOps\nprivate @android.annotation.Nullable android.app.HistoricalFeatureOps splice(double)\nprivate void merge(android.app.HistoricalFeatureOps)\nprivate void filter(java.lang.String[],int,double)\nprivate boolean isEmpty()\nprivate void increaseAccessCount(int,int,int,long)\nprivate void increaseRejectCount(int,int,int,long)\nprivate void increaseAccessDuration(int,int,int,long)\npublic @android.annotation.IntRange(from=0L) int getOpCount()\npublic @android.annotation.NonNull android.app.HistoricalOp getOpAt(int)\npublic @android.annotation.Nullable android.app.HistoricalOp getOp(java.lang.String)\nprivate void accept(android.app.HistoricalOpsVisitor)\nprivate @android.annotation.NonNull android.app.HistoricalOp getOrCreateHistoricalOp(int)\nclass HistoricalFeatureOps extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genEqualsHashCode=true, genHiddenCopyConstructor=true)")
+ @Deprecated
+ private void __metadata() {}
+ */
- @Override
- public int hashCode() {
- int result = mPackageName != null ? mPackageName.hashCode() : 0;
- result = 31 * result + (mHistoricalOps != null ? mHistoricalOps.hashCode() : 0);
- return result;
- }
+ //@formatter:on
+ // End of generated code
+
}
/**
@@ -5272,13 +5789,13 @@
if (mOp != other.mOp) {
return false;
}
- if (!Objects.equals(mAccessCount, other.mAccessCount)) {
+ if (!equalsLongSparseLongArray(mAccessCount, other.mAccessCount)) {
return false;
}
- if (!Objects.equals(mRejectCount, other.mRejectCount)) {
+ if (!equalsLongSparseLongArray(mRejectCount, other.mRejectCount)) {
return false;
}
- return Objects.equals(mAccessDuration, other.mAccessDuration);
+ return equalsLongSparseLongArray(mAccessDuration, other.mAccessDuration);
}
@Override
@@ -5605,9 +6122,9 @@
Objects.requireNonNull(executor, "executor cannot be null");
Objects.requireNonNull(callback, "callback cannot be null");
try {
- mService.getHistoricalOps(request.mUid, request.mPackageName, request.mOpNames,
- request.mBeginTimeMillis, request.mEndTimeMillis, request.mFlags,
- new RemoteCallback((result) -> {
+ mService.getHistoricalOps(request.mUid, request.mPackageName, request.mFeatureId,
+ request.mOpNames, request.mFilter, request.mBeginTimeMillis,
+ request.mEndTimeMillis, request.mFlags, new RemoteCallback((result) -> {
final HistoricalOps ops = result.getParcelable(KEY_HISTORICAL_OPS);
final long identity = Binder.clearCallingIdentity();
try {
@@ -5645,8 +6162,8 @@
Objects.requireNonNull(callback, "callback cannot be null");
try {
mService.getHistoricalOpsFromDiskRaw(request.mUid, request.mPackageName,
- request.mOpNames, request.mBeginTimeMillis, request.mEndTimeMillis,
- request.mFlags, new RemoteCallback((result) -> {
+ request.mFeatureId, request.mOpNames, request.mFilter, request.mBeginTimeMillis,
+ request.mEndTimeMillis, request.mFlags, new RemoteCallback((result) -> {
final HistoricalOps ops = result.getParcelable(KEY_HISTORICAL_OPS);
final long identity = Binder.clearCallingIdentity();
try {
@@ -7487,6 +8004,30 @@
return lastEvent;
}
+ private static boolean equalsLongSparseLongArray(@Nullable LongSparseLongArray a,
+ @Nullable LongSparseLongArray b) {
+ if (a == b) {
+ return true;
+ }
+
+ if (a == null || b == null) {
+ return false;
+ }
+
+ if (a.size() != b.size()) {
+ return false;
+ }
+
+ int numEntries = a.size();
+ for (int i = 0; i < numEntries; i++) {
+ if (a.keyAt(i) != b.keyAt(i) || a.valueAt(i) != b.valueAt(i)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
private static void writeLongSparseLongArrayToParcel(
@Nullable LongSparseLongArray array, @NonNull Parcel parcel) {
if (array != null) {
diff --git a/core/java/android/app/AsyncNotedAppOp.java b/core/java/android/app/AsyncNotedAppOp.java
index c3ef3ad..d993ec1 100644
--- a/core/java/android/app/AsyncNotedAppOp.java
+++ b/core/java/android/app/AsyncNotedAppOp.java
@@ -256,10 +256,10 @@
};
@DataClass.Generated(
- time = 1576864422226L,
+ time = 1578321462996L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/app/AsyncNotedAppOp.java",
- inputSignatures = "private final @android.annotation.IntRange(from=0L, to=92L) int mOpCode\nprivate final @android.annotation.IntRange(from=0L) int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.IntRange(from=0L) long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)")
+ inputSignatures = "private final @android.annotation.IntRange(from=0L, to=93L) int mOpCode\nprivate final @android.annotation.IntRange(from=0L) int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.IntRange(from=0L) long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/app/InstantAppResolverService.java b/core/java/android/app/InstantAppResolverService.java
index a413c60..0cd030e 100644
--- a/core/java/android/app/InstantAppResolverService.java
+++ b/core/java/android/app/InstantAppResolverService.java
@@ -35,6 +35,7 @@
import android.util.Log;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;
import java.util.Arrays;
@@ -297,7 +298,10 @@
public static final class InstantAppResolutionCallback {
private final IRemoteCallback mCallback;
private final int mSequence;
- InstantAppResolutionCallback(int sequence, IRemoteCallback callback) {
+
+ /** @hide **/
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public InstantAppResolutionCallback(int sequence, IRemoteCallback callback) {
mCallback = callback;
mSequence = sequence;
}
diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java
index b9893aa..8426374 100644
--- a/core/java/android/app/StatsManager.java
+++ b/core/java/android/app/StatsManager.java
@@ -157,11 +157,11 @@
public void addConfig(long configKey, byte[] config) throws StatsUnavailableException {
synchronized (sLock) {
try {
- IStatsd service = getIStatsdLocked();
+ IStatsManagerService service = getIStatsManagerServiceLocked();
// can throw IllegalArgumentException
service.addConfiguration(configKey, config, mContext.getOpPackageName());
} catch (RemoteException e) {
- Slog.e(TAG, "Failed to connect to statsd when adding configuration");
+ Slog.e(TAG, "Failed to connect to statsmanager when adding configuration");
throw new StatsUnavailableException("could not connect", e);
} catch (SecurityException e) {
throw new StatsUnavailableException(e.getMessage(), e);
@@ -194,10 +194,10 @@
public void removeConfig(long configKey) throws StatsUnavailableException {
synchronized (sLock) {
try {
- IStatsd service = getIStatsdLocked();
+ IStatsManagerService service = getIStatsManagerServiceLocked();
service.removeConfiguration(configKey, mContext.getOpPackageName());
} catch (RemoteException e) {
- Slog.e(TAG, "Failed to connect to statsd when removing configuration");
+ Slog.e(TAG, "Failed to connect to statsmanager when removing configuration");
throw new StatsUnavailableException("could not connect", e);
} catch (SecurityException e) {
throw new StatsUnavailableException(e.getMessage(), e);
@@ -255,18 +255,17 @@
throws StatsUnavailableException {
synchronized (sLock) {
try {
- IStatsd service = getIStatsdLocked();
+ IStatsManagerService service = getIStatsManagerServiceLocked();
if (pendingIntent != null) {
- // Extracts IIntentSender from the PendingIntent and turns it into an IBinder.
- IBinder intentSender = pendingIntent.getTarget().asBinder();
- service.setBroadcastSubscriber(configKey, subscriberId, intentSender,
+ service.setBroadcastSubscriber(configKey, subscriberId, pendingIntent,
mContext.getOpPackageName());
} else {
service.unsetBroadcastSubscriber(configKey, subscriberId,
mContext.getOpPackageName());
}
} catch (RemoteException e) {
- Slog.e(TAG, "Failed to connect to statsd when adding broadcast subscriber", e);
+ Slog.e(TAG, "Failed to connect to statsmanager when adding broadcast subscriber",
+ e);
throw new StatsUnavailableException("could not connect", e);
} catch (SecurityException e) {
throw new StatsUnavailableException(e.getMessage(), e);
@@ -309,18 +308,16 @@
throws StatsUnavailableException {
synchronized (sLock) {
try {
- IStatsd service = getIStatsdLocked();
+ IStatsManagerService service = getIStatsManagerServiceLocked();
if (pendingIntent == null) {
service.removeDataFetchOperation(configKey, mContext.getOpPackageName());
} else {
- // Extracts IIntentSender from the PendingIntent and turns it into an IBinder.
- IBinder intentSender = pendingIntent.getTarget().asBinder();
- service.setDataFetchOperation(configKey, intentSender,
+ service.setDataFetchOperation(configKey, pendingIntent,
mContext.getOpPackageName());
}
} catch (RemoteException e) {
- Slog.e(TAG, "Failed to connect to statsd when registering data listener.");
+ Slog.e(TAG, "Failed to connect to statsmanager when registering data listener.");
throw new StatsUnavailableException("could not connect", e);
} catch (SecurityException e) {
throw new StatsUnavailableException(e.getMessage(), e);
@@ -347,20 +344,18 @@
throws StatsUnavailableException {
synchronized (sLock) {
try {
- IStatsd service = getIStatsdLocked();
+ IStatsManagerService service = getIStatsManagerServiceLocked();
if (pendingIntent == null) {
service.removeActiveConfigsChangedOperation(mContext.getOpPackageName());
return new long[0];
} else {
- // Extracts IIntentSender from the PendingIntent and turns it into an IBinder.
- IBinder intentSender = pendingIntent.getTarget().asBinder();
- return service.setActiveConfigsChangedOperation(intentSender,
+ return service.setActiveConfigsChangedOperation(pendingIntent,
mContext.getOpPackageName());
}
} catch (RemoteException e) {
- Slog.e(TAG,
- "Failed to connect to statsd when registering active configs listener.");
+ Slog.e(TAG, "Failed to connect to statsmanager "
+ + "when registering active configs listener.");
throw new StatsUnavailableException("could not connect", e);
} catch (SecurityException e) {
throw new StatsUnavailableException(e.getMessage(), e);
@@ -395,10 +390,10 @@
public byte[] getReports(long configKey) throws StatsUnavailableException {
synchronized (sLock) {
try {
- IStatsd service = getIStatsdLocked();
+ IStatsManagerService service = getIStatsManagerServiceLocked();
return service.getData(configKey, mContext.getOpPackageName());
} catch (RemoteException e) {
- Slog.e(TAG, "Failed to connect to statsd when getting data");
+ Slog.e(TAG, "Failed to connect to statsmanager when getting data");
throw new StatsUnavailableException("could not connect", e);
} catch (SecurityException e) {
throw new StatsUnavailableException(e.getMessage(), e);
@@ -432,10 +427,10 @@
public byte[] getStatsMetadata() throws StatsUnavailableException {
synchronized (sLock) {
try {
- IStatsd service = getIStatsdLocked();
+ IStatsManagerService service = getIStatsManagerServiceLocked();
return service.getMetadata(mContext.getOpPackageName());
} catch (RemoteException e) {
- Slog.e(TAG, "Failed to connect to statsd when getting metadata");
+ Slog.e(TAG, "Failed to connect to statsmanager when getting metadata");
throw new StatsUnavailableException("could not connect", e);
} catch (SecurityException e) {
throw new StatsUnavailableException(e.getMessage(), e);
@@ -467,21 +462,19 @@
throws StatsUnavailableException {
synchronized (sLock) {
try {
- IStatsd service = getIStatsdLocked();
+ IStatsManagerService service = getIStatsManagerServiceLocked();
if (service == null) {
- if (DEBUG) {
- Slog.d(TAG, "Failed to find statsd when getting experiment IDs");
- }
- return new long[0];
+ throw new StatsUnavailableException("Failed to find statsmanager when "
+ + "getting experiment IDs");
}
return service.getRegisteredExperimentIds();
} catch (RemoteException e) {
if (DEBUG) {
Slog.d(TAG,
- "Failed to connect to StatsCompanionService when getting "
+ "Failed to connect to StatsManagerService when getting "
+ "registered experiment IDs");
}
- return new long[0];
+ throw new StatsUnavailableException("could not connect", e);
}
}
}
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index a1765c8..078e453 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -153,6 +153,11 @@
*/
public static final int DEFAULT_SETUP_DISABLE2_FLAGS = DISABLE2_ROTATE_SUGGESTIONS;
+ /**
+ * disable flags to be applied when the device is sim-locked.
+ */
+ private static final int DEFAULT_SIM_LOCKED_DISABLED_FLAGS = DISABLE_EXPAND;
+
/** @hide */
public static final int NAVIGATION_HINT_BACK_ALT = 1 << 0;
/** @hide */
@@ -385,6 +390,30 @@
}
/**
+ * Enable or disable expansion of the status bar. When the device is SIM-locked, the status
+ * bar should not be expandable.
+ *
+ * @param disabled If {@code true}, the status bar will be set to non-expandable. If
+ * {@code false}, re-enables expansion of the status bar.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.STATUS_BAR)
+ public void setDisabledForSimNetworkLock(boolean disabled) {
+ try {
+ final int userId = Binder.getCallingUserHandle().getIdentifier();
+ final IStatusBarService svc = getService();
+ if (svc != null) {
+ svc.disableForUser(disabled ? DEFAULT_SIM_LOCKED_DISABLED_FLAGS : DISABLE_NONE,
+ mToken, mContext.getPackageName(), userId);
+ }
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Get this app's currently requested disabled components
*
* @return a new DisableInfo
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index c585b5f..7b45b72 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -48,6 +48,10 @@
}
],
"file_patterns": ["INotificationManager\\.aidl"]
+ },
+ {
+ "name": "FrameworksInstantAppResolverTests",
+ "file_patterns": ["(/|^)InstantAppResolve[^/]*"]
}
],
"postsubmit": [
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 8daaaaa..2aac94c 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6756,6 +6756,34 @@
}
/**
+ * @hide
+ * Privileged apps can use this method to find out if the device was provisioned as
+ * organization-owend device with a managed profile.
+ *
+ * This, together with checking whether the device has a device owner (by calling
+ * {@link #isDeviceManaged()}), could be used to learn whether the device is owned by an
+ * organization or an individual:
+ * If this method returns true OR {@link #isDeviceManaged()} returns true, then
+ * the device is owned by an organization. Otherwise, it's owned by an individual.
+ *
+ * @return {@code true} if the device was provisioned as organization-owned device,
+ * {@code false} otherwise.
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ public boolean isOrganizationOwnedDeviceWithManagedProfile() {
+ throwIfParentInstance("isOrganizationOwnedDeviceWithManagedProfile");
+ if (mService != null) {
+ try {
+ return mService.isOrganizationOwnedDeviceWithManagedProfile();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+
+ /**
* Returns whether the specified package can read the device identifiers.
*
* @param packageName The package name of the app to check for device identifier access.
@@ -8484,14 +8512,16 @@
}
/**
- * Called by device owner to set the system wall clock time. This only takes effect if called
- * when {@link android.provider.Settings.Global#AUTO_TIME} is 0, otherwise {@code false} will be
- * returned.
+ * Called by a device owner or a profile owner of an organization-owned managed
+ * profile to set the system wall clock time. This only takes effect if called when
+ * {@link android.provider.Settings.Global#AUTO_TIME} is 0, otherwise {@code false}
+ * will be returned.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with
* @param millis time in milliseconds since the Epoch
* @return {@code true} if set time succeeded, {@code false} otherwise.
- * @throws SecurityException if {@code admin} is not a device owner.
+ * @throws SecurityException if {@code admin} is not a device owner or a profile owner
+ * of an organization-owned managed profile.
*/
public boolean setTime(@NonNull ComponentName admin, long millis) {
throwIfParentInstance("setTime");
@@ -8506,16 +8536,18 @@
}
/**
- * Called by device owner to set the system's persistent default time zone. This only takes
- * effect if called when {@link android.provider.Settings.Global#AUTO_TIME_ZONE} is 0, otherwise
- * {@code false} will be returned.
+ * Called by a device owner or a profile owner of an organization-owned managed
+ * profile to set the system's persistent default time zone. This only takes
+ * effect if called when {@link android.provider.Settings.Global#AUTO_TIME_ZONE}
+ * is 0, otherwise {@code false} will be returned.
*
* @see android.app.AlarmManager#setTimeZone(String)
* @param admin Which {@link DeviceAdminReceiver} this request is associated with
* @param timeZone one of the Olson ids from the list returned by
* {@link java.util.TimeZone#getAvailableIDs}
* @return {@code true} if set timezone succeeded, {@code false} otherwise.
- * @throws SecurityException if {@code admin} is not a device owner.
+ * @throws SecurityException if {@code admin} is not a device owner or a profile owner
+ * of an organization-owned managed profile.
*/
public boolean setTimeZone(@NonNull ComponentName admin, String timeZone) {
throwIfParentInstance("setTimeZone");
@@ -11203,6 +11235,40 @@
}
/**
+ * Returns the combined set of the following:
+ * <ul>
+ * <li>The package names that the admin has previously set as allowed to request user consent
+ * for cross-profile communication, via {@link
+ * #setCrossProfilePackages(ComponentName, Set)}.</li>
+ * <li>The default package names set by the OEM that are allowed to request user consent for
+ * cross-profile communication without being explicitly enabled by the admin, via
+ * {@link com.android.internal.R.array#cross_profile_apps}</li>
+ * </ul>
+ *
+ * @return the combined set of whitelisted package names set via
+ * {@link #setCrossProfilePackages(ComponentName, Set)} and
+ * {@link com.android.internal.R.array#cross_profile_apps}
+ *
+ * @hide
+ */
+ @RequiresPermission(anyOf = {
+ permission.INTERACT_ACROSS_USERS_FULL,
+ permission.INTERACT_ACROSS_USERS,
+ permission.INTERACT_ACROSS_PROFILES
+ })
+ public @NonNull Set<String> getAllCrossProfilePackages() {
+ throwIfParentInstance("getDefaultCrossProfilePackages");
+ if (mService != null) {
+ try {
+ return new ArraySet<>(mService.getAllCrossProfilePackages());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return Collections.emptySet();
+ }
+
+ /**
* Returns whether the device is being used as a managed kiosk. These requirements are as
* follows:
* <ul>
@@ -11299,4 +11365,39 @@
}
return false;
}
+
+ /**
+ * Called by Device owner to set packages as protected. User will not be able to clear app
+ * data or force-stop protected packages.
+ *
+ * @param admin which {@link DeviceAdminReceiver} this request is associated with
+ * @param packages The package names to protect.
+ * @throws SecurityException if {@code admin} is not a device owner.
+ */
+ public void setProtectedPackages(@NonNull ComponentName admin, @NonNull List<String> packages) {
+ if (mService != null) {
+ try {
+ mService.setProtectedPackages(admin, packages);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Returns the list of packages protected by the device owner.
+ *
+ * @param admin which {@link DeviceAdminReceiver} this request is associated with
+ * @throws SecurityException if {@code admin} is not a device owner.
+ */
+ public @NonNull List<String> getProtectedPackages(@NonNull ComponentName admin) {
+ if (mService != null) {
+ try {
+ return mService.getProtectedPackages(admin);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+ return Collections.emptyList();
+ }
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 9c82ff6..3eec46b 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -156,6 +156,7 @@
void setProfileName(in ComponentName who, String profileName);
void clearProfileOwner(in ComponentName who);
boolean hasUserSetupCompleted();
+ boolean isOrganizationOwnedDeviceWithManagedProfile();
boolean checkDeviceIdentifierAccess(in String packageName, int pid, int uid);
@@ -444,10 +445,16 @@
void setCrossProfilePackages(in ComponentName admin, in List<String> packageNames);
List<String> getCrossProfilePackages(in ComponentName admin);
+ List<String> getAllCrossProfilePackages();
+
boolean isManagedKiosk();
boolean isUnattendedManagedKiosk();
boolean startViewCalendarEventInManagedProfile(String packageName, long eventId, long start, long end, boolean allDay, int flags);
boolean setKeyGrantForApp(in ComponentName admin, String callerPackage, String alias, String packageName, boolean hasGrant);
+
+ void setProtectedPackages(in ComponentName admin, in List<String> packages);
+
+ List<String> getProtectedPackages(in ComponentName admin);
}
diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java
index f0b87a8..91cf120 100644
--- a/core/java/android/app/admin/SecurityLog.java
+++ b/core/java/android/app/admin/SecurityLog.java
@@ -81,6 +81,7 @@
TAG_CRYPTO_SELF_TEST_COMPLETED,
TAG_KEY_INTEGRITY_VIOLATION,
TAG_CERT_VALIDATION_FAILURE,
+ TAG_CAMERA_POLICY_SET
})
public @interface SecurityLogTag {}
@@ -433,6 +434,19 @@
SecurityLogTags.SECURITY_CERT_VALIDATION_FAILURE;
/**
+ * Indicates that the admin has set policy to disable camera.
+ * The log entry contains the following information about the event, encapsulated in an
+ * {@link Object} array and accessible via {@link SecurityEvent#getData()}:
+ * <li> [0] admin package name ({@code String})
+ * <li> [1] admin user ID ({@code Integer})
+ * <li> [2] target user ID ({@code Integer})
+ * <li> [3] whether the camera is disabled or not ({@code Integer}, 1 if it's disabled,
+ * 0 if enabled)
+ */
+ public static final int TAG_CAMERA_POLICY_SET =
+ SecurityLogTags.SECURITY_CAMERA_POLICY_SET;
+
+ /**
* Event severity level indicating that the event corresponds to normal workflow.
*/
public static final int LEVEL_INFO = 1;
@@ -561,6 +575,7 @@
case TAG_MAX_PASSWORD_ATTEMPTS_SET:
case TAG_USER_RESTRICTION_ADDED:
case TAG_USER_RESTRICTION_REMOVED:
+ case TAG_CAMERA_POLICY_SET:
return LEVEL_INFO;
case TAG_CERT_AUTHORITY_REMOVED:
case TAG_CRYPTO_SELF_TEST_COMPLETED:
diff --git a/core/java/android/app/admin/SecurityLogTags.logtags b/core/java/android/app/admin/SecurityLogTags.logtags
index fe2519d..4e67fe2 100644
--- a/core/java/android/app/admin/SecurityLogTags.logtags
+++ b/core/java/android/app/admin/SecurityLogTags.logtags
@@ -38,3 +38,4 @@
210031 security_crypto_self_test_completed (success|1)
210032 security_key_integrity_violation (key_id|3),(uid|1)
210033 security_cert_validation_failure (reason|3)
+210034 security_camera_policy_set (package|3),(admin_user|1),(target_user|1),(disabled|1)
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index 61eeacc..ea66fd47 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -630,9 +630,12 @@
* {@link Manifest.permission#OBSERVE_ROLE_HOLDERS}, as required by
* {@link android.provider.Telephony.Sms#getDefaultSmsPackage(Context)}
*
+ * @param userId The user ID to get the default SMS package for.
+ * @return the package name of the default SMS app, or {@code null} if not configured.
* @hide
*/
@Nullable
+ @SystemApi
public String getDefaultSmsPackage(@UserIdInt int userId) {
try {
return mService.getDefaultSmsPackage(userId);
diff --git a/core/java/android/app/timedetector/ITimeDetectorService.aidl b/core/java/android/app/timedetector/ITimeDetectorService.aidl
index 9877fc7..de8f470 100644
--- a/core/java/android/app/timedetector/ITimeDetectorService.aidl
+++ b/core/java/android/app/timedetector/ITimeDetectorService.aidl
@@ -17,6 +17,7 @@
package android.app.timedetector;
import android.app.timedetector.ManualTimeSuggestion;
+import android.app.timedetector.NetworkTimeSuggestion;
import android.app.timedetector.PhoneTimeSuggestion;
/**
@@ -35,4 +36,5 @@
interface ITimeDetectorService {
void suggestPhoneTime(in PhoneTimeSuggestion timeSuggestion);
void suggestManualTime(in ManualTimeSuggestion timeSuggestion);
+ void suggestNetworkTime(in NetworkTimeSuggestion timeSuggestion);
}
diff --git a/core/java/android/app/timedetector/ManualTimeSuggestion.java b/core/java/android/app/timedetector/ManualTimeSuggestion.java
index 55f92be..50de7385 100644
--- a/core/java/android/app/timedetector/ManualTimeSuggestion.java
+++ b/core/java/android/app/timedetector/ManualTimeSuggestion.java
@@ -20,7 +20,7 @@
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
-import android.util.TimestampedValue;
+import android.os.TimestampedValue;
import java.util.ArrayList;
import java.util.Arrays;
diff --git a/core/java/android/app/timedetector/NetworkTimeSuggestion.aidl b/core/java/android/app/timedetector/NetworkTimeSuggestion.aidl
new file mode 100644
index 0000000..731c907
--- /dev/null
+++ b/core/java/android/app/timedetector/NetworkTimeSuggestion.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.timedetector;
+
+parcelable NetworkTimeSuggestion;
diff --git a/core/java/android/app/timedetector/NetworkTimeSuggestion.java b/core/java/android/app/timedetector/NetworkTimeSuggestion.java
new file mode 100644
index 0000000..17e9c5a
--- /dev/null
+++ b/core/java/android/app/timedetector/NetworkTimeSuggestion.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.timedetector;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.TimestampedValue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A time signal from a network time source like NTP. The value consists of the number of
+ * milliseconds elapsed since 1/1/1970 00:00:00 UTC and the time according to the elapsed realtime
+ * clock when that number was established. The elapsed realtime clock is considered accurate but
+ * volatile, so time signals must not be persisted across device resets.
+ *
+ * @hide
+ */
+public final class NetworkTimeSuggestion implements Parcelable {
+
+ public static final @NonNull Creator<NetworkTimeSuggestion> CREATOR =
+ new Creator<NetworkTimeSuggestion>() {
+ public NetworkTimeSuggestion createFromParcel(Parcel in) {
+ return NetworkTimeSuggestion.createFromParcel(in);
+ }
+
+ public NetworkTimeSuggestion[] newArray(int size) {
+ return new NetworkTimeSuggestion[size];
+ }
+ };
+
+ @NonNull
+ private final TimestampedValue<Long> mUtcTime;
+ @Nullable
+ private ArrayList<String> mDebugInfo;
+
+ public NetworkTimeSuggestion(@NonNull TimestampedValue<Long> utcTime) {
+ mUtcTime = Objects.requireNonNull(utcTime);
+ Objects.requireNonNull(utcTime.getValue());
+ }
+
+ private static NetworkTimeSuggestion createFromParcel(Parcel in) {
+ TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */);
+ NetworkTimeSuggestion suggestion = new NetworkTimeSuggestion(utcTime);
+ @SuppressWarnings("unchecked")
+ ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */);
+ suggestion.mDebugInfo = debugInfo;
+ return suggestion;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeParcelable(mUtcTime, 0);
+ dest.writeList(mDebugInfo);
+ }
+
+ @NonNull
+ public TimestampedValue<Long> getUtcTime() {
+ return mUtcTime;
+ }
+
+ @NonNull
+ public List<String> getDebugInfo() {
+ return mDebugInfo == null
+ ? Collections.emptyList() : Collections.unmodifiableList(mDebugInfo);
+ }
+
+ /**
+ * Associates information with the instance that can be useful for debugging / logging. The
+ * information is present in {@link #toString()} but is not considered for
+ * {@link #equals(Object)} and {@link #hashCode()}.
+ */
+ public void addDebugInfo(String... debugInfos) {
+ if (mDebugInfo == null) {
+ mDebugInfo = new ArrayList<>();
+ }
+ mDebugInfo.addAll(Arrays.asList(debugInfos));
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ NetworkTimeSuggestion that = (NetworkTimeSuggestion) o;
+ return Objects.equals(mUtcTime, that.mUtcTime);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mUtcTime);
+ }
+
+ @Override
+ public String toString() {
+ return "NetworkTimeSuggestion{"
+ + "mUtcTime=" + mUtcTime
+ + ", mDebugInfo=" + mDebugInfo
+ + '}';
+ }
+}
diff --git a/core/java/android/app/timedetector/PhoneTimeSuggestion.java b/core/java/android/app/timedetector/PhoneTimeSuggestion.java
index 4a89a12..479e4b4 100644
--- a/core/java/android/app/timedetector/PhoneTimeSuggestion.java
+++ b/core/java/android/app/timedetector/PhoneTimeSuggestion.java
@@ -20,7 +20,7 @@
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
-import android.util.TimestampedValue;
+import android.os.TimestampedValue;
import java.util.ArrayList;
import java.util.Collections;
diff --git a/core/java/android/app/timedetector/TimeDetector.java b/core/java/android/app/timedetector/TimeDetector.java
index 48d5cd2..54dd1be 100644
--- a/core/java/android/app/timedetector/TimeDetector.java
+++ b/core/java/android/app/timedetector/TimeDetector.java
@@ -24,8 +24,8 @@
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.os.SystemClock;
+import android.os.TimestampedValue;
import android.util.Log;
-import android.util.TimestampedValue;
/**
* The interface through which system components can send signals to the TimeDetectorService.
@@ -48,7 +48,7 @@
* signal if better signals are available such as those that come from more reliable sources or
* were determined more recently.
*/
- @RequiresPermission(android.Manifest.permission.SET_TIME)
+ @RequiresPermission(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE)
public void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) {
if (DEBUG) {
Log.d(TAG, "suggestPhoneTime called: " + timeSuggestion);
@@ -63,7 +63,7 @@
/**
* Suggests the user's manually entered current time to the detector.
*/
- @RequiresPermission(android.Manifest.permission.SET_TIME)
+ @RequiresPermission(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE)
public void suggestManualTime(@NonNull ManualTimeSuggestion timeSuggestion) {
if (DEBUG) {
Log.d(TAG, "suggestManualTime called: " + timeSuggestion);
@@ -85,4 +85,19 @@
manualTimeSuggestion.addDebugInfo(why);
return manualTimeSuggestion;
}
+
+ /**
+ * Suggests the time according to a network time source like NTP.
+ */
+ @RequiresPermission(android.Manifest.permission.SET_TIME)
+ public void suggestNetworkTime(NetworkTimeSuggestion timeSuggestion) {
+ if (DEBUG) {
+ Log.d(TAG, "suggestNetworkTime called: " + timeSuggestion);
+ }
+ try {
+ mITimeDetectorService.suggestNetworkTime(timeSuggestion);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java
index 387a36b..e165d8a 100644
--- a/core/java/android/app/timezonedetector/TimeZoneDetector.java
+++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java
@@ -47,7 +47,7 @@
* detector may ignore the signal based on system settings, whether better information is
* available, and so on.
*/
- @RequiresPermission(android.Manifest.permission.SET_TIME_ZONE)
+ @RequiresPermission(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE)
public void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion timeZoneSuggestion) {
if (DEBUG) {
Log.d(TAG, "suggestPhoneTimeZone called: " + timeZoneSuggestion);
@@ -63,7 +63,7 @@
* Suggests the current time zone, determined for the user's manually information, to the
* detector. The detector may ignore the signal based on system settings.
*/
- @RequiresPermission(android.Manifest.permission.SET_TIME_ZONE)
+ @RequiresPermission(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE)
public void suggestManualTimeZone(@NonNull ManualTimeZoneSuggestion timeZoneSuggestion) {
if (DEBUG) {
Log.d(TAG, "suggestManualTimeZone called: " + timeZoneSuggestion);
diff --git a/core/java/android/bluetooth/BluetoothHidHost.java b/core/java/android/bluetooth/BluetoothHidHost.java
index 8f5cdf0..c1233b8 100644
--- a/core/java/android/bluetooth/BluetoothHidHost.java
+++ b/core/java/android/bluetooth/BluetoothHidHost.java
@@ -17,9 +17,12 @@
package android.bluetooth;
import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.content.Context;
import android.os.Binder;
@@ -43,6 +46,7 @@
*
* @hide
*/
+@SystemApi
public final class BluetoothHidHost implements BluetoothProfile {
private static final String TAG = "BluetoothHidHost";
private static final boolean DBG = true;
@@ -66,6 +70,7 @@
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
* receive.
*/
+ @SuppressLint("ActionValue")
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED";
@@ -325,7 +330,7 @@
* {@inheritDoc}
*/
@Override
- public List<BluetoothDevice> getConnectedDevices() {
+ public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
final IBluetoothHidHost service = getService();
if (service != null && isEnabled()) {
@@ -342,6 +347,8 @@
/**
* {@inheritDoc}
+ *
+ * @hide
*/
@Override
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
@@ -363,7 +370,7 @@
* {@inheritDoc}
*/
@Override
- public int getConnectionState(BluetoothDevice device) {
+ public int getConnectionState(@Nullable BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
@@ -409,7 +416,7 @@
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
- public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
+ public boolean setConnectionPolicy(@Nullable BluetoothDevice device, int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
@@ -457,7 +464,7 @@
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH)
- public int getConnectionPolicy(BluetoothDevice device) {
+ public int getConnectionPolicy(@Nullable BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index c579fdf..948885e 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -16,7 +16,10 @@
package android.bluetooth;
+import android.Manifest;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
@@ -271,6 +274,42 @@
}
/**
+ * Pbap does not store connection policy, so this function only disconnects Pbap if
+ * connectionPolicy is CONNECTION_POLICY_FORBIDDEN.
+ *
+ * <p> The device should already be paired.
+ * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
+ * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
+ *
+ * @param device Paired bluetooth device
+ * @param connectionPolicy is the connection policy to set to for this profile
+ * @return true if pbap is successfully disconnected, false otherwise
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
+ @ConnectionPolicy int connectionPolicy) {
+ if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
+ try {
+ final IBluetoothPbap service = mService;
+ if (service != null && isEnabled()
+ && isValidDevice(device)) {
+ if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+ return false;
+ }
+ return service.setConnectionPolicy(device, connectionPolicy);
+ }
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return false;
+ }
+ }
+
+ /**
* Disconnects the current Pbap client (PCE). Currently this call blocks,
* it may soon be made asynchronous. Returns false if this proxy object is
* not currently connected to the Pbap service.
diff --git a/core/java/android/content/ComponentName.java b/core/java/android/content/ComponentName.java
index 27960b0..1967a01 100644
--- a/core/java/android/content/ComponentName.java
+++ b/core/java/android/content/ComponentName.java
@@ -305,6 +305,12 @@
proto.end(token);
}
+ /**
+ * {@inheritDoc}
+ *
+ * <p>Two components are considered to be equal if the packages in which they reside have the
+ * same name, and if the classes that implement each component also have the same name.
+ */
@Override
public boolean equals(Object obj) {
try {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 4815d78..8dd9381 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3992,6 +3992,7 @@
*/
public static final String NETWORK_STATS_SERVICE = "netstats";
/** {@hide} */
+ @SystemApi
public static final String NETWORK_POLICY_SERVICE = "netpolicy";
/** {@hide} */
public static final String NETWORK_WATCHLIST_SERVICE = "network_watchlist";
@@ -4015,6 +4016,7 @@
* @see android.net.wifi.WifiCondManager
* @hide
*/
+ @SystemApi
public static final String WIFI_COND_SERVICE = "wificond";
/**
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 7967708..3bb0f92 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4224,9 +4224,10 @@
public static final String ACTION_SERVICE_STATE = "android.intent.action.SERVICE_STATE";
/**
- * Used for looking up a Data Loader Service providers.
+ * Used for looking up a Data Loader Service provider.
* @hide
*/
+ @SystemApi
@SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
public static final String ACTION_LOAD_DATA = "android.intent.action.LOAD_DATA";
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index f792127..b85c58a 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2918,6 +2918,18 @@
public static final String FEATURE_IPSEC_TUNNELS = "android.software.ipsec_tunnels";
/**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has
+ * the requisite hardware support to support reboot escrow of synthetic password for updates.
+ *
+ * <p>This feature implies that the device has the RebootEscrow HAL implementation.
+ *
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_REBOOT_ESCROW = "android.hardware.reboot_escrow";
+
+ /**
* Extra field name for the URI to a verification file. Passed to a package
* verifier.
*
diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING
index df4ae09..0549c34 100644
--- a/core/java/android/content/pm/TEST_MAPPING
+++ b/core/java/android/content/pm/TEST_MAPPING
@@ -9,5 +9,11 @@
{
"path": "system/apex/tests"
}
+ ],
+ "presubmit": [
+ {
+ "name": "FrameworksInstantAppResolverTests",
+ "file_patterns": ["(/|^)InstantApp[^/]*"]
+ }
]
}
diff --git a/core/java/android/content/pm/parsing/ComponentParseUtils.java b/core/java/android/content/pm/parsing/ComponentParseUtils.java
index 7b24d3d..5956857 100644
--- a/core/java/android/content/pm/parsing/ComponentParseUtils.java
+++ b/core/java/android/content/pm/parsing/ComponentParseUtils.java
@@ -465,7 +465,8 @@
}
public void setPermission(String permission) {
- this.permission = TextUtils.safeIntern(permission);
+ // Empty string must be converted to null
+ this.permission = TextUtils.isEmpty(permission) ? null : permission.intern();
}
public String getPermission() {
@@ -842,7 +843,9 @@
}
public void setReadPermission(String readPermission) {
- this.readPermission = TextUtils.safeIntern(readPermission);
+ // Empty string must be converted to null
+ this.readPermission = TextUtils.isEmpty(readPermission)
+ ? null : readPermission.intern();
}
public String getReadPermission() {
@@ -850,7 +853,9 @@
}
public void setWritePermission(String writePermission) {
- this.writePermission = TextUtils.safeIntern(writePermission);
+ // Empty string must be converted to null
+ this.writePermission = TextUtils.isEmpty(writePermission)
+ ? null : writePermission.intern();
}
public String getWritePermission() {
diff --git a/core/java/android/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java
index cc5d3b1..3effc5a 100644
--- a/core/java/android/database/AbstractCursor.java
+++ b/core/java/android/database/AbstractCursor.java
@@ -17,20 +17,19 @@
package android.database;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentResolver;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
-import com.android.internal.util.Preconditions;
-
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
/**
@@ -416,8 +415,8 @@
@Override
public void setNotificationUris(@NonNull ContentResolver cr, @NonNull List<Uri> notifyUris) {
- Preconditions.checkNotNull(cr);
- Preconditions.checkNotNull(notifyUris);
+ Objects.requireNonNull(cr);
+ Objects.requireNonNull(notifyUris);
setNotificationUris(cr, notifyUris, cr.getUserId(), true);
}
diff --git a/core/java/android/database/AbstractWindowedCursor.java b/core/java/android/database/AbstractWindowedCursor.java
index a988f068..daf7d2b 100644
--- a/core/java/android/database/AbstractWindowedCursor.java
+++ b/core/java/android/database/AbstractWindowedCursor.java
@@ -16,7 +16,7 @@
package android.database;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
/**
* A base class for Cursors that store their data in {@link CursorWindow}s.
diff --git a/core/java/android/database/BulkCursorNative.java b/core/java/android/database/BulkCursorNative.java
index 77a13cf..8ea450c 100644
--- a/core/java/android/database/BulkCursorNative.java
+++ b/core/java/android/database/BulkCursorNative.java
@@ -16,7 +16,7 @@
package android.database;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
diff --git a/core/java/android/database/ContentObserver.java b/core/java/android/database/ContentObserver.java
index 798b783..69ca581 100644
--- a/core/java/android/database/ContentObserver.java
+++ b/core/java/android/database/ContentObserver.java
@@ -16,7 +16,7 @@
package android.database;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.net.Uri;
import android.os.Handler;
import android.os.UserHandle;
diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java
index 6873577..063a2d0 100644
--- a/core/java/android/database/CursorWindow.java
+++ b/core/java/android/database/CursorWindow.java
@@ -17,7 +17,7 @@
package android.database;
import android.annotation.BytesLong;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.Resources;
import android.database.sqlite.SQLiteClosable;
import android.database.sqlite.SQLiteException;
diff --git a/core/java/android/database/CursorWrapper.java b/core/java/android/database/CursorWrapper.java
index c9cafaf..4496f80 100644
--- a/core/java/android/database/CursorWrapper.java
+++ b/core/java/android/database/CursorWrapper.java
@@ -16,7 +16,7 @@
package android.database;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentResolver;
import android.net.Uri;
import android.os.Bundle;
diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java
index 992da31..4246b84 100644
--- a/core/java/android/database/DatabaseUtils.java
+++ b/core/java/android/database/DatabaseUtils.java
@@ -17,7 +17,7 @@
package android.database;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentValues;
import android.content.Context;
import android.content.OperationApplicationException;
diff --git a/core/java/android/database/MatrixCursor.java b/core/java/android/database/MatrixCursor.java
index b0d399c..050a49a 100644
--- a/core/java/android/database/MatrixCursor.java
+++ b/core/java/android/database/MatrixCursor.java
@@ -16,7 +16,7 @@
package android.database;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import java.util.ArrayList;
diff --git a/core/java/android/database/sqlite/DatabaseObjectNotClosedException.java b/core/java/android/database/sqlite/DatabaseObjectNotClosedException.java
index 2af06e1..ba546f3 100644
--- a/core/java/android/database/sqlite/DatabaseObjectNotClosedException.java
+++ b/core/java/android/database/sqlite/DatabaseObjectNotClosedException.java
@@ -16,7 +16,7 @@
package android.database.sqlite;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
/**
* An exception that indicates that garbage-collector is finalizing a database object
diff --git a/core/java/android/database/sqlite/SQLiteClosable.java b/core/java/android/database/sqlite/SQLiteClosable.java
index d6a71da..2fca729 100644
--- a/core/java/android/database/sqlite/SQLiteClosable.java
+++ b/core/java/android/database/sqlite/SQLiteClosable.java
@@ -16,7 +16,8 @@
package android.database.sqlite;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
+
import java.io.Closeable;
/**
diff --git a/core/java/android/database/sqlite/SQLiteCursor.java b/core/java/android/database/sqlite/SQLiteCursor.java
index e3c4098..4559e91 100644
--- a/core/java/android/database/sqlite/SQLiteCursor.java
+++ b/core/java/android/database/sqlite/SQLiteCursor.java
@@ -16,7 +16,7 @@
package android.database.sqlite;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.database.AbstractWindowedCursor;
import android.database.CursorWindow;
import android.database.DatabaseUtils;
diff --git a/core/java/android/database/sqlite/SQLiteCustomFunction.java b/core/java/android/database/sqlite/SQLiteCustomFunction.java
index 41b78d3..1ace40d 100644
--- a/core/java/android/database/sqlite/SQLiteCustomFunction.java
+++ b/core/java/android/database/sqlite/SQLiteCustomFunction.java
@@ -16,7 +16,7 @@
package android.database.sqlite;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
/**
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index caf3e93..44c78aa 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -20,9 +20,9 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
import android.app.ActivityManager;
import android.app.ActivityThread;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
@@ -60,6 +60,7 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Objects;
import java.util.WeakHashMap;
/**
@@ -2691,7 +2692,7 @@
*/
@NonNull
public Builder setJournalMode(@NonNull String journalMode) {
- Preconditions.checkNotNull(journalMode);
+ Objects.requireNonNull(journalMode);
mJournalMode = journalMode;
return this;
}
@@ -2703,7 +2704,7 @@
*/
@NonNull
public Builder setSynchronousMode(@NonNull String syncMode) {
- Preconditions.checkNotNull(syncMode);
+ Objects.requireNonNull(syncMode);
mSyncMode = syncMode;
return this;
}
diff --git a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
index fcdaf0a..6a52b72 100644
--- a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
+++ b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
@@ -16,7 +16,7 @@
package android.database.sqlite;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import java.util.ArrayList;
import java.util.Locale;
diff --git a/core/java/android/database/sqlite/SQLiteDebug.java b/core/java/android/database/sqlite/SQLiteDebug.java
index 3d0ac61..165f863 100644
--- a/core/java/android/database/sqlite/SQLiteDebug.java
+++ b/core/java/android/database/sqlite/SQLiteDebug.java
@@ -17,14 +17,13 @@
package android.database.sqlite;
import android.annotation.TestApi;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Process;
import android.os.SystemProperties;
import android.util.Log;
import android.util.Printer;
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
import java.util.ArrayList;
/**
diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java
index 62cec0e..3341800 100644
--- a/core/java/android/database/sqlite/SQLiteOpenHelper.java
+++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java
@@ -19,7 +19,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.database.DatabaseErrorHandler;
import android.database.SQLException;
@@ -27,9 +27,8 @@
import android.os.FileUtils;
import android.util.Log;
-import com.android.internal.util.Preconditions;
-
import java.io.File;
+import java.util.Objects;
/**
* A helper class to manage database creation and version management.
@@ -161,7 +160,7 @@
private SQLiteOpenHelper(@Nullable Context context, @Nullable String name, int version,
int minimumSupportedVersion,
@NonNull SQLiteDatabase.OpenParams.Builder openParamsBuilder) {
- Preconditions.checkNotNull(openParamsBuilder);
+ Objects.requireNonNull(openParamsBuilder);
if (version < 1) throw new IllegalArgumentException("Version must be >= 1, was " + version);
mContext = context;
@@ -245,7 +244,7 @@
* @throws IllegalStateException if the database is already open
*/
public void setOpenParams(@NonNull SQLiteDatabase.OpenParams openParams) {
- Preconditions.checkNotNull(openParams);
+ Objects.requireNonNull(openParams);
synchronized (this) {
if (mDatabase != null && mDatabase.isOpen()) {
throw new IllegalStateException(
diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java
index 8304133..de1c543 100644
--- a/core/java/android/database/sqlite/SQLiteProgram.java
+++ b/core/java/android/database/sqlite/SQLiteProgram.java
@@ -16,7 +16,7 @@
package android.database.sqlite;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.database.DatabaseUtils;
import android.os.CancellationSignal;
diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
index f7137705..bba14c3 100644
--- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java
+++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
@@ -18,7 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.DatabaseUtils;
diff --git a/core/java/android/database/sqlite/SQLiteSession.java b/core/java/android/database/sqlite/SQLiteSession.java
index a9ac9e7..24b62b8 100644
--- a/core/java/android/database/sqlite/SQLiteSession.java
+++ b/core/java/android/database/sqlite/SQLiteSession.java
@@ -16,7 +16,7 @@
package android.database.sqlite;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.database.CursorWindow;
import android.database.DatabaseUtils;
import android.os.CancellationSignal;
diff --git a/core/java/android/database/sqlite/SQLiteStatement.java b/core/java/android/database/sqlite/SQLiteStatement.java
index 42e7ac7..9fda8b0 100644
--- a/core/java/android/database/sqlite/SQLiteStatement.java
+++ b/core/java/android/database/sqlite/SQLiteStatement.java
@@ -16,7 +16,7 @@
package android.database.sqlite;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.ParcelFileDescriptor;
/**
diff --git a/core/java/android/database/sqlite/SqliteWrapper.java b/core/java/android/database/sqlite/SqliteWrapper.java
index e317164..f335fbd 100644
--- a/core/java/android/database/sqlite/SqliteWrapper.java
+++ b/core/java/android/database/sqlite/SqliteWrapper.java
@@ -17,12 +17,11 @@
package android.database.sqlite;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
-import android.database.sqlite.SQLiteException;
import android.net.Uri;
import android.util.Log;
import android.widget.Toast;
diff --git a/core/java/android/ddm/DdmHandleAppName.java b/core/java/android/ddm/DdmHandleAppName.java
index de7acba..4f55921 100644
--- a/core/java/android/ddm/DdmHandleAppName.java
+++ b/core/java/android/ddm/DdmHandleAppName.java
@@ -16,7 +16,7 @@
package android.ddm;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.util.Log;
import org.apache.harmony.dalvik.ddmc.Chunk;
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 8e8a81d..25279b3 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -16,14 +16,20 @@
package android.hardware;
-import static android.system.OsConstants.*;
+import static android.system.OsConstants.EACCES;
+import static android.system.OsConstants.EBUSY;
+import static android.system.OsConstants.EINVAL;
+import static android.system.OsConstants.ENODEV;
+import static android.system.OsConstants.ENOSYS;
+import static android.system.OsConstants.EOPNOTSUPP;
+import static android.system.OsConstants.EUSERS;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.UnsupportedAppUsage;
import android.app.ActivityThread;
import android.app.AppOpsManager;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.graphics.ImageFormat;
import android.graphics.Point;
diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java
index 5b25f5a..e97a5b9 100644
--- a/core/java/android/hardware/HardwareBuffer.java
+++ b/core/java/android/hardware/HardwareBuffer.java
@@ -20,7 +20,7 @@
import android.annotation.IntRange;
import android.annotation.LongDef;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.GraphicBuffer;
import android.os.Build;
import android.os.Parcel;
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index e78fb7f..a71a7b6 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -18,7 +18,7 @@
package android.hardware;
import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
/**
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 8c910b2..64c45bf 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -16,7 +16,7 @@
package android.hardware;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
/**
* This class represents a {@link android.hardware.Sensor Sensor} event and
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 524a0c4..6bf754f 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -18,7 +18,7 @@
import android.annotation.SystemApi;
import android.annotation.SystemService;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
diff --git a/core/java/android/hardware/SerialManager.java b/core/java/android/hardware/SerialManager.java
index 571c3cc..b51382e 100644
--- a/core/java/android/hardware/SerialManager.java
+++ b/core/java/android/hardware/SerialManager.java
@@ -17,7 +17,7 @@
package android.hardware;
import android.annotation.SystemService;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
diff --git a/core/java/android/hardware/SerialPort.java b/core/java/android/hardware/SerialPort.java
index 78ac3c0..0fcaa49 100644
--- a/core/java/android/hardware/SerialPort.java
+++ b/core/java/android/hardware/SerialPort.java
@@ -16,12 +16,11 @@
package android.hardware;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.ParcelFileDescriptor;
import java.io.FileDescriptor;
import java.io.IOException;
-
import java.nio.ByteBuffer;
/**
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 7abfabf..974913b 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -16,7 +16,7 @@
package android.hardware;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java
index d28b7c5..5a13651 100644
--- a/core/java/android/hardware/biometrics/BiometricConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricConstants.java
@@ -18,8 +18,7 @@
import static android.hardware.biometrics.BiometricManager.Authenticators;
-import android.annotation.UnsupportedAppUsage;
-
+import android.compat.annotation.UnsupportedAppUsage;
/**
* Interface containing all of the biometric modality agnostic constants.
diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
index b025508..5c74456 100644
--- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
@@ -16,8 +16,8 @@
package android.hardware.biometrics;
-import android.annotation.UnsupportedAppUsage;
import android.app.KeyguardManager;
+import android.compat.annotation.UnsupportedAppUsage;
import android.hardware.fingerprint.FingerprintManager;
/**
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index b605866..a45648f 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -18,7 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.impl.PublicKey;
import android.hardware.camera2.impl.SyntheticKey;
@@ -1212,7 +1212,7 @@
/**
* <p>Minimum and maximum zoom ratios supported by this camera device.</p>
* <p>If the camera device supports zoom-out from 1x zoom, minZoom will be less than 1.0, and
- * setting android.control.zoomRation to values less than 1.0 increases the camera's field
+ * setting {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} to values less than 1.0 increases the camera's field
* of view.</p>
* <p><b>Units</b>: A pair of zoom ratio in floating points: (minZoom, maxZoom)</p>
* <p><b>Range of valid values:</b><br></p>
@@ -1222,6 +1222,7 @@
* Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
* {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
*
+ * @see CaptureRequest#CONTROL_ZOOM_RATIO
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
*/
@PublicKey
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 6bf5783..b9af8f5 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -18,7 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.impl.PublicKey;
import android.hardware.camera2.impl.SyntheticKey;
@@ -2154,7 +2154,7 @@
* stream combinations of LIMITED hardware level are guaranteed.</p>
* <p>For a logical multi-camera, bokeh may be implemented by stereo vision from sub-cameras
* with different field of view. As a result, when bokeh mode is enabled, the camera device
- * may override android.scaler.CropRegion, and the field of view will be smaller than when
+ * may override {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}, and the field of view will be smaller than when
* bokeh mode is off.</p>
* <p><b>Possible values:</b>
* <ul>
@@ -2163,6 +2163,8 @@
* <li>{@link #CONTROL_BOKEH_MODE_CONTINUOUS CONTINUOUS}</li>
* </ul></p>
* <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#SCALER_CROP_REGION
* @see #CONTROL_BOKEH_MODE_OFF
* @see #CONTROL_BOKEH_MODE_STILL_CAPTURE
* @see #CONTROL_BOKEH_MODE_CONTINUOUS
@@ -2740,32 +2742,70 @@
* <p>For devices not supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
* system always follows that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with <code>(0, 0)</code> being
* the top-left pixel of the active array.</p>
- * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
- * system depends on the mode being set.
- * When the distortion correction mode is OFF, the coordinate system follows
- * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with
- * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array.
- * When the distortion correction mode is not OFF, the coordinate system follows
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
- * <code>(0, 0)</code> being the top-left pixel of the active array.</p>
- * <p>Output streams use this rectangle to produce their output,
- * cropping to a smaller region if necessary to maintain the
- * stream's aspect ratio, then scaling the sensor input to
+ * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate system
+ * depends on the mode being set. When the distortion correction mode is OFF, the
+ * coordinate system follows {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with <code>(0,
+ * 0)</code> being the top-left pixel of the pre-correction active array. When the distortion
+ * correction mode is not OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with <code>(0, 0)</code> being the top-left pixel of the
+ * active array.</p>
+ * <p>Output streams use this rectangle to produce their output, cropping to a smaller region
+ * if necessary to maintain the stream's aspect ratio, then scaling the sensor input to
* match the output's configured resolution.</p>
- * <p>The crop region is applied after the RAW to other color
- * space (e.g. YUV) conversion. Since raw streams
- * (e.g. RAW16) don't have the conversion stage, they are not
+ * <p>The crop region is applied after the RAW to other color space (e.g. YUV)
+ * conversion. Since raw streams (e.g. RAW16) don't have the conversion stage, they are not
* croppable. The crop region will be ignored by raw streams.</p>
- * <p>For non-raw streams, any additional per-stream cropping will
- * be done to maximize the final pixel area of the stream.</p>
- * <p>For example, if the crop region is set to a 4:3 aspect
- * ratio, then 4:3 streams will use the exact crop
- * region. 16:9 streams will further crop vertically
- * (letterbox).</p>
- * <p>Conversely, if the crop region is set to a 16:9, then 4:3
- * outputs will crop horizontally (pillarbox), and 16:9
- * streams will match exactly. These additional crops will
- * be centered within the crop region.</p>
+ * <p>For non-raw streams, any additional per-stream cropping will be done to maximize the
+ * final pixel area of the stream.</p>
+ * <p>For example, if the crop region is set to a 4:3 aspect ratio, then 4:3 streams will use
+ * the exact crop region. 16:9 streams will further crop vertically (letterbox).</p>
+ * <p>Conversely, if the crop region is set to a 16:9, then 4:3 outputs will crop horizontally
+ * (pillarbox), and 16:9 streams will match exactly. These additional crops will be
+ * centered within the crop region.</p>
+ * <p>To illustrate, here are several scenarios of different crop regions and output streams,
+ * for a hypothetical camera device with an active array of size <code>(2000,1500)</code>. Note that
+ * several of these examples use non-centered crop regions for ease of illustration; such
+ * regions are only supported on devices with FREEFORM capability
+ * ({@link CameraCharacteristics#SCALER_CROPPING_TYPE android.scaler.croppingType} <code>== FREEFORM</code>), but this does not affect the way the crop
+ * rules work otherwise.</p>
+ * <ul>
+ * <li>Camera Configuration:<ul>
+ * <li>Active array size: <code>2000x1500</code> (3 MP, 4:3 aspect ratio)</li>
+ * <li>Output stream #1: <code>640x480</code> (VGA, 4:3 aspect ratio)</li>
+ * <li>Output stream #2: <code>1280x720</code> (720p, 16:9 aspect ratio)</li>
+ * </ul>
+ * </li>
+ * <li>Case #1: 4:3 crop region with 2x digital zoom<ul>
+ * <li>Crop region: <code>Rect(500, 375, 1500, 1125) // (left, top, right, bottom)</code></li>
+ * <li><img alt="4:3 aspect ratio crop diagram" src="/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-ratio.png" /></li>
+ * <li><code>640x480</code> stream source area: <code>(500, 375, 1500, 1125)</code> (equal to crop region)</li>
+ * <li><code>1280x720</code> stream source area: <code>(500, 469, 1500, 1031)</code> (letterboxed)</li>
+ * </ul>
+ * </li>
+ * <li>Case #2: 16:9 crop region with ~1.5x digital zoom.<ul>
+ * <li>Crop region: <code>Rect(500, 375, 1833, 1125)</code></li>
+ * <li><img alt="16:9 aspect ratio crop diagram" src="/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-169-ratio.png" /></li>
+ * <li><code>640x480</code> stream source area: <code>(666, 375, 1666, 1125)</code> (pillarboxed)</li>
+ * <li><code>1280x720</code> stream source area: <code>(500, 375, 1833, 1125)</code> (equal to crop region)</li>
+ * </ul>
+ * </li>
+ * <li>Case #3: 1:1 crop region with ~2.6x digital zoom.<ul>
+ * <li>Crop region: <code>Rect(500, 375, 1250, 1125)</code></li>
+ * <li><img alt="1:1 aspect ratio crop diagram" src="/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-11-ratio.png" /></li>
+ * <li><code>640x480</code> stream source area: <code>(500, 469, 1250, 1031)</code> (letterboxed)</li>
+ * <li><code>1280x720</code> stream source area: <code>(500, 543, 1250, 957)</code> (letterboxed)</li>
+ * </ul>
+ * </li>
+ * <li>Case #4: Replace <code>640x480</code> stream with <code>1024x1024</code> stream, with 4:3 crop region:<ul>
+ * <li>Crop region: <code>Rect(500, 375, 1500, 1125)</code></li>
+ * <li><img alt="Square output, 4:3 aspect ratio crop diagram" src="/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-square-ratio.png" /></li>
+ * <li><code>1024x1024</code> stream source area: <code>(625, 375, 1375, 1125)</code> (pillarboxed)</li>
+ * <li><code>1280x720</code> stream source area: <code>(500, 469, 1500, 1031)</code> (letterboxed)</li>
+ * <li>Note that in this case, neither of the two outputs is a subset of the other, with
+ * each containing image data the other doesn't have.</li>
+ * </ul>
+ * </li>
+ * </ul>
* <p>If the coordinate system is {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, the width and height
* of the crop region cannot be set to be smaller than
* <code>floor( activeArraySize.width / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code> and
@@ -2776,18 +2816,16 @@
* and
* <code>floor( preCorrectionActiveArraySize.height / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code>,
* respectively.</p>
- * <p>The camera device may adjust the crop region to account
- * for rounding and other hardware requirements; the final
- * crop region used will be included in the output capture
- * result.</p>
+ * <p>The camera device may adjust the crop region to account for rounding and other hardware
+ * requirements; the final crop region used will be included in the output capture result.</p>
* <p>Starting from API level 30, it's strongly recommended to use {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}
* to take advantage of better support for zoom with logical multi-camera. The benefits
* include better precision with optical-digital zoom combination, and ability to do
* zoom-out from 1.0x. When using {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for zoom, the crop region in
* the capture request must be either letterboxing or pillarboxing (but not both). The
* coordinate system is post-zoom, meaning that the activeArraySize or
- * preCorrectionActiveArraySize covers the camera device's field of view "after" zoom.
- * See {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for details.</p>
+ * preCorrectionActiveArraySize covers the camera device's field of view "after" zoom. See
+ * {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for details.</p>
* <p><b>Units</b>: Pixel coordinates relative to
* {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
* {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on distortion correction
@@ -2797,6 +2835,7 @@
* @see CaptureRequest#CONTROL_ZOOM_RATIO
* @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM
+ * @see CameraCharacteristics#SCALER_CROPPING_TYPE
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
* @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
*/
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index c995623..9b305b32 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -18,7 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.impl.CaptureResultExtras;
import android.hardware.camera2.impl.PublicKey;
@@ -2384,7 +2384,7 @@
* stream combinations of LIMITED hardware level are guaranteed.</p>
* <p>For a logical multi-camera, bokeh may be implemented by stereo vision from sub-cameras
* with different field of view. As a result, when bokeh mode is enabled, the camera device
- * may override android.scaler.CropRegion, and the field of view will be smaller than when
+ * may override {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}, and the field of view will be smaller than when
* bokeh mode is off.</p>
* <p><b>Possible values:</b>
* <ul>
@@ -2393,6 +2393,8 @@
* <li>{@link #CONTROL_BOKEH_MODE_CONTINUOUS CONTINUOUS}</li>
* </ul></p>
* <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#SCALER_CROP_REGION
* @see #CONTROL_BOKEH_MODE_OFF
* @see #CONTROL_BOKEH_MODE_STILL_CAPTURE
* @see #CONTROL_BOKEH_MODE_CONTINUOUS
@@ -3379,32 +3381,70 @@
* <p>For devices not supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
* system always follows that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with <code>(0, 0)</code> being
* the top-left pixel of the active array.</p>
- * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
- * system depends on the mode being set.
- * When the distortion correction mode is OFF, the coordinate system follows
- * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with
- * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array.
- * When the distortion correction mode is not OFF, the coordinate system follows
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
- * <code>(0, 0)</code> being the top-left pixel of the active array.</p>
- * <p>Output streams use this rectangle to produce their output,
- * cropping to a smaller region if necessary to maintain the
- * stream's aspect ratio, then scaling the sensor input to
+ * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate system
+ * depends on the mode being set. When the distortion correction mode is OFF, the
+ * coordinate system follows {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with <code>(0,
+ * 0)</code> being the top-left pixel of the pre-correction active array. When the distortion
+ * correction mode is not OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with <code>(0, 0)</code> being the top-left pixel of the
+ * active array.</p>
+ * <p>Output streams use this rectangle to produce their output, cropping to a smaller region
+ * if necessary to maintain the stream's aspect ratio, then scaling the sensor input to
* match the output's configured resolution.</p>
- * <p>The crop region is applied after the RAW to other color
- * space (e.g. YUV) conversion. Since raw streams
- * (e.g. RAW16) don't have the conversion stage, they are not
+ * <p>The crop region is applied after the RAW to other color space (e.g. YUV)
+ * conversion. Since raw streams (e.g. RAW16) don't have the conversion stage, they are not
* croppable. The crop region will be ignored by raw streams.</p>
- * <p>For non-raw streams, any additional per-stream cropping will
- * be done to maximize the final pixel area of the stream.</p>
- * <p>For example, if the crop region is set to a 4:3 aspect
- * ratio, then 4:3 streams will use the exact crop
- * region. 16:9 streams will further crop vertically
- * (letterbox).</p>
- * <p>Conversely, if the crop region is set to a 16:9, then 4:3
- * outputs will crop horizontally (pillarbox), and 16:9
- * streams will match exactly. These additional crops will
- * be centered within the crop region.</p>
+ * <p>For non-raw streams, any additional per-stream cropping will be done to maximize the
+ * final pixel area of the stream.</p>
+ * <p>For example, if the crop region is set to a 4:3 aspect ratio, then 4:3 streams will use
+ * the exact crop region. 16:9 streams will further crop vertically (letterbox).</p>
+ * <p>Conversely, if the crop region is set to a 16:9, then 4:3 outputs will crop horizontally
+ * (pillarbox), and 16:9 streams will match exactly. These additional crops will be
+ * centered within the crop region.</p>
+ * <p>To illustrate, here are several scenarios of different crop regions and output streams,
+ * for a hypothetical camera device with an active array of size <code>(2000,1500)</code>. Note that
+ * several of these examples use non-centered crop regions for ease of illustration; such
+ * regions are only supported on devices with FREEFORM capability
+ * ({@link CameraCharacteristics#SCALER_CROPPING_TYPE android.scaler.croppingType} <code>== FREEFORM</code>), but this does not affect the way the crop
+ * rules work otherwise.</p>
+ * <ul>
+ * <li>Camera Configuration:<ul>
+ * <li>Active array size: <code>2000x1500</code> (3 MP, 4:3 aspect ratio)</li>
+ * <li>Output stream #1: <code>640x480</code> (VGA, 4:3 aspect ratio)</li>
+ * <li>Output stream #2: <code>1280x720</code> (720p, 16:9 aspect ratio)</li>
+ * </ul>
+ * </li>
+ * <li>Case #1: 4:3 crop region with 2x digital zoom<ul>
+ * <li>Crop region: <code>Rect(500, 375, 1500, 1125) // (left, top, right, bottom)</code></li>
+ * <li><img alt="4:3 aspect ratio crop diagram" src="/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-ratio.png" /></li>
+ * <li><code>640x480</code> stream source area: <code>(500, 375, 1500, 1125)</code> (equal to crop region)</li>
+ * <li><code>1280x720</code> stream source area: <code>(500, 469, 1500, 1031)</code> (letterboxed)</li>
+ * </ul>
+ * </li>
+ * <li>Case #2: 16:9 crop region with ~1.5x digital zoom.<ul>
+ * <li>Crop region: <code>Rect(500, 375, 1833, 1125)</code></li>
+ * <li><img alt="16:9 aspect ratio crop diagram" src="/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-169-ratio.png" /></li>
+ * <li><code>640x480</code> stream source area: <code>(666, 375, 1666, 1125)</code> (pillarboxed)</li>
+ * <li><code>1280x720</code> stream source area: <code>(500, 375, 1833, 1125)</code> (equal to crop region)</li>
+ * </ul>
+ * </li>
+ * <li>Case #3: 1:1 crop region with ~2.6x digital zoom.<ul>
+ * <li>Crop region: <code>Rect(500, 375, 1250, 1125)</code></li>
+ * <li><img alt="1:1 aspect ratio crop diagram" src="/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-11-ratio.png" /></li>
+ * <li><code>640x480</code> stream source area: <code>(500, 469, 1250, 1031)</code> (letterboxed)</li>
+ * <li><code>1280x720</code> stream source area: <code>(500, 543, 1250, 957)</code> (letterboxed)</li>
+ * </ul>
+ * </li>
+ * <li>Case #4: Replace <code>640x480</code> stream with <code>1024x1024</code> stream, with 4:3 crop region:<ul>
+ * <li>Crop region: <code>Rect(500, 375, 1500, 1125)</code></li>
+ * <li><img alt="Square output, 4:3 aspect ratio crop diagram" src="/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-square-ratio.png" /></li>
+ * <li><code>1024x1024</code> stream source area: <code>(625, 375, 1375, 1125)</code> (pillarboxed)</li>
+ * <li><code>1280x720</code> stream source area: <code>(500, 469, 1500, 1031)</code> (letterboxed)</li>
+ * <li>Note that in this case, neither of the two outputs is a subset of the other, with
+ * each containing image data the other doesn't have.</li>
+ * </ul>
+ * </li>
+ * </ul>
* <p>If the coordinate system is {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, the width and height
* of the crop region cannot be set to be smaller than
* <code>floor( activeArraySize.width / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code> and
@@ -3415,18 +3455,16 @@
* and
* <code>floor( preCorrectionActiveArraySize.height / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code>,
* respectively.</p>
- * <p>The camera device may adjust the crop region to account
- * for rounding and other hardware requirements; the final
- * crop region used will be included in the output capture
- * result.</p>
+ * <p>The camera device may adjust the crop region to account for rounding and other hardware
+ * requirements; the final crop region used will be included in the output capture result.</p>
* <p>Starting from API level 30, it's strongly recommended to use {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}
* to take advantage of better support for zoom with logical multi-camera. The benefits
* include better precision with optical-digital zoom combination, and ability to do
* zoom-out from 1.0x. When using {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for zoom, the crop region in
* the capture request must be either letterboxing or pillarboxing (but not both). The
* coordinate system is post-zoom, meaning that the activeArraySize or
- * preCorrectionActiveArraySize covers the camera device's field of view "after" zoom.
- * See {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for details.</p>
+ * preCorrectionActiveArraySize covers the camera device's field of view "after" zoom. See
+ * {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for details.</p>
* <p><b>Units</b>: Pixel coordinates relative to
* {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
* {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on distortion correction
@@ -3436,6 +3474,7 @@
* @see CaptureRequest#CONTROL_ZOOM_RATIO
* @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM
+ * @see CameraCharacteristics#SCALER_CROPPING_TYPE
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
* @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
*/
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 41435c9..67e7a4c 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -50,8 +50,6 @@
import android.util.SparseArray;
import android.view.Surface;
-import com.android.internal.util.Preconditions;
-
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Collection;
@@ -60,6 +58,7 @@
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -2482,7 +2481,7 @@
private final Handler mHandler;
public CameraHandlerExecutor(@NonNull Handler handler) {
- mHandler = Preconditions.checkNotNull(handler);
+ mHandler = Objects.requireNonNull(handler);
}
@Override
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 6f90db4..1fab666 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -16,13 +16,12 @@
package android.hardware.camera2.impl;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.ImageFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraMetadata;
-import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.marshal.MarshalQueryable;
@@ -54,7 +53,6 @@
import android.hardware.camera2.params.HighSpeedVideoConfiguration;
import android.hardware.camera2.params.LensShadingMap;
import android.hardware.camera2.params.MandatoryStreamCombination;
-import android.hardware.camera2.params.MandatoryStreamCombination.MandatoryStreamInformation;
import android.hardware.camera2.params.OisSample;
import android.hardware.camera2.params.RecommendedStreamConfiguration;
import android.hardware.camera2.params.RecommendedStreamConfigurationMap;
@@ -73,14 +71,13 @@
import android.util.Range;
import android.util.Size;
-import com.android.internal.util.Preconditions;
-
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
+import java.util.Objects;
/**
* Implementation of camera metadata marshal/unmarshal across Binder to
@@ -432,7 +429,7 @@
* @return the field corresponding to the {@code key}, or {@code null} if no value was set
*/
public <T> T get(Key<T> key) {
- Preconditions.checkNotNull(key, "key must not be null");
+ Objects.requireNonNull(key, "key must not be null");
// Check if key has been overridden to use a wrapper class on the java side.
GetCommand g = sGetCommandMap.get(key);
diff --git a/core/java/android/hardware/camera2/params/BlackLevelPattern.java b/core/java/android/hardware/camera2/params/BlackLevelPattern.java
index 283977f..c3dc25f 100644
--- a/core/java/android/hardware/camera2/params/BlackLevelPattern.java
+++ b/core/java/android/hardware/camera2/params/BlackLevelPattern.java
@@ -16,9 +16,8 @@
package android.hardware.camera2.params;
-import static com.android.internal.util.Preconditions.checkNotNull;
-
import java.util.Arrays;
+import java.util.Objects;
/**
* Immutable class to store a 4-element vector of integers corresponding to a 2x2 pattern
@@ -88,7 +87,7 @@
* @throws NullPointerException if the destination is null.
*/
public void copyTo(int[] destination, int offset) {
- checkNotNull(destination, "destination must not be null");
+ Objects.requireNonNull(destination, "destination must not be null");
if (offset < 0) {
throw new IllegalArgumentException("Null offset passed to copyTo");
}
diff --git a/core/java/android/hardware/camera2/params/LensShadingMap.java b/core/java/android/hardware/camera2/params/LensShadingMap.java
index 9eb276f..38c1dd4 100644
--- a/core/java/android/hardware/camera2/params/LensShadingMap.java
+++ b/core/java/android/hardware/camera2/params/LensShadingMap.java
@@ -25,12 +25,12 @@
import static com.android.internal.util.Preconditions.checkArgumentNonnegative;
import static com.android.internal.util.Preconditions.checkArgumentPositive;
import static com.android.internal.util.Preconditions.checkArrayElementsInRange;
-import static com.android.internal.util.Preconditions.checkNotNull;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.utils.HashCodeHelpers;
import java.util.Arrays;
+import java.util.Objects;
/**
* Immutable class for describing a {@code 4 x N x M} lens shading map of floats.
@@ -70,7 +70,7 @@
mRows = checkArgumentPositive(rows, "rows must be positive");
mColumns = checkArgumentPositive(columns, "columns must be positive");
- mElements = checkNotNull(elements, "elements must not be null");
+ mElements = Objects.requireNonNull(elements, "elements must not be null");
if (elements.length != getGainFactorCount()) {
throw new IllegalArgumentException("elements must be " + getGainFactorCount() +
@@ -203,7 +203,7 @@
*/
public void copyGainFactors(final float[] destination, final int offset) {
checkArgumentNonnegative(offset, "offset must not be negative");
- checkNotNull(destination, "destination must not be null");
+ Objects.requireNonNull(destination, "destination must not be null");
if (destination.length + offset < getGainFactorCount()) {
throw new ArrayIndexOutOfBoundsException("destination too small to fit elements");
}
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index 336edc1..478922d 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -17,7 +17,6 @@
package android.hardware.camera2.params;
import static com.android.internal.util.Preconditions.checkArrayElementsNotNull;
-import static com.android.internal.util.Preconditions.checkNotNull;
import android.graphics.ImageFormat;
import android.graphics.PixelFormat;
@@ -491,7 +490,7 @@
* @see #isOutputSupportedFor(Surface)
*/
public static <T> boolean isOutputSupportedFor(Class<T> klass) {
- checkNotNull(klass, "klass must not be null");
+ Objects.requireNonNull(klass, "klass must not be null");
if (klass == android.media.ImageReader.class) {
return true;
@@ -548,7 +547,7 @@
* @see #isOutputSupportedFor(Class)
*/
public boolean isOutputSupportedFor(Surface surface) {
- checkNotNull(surface, "surface must not be null");
+ Objects.requireNonNull(surface, "surface must not be null");
Size surfaceSize = SurfaceUtils.getSurfaceSize(surface);
int surfaceFormat = SurfaceUtils.getSurfaceFormat(surface);
@@ -897,7 +896,7 @@
* @see PixelFormat
*/
public long getOutputMinFrameDuration(int format, Size size) {
- checkNotNull(size, "size must not be null");
+ Objects.requireNonNull(size, "size must not be null");
checkArgumentFormatSupported(format, /*output*/true);
return getInternalFormatDuration(imageFormatToInternal(format),
diff --git a/core/java/android/hardware/camera2/utils/HashCodeHelpers.java b/core/java/android/hardware/camera2/utils/HashCodeHelpers.java
index 526f086..16f3f2a 100644
--- a/core/java/android/hardware/camera2/utils/HashCodeHelpers.java
+++ b/core/java/android/hardware/camera2/utils/HashCodeHelpers.java
@@ -16,7 +16,7 @@
package android.hardware.camera2.utils;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
/**
* Provide hashing functions using the Modified Bernstein hash
diff --git a/core/java/android/hardware/camera2/utils/SurfaceUtils.java b/core/java/android/hardware/camera2/utils/SurfaceUtils.java
index d3c4505..abe1372 100644
--- a/core/java/android/hardware/camera2/utils/SurfaceUtils.java
+++ b/core/java/android/hardware/camera2/utils/SurfaceUtils.java
@@ -16,7 +16,7 @@
package android.hardware.camera2.utils;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.ImageFormat;
import android.hardware.camera2.legacy.LegacyCameraDevice;
import android.hardware.camera2.legacy.LegacyExceptionUtils.BufferQueueAbandonedException;
diff --git a/core/java/android/hardware/camera2/utils/TypeReference.java b/core/java/android/hardware/camera2/utils/TypeReference.java
index d9ba31b..435ed15 100644
--- a/core/java/android/hardware/camera2/utils/TypeReference.java
+++ b/core/java/android/hardware/camera2/utils/TypeReference.java
@@ -16,7 +16,10 @@
package android.hardware.camera2.utils;
-import android.annotation.UnsupportedAppUsage;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.compat.annotation.UnsupportedAppUsage;
+
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
@@ -24,8 +27,6 @@
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
-import static com.android.internal.util.Preconditions.*;
-
/**
* Super type token; allows capturing generic types at runtime by forcing them to be reified.
*
diff --git a/core/java/android/hardware/display/AmbientBrightnessDayStats.java b/core/java/android/hardware/display/AmbientBrightnessDayStats.java
index 350bc30d..26fd265 100644
--- a/core/java/android/hardware/display/AmbientBrightnessDayStats.java
+++ b/core/java/android/hardware/display/AmbientBrightnessDayStats.java
@@ -27,6 +27,7 @@
import java.time.LocalDate;
import java.util.Arrays;
+import java.util.Objects;
/**
* AmbientBrightnessDayStats stores and manipulates brightness stats over a single day.
@@ -70,8 +71,8 @@
*/
public AmbientBrightnessDayStats(@NonNull LocalDate localDate,
@NonNull float[] bucketBoundaries, float[] stats) {
- Preconditions.checkNotNull(localDate);
- Preconditions.checkNotNull(bucketBoundaries);
+ Objects.requireNonNull(localDate);
+ Objects.requireNonNull(bucketBoundaries);
Preconditions.checkArrayElementsInRange(bucketBoundaries, 0, Float.MAX_VALUE,
"bucketBoundaries");
if (bucketBoundaries.length < 1) {
diff --git a/core/java/android/hardware/display/BrightnessConfiguration.java b/core/java/android/hardware/display/BrightnessConfiguration.java
index 0a38538..13122d2 100644
--- a/core/java/android/hardware/display/BrightnessConfiguration.java
+++ b/core/java/android/hardware/display/BrightnessConfiguration.java
@@ -541,8 +541,8 @@
* @throws IllegalArgumentException if the nit levels are not monotonically increasing.
*/
public Builder(float[] lux, float[] nits) {
- Preconditions.checkNotNull(lux);
- Preconditions.checkNotNull(nits);
+ Objects.requireNonNull(lux);
+ Objects.requireNonNull(nits);
if (lux.length == 0 || nits.length == 0) {
throw new IllegalArgumentException("Lux and nits arrays must not be empty");
}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 0b25dbd..799dff9 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -23,8 +23,8 @@
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
import android.app.KeyguardManager;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.graphics.Point;
import android.media.projection.MediaProjection;
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index ea63776..2a58495 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -18,7 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.pm.ParceledListSlice;
import android.content.res.Resources;
diff --git a/core/java/android/hardware/display/WifiDisplay.java b/core/java/android/hardware/display/WifiDisplay.java
index 55e6051..5bbbbf9 100644
--- a/core/java/android/hardware/display/WifiDisplay.java
+++ b/core/java/android/hardware/display/WifiDisplay.java
@@ -16,7 +16,7 @@
package android.hardware.display;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/android/hardware/display/WifiDisplayStatus.java b/core/java/android/hardware/display/WifiDisplayStatus.java
index 1973e51..e2a825f 100644
--- a/core/java/android/hardware/display/WifiDisplayStatus.java
+++ b/core/java/android/hardware/display/WifiDisplayStatus.java
@@ -16,7 +16,7 @@
package android.hardware.display;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 22f8590..7bc4529 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -26,8 +26,8 @@
import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
-import android.annotation.UnsupportedAppUsage;
import android.app.ActivityManager;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.biometrics.BiometricAuthenticator;
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 0c0f248..8d32db0 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -20,7 +20,7 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemService;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.media.AudioAttributes;
import android.os.Binder;
diff --git a/core/java/android/hardware/location/GeofenceHardware.java b/core/java/android/hardware/location/GeofenceHardware.java
index 23d8d01..a1866af 100644
--- a/core/java/android/hardware/location/GeofenceHardware.java
+++ b/core/java/android/hardware/location/GeofenceHardware.java
@@ -16,7 +16,7 @@
package android.hardware.location;
import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.location.Location;
import android.os.Build;
import android.os.RemoteException;
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index 5484df4..55505ba 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -27,8 +27,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
import android.app.ActivityThread;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.media.AudioFormat;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
@@ -44,12 +44,10 @@
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.List;
import java.util.UUID;
/**
- * The SoundTrigger class provides access via JNI to the native service managing
- * the sound trigger HAL.
+ * The SoundTrigger class provides access to the service managing the sound trigger HAL.
*
* @hide
*/
diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
index 7cf5600..7291419 100644
--- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
+++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
@@ -18,11 +18,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
-import android.media.soundtrigger_middleware.ModelParameterRange;
import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
import android.media.soundtrigger_middleware.PhraseSoundModel;
import android.media.soundtrigger_middleware.RecognitionEvent;
diff --git a/core/java/android/hardware/usb/UsbAccessory.java b/core/java/android/hardware/usb/UsbAccessory.java
index a725205..a94266b 100644
--- a/core/java/android/hardware/usb/UsbAccessory.java
+++ b/core/java/android/hardware/usb/UsbAccessory.java
@@ -26,6 +26,8 @@
import com.android.internal.util.Preconditions;
import java.util.Objects;
+import java.util.Objects;
+
/**
* A class representing a USB accessory, which is an external hardware component
* that communicates with an android application over USB.
diff --git a/core/java/android/hardware/usb/UsbDevice.java b/core/java/android/hardware/usb/UsbDevice.java
index 67e05c8..a0f64cd 100644
--- a/core/java/android/hardware/usb/UsbDevice.java
+++ b/core/java/android/hardware/usb/UsbDevice.java
@@ -18,13 +18,14 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
import android.app.ActivityThread;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import com.android.internal.util.Preconditions;
+
import java.util.Objects;
/**
diff --git a/core/java/android/hardware/usb/UsbDeviceConnection.java b/core/java/android/hardware/usb/UsbDeviceConnection.java
index 1a5cdd9..53a5785 100644
--- a/core/java/android/hardware/usb/UsbDeviceConnection.java
+++ b/core/java/android/hardware/usb/UsbDeviceConnection.java
@@ -20,7 +20,7 @@
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Build;
import android.os.ParcelFileDescriptor;
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index eb148b9..73b9d17 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -26,8 +26,8 @@
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
import android.annotation.SystemService;
-import android.annotation.UnsupportedAppUsage;
import android.app.PendingIntent;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
diff --git a/core/java/android/hardware/usb/UsbRequest.java b/core/java/android/hardware/usb/UsbRequest.java
index d464ab5..473df71 100644
--- a/core/java/android/hardware/usb/UsbRequest.java
+++ b/core/java/android/hardware/usb/UsbRequest.java
@@ -17,7 +17,7 @@
package android.hardware.usb;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.util.Log;
diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
index 7d4849f..9327b24 100644
--- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
@@ -16,7 +16,7 @@
package android.inputmethodservice;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.graphics.Rect;
import android.os.Bundle;
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 62f0196..4d5fabb 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -18,7 +18,7 @@
import android.annotation.BinderThread;
import android.annotation.MainThread;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index fe4f860..8e52ee9 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -34,9 +34,9 @@
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
import android.app.ActivityManager;
import android.app.Dialog;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Configuration;
@@ -517,7 +517,9 @@
@Override
public void onCreateInlineSuggestionsRequest(ComponentName componentName,
AutofillId autofillId, IInlineSuggestionsRequestCallback cb) {
- Log.d(TAG, "InputMethodService received onCreateInlineSuggestionsRequest()");
+ if (DEBUG) {
+ Log.d(TAG, "InputMethodService received onCreateInlineSuggestionsRequest()");
+ }
handleOnCreateInlineSuggestionsRequest(componentName, autofillId, cb);
}
@@ -755,7 +757,12 @@
private void handleOnCreateInlineSuggestionsRequest(@NonNull ComponentName componentName,
@NonNull AutofillId autofillId, @NonNull IInlineSuggestionsRequestCallback callback) {
if (!mInputStarted) {
- Log.w(TAG, "onStartInput() not called yet");
+ try {
+ Log.w(TAG, "onStartInput() not called yet");
+ callback.onInlineSuggestionsUnsupported();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to call onInlineSuggestionsUnsupported.", e);
+ }
return;
}
@@ -768,8 +775,12 @@
private void handleOnInlineSuggestionsResponse(@NonNull ComponentName componentName,
@NonNull AutofillId autofillId, @NonNull InlineSuggestionsResponse response) {
if (!mInlineSuggestionsRequestInfo.validate(componentName)) {
- Log.d(TAG, "Response component=" + componentName + " differs from request component="
- + mInlineSuggestionsRequestInfo.mComponentName + ", ignoring response");
+ if (DEBUG) {
+ Log.d(TAG,
+ "Response component=" + componentName + " differs from request component="
+ + mInlineSuggestionsRequestInfo.mComponentName
+ + ", ignoring response");
+ }
return;
}
onInlineSuggestionsResponse(response);
@@ -841,7 +852,7 @@
*/
public boolean validate(ComponentName componentName) {
final boolean result = componentName.equals(mComponentName);
- if (!result) {
+ if (DEBUG && !result) {
Log.d(TAG, "Cached request info ComponentName=" + mComponentName
+ " differs from received ComponentName=" + componentName);
}
diff --git a/core/java/android/inputmethodservice/Keyboard.java b/core/java/android/inputmethodservice/Keyboard.java
index 3f25113..c85fcd9 100644
--- a/core/java/android/inputmethodservice/Keyboard.java
+++ b/core/java/android/inputmethodservice/Keyboard.java
@@ -16,8 +16,8 @@
package android.inputmethodservice;
-import android.annotation.UnsupportedAppUsage;
import android.annotation.XmlRes;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
index 45f067b..b780b21 100644
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -16,7 +16,7 @@
package android.inputmethodservice;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java
new file mode 100644
index 0000000..6afdb5e
--- /dev/null
+++ b/core/java/android/net/ConnectivityDiagnosticsManager.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.PersistableBundle;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
+/**
+ * Class that provides utilities for collecting network connectivity diagnostics information.
+ * Connectivity information is made available through triggerable diagnostics tools and by listening
+ * to System validations. Some diagnostics information may be permissions-restricted.
+ *
+ * <p>ConnectivityDiagnosticsManager is intended for use by applications offering network
+ * connectivity on a user device. These tools will provide several mechanisms for these applications
+ * to be alerted to network conditions as well as diagnose potential network issues themselves.
+ *
+ * <p>The primary responsibilities of this class are to:
+ *
+ * <ul>
+ * <li>Allow permissioned applications to register and unregister callbacks for network event
+ * notifications
+ * <li>Invoke callbacks for network event notifications, including:
+ * <ul>
+ * <li>Network validations
+ * <li>Data stalls
+ * <li>Connectivity reports from applications
+ * </ul>
+ * </ul>
+ */
+public class ConnectivityDiagnosticsManager {
+ public static final int DETECTION_METHOD_DNS_EVENTS = 1;
+ public static final int DETECTION_METHOD_TCP_METRICS = 2;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = {"DETECTION_METHOD_"},
+ value = {DETECTION_METHOD_DNS_EVENTS, DETECTION_METHOD_TCP_METRICS})
+ public @interface DetectionMethod {}
+
+ /** @hide */
+ public ConnectivityDiagnosticsManager() {}
+
+ /** Class that includes connectivity information for a specific Network at a specific time. */
+ public static class ConnectivityReport {
+ /** The Network for which this ConnectivityReport applied */
+ @NonNull public final Network network;
+
+ /**
+ * The timestamp for the report. The timestamp is taken from {@link
+ * System#currentTimeMillis}.
+ */
+ public final long reportTimestamp;
+
+ /** LinkProperties available on the Network at the reported timestamp */
+ @NonNull public final LinkProperties linkProperties;
+
+ /** NetworkCapabilities available on the Network at the reported timestamp */
+ @NonNull public final NetworkCapabilities networkCapabilities;
+
+ /** PersistableBundle that may contain additional info about the report */
+ @NonNull public final PersistableBundle additionalInfo;
+
+ /**
+ * Constructor for ConnectivityReport.
+ *
+ * <p>Apps should obtain instances through {@link
+ * ConnectivityDiagnosticsCallback#onConnectivityReport} instead of instantiating their own
+ * instances (unless for testing purposes).
+ *
+ * @param network The Network for which this ConnectivityReport applies
+ * @param reportTimestamp The timestamp for the report
+ * @param linkProperties The LinkProperties available on network at reportTimestamp
+ * @param networkCapabilities The NetworkCapabilities available on network at
+ * reportTimestamp
+ * @param additionalInfo A PersistableBundle that may contain additional info about the
+ * report
+ */
+ public ConnectivityReport(
+ @NonNull Network network,
+ long reportTimestamp,
+ @NonNull LinkProperties linkProperties,
+ @NonNull NetworkCapabilities networkCapabilities,
+ @NonNull PersistableBundle additionalInfo) {
+ this.network = network;
+ this.reportTimestamp = reportTimestamp;
+ this.linkProperties = linkProperties;
+ this.networkCapabilities = networkCapabilities;
+ this.additionalInfo = additionalInfo;
+ }
+ }
+
+ /** Class that includes information for a suspected data stall on a specific Network */
+ public static class DataStallReport {
+ /** The Network for which this DataStallReport applied */
+ @NonNull public final Network network;
+
+ /**
+ * The timestamp for the report. The timestamp is taken from {@link
+ * System#currentTimeMillis}.
+ */
+ public final long reportTimestamp;
+
+ /** The detection method used to identify the suspected data stall */
+ @DetectionMethod public final int detectionMethod;
+
+ /** PersistableBundle that may contain additional information on the suspected data stall */
+ @NonNull public final PersistableBundle stallDetails;
+
+ /**
+ * Constructor for DataStallReport.
+ *
+ * <p>Apps should obtain instances through {@link
+ * ConnectivityDiagnosticsCallback#onDataStallSuspected} instead of instantiating their own
+ * instances (unless for testing purposes).
+ *
+ * @param network The Network for which this DataStallReport applies
+ * @param reportTimestamp The timestamp for the report
+ * @param detectionMethod The detection method used to identify this data stall
+ * @param stallDetails A PersistableBundle that may contain additional info about the report
+ */
+ public DataStallReport(
+ @NonNull Network network,
+ long reportTimestamp,
+ @DetectionMethod int detectionMethod,
+ @NonNull PersistableBundle stallDetails) {
+ this.network = network;
+ this.reportTimestamp = reportTimestamp;
+ this.detectionMethod = detectionMethod;
+ this.stallDetails = stallDetails;
+ }
+ }
+
+ /**
+ * Abstract base class for Connectivity Diagnostics callbacks. Used for notifications about
+ * network connectivity events. Must be extended by applications wanting notifications.
+ */
+ public abstract static class ConnectivityDiagnosticsCallback {
+ /**
+ * Called when the platform completes a data connectivity check. This will also be invoked
+ * upon registration with the latest report.
+ *
+ * <p>The Network specified in the ConnectivityReport may not be active any more when this
+ * method is invoked.
+ *
+ * @param report The ConnectivityReport containing information about a connectivity check
+ */
+ public void onConnectivityReport(@NonNull ConnectivityReport report) {}
+
+ /**
+ * Called when the platform suspects a data stall on some Network.
+ *
+ * <p>The Network specified in the DataStallReport may not be active any more when this
+ * method is invoked.
+ *
+ * @param report The DataStallReport containing information about the suspected data stall
+ */
+ public void onDataStallSuspected(@NonNull DataStallReport report) {}
+
+ /**
+ * Called when any app reports connectivity to the System.
+ *
+ * @param network The Network for which connectivity has been reported
+ * @param hasConnectivity The connectivity reported to the System
+ */
+ public void onNetworkConnectivityReported(
+ @NonNull Network network, boolean hasConnectivity) {}
+ }
+
+ /**
+ * Registers a ConnectivityDiagnosticsCallback with the System.
+ *
+ * <p>Only apps that offer network connectivity to the user are allowed to register callbacks.
+ * This includes:
+ *
+ * <ul>
+ * <li>Carrier apps with active subscriptions
+ * <li>Active VPNs
+ * <li>WiFi Suggesters
+ * </ul>
+ *
+ * <p>Callbacks will be limited to receiving notifications for networks over which apps provide
+ * connectivity.
+ *
+ * <p>If a registering app loses its relevant permissions, any callbacks it registered will
+ * silently stop receiving callbacks.
+ *
+ * <p>Each register() call <b>MUST</b> use a unique ConnectivityDiagnosticsCallback instance. If
+ * a single instance is registered with multiple NetworkRequests, an IllegalArgumentException
+ * will be thrown.
+ *
+ * @param request The NetworkRequest that will be used to match with Networks for which
+ * callbacks will be fired
+ * @param e The Executor to be used for running the callback method invocations
+ * @param callback The ConnectivityDiagnosticsCallback that the caller wants registered with the
+ * System
+ * @throws IllegalArgumentException if the same callback instance is registered with multiple
+ * NetworkRequests
+ * @throws SecurityException if the caller does not have appropriate permissions to register a
+ * callback
+ */
+ public void registerConnectivityDiagnosticsCallback(
+ @NonNull NetworkRequest request,
+ @NonNull Executor e,
+ @NonNull ConnectivityDiagnosticsCallback callback) {
+ // TODO(b/143187964): implement ConnectivityDiagnostics functionality
+ throw new UnsupportedOperationException("registerCallback() not supported yet");
+ }
+
+ /**
+ * Unregisters a ConnectivityDiagnosticsCallback with the System.
+ *
+ * <p>If the given callback is not currently registered with the System, this operation will be
+ * a no-op.
+ *
+ * @param callback The ConnectivityDiagnosticsCallback to be unregistered from the System.
+ */
+ public void unregisterConnectivityDiagnosticsCallback(
+ @NonNull ConnectivityDiagnosticsCallback callback) {
+ // TODO(b/143187964): implement ConnectivityDiagnostics functionality
+ throw new UnsupportedOperationException("registerCallback() not supported yet");
+ }
+}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 3ed51d7..6da16a8 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -27,8 +27,8 @@
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
import android.app.PendingIntent;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
import android.net.IpSecManager.UdpEncapsulationSocket;
@@ -3072,6 +3072,7 @@
* @hide
*/
@RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_AIRPLANE_MODE,
android.Manifest.permission.NETWORK_SETTINGS,
android.Manifest.permission.NETWORK_SETUP_WIZARD,
android.Manifest.permission.NETWORK_STACK})
diff --git a/core/java/android/net/DhcpResults.java b/core/java/android/net/DhcpResults.java
index 97be51a..059cd94 100644
--- a/core/java/android/net/DhcpResults.java
+++ b/core/java/android/net/DhcpResults.java
@@ -16,7 +16,7 @@
package android.net;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.net.shared.InetAddressUtils;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/android/net/EthernetManager.java b/core/java/android/net/EthernetManager.java
index 7256502..fd015b4 100644
--- a/core/java/android/net/EthernetManager.java
+++ b/core/java/android/net/EthernetManager.java
@@ -17,7 +17,7 @@
package android.net;
import android.annotation.SystemService;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
diff --git a/core/java/android/net/InterfaceConfiguration.java b/core/java/android/net/InterfaceConfiguration.java
index 1ae44e1..37425ff 100644
--- a/core/java/android/net/InterfaceConfiguration.java
+++ b/core/java/android/net/InterfaceConfiguration.java
@@ -16,7 +16,7 @@
package android.net;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/android/net/IpConfiguration.java b/core/java/android/net/IpConfiguration.java
index dddb64d..23d5ff7 100644
--- a/core/java/android/net/IpConfiguration.java
+++ b/core/java/android/net/IpConfiguration.java
@@ -20,8 +20,7 @@
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
-import android.net.StaticIpConfiguration;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java
index 93dd2e4..bf8b38f 100644
--- a/core/java/android/net/LinkAddress.java
+++ b/core/java/android/net/LinkAddress.java
@@ -30,7 +30,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index ed509cb..2792c56 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -20,7 +20,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -63,6 +63,7 @@
private String mPrivateDnsServerName;
private String mDomains;
private ArrayList<RouteInfo> mRoutes = new ArrayList<>();
+ private Inet4Address mDhcpServerAddress;
private ProxyInfo mHttpProxy;
private int mMtu;
// in the format "rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max"
@@ -196,6 +197,7 @@
addStackedLink(l);
}
setMtu(source.mMtu);
+ setDhcpServerAddress(source.getDhcpServerAddress());
mTcpBufferSizes = source.mTcpBufferSizes;
mNat64Prefix = source.mNat64Prefix;
mWakeOnLanSupported = source.mWakeOnLanSupported;
@@ -460,6 +462,24 @@
}
/**
+ * Set DHCP server address.
+ *
+ * @param serverAddress the server address to set.
+ */
+ public void setDhcpServerAddress(@Nullable Inet4Address serverAddress) {
+ mDhcpServerAddress = serverAddress;
+ }
+
+ /**
+ * Get DHCP server address
+ *
+ * @return The current DHCP server address.
+ */
+ public @Nullable Inet4Address getDhcpServerAddress() {
+ return mDhcpServerAddress;
+ }
+
+ /**
* Returns the private DNS server name that is in use. If not {@code null},
* private DNS is in strict mode. In this mode, applications should ensure
* that all DNS queries are encrypted and sent to this hostname and that
@@ -851,6 +871,7 @@
mHttpProxy = null;
mStackedLinks.clear();
mMtu = 0;
+ mDhcpServerAddress = null;
mTcpBufferSizes = null;
mNat64Prefix = null;
mWakeOnLanSupported = false;
@@ -919,6 +940,11 @@
resultJoiner.add("WakeOnLanSupported: true");
}
+ if (mDhcpServerAddress != null) {
+ resultJoiner.add("ServerAddress:");
+ resultJoiner.add(mDhcpServerAddress.toString());
+ }
+
if (mTcpBufferSizes != null) {
resultJoiner.add("TcpBufferSizes:");
resultJoiner.add(mTcpBufferSizes);
@@ -1273,6 +1299,17 @@
}
/**
+ * Compares this {@code LinkProperties} DHCP server address against the target
+ *
+ * @param target LinkProperties to compare.
+ * @return {@code true} if both are identical, {@code false} otherwise.
+ * @hide
+ */
+ public boolean isIdenticalDhcpServerAddress(@NonNull LinkProperties target) {
+ return Objects.equals(mDhcpServerAddress, target.mDhcpServerAddress);
+ }
+
+ /**
* Compares this {@code LinkProperties} interface addresses against the target
*
* @param target LinkProperties to compare.
@@ -1489,6 +1526,7 @@
*/
return isIdenticalInterfaceName(target)
&& isIdenticalAddresses(target)
+ && isIdenticalDhcpServerAddress(target)
&& isIdenticalDnses(target)
&& isIdenticalPrivateDns(target)
&& isIdenticalValidatedPrivateDnses(target)
@@ -1613,6 +1651,7 @@
+ mMtu * 51
+ ((null == mTcpBufferSizes) ? 0 : mTcpBufferSizes.hashCode())
+ (mUsePrivateDns ? 57 : 0)
+ + ((null == mDhcpServerAddress) ? 0 : mDhcpServerAddress.hashCode())
+ mPcscfs.size() * 67
+ ((null == mPrivateDnsServerName) ? 0 : mPrivateDnsServerName.hashCode())
+ Objects.hash(mNat64Prefix)
@@ -1635,6 +1674,7 @@
dest.writeString(mPrivateDnsServerName);
writeAddresses(dest, mPcscfs);
dest.writeString(mDomains);
+ writeAddress(dest, mDhcpServerAddress);
dest.writeInt(mMtu);
dest.writeString(mTcpBufferSizes);
dest.writeInt(mRoutes.size());
@@ -1663,8 +1703,9 @@
}
}
- private static void writeAddress(@NonNull Parcel dest, @NonNull InetAddress addr) {
- dest.writeByteArray(addr.getAddress());
+ private static void writeAddress(@NonNull Parcel dest, @Nullable InetAddress addr) {
+ byte[] addressBytes = (addr == null ? null : addr.getAddress());
+ dest.writeByteArray(addressBytes);
if (addr instanceof Inet6Address) {
final Inet6Address v6Addr = (Inet6Address) addr;
final boolean hasScopeId = v6Addr.getScopeId() != 0;
@@ -1673,9 +1714,11 @@
}
}
- @NonNull
+ @Nullable
private static InetAddress readAddress(@NonNull Parcel p) throws UnknownHostException {
final byte[] addr = p.createByteArray();
+ if (addr == null) return null;
+
if (addr.length == INET6_ADDR_LENGTH) {
final boolean hasScopeId = p.readBoolean();
final int scopeId = hasScopeId ? p.readInt() : 0;
@@ -1722,6 +1765,10 @@
} catch (UnknownHostException e) { }
}
netProp.setDomains(in.readString());
+ try {
+ netProp.setDhcpServerAddress((Inet4Address) InetAddress
+ .getByAddress(in.createByteArray()));
+ } catch (UnknownHostException e) { }
netProp.setMtu(in.readInt());
netProp.setTcpBufferSizes(in.readString());
addressCount = in.readInt();
diff --git a/core/java/android/net/LinkQualityInfo.java b/core/java/android/net/LinkQualityInfo.java
index 2e6915f..aa56cff 100644
--- a/core/java/android/net/LinkQualityInfo.java
+++ b/core/java/android/net/LinkQualityInfo.java
@@ -16,7 +16,7 @@
package android.net;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/android/net/LocalSocket.java b/core/java/android/net/LocalSocket.java
index 6a2031b..5b38f78 100644
--- a/core/java/android/net/LocalSocket.java
+++ b/core/java/android/net/LocalSocket.java
@@ -16,7 +16,8 @@
package android.net;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
+
import java.io.Closeable;
import java.io.FileDescriptor;
import java.io.IOException;
diff --git a/core/java/android/net/LocalSocketImpl.java b/core/java/android/net/LocalSocketImpl.java
index b066a15..e80e3a6 100644
--- a/core/java/android/net/LocalSocketImpl.java
+++ b/core/java/android/net/LocalSocketImpl.java
@@ -16,7 +16,7 @@
package android.net;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.system.ErrnoException;
import android.system.Int32Ref;
import android.system.Os;
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index 8729514..74c9aac 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -19,7 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.net.wifi.WifiInfo;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/android/net/MobileLinkQualityInfo.java b/core/java/android/net/MobileLinkQualityInfo.java
index a10a14d..a65de6b 100644
--- a/core/java/android/net/MobileLinkQualityInfo.java
+++ b/core/java/android/net/MobileLinkQualityInfo.java
@@ -16,7 +16,7 @@
package android.net;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
/**
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index c6c73fe..c5681cb 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -19,7 +19,7 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.system.ErrnoException;
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index ff4bf2d..60bd573 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -17,7 +17,7 @@
package android.net;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 4cee5f3..33c39d4 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -21,7 +21,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.net.ConnectivityManager.NetworkCallback;
import android.os.Build;
import android.os.Parcel;
diff --git a/core/java/android/net/NetworkFactory.java b/core/java/android/net/NetworkFactory.java
index 5b1d12c..42ab925 100644
--- a/core/java/android/net/NetworkFactory.java
+++ b/core/java/android/net/NetworkFactory.java
@@ -16,7 +16,7 @@
package android.net;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index 92f105f..d0c5363 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -17,7 +17,7 @@
package android.net;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java
index 33baebb..4f05c9b 100644
--- a/core/java/android/net/NetworkPolicy.java
+++ b/core/java/android/net/NetworkPolicy.java
@@ -16,7 +16,7 @@
package android.net;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.BackupUtils;
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 9150aae..de962f8 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -19,8 +19,8 @@
import static android.content.pm.PackageManager.GET_SIGNATURES;
import android.annotation.SystemService;
-import android.annotation.UnsupportedAppUsage;
import android.app.ActivityManager;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
diff --git a/core/java/android/net/NetworkQuotaInfo.java b/core/java/android/net/NetworkQuotaInfo.java
index a46cdde..2e52d9c 100644
--- a/core/java/android/net/NetworkQuotaInfo.java
+++ b/core/java/android/net/NetworkQuotaInfo.java
@@ -16,7 +16,7 @@
package android.net;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 2992127..9731f3c 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -20,7 +20,7 @@
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.net.NetworkCapabilities.NetCapability;
import android.net.NetworkCapabilities.Transport;
import android.os.Build;
@@ -247,9 +247,8 @@
* removing even the capabilities that are set by default when the object is constructed.
*
* @return The builder to facilitate chaining.
- * @hide
*/
- @UnsupportedAppUsage
+ @NonNull
public Builder clearCapabilities() {
mNetworkCapabilities.clearAll();
return this;
diff --git a/core/java/android/net/NetworkSpecifier.java b/core/java/android/net/NetworkSpecifier.java
index 2bc3eb5..cf31d21 100644
--- a/core/java/android/net/NetworkSpecifier.java
+++ b/core/java/android/net/NetworkSpecifier.java
@@ -16,6 +16,9 @@
package android.net;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+
/**
* Describes specific properties of a requested network for use in a {@link NetworkRequest}.
*
@@ -31,7 +34,8 @@
*
* @hide
*/
- public abstract boolean satisfiedBy(NetworkSpecifier other);
+ @SystemApi
+ public abstract boolean satisfiedBy(@Nullable NetworkSpecifier other);
/**
* Optional method which can be overridden by concrete implementations of NetworkSpecifier to
@@ -45,6 +49,7 @@
*
* @hide
*/
+ @SystemApi
public void assertValidFromUid(int requestorUid) {
// empty
}
@@ -68,6 +73,8 @@
*
* @hide
*/
+ @SystemApi
+ @Nullable
public NetworkSpecifier redact() {
// TODO (b/122160111): convert default to null once all platform NetworkSpecifiers
// implement this method.
diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java
index 292cf50..e449615 100644
--- a/core/java/android/net/NetworkState.java
+++ b/core/java/android/net/NetworkState.java
@@ -16,7 +16,7 @@
package android.net;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 6c7aa13..45bf4d2 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -19,7 +19,7 @@
import static android.os.Process.CLAT_UID;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java
index d96d2ee..6d46c20 100644
--- a/core/java/android/net/NetworkStatsHistory.java
+++ b/core/java/android/net/NetworkStatsHistory.java
@@ -30,7 +30,7 @@
import static com.android.internal.util.ArrayUtils.total;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.service.NetworkStatsHistoryBucketProto;
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index 87c7118..5e6c47a 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -34,7 +34,7 @@
import static android.net.NetworkStats.ROAMING_YES;
import static android.net.wifi.WifiInfo.removeDoubleQuotes;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.BackupUtils;
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index d0f54b4..08cc4e2 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -20,7 +20,7 @@
import static android.system.OsConstants.AF_INET6;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.net.shared.Inet4AddressUtils;
import android.os.Build;
import android.system.ErrnoException;
@@ -61,13 +61,6 @@
public static native void detachBPFFilter(FileDescriptor fd) throws SocketException;
/**
- * Configures a socket for receiving ICMPv6 router solicitations and sending advertisements.
- * @param fd the socket's {@link FileDescriptor}.
- * @param ifIndex the interface index.
- */
- public native static void setupRaSocket(FileDescriptor fd, int ifIndex) throws SocketException;
-
- /**
* Binds the current process to the network designated by {@code netId}. All sockets created
* in the future (and not explicitly bound via a bound {@link SocketFactory} (see
* {@link Network#getSocketFactory}) will be bound to this network. Note that if this
diff --git a/core/java/android/net/OWNERS b/core/java/android/net/OWNERS
index a1c7fce..767b693 100644
--- a/core/java/android/net/OWNERS
+++ b/core/java/android/net/OWNERS
@@ -8,4 +8,4 @@
reminv@google.com
satk@google.com
-per-file SSL*, Uri*, Url* = flooey@google.com, narayan@google.com, tobiast@google.com
+per-file SSL*, Uri*, Url* = prb@google.com, dauletz@google.com, narayan@google.com, tobiast@google.com
diff --git a/core/java/android/net/Proxy.java b/core/java/android/net/Proxy.java
index 4600942..4ba7394 100644
--- a/core/java/android/net/Proxy.java
+++ b/core/java/android/net/Proxy.java
@@ -18,7 +18,7 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
diff --git a/core/java/android/net/ProxyInfo.java b/core/java/android/net/ProxyInfo.java
index 9d92db4..ffe9ae9 100644
--- a/core/java/android/net/ProxyInfo.java
+++ b/core/java/android/net/ProxyInfo.java
@@ -18,7 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index 52d3fc4..ea6002c 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -21,7 +21,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java
index 39cb323..8b6ac42 100644
--- a/core/java/android/net/SSLCertificateSocketFactory.java
+++ b/core/java/android/net/SSLCertificateSocketFactory.java
@@ -16,7 +16,7 @@
package android.net;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.SystemProperties;
import android.util.Log;
diff --git a/core/java/android/net/SSLSessionCache.java b/core/java/android/net/SSLSessionCache.java
index 9667e82..944bc54 100644
--- a/core/java/android/net/SSLSessionCache.java
+++ b/core/java/android/net/SSLSessionCache.java
@@ -16,7 +16,7 @@
package android.net;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.util.Log;
diff --git a/core/java/android/net/SntpClient.java b/core/java/android/net/SntpClient.java
index f9c2def..8c6faf6 100644
--- a/core/java/android/net/SntpClient.java
+++ b/core/java/android/net/SntpClient.java
@@ -16,7 +16,7 @@
package android.net;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.SystemClock;
import android.util.Log;
diff --git a/core/java/android/net/StaticIpConfiguration.java b/core/java/android/net/StaticIpConfiguration.java
index 990c114..f24a9bd 100644
--- a/core/java/android/net/StaticIpConfiguration.java
+++ b/core/java/android/net/StaticIpConfiguration.java
@@ -20,7 +20,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.net.shared.InetAddressUtils;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index bf4884a..162d6e2 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -20,10 +20,10 @@
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
import android.app.DownloadManager;
import android.app.backup.BackupManager;
import android.app.usage.NetworkStatsManager;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.media.MediaPlayer;
import android.os.Build;
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 44d977a..525bbfd 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -19,7 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Intent;
import android.os.Environment;
import android.os.Parcel;
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index ed7cddc..4b804b0 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -23,11 +23,11 @@
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
import android.app.Activity;
import android.app.PendingIntent;
import android.app.Service;
import android.app.admin.DevicePolicyManager;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
diff --git a/core/java/android/net/WebAddress.java b/core/java/android/net/WebAddress.java
index 994c794..aa3777d 100644
--- a/core/java/android/net/WebAddress.java
+++ b/core/java/android/net/WebAddress.java
@@ -20,7 +20,7 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import java.util.Locale;
diff --git a/core/java/android/net/http/OWNERS b/core/java/android/net/http/OWNERS
index 6b8c9ed..3092612 100644
--- a/core/java/android/net/http/OWNERS
+++ b/core/java/android/net/http/OWNERS
@@ -1,3 +1,4 @@
-flooey@google.com
narayan@google.com
tobiast@google.com
+include platform/libcore:/OWNERS
+include platform/external/conscrypt:/OWNERS
diff --git a/core/java/android/net/http/SslCertificate.java b/core/java/android/net/http/SslCertificate.java
index 01dd08f..250cff2 100644
--- a/core/java/android/net/http/SslCertificate.java
+++ b/core/java/android/net/http/SslCertificate.java
@@ -17,7 +17,7 @@
package android.net.http;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Bundle;
import android.text.format.DateFormat;
diff --git a/core/java/android/net/http/SslError.java b/core/java/android/net/http/SslError.java
index b3f2fb7..d43c616 100644
--- a/core/java/android/net/http/SslError.java
+++ b/core/java/android/net/http/SslError.java
@@ -16,8 +16,9 @@
package android.net.http;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
+
import java.security.cert.X509Certificate;
/**
diff --git a/core/java/android/net/metrics/ApfProgramEvent.java b/core/java/android/net/metrics/ApfProgramEvent.java
index 8243be9..f93907a 100644
--- a/core/java/android/net/metrics/ApfProgramEvent.java
+++ b/core/java/android/net/metrics/ApfProgramEvent.java
@@ -21,7 +21,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
diff --git a/core/java/android/net/metrics/ApfStats.java b/core/java/android/net/metrics/ApfStats.java
index eac5579..b221cb9 100644
--- a/core/java/android/net/metrics/ApfStats.java
+++ b/core/java/android/net/metrics/ApfStats.java
@@ -20,7 +20,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/android/net/metrics/DhcpClientEvent.java b/core/java/android/net/metrics/DhcpClientEvent.java
index 5f9f507..8fc1ef8 100644
--- a/core/java/android/net/metrics/DhcpClientEvent.java
+++ b/core/java/android/net/metrics/DhcpClientEvent.java
@@ -20,7 +20,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 3d44944..9f592e8 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -1888,10 +1888,13 @@
/**
* Note: currently only works when the requested pid has the same UID
* as the caller.
+ *
+ * @return true if the meminfo was read successfully, false if not (i.e., given pid has gone).
+ *
* @hide
*/
@UnsupportedAppUsage
- public static native void getMemoryInfo(int pid, MemoryInfo memoryInfo);
+ public static native boolean getMemoryInfo(int pid, MemoryInfo memoryInfo);
/**
* Retrieves the PSS memory used by the process as given by the
@@ -1904,6 +1907,8 @@
* array of up to 3 entries to also receive (up to 3 values in order): the Uss and SwapPss and
* Rss (only filled in as of {@link android.os.Build.VERSION_CODES#P}) of the process, and
* another array to also retrieve the separate memtrack size.
+ *
+ * @return The PSS memory usage, or 0 if failed to retrieve (i.e., given pid has gone).
* @hide
*/
public static native long getPss(int pid, long[] outUssSwapPssRss, long[] outMemtrack);
diff --git a/core/java/android/os/ExternalVibration.java b/core/java/android/os/ExternalVibration.java
index 37ca868..e62244f 100644
--- a/core/java/android/os/ExternalVibration.java
+++ b/core/java/android/os/ExternalVibration.java
@@ -84,6 +84,10 @@
return mAttrs;
}
+ public VibrationAttributes getVibrationAttributes() {
+ return new VibrationAttributes.Builder(mAttrs, null).build();
+ }
+
/**
* Mutes the external vibration if it's playing and unmuted.
*
@@ -157,7 +161,6 @@
out.writeInt(mUid);
out.writeString(mPkg);
writeAudioAttributes(mAttrs, out, flags);
- out.writeParcelable(mAttrs, flags);
out.writeStrongBinder(mController.asBinder());
out.writeStrongBinder(mToken);
}
diff --git a/core/java/android/os/IRecoverySystem.aidl b/core/java/android/os/IRecoverySystem.aidl
index c5ceecd..2561e1e 100644
--- a/core/java/android/os/IRecoverySystem.aidl
+++ b/core/java/android/os/IRecoverySystem.aidl
@@ -17,6 +17,7 @@
package android.os;
+import android.content.IntentSender;
import android.os.IRecoverySystemProgressListener;
/** @hide */
@@ -26,4 +27,7 @@
boolean setupBcb(in String command);
boolean clearBcb();
void rebootRecoveryWithCommand(in String command);
+ boolean requestLskf(in String updateToken, in IntentSender sender);
+ boolean clearLskf();
+ boolean rebootWithLskf(in String updateToken, in String reason);
}
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index e81a505..edaaf81 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -62,7 +62,7 @@
boolean canAddMoreManagedProfiles(int userId, boolean allowedToRemoveOne);
UserInfo getProfileParent(int userId);
boolean isSameProfileGroup(int userId, int otherUserHandle);
- String getUserTypeForUser(int userId);
+ boolean isUserOfType(int userId, in String userType);
@UnsupportedAppUsage
UserInfo getUserInfo(int userId);
String getUserAccount(int userId);
diff --git a/core/java/android/os/IVibratorService.aidl b/core/java/android/os/IVibratorService.aidl
index 6b881fe..7b2d148 100644
--- a/core/java/android/os/IVibratorService.aidl
+++ b/core/java/android/os/IVibratorService.aidl
@@ -16,17 +16,18 @@
package android.os;
-import android.media.AudioAttributes;
import android.os.VibrationEffect;
+import android.os.VibrationAttributes;
/** {@hide} */
interface IVibratorService
{
boolean hasVibrator();
boolean hasAmplitudeControl();
- boolean setAlwaysOnEffect(int id, in VibrationEffect effect, in AudioAttributes attributes);
- void vibrate(int uid, String opPkg, in VibrationEffect effect, in AudioAttributes attributes,
- String reason, IBinder token);
+ boolean setAlwaysOnEffect(int id, in VibrationEffect effect,
+ in VibrationAttributes attributes);
+ void vibrate(int uid, String opPkg, in VibrationEffect effect,
+ in VibrationAttributes attributes, String reason, IBinder token);
void cancelVibrate(IBinder token);
}
diff --git a/core/java/android/os/PersistableBundle.java b/core/java/android/os/PersistableBundle.java
index b40283f..7a837e1 100644
--- a/core/java/android/os/PersistableBundle.java
+++ b/core/java/android/os/PersistableBundle.java
@@ -16,17 +16,24 @@
package android.os;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.ArrayMap;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.util.ArrayList;
/**
@@ -339,4 +346,44 @@
proto.end(token);
}
+
+ /**
+ * Writes the content of the {@link PersistableBundle} to a {@link OutputStream}.
+ *
+ * <p>The content can be read by a {@link #readFromStream}.
+ *
+ * @see #readFromStream
+ */
+ public void writeToStream(@NonNull OutputStream outputStream) throws IOException {
+ FastXmlSerializer serializer = new FastXmlSerializer();
+ serializer.setOutput(outputStream, UTF_8.name());
+ serializer.startTag(null, "bundle");
+ try {
+ saveToXml(serializer);
+ } catch (XmlPullParserException e) {
+ throw new IOException(e);
+ }
+ serializer.endTag(null, "bundle");
+ serializer.flush();
+ }
+
+ /**
+ * Reads a {@link PersistableBundle} from an {@link InputStream}.
+ *
+ * <p>The stream must be generated by {@link #writeToStream}.
+ *
+ * @see #writeToStream
+ */
+ @NonNull
+ public static PersistableBundle readFromStream(@NonNull InputStream inputStream)
+ throws IOException {
+ try {
+ XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
+ parser.setInput(inputStream, UTF_8.name());
+ parser.next();
+ return PersistableBundle.restoreFromXml(parser);
+ } catch (XmlPullParserException e) {
+ throw new IOException(e);
+ }
+ }
}
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 1901820..cdcb3ff 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -18,6 +18,8 @@
import static java.nio.charset.StandardCharsets.UTF_8;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
@@ -29,6 +31,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.IntentSender;
import android.content.pm.PackageManager;
import android.provider.Settings;
import android.telephony.SubscriptionInfo;
@@ -624,22 +627,91 @@
}
/**
- * Schedule to install the given package on next boot. The caller needs to
- * ensure that the package must have been processed (uncrypt'd) if needed.
- * It sets up the command in BCB (bootloader control block), which will
- * be read by the bootloader and the recovery image.
+ * Prepare to apply an unattended update by asking the user for their Lock Screen Knowledge
+ * Factor (LSKF). If supplied, the {@code intentSender} will be called when the system is setup
+ * and ready to apply the OTA.
+ * <p>
+ * When the system is already prepared for update and this API is called again with the same
+ * {@code updateToken}, it will not call the intent sender nor request the user enter their Lock
+ * Screen Knowledge Factor.
+ * <p>
+ * When this API is called again with a different {@code updateToken}, the prepared-for-update
+ * status is reset and process repeats as though it's the initial call to this method as
+ * described in the first paragraph.
*
- * @param Context the Context to use.
- * @param packageFile the package to be installed.
- *
- * @throws IOException if there were any errors setting up the BCB.
- *
+ * @param context the Context to use.
+ * @param updateToken token used to indicate which update was prepared
+ * @param intentSender the intent to call when the update is prepared; may be {@code null}
+ * @throws IOException if there were any errors setting up unattended update
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.RECOVERY)
- public static void scheduleUpdateOnBoot(Context context, File packageFile)
+ public static void prepareForUnattendedUpdate(@NonNull Context context,
+ @NonNull String updateToken, @Nullable IntentSender intentSender) throws IOException {
+ if (updateToken == null) {
+ throw new NullPointerException("updateToken == null");
+ }
+ RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
+ if (!rs.requestLskf(updateToken, intentSender)) {
+ throw new IOException("preparation for update failed");
+ }
+ }
+
+ /**
+ * Request that any previously requested Lock Screen Knowledge Factor (LSKF) is cleared and
+ * the preparation for unattended update is reset.
+ *
+ * @param context the Context to use.
+ * @throws IOException if there were any errors setting up unattended update
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.RECOVERY)
+ public static boolean clearPrepareForUnattendedUpdate(@NonNull Context context)
throws IOException {
+ RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
+ return rs.clearLskf();
+ }
+
+ /**
+ * Request that the device reboot and apply the update that has been prepared. The
+ * {@code updateToken} must match what was given for {@link #prepareForUnattendedUpdate} or
+ * this will return {@code false}.
+ *
+ * @param context the Context to use.
+ * @param updateToken the token used to call {@link #prepareForUnattendedUpdate} before
+ * @param reason the reboot reason to give to the {@link PowerManager}
+ * @throws IOException if there were any errors setting up unattended update
+ * @return false if the reboot couldn't proceed because the device wasn't ready for an
+ * unattended reboot or if the {@code updateToken} did not match the previously
+ * given token
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.RECOVERY)
+ public static boolean rebootAndApply(@NonNull Context context, @NonNull String updateToken,
+ @NonNull String reason) throws IOException {
+ if (updateToken == null) {
+ throw new NullPointerException("updateToken == null");
+ }
+ RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
+ return rs.rebootWithLskf(updateToken, reason);
+ }
+
+ /**
+ * Schedule to install the given package on next boot. The caller needs to ensure that the
+ * package must have been processed (uncrypt'd) if needed. It sets up the command in BCB
+ * (bootloader control block), which will be read by the bootloader and the recovery image.
+ *
+ * @param context the Context to use.
+ * @param packageFile the package to be installed.
+ * @throws IOException if there were any errors setting up the BCB.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.RECOVERY)
+ public static void scheduleUpdateOnBoot(Context context, File packageFile) throws IOException {
String filename = packageFile.getCanonicalPath();
boolean securityUpdate = filename.endsWith("_s.zip");
@@ -1204,6 +1276,49 @@
}
/**
+ * Begins the process of asking the user for the Lock Screen Knowledge Factor.
+ *
+ * @param updateToken token that will be used in calls to {@link #rebootAndApply} to ensure
+ * that the preparation was for the correct update
+ * @return true if the request was correct
+ * @throws IOException if the recovery system service could not be contacted
+ */
+ private boolean requestLskf(String updateToken, IntentSender sender) throws IOException {
+ try {
+ return mService.requestLskf(updateToken, sender);
+ } catch (RemoteException e) {
+ throw new IOException("could request update");
+ }
+ }
+
+ /**
+ * Calls the recovery system service and clears the setup for the OTA.
+ *
+ * @return true if the setup for OTA was cleared
+ * @throws IOException if the recovery system service could not be contacted
+ */
+ private boolean clearLskf() throws IOException {
+ try {
+ return mService.clearLskf();
+ } catch (RemoteException e) {
+ throw new IOException("could not clear LSKF");
+ }
+ }
+
+ /**
+ * Calls the recovery system service to reboot and apply update.
+ *
+ * @param updateToken the update token for which the update was prepared
+ */
+ private boolean rebootWithLskf(String updateToken, String reason) throws IOException {
+ try {
+ return mService.rebootWithLskf(updateToken, reason);
+ } catch (RemoteException e) {
+ throw new IOException("could not reboot for update");
+ }
+ }
+
+ /**
* Internally, recovery treats each line of the command file as a separate
* argv, so we only need to protect against newlines and nulls.
*/
diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java
index f585c75..c1542c7 100644
--- a/core/java/android/os/SystemVibrator.java
+++ b/core/java/android/os/SystemVibrator.java
@@ -76,7 +76,8 @@
return false;
}
try {
- return mService.setAlwaysOnEffect(id, effect, attributes);
+ VibrationAttributes atr = new VibrationAttributes.Builder(attributes, effect).build();
+ return mService.setAlwaysOnEffect(id, effect, atr);
} catch (RemoteException e) {
Log.w(TAG, "Failed to set always-on effect.", e);
}
@@ -91,7 +92,11 @@
return;
}
try {
- mService.vibrate(uid, opPkg, effect, attributes, reason, mToken);
+ if (attributes == null) {
+ attributes = new AudioAttributes.Builder().build();
+ }
+ VibrationAttributes atr = new VibrationAttributes.Builder(attributes, effect).build();
+ mService.vibrate(uid, opPkg, effect, atr, reason, mToken);
} catch (RemoteException e) {
Log.w(TAG, "Failed to vibrate.", e);
}
diff --git a/core/java/android/os/TelephonyServiceManager.java b/core/java/android/os/TelephonyServiceManager.java
index 1211dd6..064cf7d 100644
--- a/core/java/android/os/TelephonyServiceManager.java
+++ b/core/java/android/os/TelephonyServiceManager.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.content.Context;
/**
* Provides a way to register and obtain the system service binder objects managed by the telephony
@@ -51,8 +52,8 @@
/**
* Register a system server binding object for a service.
*/
- public void register(@NonNull IBinder binder) {
- ServiceManager.addService(mServiceName, binder);
+ public void register(@NonNull IBinder service) {
+ ServiceManager.addService(mServiceName, service);
}
/**
@@ -114,25 +115,123 @@
*/
@NonNull
public ServiceRegisterer getTelephonyServiceRegisterer() {
- return new ServiceRegisterer("phone");
+ return new ServiceRegisterer(Context.TELEPHONY_SERVICE);
}
+ /**
+ * Returns {@link ServiceRegisterer} for the telephony registry service.
+ */
+ @NonNull
+ public ServiceRegisterer getTelephonyRegistryServiceRegisterer() {
+ return new ServiceRegisterer("telephony.registry");
+ }
-// TODO: Add more services...
-//
-// /**
-// * Returns {@link ServiceRegisterer} for the "subscription" service.
-// */
-// @NonNull
-// public ServiceRegisterer getSubscriptionServiceRegisterer() {
-// return new ServiceRegisterer("isub");
-// }
-//
-// /**
-// * Returns {@link ServiceRegisterer} for the "SMS" service.
-// */
-// @NonNull
-// public ServiceRegisterer getSmsServiceRegisterer() {
-// return new ServiceRegisterer("isms");
-// }
+ /**
+ * Returns {@link ServiceRegisterer} for the telephony IMS service.
+ */
+ @NonNull
+ public ServiceRegisterer getTelephonyImsServiceRegisterer() {
+ return new ServiceRegisterer(Context.TELEPHONY_IMS_SERVICE);
+ }
+
+ /**
+ * Returns {@link ServiceRegisterer} for the telephony RCS message service.
+ */
+ @NonNull
+ public ServiceRegisterer getTelephonyRcsMessageServiceRegisterer() {
+ return new ServiceRegisterer(Context.TELEPHONY_RCS_MESSAGE_SERVICE);
+ }
+
+ /**
+ * Returns {@link ServiceRegisterer} for the subscription service.
+ */
+ @NonNull
+ public ServiceRegisterer getSubscriptionServiceRegisterer() {
+ return new ServiceRegisterer("isub");
+ }
+
+ /**
+ * Returns {@link ServiceRegisterer} for the network policy service.
+ */
+ @NonNull
+ public ServiceRegisterer getNetworkPolicyServiceRegisterer() {
+ return new ServiceRegisterer(Context.NETWORK_POLICY_SERVICE);
+ }
+
+ /**
+ * Returns {@link ServiceRegisterer} for the phone sub service.
+ */
+ @NonNull
+ public ServiceRegisterer getPhoneSubServiceRegisterer() {
+ return new ServiceRegisterer("iphonesubinfo");
+ }
+
+ /**
+ * Returns {@link ServiceRegisterer} for the opportunistic network service.
+ */
+ @NonNull
+ public ServiceRegisterer getOpportunisticNetworkServiceRegisterer() {
+ return new ServiceRegisterer("ions");
+ }
+
+ /**
+ * Returns {@link ServiceRegisterer} for the carrier config service.
+ */
+ @NonNull
+ public ServiceRegisterer getCarrierConfigServiceRegisterer() {
+ return new ServiceRegisterer(Context.CARRIER_CONFIG_SERVICE);
+ }
+
+ /**
+ * Returns {@link ServiceRegisterer} for the "SMS" service.
+ */
+ @NonNull
+ public ServiceRegisterer getSmsServiceRegisterer() {
+ return new ServiceRegisterer("isms");
+ }
+
+ /**
+ * Returns {@link ServiceRegisterer} for the eUICC controller service.
+ */
+ @NonNull
+ public ServiceRegisterer getEuiccControllerService() {
+ return new ServiceRegisterer("econtroller");
+ }
+
+ @NonNull
+ public ServiceRegisterer getEuiccCardControllerServiceRegisterer() {
+ return new ServiceRegisterer("euicc_card_controller");
+ }
+
+ /**
+ * Returns {@link ServiceRegisterer} for the package manager service.
+ */
+ @NonNull
+ public ServiceRegisterer getPackageManagerServiceRegisterer() {
+ return new ServiceRegisterer("package");
+ }
+
+ /**
+ * Returns {@link ServiceRegisterer} for the permission manager service.
+ */
+ @NonNull
+ public ServiceRegisterer getPermissionManagerServiceRegisterer() {
+ return new ServiceRegisterer("permissionmgr");
+ }
+
+ /**
+ * Returns {@link ServiceRegisterer} for the ICC phone book service.
+ */
+ @NonNull
+ public ServiceRegisterer getIccPhoneBookServiceRegisterer() {
+ return new ServiceRegisterer("simphonebook");
+ }
+
+ /**
+ * Returns {@link ServiceRegisterer} for the window service.
+ */
+ @NonNull
+ public ServiceRegisterer getWindowServiceRegisterer() {
+ return new ServiceRegisterer(Context.WINDOW_SERVICE);
+ }
}
diff --git a/core/java/android/util/TimestampedValue.java b/core/java/android/os/TimestampedValue.java
similarity index 97%
rename from core/java/android/util/TimestampedValue.java
rename to core/java/android/os/TimestampedValue.java
index 4505673..348574e 100644
--- a/core/java/android/util/TimestampedValue.java
+++ b/core/java/android/os/TimestampedValue.java
@@ -14,13 +14,10 @@
* limitations under the License.
*/
-package android.util;
+package android.os;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.SystemClock;
import java.util.Objects;
diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java
index a9ddffe..16f7b39 100644
--- a/core/java/android/os/UpdateEngine.java
+++ b/core/java/android/os/UpdateEngine.java
@@ -16,6 +16,7 @@
package android.os;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.IUpdateEngine;
@@ -140,8 +141,43 @@
* {@code SWITCH_SLOT_ON_REBOOT=0}. See {@link #applyPayload}.
*/
public static final int UPDATED_BUT_NOT_ACTIVE = 52;
+
+ /**
+ * Error code: there is not enough space on the device to apply the update. User should
+ * be prompted to free up space and re-try the update.
+ *
+ * <p>See {@link UpdateEngine#allocateSpace}.
+ */
+ public static final int NOT_ENOUGH_SPACE = 60;
+
+ /**
+ * Error code: the device is corrupted and no further updates may be applied.
+ *
+ * <p>See {@link UpdateEngine#cleanupAppliedPayload}.
+ */
+ public static final int DEVICE_CORRUPTED = 61;
}
+ /** @hide */
+ @IntDef(value = {
+ ErrorCodeConstants.SUCCESS,
+ ErrorCodeConstants.ERROR,
+ ErrorCodeConstants.FILESYSTEM_COPIER_ERROR,
+ ErrorCodeConstants.POST_INSTALL_RUNNER_ERROR,
+ ErrorCodeConstants.PAYLOAD_MISMATCHED_TYPE_ERROR,
+ ErrorCodeConstants.INSTALL_DEVICE_OPEN_ERROR,
+ ErrorCodeConstants.KERNEL_DEVICE_OPEN_ERROR,
+ ErrorCodeConstants.DOWNLOAD_TRANSFER_ERROR,
+ ErrorCodeConstants.PAYLOAD_HASH_MISMATCH_ERROR,
+ ErrorCodeConstants.PAYLOAD_SIZE_MISMATCH_ERROR,
+ ErrorCodeConstants.DOWNLOAD_PAYLOAD_VERIFICATION_ERROR,
+ ErrorCodeConstants.PAYLOAD_TIMESTAMP_ERROR,
+ ErrorCodeConstants.UPDATED_BUT_NOT_ACTIVE,
+ ErrorCodeConstants.NOT_ENOUGH_SPACE,
+ ErrorCodeConstants.DEVICE_CORRUPTED,
+ })
+ public @interface ErrorCode {}
+
/**
* Status codes for update engine. Values must agree with the ones in
* {@code system/update_engine/client_library/include/update_engine/update_status.h}.
@@ -419,4 +455,138 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Return value of {@link #allocateSpace.}
+ */
+ public static final class AllocateSpaceResult {
+ private @ErrorCode int mErrorCode = ErrorCodeConstants.SUCCESS;
+ private long mFreeSpaceRequired = 0;
+ private AllocateSpaceResult() {}
+ /**
+ * Error code.
+ *
+ * @return The following error codes:
+ * <ul>
+ * <li>{@link ErrorCodeConstants#SUCCESS} if space has been allocated
+ * successfully.</li>
+ * <li>{@link ErrorCodeConstants#NOT_ENOUGH_SPACE} if insufficient
+ * space.</li>
+ * <li>Other {@link ErrorCodeConstants} for other errors.</li>
+ * </ul>
+ */
+ @ErrorCode
+ public int errorCode() {
+ return mErrorCode;
+ }
+
+ /**
+ * Estimated total space that needs to be available on the userdata partition to apply the
+ * payload (in bytes).
+ *
+ * <p>
+ * Note that in practice, more space needs to be made available before applying the payload
+ * to keep the device working.
+ *
+ * @return The following values:
+ * <ul>
+ * <li>zero if {@link #errorCode} returns {@link ErrorCodeConstants#SUCCESS}</li>
+ * <li>non-zero if {@link #errorCode} returns {@link ErrorCodeConstants#NOT_ENOUGH_SPACE}.
+ * Value is the estimated total space required on userdata partition.</li>
+ * </ul>
+ * @throws IllegalStateException if {@link #errorCode} is not one of the above.
+ *
+ */
+ public long freeSpaceRequired() {
+ if (mErrorCode == ErrorCodeConstants.SUCCESS) {
+ return 0;
+ }
+ if (mErrorCode == ErrorCodeConstants.NOT_ENOUGH_SPACE) {
+ return mFreeSpaceRequired;
+ }
+ throw new IllegalStateException(String.format(
+ "freeSpaceRequired() is not available when error code is %d", mErrorCode));
+ }
+ }
+
+ /**
+ * Initialize partitions for a payload associated with the given payload
+ * metadata {@code payloadMetadataFilename} by preallocating required space.
+ *
+ * <p>This function should be called after payload has been verified after
+ * {@link #verifyPayloadMetadata}. This function does not verify whether
+ * the given payload is applicable or not.
+ *
+ * <p>Implementation of {@code allocateSpace} uses
+ * {@code headerKeyValuePairs} to determine whether space has been allocated
+ * for a different or same payload previously. If space has been allocated
+ * for a different payload before, space will be reallocated for the given
+ * payload. If space has been allocated for the same payload, no actions to
+ * storage devices are taken.
+ *
+ * <p>This function is synchronous and may take a non-trivial amount of
+ * time. Callers should call this function in a background thread.
+ *
+ * @param payloadMetadataFilename See {@link #verifyPayloadMetadata}.
+ * @param headerKeyValuePairs See {@link #applyPayload}.
+ * @return See {@link AllocateSpaceResult}.
+ */
+ @NonNull
+ public AllocateSpaceResult allocateSpace(
+ @NonNull String payloadMetadataFilename,
+ @NonNull String[] headerKeyValuePairs) {
+ AllocateSpaceResult result = new AllocateSpaceResult();
+ try {
+ result.mFreeSpaceRequired = mUpdateEngine.allocateSpaceForPayload(
+ payloadMetadataFilename,
+ headerKeyValuePairs);
+ result.mErrorCode = result.mFreeSpaceRequired == 0
+ ? ErrorCodeConstants.SUCCESS
+ : ErrorCodeConstants.NOT_ENOUGH_SPACE;
+ return result;
+ } catch (ServiceSpecificException e) {
+ result.mErrorCode = e.errorCode;
+ result.mFreeSpaceRequired = 0;
+ return result;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Cleanup files used by the previous update and free up space after the
+ * device has been booted successfully into the new build.
+ *
+ * <p>In particular, this function waits until delta files for snapshots for
+ * Virtual A/B update are merged to OS partitions, then delete these delta
+ * files.
+ *
+ * <p>This function is synchronous and may take a non-trivial amount of
+ * time. Callers should call this function in a background thread.
+ *
+ * <p>This function does not delete payload binaries downloaded for a
+ * non-streaming OTA update.
+ *
+ * @return One of the following:
+ * <ul>
+ * <li>{@link ErrorCodeConstants#SUCCESS} if execution is successful.</li>
+ * <li>{@link ErrorCodeConstants#ERROR} if a transient error has occurred.
+ * The device should be able to recover after a reboot. The function should
+ * be retried after the reboot.</li>
+ * <li>{@link ErrorCodeConstants#DEVICE_CORRUPTED} if a permanent error is
+ * encountered. Device is corrupted, and future updates must not be applied.
+ * The device cannot recover without flashing and factory resets.
+ * </ul>
+ *
+ * @throws ServiceSpecificException if other transient errors has occurred.
+ * A reboot may or may not help resolving the issue.
+ */
+ @ErrorCode
+ public int cleanupAppliedPayload() {
+ try {
+ return mUpdateEngine.cleanupSuccessfulUpdate();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/os/UpdateEngineCallback.java b/core/java/android/os/UpdateEngineCallback.java
index f07294e..7fe7024 100644
--- a/core/java/android/os/UpdateEngineCallback.java
+++ b/core/java/android/os/UpdateEngineCallback.java
@@ -44,5 +44,6 @@
* unsuccessfully. The value of {@code errorCode} will be one of the
* values from {@link UpdateEngine.ErrorCodeConstants}.
*/
- public abstract void onPayloadApplicationComplete(int errorCode);
+ public abstract void onPayloadApplicationComplete(
+ @UpdateEngine.ErrorCode int errorCode);
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index fa9569b..6e199ce3 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1626,39 +1626,35 @@
}
/**
- * Returns the calling user's user type.
+ * Returns whether the current user is of the given user type, such as
+ * {@link UserManager#USER_TYPE_FULL_GUEST}.
*
- * // TODO(b/142482943): Decide on the appropriate permission requirements.
- *
- * @return the name of the user type, such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}.
+ * @return true if the user is of the given user type.
* @hide
*/
- public @NonNull String getUserType() {
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ public boolean isUserOfType(@NonNull String userType) {
try {
- return mService.getUserTypeForUser(UserHandle.myUserId());
+ return mService.isUserOfType(UserHandle.myUserId(), userType);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
}
/**
- * Returns the given user's user type.
- *
- * // TODO(b/142482943): Decide on the appropriate permission requirements.
- * Requires {@link android.Manifest.permission#MANAGE_USERS} or
- * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission, otherwise the caller
- * must be in the same profile group of specified user.
+ * Returns whether the given user is of the given user type, such as
+ * {@link UserManager#USER_TYPE_FULL_GUEST}.
*
* @param userHandle the user handle of the user whose type is being requested.
- * @return the name of the user's user type, e.g. {@link UserManager#USER_TYPE_PROFILE_MANAGED},
- * or {@code null} if there is no such user.
+ * @param userType the name of the user's user type, e.g.
+ * {@link UserManager#USER_TYPE_PROFILE_MANAGED}.
+ * @return true if the userHandle user is of type userType
* @hide
*/
- @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
- android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
- public @Nullable String getUserTypeForUser(@NonNull UserHandle userHandle) {
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ public boolean isUserOfType(@NonNull UserHandle userHandle, @NonNull String userType) {
try {
- return mService.getUserTypeForUser(userHandle.getIdentifier());
+ return mService.isUserOfType(userHandle.getIdentifier(), userType);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
diff --git a/core/java/android/os/VibrationAttributes.aidl b/core/java/android/os/VibrationAttributes.aidl
new file mode 100644
index 0000000..5c05a4e
--- /dev/null
+++ b/core/java/android/os/VibrationAttributes.aidl
@@ -0,0 +1,18 @@
+/* Copyright 2019, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.os;
+
+parcelable VibrationAttributes;
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java
new file mode 100644
index 0000000..3e16640
--- /dev/null
+++ b/core/java/android/os/VibrationAttributes.java
@@ -0,0 +1,401 @@
+/*
+ * 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;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.media.AudioAttributes;
+import android.util.Slog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * A class to encapsulate a collection of attributes describing information about a vibration
+ */
+public final class VibrationAttributes implements Parcelable {
+ private static final String TAG = "VibrationAttributes";
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = { "USAGE_CLASS_" }, value = {
+ USAGE_CLASS_UNKNOWN,
+ USAGE_CLASS_ALARM,
+ USAGE_CLASS_FEEDBACK,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface UsageClass{}
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = { "USAGE_" }, value = {
+ USAGE_UNKNOWN,
+ USAGE_ALARM,
+ USAGE_RINGTONE,
+ USAGE_NOTIFICATION,
+ USAGE_COMMUNICATION_REQUEST,
+ USAGE_TOUCH,
+ USAGE_PHYSICAL_EMULATION,
+ USAGE_HARDWARE_FEEDBACK,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Usage{}
+
+ /**
+ * Vibration usage class value to use when the vibration usage class is unknown.
+ */
+ public static final int USAGE_CLASS_UNKNOWN = 0x0;
+ /**
+ * Vibration usage class value to use when the vibration is initiated to catch user's
+ * attention, such as alarm, ringtone, and notification vibrations.
+ */
+ public static final int USAGE_CLASS_ALARM = 0x1;
+ /**
+ * Vibration usage class value to use when the vibration is initiated as a response to user's
+ * actions, such as emulation of physical effects, and texting feedback vibration.
+ */
+ public static final int USAGE_CLASS_FEEDBACK = 0x2;
+
+ /**
+ * Mask for vibration usage class value.
+ */
+ public static final int USAGE_CLASS_MASK = 0xF;
+
+ /**
+ * Usage value to use when usage is unknown.
+ */
+ public static final int USAGE_UNKNOWN = 0x0 | USAGE_CLASS_UNKNOWN;
+ /**
+ * Usage value to use for alarm vibrations.
+ */
+ public static final int USAGE_ALARM = 0x10 | USAGE_CLASS_ALARM;
+ /**
+ * Usage value to use for ringtone vibrations.
+ */
+ public static final int USAGE_RINGTONE = 0x20 | USAGE_CLASS_ALARM;
+ /**
+ * Usage value to use for notification vibrations.
+ */
+ public static final int USAGE_NOTIFICATION = 0x30 | USAGE_CLASS_ALARM;
+ /**
+ * Usage value to use for vibrations which mean a request to enter/end a
+ * communication, such as a VoIP communication or video-conference.
+ */
+ public static final int USAGE_COMMUNICATION_REQUEST = 0x40 | USAGE_CLASS_ALARM;
+ /**
+ * Usage value to use for touch vibrations.
+ */
+ public static final int USAGE_TOUCH = 0x10 | USAGE_CLASS_FEEDBACK;
+ /**
+ * Usage value to use for vibrations which emulate physical effects, such as edge squeeze.
+ */
+ public static final int USAGE_PHYSICAL_EMULATION = 0x20 | USAGE_CLASS_FEEDBACK;
+ /**
+ * Usage value to use for vibrations which provide a feedback for hardware interaction,
+ * such as a fingerprint sensor.
+ */
+ public static final int USAGE_HARDWARE_FEEDBACK = 0x30 | USAGE_CLASS_FEEDBACK;
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = { "FLAG_" }, value = {
+ FLAG_BYPASS_INTERRUPTION_POLICY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Flag{}
+
+ /**
+ * Flag requesting vibration effect to be played even under limited interruptions.
+ */
+ public static final int FLAG_BYPASS_INTERRUPTION_POLICY = 0x1;
+
+ // If a vibration is playing for longer than 5s, it's probably not haptic feedback
+ private static final long MAX_HAPTIC_FEEDBACK_DURATION = 5000;
+
+ private final int mUsage;
+ private final int mFlags;
+
+ private final AudioAttributes mAudioAttributes;
+
+ private VibrationAttributes(int usage, int flags, @NonNull AudioAttributes audio) {
+ mUsage = usage;
+ mFlags = flags;
+ mAudioAttributes = audio;
+ }
+
+ /**
+ * Return the vibration usage class.
+ * @return USAGE_CLASS_ALARM, USAGE_CLASS_FEEDBACK or USAGE_CLASS_UNKNOWN
+ */
+ public int getUsageClass() {
+ return mUsage & USAGE_CLASS_MASK;
+ }
+
+ /**
+ * Return the vibration usage.
+ * @return one of the values that can be set in {@link Builder#setUsage(int)}
+ */
+ public int getUsage() {
+ return mUsage;
+ }
+
+ /**
+ * Return the flags.
+ * @return a combined mask of all flags
+ */
+ public int getFlags() {
+ return mFlags;
+ }
+
+ /**
+ * Check whether a flag is set
+ * @return true if a flag is set and false otherwise
+ */
+ public boolean isFlagSet(int flag) {
+ return (mFlags & flag) > 0;
+ }
+
+ /**
+ * Return AudioAttributes equivalent to this VibrationAttributes.
+ * @deprecated Temporary support of AudioAttributes, will be removed when out of WIP
+ * @hide
+ */
+ @Deprecated
+ @TestApi
+ public @NonNull AudioAttributes getAudioAttributes() {
+ return mAudioAttributes;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mUsage);
+ dest.writeInt(mFlags);
+ dest.writeParcelable(mAudioAttributes, flags);
+ }
+
+ private VibrationAttributes(Parcel src) {
+ mUsage = src.readInt();
+ mFlags = src.readInt();
+ mAudioAttributes = (AudioAttributes) src.readParcelable(
+ AudioAttributes.class.getClassLoader());
+ }
+
+ public static final @NonNull Parcelable.Creator<VibrationAttributes>
+ CREATOR = new Parcelable.Creator<VibrationAttributes>() {
+ public VibrationAttributes createFromParcel(Parcel p) {
+ return new VibrationAttributes(p);
+ }
+ public VibrationAttributes[] newArray(int size) {
+ return new VibrationAttributes[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ VibrationAttributes rhs = (VibrationAttributes) o;
+ return mUsage == rhs.mUsage && mFlags == rhs.mFlags;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mUsage, mFlags);
+ }
+
+ @Override
+ public String toString() {
+ return "VibrationAttributes:"
+ + " Usage=" + usageToString()
+ + " Flags=" + mFlags;
+ }
+
+ /** @hide */
+ public String usageToString() {
+ return usageToString(mUsage);
+ }
+
+ /** @hide */
+ public String usageToString(int usage) {
+ switch (usage) {
+ case USAGE_UNKNOWN:
+ return "UNKNOWN";
+ case USAGE_ALARM:
+ return "ALARM";
+ case USAGE_RINGTONE:
+ return "RIGNTONE";
+ case USAGE_NOTIFICATION:
+ return "NOTIFICATION";
+ case USAGE_COMMUNICATION_REQUEST:
+ return "COMMUNICATION_REQUEST";
+ case USAGE_TOUCH:
+ return "TOUCH";
+ case USAGE_PHYSICAL_EMULATION:
+ return "PHYSICAL_EMULATION";
+ case USAGE_HARDWARE_FEEDBACK:
+ return "HARDWARE_FEEDBACK";
+ default:
+ return "unknown usage " + usage;
+ }
+ }
+
+ /**
+ * Builder class for {@link VibrationAttributes} objects.
+ * By default, all information is set to UNKNOWN.
+ */
+ public static final class Builder {
+ private int mUsage = USAGE_UNKNOWN;
+ private int mFlags = 0x0;
+
+ private AudioAttributes mAudioAttributes = new AudioAttributes.Builder().build();
+
+ /**
+ * Constructs a new Builder with the defaults.
+ */
+ public Builder() {
+ }
+
+ /**
+ * Constructs a new Builder from a given VibrationAttributes.
+ */
+ public Builder(@Nullable VibrationAttributes vib) {
+ if (vib != null) {
+ mUsage = vib.mUsage;
+ mFlags = vib.mFlags;
+ mAudioAttributes = vib.mAudioAttributes;
+ }
+ }
+
+ /**
+ * Constructs a new Builder from AudioAttributes.
+ * @hide
+ */
+ @TestApi
+ public Builder(@NonNull AudioAttributes audio,
+ @Nullable VibrationEffect effect) {
+ mAudioAttributes = audio;
+ setUsage(audio);
+ applyHapticFeedbackHeuristics(effect);
+ }
+
+ private void applyHapticFeedbackHeuristics(@Nullable VibrationEffect effect) {
+ if (effect != null) {
+ if (mUsage == USAGE_UNKNOWN && effect instanceof VibrationEffect.Prebaked) {
+ VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect;
+ switch (prebaked.getId()) {
+ case VibrationEffect.EFFECT_CLICK:
+ case VibrationEffect.EFFECT_DOUBLE_CLICK:
+ case VibrationEffect.EFFECT_HEAVY_CLICK:
+ case VibrationEffect.EFFECT_TEXTURE_TICK:
+ case VibrationEffect.EFFECT_TICK:
+ case VibrationEffect.EFFECT_POP:
+ case VibrationEffect.EFFECT_THUD:
+ mUsage = USAGE_TOUCH;
+ break;
+ default:
+ Slog.w(TAG, "Unknown prebaked vibration effect, assuming it isn't "
+ + "haptic feedback");
+ }
+ }
+ final long duration = effect.getDuration();
+ if (mUsage == USAGE_UNKNOWN && duration >= 0
+ && duration < MAX_HAPTIC_FEEDBACK_DURATION) {
+ mUsage = USAGE_TOUCH;
+ }
+ }
+ }
+
+ private void setUsage(@NonNull AudioAttributes audio) {
+ switch (audio.getUsage()) {
+ case AudioAttributes.USAGE_NOTIFICATION:
+ case AudioAttributes.USAGE_NOTIFICATION_EVENT:
+ case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
+ case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
+ mUsage = USAGE_NOTIFICATION;
+ break;
+ case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
+ case AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY:
+ mUsage = USAGE_COMMUNICATION_REQUEST;
+ break;
+ case AudioAttributes.USAGE_NOTIFICATION_RINGTONE:
+ mUsage = USAGE_RINGTONE;
+ break;
+ case AudioAttributes.USAGE_ASSISTANCE_SONIFICATION:
+ mUsage = USAGE_TOUCH;
+ break;
+ case AudioAttributes.USAGE_ALARM:
+ mUsage = USAGE_ALARM;
+ break;
+ default:
+ mUsage = USAGE_UNKNOWN;
+ }
+ }
+
+ /**
+ * Combines all of the attributes that have been set and returns a new
+ * {@link VibrationAttributes} object.
+ * @return a new {@link VibrationAttributes} object
+ */
+ public @NonNull VibrationAttributes build() {
+ VibrationAttributes ans = new VibrationAttributes(mUsage, mFlags,
+ mAudioAttributes);
+ return ans;
+ }
+
+ /**
+ * Sets the attribute describing the type of corresponding vibration.
+ * @param usage one of {@link VibrationAttributes#USAGE_ALARM},
+ * {@link VibrationAttributes#USAGE_RINGTONE},
+ * {@link VibrationAttributes#USAGE_NOTIFICATION},
+ * {@link VibrationAttributes#USAGE_COMMUNICATION_REQUEST},
+ * {@link VibrationAttributes#USAGE_TOUCH},
+ * {@link VibrationAttributes#USAGE_PHYSICAL_EMULATION},
+ * {@link VibrationAttributes#USAGE_HARDWARE_FEEDBACK}.
+ * @return the same Builder instance.
+ */
+ public @NonNull Builder setUsage(int usage) {
+ mUsage = usage;
+ return this;
+ }
+
+ /**
+ * Replaces flags
+ * @param flags any combination of flags.
+ * @return the same Builder instance.
+ */
+ public @NonNull Builder replaceFlags(int flags) {
+ mFlags = flags;
+ return this;
+ }
+ }
+}
+
diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java
index 2138d553..63335a0 100644
--- a/core/java/android/os/incremental/IncrementalFileStorages.java
+++ b/core/java/android/os/incremental/IncrementalFileStorages.java
@@ -38,6 +38,7 @@
import android.content.pm.DataLoaderParams;
import android.content.pm.InstallationFile;
import android.os.IVold;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.ArraySet;
@@ -208,7 +209,8 @@
if (!hasObb()) {
return;
}
- final String mainObbDir = String.format("/storage/emulated/0/Android/obb/%s", mPackageName);
+ final String obbDir = "/storage/emulated/0/Android/obb";
+ final String packageObbDir = String.format("%s/%s", obbDir, mPackageName);
final String packageObbDirRoot =
String.format("/mnt/runtime/%s/emulated/0/Android/obb/", mPackageName);
final String[] obbDirs = {
@@ -217,12 +219,12 @@
packageObbDirRoot + "full",
packageObbDirRoot + "default",
String.format("/data/media/0/Android/obb/%s", mPackageName),
- mainObbDir,
+ packageObbDir,
};
try {
- Slog.i(TAG, "Creating obb directory '" + mainObbDir + "'");
+ Slog.i(TAG, "Creating obb directory '" + packageObbDir + "'");
final IVold vold = IVold.Stub.asInterface(ServiceManager.getServiceOrThrow("vold"));
- vold.mkdirs(mainObbDir);
+ vold.setupAppDir(packageObbDir, obbDir, Process.ROOT_UID);
for (String d : obbDirs) {
mObbStorage.bindPermanent(d);
}
@@ -230,7 +232,7 @@
Slog.e(TAG, "vold service is not found.");
cleanUp();
} catch (IOException | RemoteException ex) {
- Slog.e(TAG, "Failed to create obb dir at: " + mainObbDir, ex);
+ Slog.e(TAG, "Failed to create obb dir at: " + packageObbDir, ex);
cleanUp();
}
}
diff --git a/core/java/android/provider/ContactsInternal.java b/core/java/android/provider/ContactsInternal.java
index 69c4b9b..aa2a89c 100644
--- a/core/java/android/provider/ContactsInternal.java
+++ b/core/java/android/provider/ContactsInternal.java
@@ -15,15 +15,14 @@
*/
package android.provider;
-import android.annotation.UnsupportedAppUsage;
import android.app.admin.DevicePolicyManager;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ActivityNotFoundException;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.UriMatcher;
import android.net.Uri;
-import android.os.Process;
import android.os.UserHandle;
import android.text.TextUtils;
import android.widget.Toast;
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index eb09930..0a3c333 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -22,7 +22,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentInterface;
import android.content.ContentResolver;
import android.content.Context;
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index 9a384c6..48410a7 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -16,8 +16,8 @@
package android.provider;
-import android.annotation.UnsupportedAppUsage;
import android.app.DownloadManager;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.net.NetworkPolicyManager;
import android.net.Uri;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 3aa534e..07cbcfb 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -26,7 +26,6 @@
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
import android.annotation.UserIdInt;
import android.app.ActivityThread;
import android.app.AppOpsManager;
@@ -36,6 +35,7 @@
import android.app.NotificationManager;
import android.app.SearchManager;
import android.app.WallpaperManager;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.ContentValues;
@@ -68,7 +68,6 @@
import android.os.ServiceManager;
import android.os.UserHandle;
import android.speech.tts.TextToSpeech;
-import android.telephony.SubscriptionManager;
import android.text.TextUtils;
import android.util.AndroidException;
import android.util.ArrayMap;
@@ -252,6 +251,9 @@
/** @hide */
public static final String EXTRA_NETWORK_TEMPLATE = "network_template";
+ /** @hide */
+ public static final String KEY_CONFIG_SET_RETURN = "config_set_return";
+
/**
* An int extra specifying a subscription ID.
*
@@ -2504,13 +2506,14 @@
args.putString(CALL_METHOD_PREFIX_KEY, prefix);
args.putSerializable(CALL_METHOD_FLAGS_KEY, keyValues);
IContentProvider cp = mProviderHolder.getProvider(cr);
- cp.call(cr.getPackageName(), cr.getFeatureId(), mProviderHolder.mUri.getAuthority(),
+ Bundle bundle = cp.call(cr.getPackageName(), cr.getFeatureId(),
+ mProviderHolder.mUri.getAuthority(),
mCallSetAllCommand, null, args);
+ return bundle.getBoolean(KEY_CONFIG_SET_RETURN);
} catch (RemoteException e) {
// Not supported by the remote side
return false;
}
- return true;
}
@UnsupportedAppUsage
@@ -12455,16 +12458,17 @@
/**
* Whether the Volte is enabled. If this setting is not set then we use the Carrier Config
- * value {@link CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL}.
+ * value
+ * {@link android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL}.
* <p>
* Type: int (0 for false, 1 for true)
* @hide
- * @deprecated Use {@link android.telephony.SubscriptionManager#ENHANCED_4G_MODE_ENABLED}
+ * @deprecated Use {@link android.provider.Telephony.SimInfo#ENHANCED_4G_MODE_ENABLED}
* instead.
*/
@Deprecated
public static final String ENHANCED_4G_MODE_ENABLED =
- SubscriptionManager.ENHANCED_4G_MODE_ENABLED;
+ Telephony.SimInfo.ENHANCED_4G_MODE_ENABLED;
/**
* Whether VT (Video Telephony over IMS) is enabled
@@ -12472,10 +12476,10 @@
* Type: int (0 for false, 1 for true)
*
* @hide
- * @deprecated Use {@link android.telephony.SubscriptionManager#VT_IMS_ENABLED} instead.
+ * @deprecated Use {@link android.provider.Telephony.SimInfo#VT_IMS_ENABLED} instead.
*/
@Deprecated
- public static final String VT_IMS_ENABLED = SubscriptionManager.VT_IMS_ENABLED;
+ public static final String VT_IMS_ENABLED = Telephony.SimInfo.VT_IMS_ENABLED;
/**
* Whether WFC is enabled
@@ -12483,10 +12487,10 @@
* Type: int (0 for false, 1 for true)
*
* @hide
- * @deprecated Use {@link android.telephony.SubscriptionManager#WFC_IMS_ENABLED} instead.
+ * @deprecated Use {@link android.provider.Telephony.SimInfo#WFC_IMS_ENABLED} instead.
*/
@Deprecated
- public static final String WFC_IMS_ENABLED = SubscriptionManager.WFC_IMS_ENABLED;
+ public static final String WFC_IMS_ENABLED = Telephony.SimInfo.WFC_IMS_ENABLED;
/**
* WFC mode on home/non-roaming network.
@@ -12494,10 +12498,10 @@
* Type: int - 2=Wi-Fi preferred, 1=Cellular preferred, 0=Wi-Fi only
*
* @hide
- * @deprecated Use {@link android.telephony.SubscriptionManager#WFC_IMS_MODE} instead.
+ * @deprecated Use {@link android.provider.Telephony.SimInfo#WFC_IMS_MODE} instead.
*/
@Deprecated
- public static final String WFC_IMS_MODE = SubscriptionManager.WFC_IMS_MODE;
+ public static final String WFC_IMS_MODE = Telephony.SimInfo.WFC_IMS_MODE;
/**
* WFC mode on roaming network.
@@ -12505,11 +12509,11 @@
* Type: int - see {@link #WFC_IMS_MODE} for values
*
* @hide
- * @deprecated Use {@link android.telephony.SubscriptionManager#WFC_IMS_ROAMING_MODE}
+ * @deprecated Use {@link android.provider.Telephony.SimInfo#WFC_IMS_ROAMING_MODE}
* instead.
*/
@Deprecated
- public static final String WFC_IMS_ROAMING_MODE = SubscriptionManager.WFC_IMS_ROAMING_MODE;
+ public static final String WFC_IMS_ROAMING_MODE = Telephony.SimInfo.WFC_IMS_ROAMING_MODE;
/**
* Whether WFC roaming is enabled
@@ -12517,12 +12521,12 @@
* Type: int (0 for false, 1 for true)
*
* @hide
- * @deprecated Use {@link android.telephony.SubscriptionManager#WFC_IMS_ROAMING_ENABLED}
+ * @deprecated Use {@link android.provider.Telephony.SimInfo#WFC_IMS_ROAMING_ENABLED}
* instead
*/
@Deprecated
public static final String WFC_IMS_ROAMING_ENABLED =
- SubscriptionManager.WFC_IMS_ROAMING_ENABLED;
+ Telephony.SimInfo.WFC_IMS_ROAMING_ENABLED;
/**
* Whether user can enable/disable LTE as a preferred network. A carrier might control
@@ -13709,14 +13713,6 @@
"backup_agent_timeout_parameters";
/**
- * Whether the backup system service supports multiple users (0 = disabled, 1 = enabled). If
- * disabled, the service will only be active for the system user.
- *
- * @hide
- */
- public static final String BACKUP_MULTI_USER_ENABLED = "backup_multi_user_enabled";
-
- /**
* Blacklist of GNSS satellites.
*
* This is a list of integers separated by commas to represent pairs of (constellation,
@@ -13985,14 +13981,18 @@
*/
@RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG)
static boolean setStrings(@NonNull ContentResolver resolver, @NonNull String namespace,
- @NonNull Map<String, String> keyValues) {
+ @NonNull Map<String, String> keyValues) throws DeviceConfig.BadConfigException {
HashMap<String, String> compositeKeyValueMap = new HashMap<>(keyValues.keySet().size());
for (Map.Entry<String, String> entry : keyValues.entrySet()) {
compositeKeyValueMap.put(
createCompositeName(namespace, entry.getKey()), entry.getValue());
}
- return sNameValueCache.setStringsForPrefix(resolver, createPrefix(namespace),
- compositeKeyValueMap);
+ // If can't set given configuration that means it's bad
+ if (!sNameValueCache.setStringsForPrefix(resolver, createPrefix(namespace),
+ compositeKeyValueMap)) {
+ throw new DeviceConfig.BadConfigException();
+ }
+ return true;
}
/**
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index fed3d7ba..2e7ac3f5 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -24,8 +24,8 @@
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
import android.compat.annotation.ChangeId;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.ContentValues;
@@ -36,12 +36,15 @@
import android.database.sqlite.SqliteWrapper;
import android.net.Uri;
import android.os.Build;
+import android.os.Bundle;
import android.os.Parcel;
+import android.telephony.CarrierConfigManager;
import android.telephony.Rlog;
import android.telephony.ServiceState;
import android.telephony.SmsMessage;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.telephony.UiccAccessRule;
import android.text.TextUtils;
import android.util.Patterns;
@@ -3559,7 +3562,8 @@
* can manage DPC-owned APNs.
* @hide
*/
- public static final Uri DPC_URI = Uri.parse("content://telephony/carriers/dpc");
+ @SystemApi
+ public static final @NonNull Uri DPC_URI = Uri.parse("content://telephony/carriers/dpc");
/**
* The {@code content://} style URL to be called from Telephony to query APNs.
@@ -3868,6 +3872,13 @@
public static final String USER_EDITABLE = "user_editable";
/**
+ * Integer value denoting an invalid APN id
+ * @hide
+ */
+ @SystemApi
+ public static final int INVALID_APN_ID = -1;
+
+ /**
* {@link #EDITED_STATUS APN edit status} indicates that this APN has not been edited or
* fails to edit.
* <p>Type: INTEGER </p>
@@ -4097,10 +4108,117 @@
public static final Uri MESSAGE_HISTORY_URI = Uri.parse("content://cellbroadcasts/history");
/**
+ * The authority for the legacy cellbroadcast provider.
+ * This is used for OEM data migration. OEMs want to migrate message history or
+ * sharepreference data to mainlined cellbroadcastreceiver app, should have a
+ * contentprovider with authority: cellbroadcast-legacy. Mainlined cellbroadcastreceiver
+ * will interact with this URI to retrieve data and persists to mainlined cellbroadcast app.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final @NonNull String AUTHORITY_LEGACY = "cellbroadcast-legacy";
+
+ /**
+ * A content:// style uri to the authority for the legacy cellbroadcast provider.
+ * @hide
+ */
+ @SystemApi
+ public static final @NonNull Uri AUTHORITY_LEGACY_URI =
+ Uri.parse("content://cellbroadcast-legacy");
+
+ /**
+ * Method name to {@link android.content.ContentProvider#call(String, String, Bundle)
+ * for {@link #AUTHORITY_LEGACY}. Used to query cellbroadcast {@link Preference},
+ * containing following supported entries
+ * <ul>
+ * <li>{@link #ENABLE_AREA_UPDATE_INFO_PREF}</li>
+ * <li>{@link #ENABLE_TEST_ALERT_PREF}</li>
+ * <li>{@link #ENABLE_STATE_LOCAL_TEST_PREF}</li>
+ * <li>{@link #ENABLE_PUBLIC_SAFETY_PREF}</li>
+ * <li>{@link #ENABLE_CMAS_AMBER_PREF}</li>
+ * <li>{@link #ENABLE_CMAS_SEVERE_THREAT_PREF}</li>
+ * <li>{@link #ENABLE_CMAS_EXTREME_THREAT_PREF}</li>
+ * <li>{@link #ENABLE_CMAS_PRESIDENTIAL_PREF}</li>
+ * <li>{@link #ENABLE_ALERT_VIBRATION_PREF}</li>
+ * <li>{@link #ENABLE_EMERGENCY_PERF}</li>
+ * <li>{@link #ENABLE_FULL_VOLUME_PREF}</li>
+ * <li>{@link #ENABLE_CMAS_IN_SECOND_LANGUAGE_PREF}</li>
+ * </ul>
+ * @hide
+ */
+ @SystemApi
+ public static final @NonNull String CALL_METHOD_GET_PREFERENCE = "get_preference";
+
+ /**
+ * Arg name to {@link android.content.ContentProvider#call(String, String, Bundle)}
+ * for {@link #AUTHORITY_LEGACY}.
+ * Contains all supported shared preferences for cellbroadcast.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final class Preference {
+ /**
+ * Not Instantiatable.
+ * @hide
+ */
+ private Preference() {}
+
+ /** Preference to enable area update info alert */
+ public static final @NonNull String ENABLE_AREA_UPDATE_INFO_PREF =
+ "enable_area_update_info_alerts";
+
+ /** Preference to enable test alert */
+ public static final @NonNull String ENABLE_TEST_ALERT_PREF =
+ "enable_test_alerts";
+
+ /** Preference to enable state local test alert */
+ public static final @NonNull String ENABLE_STATE_LOCAL_TEST_PREF
+ = "enable_state_local_test_alerts";
+
+ /** Preference to enable public safety alert */
+ public static final @NonNull String ENABLE_PUBLIC_SAFETY_PREF
+ = "enable_public_safety_messages";
+
+ /** Preference to enable amber alert */
+ public static final @NonNull String ENABLE_CMAS_AMBER_PREF
+ = "enable_cmas_amber_alerts";
+
+ /** Preference to enable severe threat alert */
+ public static final @NonNull String ENABLE_CMAS_SEVERE_THREAT_PREF
+ = "enable_cmas_severe_threat_alerts";
+
+ /** Preference to enable extreme threat alert */
+ public static final @NonNull String ENABLE_CMAS_EXTREME_THREAT_PREF =
+ "enable_cmas_extreme_threat_alerts";
+
+ /** Preference to enable presidential alert */
+ public static final @NonNull String ENABLE_CMAS_PRESIDENTIAL_PREF =
+ "enable_cmas_presidential_alerts";
+
+ /** Preference to enable alert vibration */
+ public static final @NonNull String ENABLE_ALERT_VIBRATION_PREF =
+ "enable_alert_vibrate";
+
+ /** Preference to enable emergency alert */
+ public static final @NonNull String ENABLE_EMERGENCY_PERF =
+ "enable_emergency_alerts";
+
+ /** Preference to enable volume for alerts */
+ public static final @NonNull String ENABLE_FULL_VOLUME_PREF =
+ "use_full_volume";
+
+ /** Preference to enable receive alerts in second language */
+ public static final @NonNull String ENABLE_CMAS_IN_SECOND_LANGUAGE_PREF =
+ "receive_cmas_in_second_language";
+ }
+
+ /**
* The subscription which received this cell broadcast message.
* <P>Type: INTEGER</P>
*/
- public static final String SUB_ID = "sub_id";
+ public static final String SUBSCRIPTION_ID = "sub_id";
/**
* The slot which received this cell broadcast message.
@@ -4358,7 +4476,7 @@
public static final String[] QUERY_COLUMNS_FWK = {
_ID,
SLOT_INDEX,
- SUB_ID,
+ SUBSCRIPTION_ID,
GEOGRAPHICAL_SCOPE,
PLMN,
LAC,
@@ -4864,5 +4982,402 @@
*/
@NonNull
public static final Uri CONTENT_URI = Uri.parse("content://telephony/siminfo");
+
+ /**
+ * TelephonyProvider unique key column name is the subscription id.
+ * <P>Type: TEXT (String)</P>
+ */
+ public static final String UNIQUE_KEY_SUBSCRIPTION_ID = "_id";
+
+ /**
+ * TelephonyProvider column name for a unique identifier for the subscription within the
+ * specific subscription type. For example, it contains SIM ICC Identifier subscriptions
+ * on Local SIMs. and Mac-address for Remote-SIM Subscriptions for Bluetooth devices.
+ * <P>Type: TEXT (String)</P>
+ */
+ public static final String ICC_ID = "icc_id";
+
+ /**
+ * TelephonyProvider column name for user SIM_SlOT_INDEX
+ * <P>Type: INTEGER (int)</P>
+ */
+ public static final String SIM_SLOT_INDEX = "sim_id";
+
+ /**
+ * SIM is not inserted
+ */
+ public static final int SIM_NOT_INSERTED = -1;
+
+ /**
+ * TelephonyProvider column name Subscription-type.
+ * <P>Type: INTEGER (int)</P> {@link #SUBSCRIPTION_TYPE_LOCAL_SIM} for Local-SIM
+ * Subscriptions, {@link #SUBSCRIPTION_TYPE_REMOTE_SIM} for Remote-SIM Subscriptions.
+ * Default value is 0.
+ */
+ public static final String SUBSCRIPTION_TYPE = "subscription_type";
+
+ /**
+ * This constant is to designate a subscription as a Local-SIM Subscription.
+ * <p> A Local-SIM can be a physical SIM inserted into a sim-slot in the device, or eSIM on
+ * the device.
+ * </p>
+ */
+ public static final int SUBSCRIPTION_TYPE_LOCAL_SIM = 0;
+
+ /**
+ * This constant is to designate a subscription as a Remote-SIM Subscription.
+ * <p>
+ * A Remote-SIM subscription is for a SIM on a phone connected to this device via some
+ * connectivity mechanism, for example bluetooth. Similar to Local SIM, this subscription
+ * can be used for SMS, Voice and data by proxying data through the connected device.
+ * Certain data of the SIM, such as IMEI, are not accessible for Remote SIMs.
+ * </p>
+ *
+ * <p>
+ * A Remote-SIM is available only as long the phone stays connected to this device.
+ * When the phone disconnects, Remote-SIM subscription is removed from this device and is
+ * no longer known. All data associated with the subscription, such as stored SMS, call
+ * logs, contacts etc, are removed from this device.
+ * </p>
+ *
+ * <p>
+ * If the phone re-connects to this device, a new Remote-SIM subscription is created for
+ * the phone. The Subscription Id associated with the new subscription is different from
+ * the Subscription Id of the previous Remote-SIM subscription created (and removed) for the
+ * phone; i.e., new Remote-SIM subscription treats the reconnected phone as a Remote-SIM
+ * that was never seen before.
+ * </p>
+ */
+ public static final int SUBSCRIPTION_TYPE_REMOTE_SIM = 1;
+
+ /**
+ * TelephonyProvider column name data_enabled_override_rules.
+ * It's a list of rules for overriding data enabled settings. The syntax is
+ * For example, "mms=nonDefault" indicates enabling data for mms in non-default
+ * subscription.
+ * "default=nonDefault&inVoiceCall" indicates enabling data for internet in non-default
+ * subscription and while is in voice call.
+ *
+ * Default value is empty string.
+ */
+ public static final String DATA_ENABLED_OVERRIDE_RULES = "data_enabled_override_rules";
+
+ /**
+ * TelephonyProvider column name for user displayed name.
+ * <P>Type: TEXT (String)</P>
+ */
+ public static final String DISPLAY_NAME = "display_name";
+
+ /**
+ * TelephonyProvider column name for the service provider name for the SIM.
+ * <P>Type: TEXT (String)</P>
+ */
+ public static final String CARRIER_NAME = "carrier_name";
+
+ /**
+ * TelephonyProvider column name for source of the user displayed name.
+ * <P>Type: INT (int)</P> with one of the NAME_SOURCE_XXXX values below
+ */
+ public static final String NAME_SOURCE = "name_source";
+
+ /** The name_source is the default, which is from the carrier id. */
+ public static final int NAME_SOURCE_DEFAULT = 0;
+
+ /**
+ * The name_source is from SIM EF_SPN.
+ */
+ public static final int NAME_SOURCE_SIM_SPN = 1;
+
+ /**
+ * The name_source is from user input
+ */
+ public static final int NAME_SOURCE_USER_INPUT = 2;
+
+ /**
+ * The name_source is carrier (carrier app, carrier config, etc.)
+ */
+ public static final int NAME_SOURCE_CARRIER = 3;
+
+ /**
+ * The name_source is from SIM EF_PNN.
+ */
+ public static final int NAME_SOURCE_SIM_PNN = 4;
+
+ /**
+ * TelephonyProvider column name for the color of a SIM.
+ * <P>Type: INTEGER (int)</P>
+ */
+ public static final String COLOR = "color";
+
+ /** TelephonyProvider column name for the default color of a SIM {@hide} */
+ public static final int COLOR_DEFAULT = 0;
+
+ /**
+ * TelephonyProvider column name for the phone number of a SIM.
+ * <P>Type: TEXT (String)</P>
+ */
+ public static final String NUMBER = "number";
+
+ /**
+ * TelephonyProvider column name for the number display format of a SIM.
+ * <P>Type: INTEGER (int)</P>
+ * @hide
+ */
+ public static final String DISPLAY_NUMBER_FORMAT = "display_number_format";
+
+ /**
+ * TelephonyProvider column name for the default display format of a SIM
+ * @hide
+ */
+ public static final int DISPLAY_NUMBER_DEFAULT = 1;
+
+ /**
+ * TelephonyProvider column name for whether data roaming is enabled.
+ * <P>Type: INTEGER (int)</P>
+ */
+ public static final String DATA_ROAMING = "data_roaming";
+
+ /** Indicates that data roaming is enabled for a subscription */
+ public static final int DATA_ROAMING_ENABLE = 1;
+
+ /** Indicates that data roaming is disabled for a subscription */
+ public static final int DATA_ROAMING_DISABLE = 0;
+
+ /** TelephonyProvider column name for default data roaming setting: disable */
+ public static final int DATA_ROAMING_DEFAULT = DATA_ROAMING_DISABLE;
+
+ /**
+ * TelephonyProvider column name for subscription carrier id.
+ * @see TelephonyManager#getSimCarrierId()
+ * <p>Type: INTEGER (int) </p>
+ */
+ public static final String CARRIER_ID = "carrier_id";
+
+ /**
+ * A comma-separated list of EHPLMNs associated with the subscription
+ * <P>Type: TEXT (String)</P>
+ */
+ public static final String EHPLMNS = "ehplmns";
+
+ /**
+ * A comma-separated list of HPLMNs associated with the subscription
+ * <P>Type: TEXT (String)</P>
+ */
+ public static final String HPLMNS = "hplmns";
+
+ /**
+ * TelephonyProvider column name for the MCC associated with a SIM, stored as a string.
+ * <P>Type: TEXT (String)</P>
+ */
+ public static final String MCC_STRING = "mcc_string";
+
+ /**
+ * TelephonyProvider column name for the MNC associated with a SIM, stored as a string.
+ * <P>Type: TEXT (String)</P>
+ */
+ public static final String MNC_STRING = "mnc_string";
+
+ /**
+ * TelephonyProvider column name for the MCC associated with a SIM.
+ * <P>Type: INTEGER (int)</P>
+ */
+ public static final String MCC = "mcc";
+
+ /**
+ * TelephonyProvider column name for the MNC associated with a SIM.
+ * <P>Type: INTEGER (int)</P>
+ */
+ public static final String MNC = "mnc";
+
+ /**
+ * TelephonyProvider column name for the iso country code associated with a SIM.
+ * <P>Type: TEXT (String)</P>
+ */
+ public static final String ISO_COUNTRY_CODE = "iso_country_code";
+
+ /**
+ * TelephonyProvider column name for the sim provisioning status associated with a SIM.
+ * <P>Type: INTEGER (int)</P>
+ * @hide
+ */
+ public static final String SIM_PROVISIONING_STATUS = "sim_provisioning_status";
+
+ /** The sim is provisioned {@hide} */
+ public static final int SIM_PROVISIONED = 0;
+
+ /**
+ * TelephonyProvider column name for whether a subscription is embedded (that is, present on
+ * an eSIM).
+ * <p>Type: INTEGER (int), 1 for embedded or 0 for non-embedded.
+ */
+ public static final String IS_EMBEDDED = "is_embedded";
+
+ /**
+ * TelephonyProvider column name for SIM card identifier. For UICC card it is the ICCID of
+ * the current enabled profile on the card, while for eUICC card it is the EID of the card.
+ * <P>Type: TEXT (String)</P>
+ */
+ public static final String CARD_ID = "card_id";
+
+ /**
+ * TelephonyProvider column name for the encoded {@link UiccAccessRule}s from
+ * {@link UiccAccessRule#encodeRules}. Only present if {@link #IS_EMBEDDED} is 1.
+ * <p>TYPE: BLOB
+ */
+ public static final String ACCESS_RULES = "access_rules";
+
+ /**
+ * TelephonyProvider column name for the encoded {@link UiccAccessRule}s from
+ * {@link UiccAccessRule#encodeRules} but for the rules that come from CarrierConfigs.
+ * Only present if there are access rules in CarrierConfigs
+ * <p>TYPE: BLOB
+ */
+ public static final String ACCESS_RULES_FROM_CARRIER_CONFIGS =
+ "access_rules_from_carrier_configs";
+
+ /**
+ * TelephonyProvider column name identifying whether an embedded subscription is on a
+ * removable card. Such subscriptions are marked inaccessible as soon as the current card
+ * is removed. Otherwise, they will remain accessible unless explicitly deleted. Only
+ * present if {@link #IS_EMBEDDED} is 1.
+ * <p>TYPE: INTEGER (int), 1 for removable or 0 for non-removable.
+ */
+ public static final String IS_REMOVABLE = "is_removable";
+
+ /** TelephonyProvider column name for extreme threat in CB settings */
+ public static final String CB_EXTREME_THREAT_ALERT = "enable_cmas_extreme_threat_alerts";
+
+ /** TelephonyProvider column name for severe threat in CB settings */
+ public static final String CB_SEVERE_THREAT_ALERT = "enable_cmas_severe_threat_alerts";
+
+ /** TelephonyProvider column name for amber alert in CB settings */
+ public static final String CB_AMBER_ALERT = "enable_cmas_amber_alerts";
+
+ /** TelephonyProvider column name for emergency alert in CB settings */
+ public static final String CB_EMERGENCY_ALERT = "enable_emergency_alerts";
+
+ /** TelephonyProvider column name for alert sound duration in CB settings */
+ public static final String CB_ALERT_SOUND_DURATION = "alert_sound_duration";
+
+ /** TelephonyProvider column name for alert reminder interval in CB settings */
+ public static final String CB_ALERT_REMINDER_INTERVAL = "alert_reminder_interval";
+
+ /** TelephonyProvider column name for enabling vibrate in CB settings */
+ public static final String CB_ALERT_VIBRATE = "enable_alert_vibrate";
+
+ /** TelephonyProvider column name for enabling alert speech in CB settings */
+ public static final String CB_ALERT_SPEECH = "enable_alert_speech";
+
+ /** TelephonyProvider column name for ETWS test alert in CB settings */
+ public static final String CB_ETWS_TEST_ALERT = "enable_etws_test_alerts";
+
+ /** TelephonyProvider column name for enable channel50 alert in CB settings */
+ public static final String CB_CHANNEL_50_ALERT = "enable_channel_50_alerts";
+
+ /** TelephonyProvider column name for CMAS test alert in CB settings */
+ public static final String CB_CMAS_TEST_ALERT = "enable_cmas_test_alerts";
+
+ /** TelephonyProvider column name for Opt out dialog in CB settings */
+ public static final String CB_OPT_OUT_DIALOG = "show_cmas_opt_out_dialog";
+
+ /**
+ * TelephonyProvider column name for enable Volte.
+ *
+ * If this setting is not initialized (set to -1) then we use the Carrier Config value
+ * {@link CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL}.
+ */
+ public static final String ENHANCED_4G_MODE_ENABLED = "volte_vt_enabled";
+
+ /** TelephonyProvider column name for enable VT (Video Telephony over IMS) */
+ public static final String VT_IMS_ENABLED = "vt_ims_enabled";
+
+ /** TelephonyProvider column name for enable Wifi calling */
+ public static final String WFC_IMS_ENABLED = "wfc_ims_enabled";
+
+ /** TelephonyProvider column name for Wifi calling mode */
+ public static final String WFC_IMS_MODE = "wfc_ims_mode";
+
+ /** TelephonyProvider column name for Wifi calling mode in roaming */
+ public static final String WFC_IMS_ROAMING_MODE = "wfc_ims_roaming_mode";
+
+ /** TelephonyProvider column name for enable Wifi calling in roaming */
+ public static final String WFC_IMS_ROAMING_ENABLED = "wfc_ims_roaming_enabled";
+
+ /**
+ * TelephonyProvider column name for whether a subscription is opportunistic, that is,
+ * whether the network it connects to is limited in functionality or coverage.
+ * For example, CBRS.
+ * <p>Type: INTEGER (int), 1 for opportunistic or 0 for non-opportunistic.
+ */
+ public static final String IS_OPPORTUNISTIC = "is_opportunistic";
+
+ /**
+ * TelephonyProvider column name for group ID. Subscriptions with same group ID
+ * are considered bundled together, and should behave as a single subscription at
+ * certain scenarios.
+ */
+ public static final String GROUP_UUID = "group_uuid";
+
+ /**
+ * TelephonyProvider column name for group owner. It's the package name who created
+ * the subscription group.
+ */
+ public static final String GROUP_OWNER = "group_owner";
+
+ /**
+ * TelephonyProvider column name for whether a subscription is metered or not, that is,
+ * whether the network it connects to charges for subscription or not. For example, paid
+ * CBRS or unpaid.
+ * @hide
+ */
+ public static final String IS_METERED = "is_metered";
+
+ /**
+ * TelephonyProvider column name for the profile class of a subscription
+ * Only present if {@link #IS_EMBEDDED} is 1.
+ * <P>Type: INTEGER (int)</P>
+ */
+ public static final String PROFILE_CLASS = "profile_class";
+
+ /**
+ * A testing profile can be pre-loaded or downloaded onto
+ * the eUICC and provides connectivity to test equipment
+ * for the purpose of testing the device and the eUICC. It
+ * is not intended to store any operator credentials.
+ */
+ public static final int PROFILE_CLASS_TESTING = 0;
+
+ /**
+ * A provisioning profile is pre-loaded onto the eUICC and
+ * provides connectivity to a mobile network solely for the
+ * purpose of provisioning profiles.
+ */
+ public static final int PROFILE_CLASS_PROVISIONING = 1;
+
+ /**
+ * An operational profile can be pre-loaded or downloaded
+ * onto the eUICC and provides services provided by the
+ * operator.
+ */
+ public static final int PROFILE_CLASS_OPERATIONAL = 2;
+
+ /**
+ * The profile class is unset. This occurs when profile class
+ * info is not available. The subscription either has no profile
+ * metadata or the profile metadata did not encode profile class.
+ */
+ public static final int PROFILE_CLASS_UNSET = -1;
+
+ /** Default profile class */
+ public static final int PROFILE_CLASS_DEFAULT = PROFILE_CLASS_UNSET;
+
+ /**
+ * IMSI (International Mobile Subscriber Identity).
+ * <P>Type: TEXT </P>
+ */
+ public static final String IMSI = "imsi";
+
+ /** Whether uicc applications is set to be enabled or disabled. By default it's enabled. */
+ public static final String UICC_APPLICATIONS_ENABLED = "uicc_applications_enabled";
}
}
diff --git a/core/java/android/security/KeystoreArguments.java b/core/java/android/security/KeystoreArguments.java
index e634234..a59c4e0 100644
--- a/core/java/android/security/KeystoreArguments.java
+++ b/core/java/android/security/KeystoreArguments.java
@@ -16,7 +16,7 @@
package android.security;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/android/security/keymaster/ExportResult.java b/core/java/android/security/keymaster/ExportResult.java
index 1be5ae9..037b852 100644
--- a/core/java/android/security/keymaster/ExportResult.java
+++ b/core/java/android/security/keymaster/ExportResult.java
@@ -16,7 +16,7 @@
package android.security.keymaster;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/android/security/keymaster/KeyCharacteristics.java b/core/java/android/security/keymaster/KeyCharacteristics.java
index 2eb2cef..d8382fa 100644
--- a/core/java/android/security/keymaster/KeyCharacteristics.java
+++ b/core/java/android/security/keymaster/KeyCharacteristics.java
@@ -16,7 +16,7 @@
package android.security.keymaster;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/android/security/keymaster/KeymasterArguments.java b/core/java/android/security/keymaster/KeymasterArguments.java
index d2dbdec..e009e12 100644
--- a/core/java/android/security/keymaster/KeymasterArguments.java
+++ b/core/java/android/security/keymaster/KeymasterArguments.java
@@ -16,7 +16,7 @@
package android.security.keymaster;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/android/security/keymaster/KeymasterBlob.java b/core/java/android/security/keymaster/KeymasterBlob.java
index 6a2024f..68365bf 100644
--- a/core/java/android/security/keymaster/KeymasterBlob.java
+++ b/core/java/android/security/keymaster/KeymasterBlob.java
@@ -16,7 +16,7 @@
package android.security.keymaster;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/android/security/keymaster/KeymasterBlobArgument.java b/core/java/android/security/keymaster/KeymasterBlobArgument.java
index fc562bd..81b08c5 100644
--- a/core/java/android/security/keymaster/KeymasterBlobArgument.java
+++ b/core/java/android/security/keymaster/KeymasterBlobArgument.java
@@ -16,7 +16,7 @@
package android.security.keymaster;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
/**
diff --git a/core/java/android/security/keymaster/KeymasterBooleanArgument.java b/core/java/android/security/keymaster/KeymasterBooleanArgument.java
index 4286aa0..25b2ac4 100644
--- a/core/java/android/security/keymaster/KeymasterBooleanArgument.java
+++ b/core/java/android/security/keymaster/KeymasterBooleanArgument.java
@@ -16,7 +16,7 @@
package android.security.keymaster;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
/**
diff --git a/core/java/android/security/keymaster/KeymasterDateArgument.java b/core/java/android/security/keymaster/KeymasterDateArgument.java
index 3e04c15..218f488 100644
--- a/core/java/android/security/keymaster/KeymasterDateArgument.java
+++ b/core/java/android/security/keymaster/KeymasterDateArgument.java
@@ -16,8 +16,9 @@
package android.security.keymaster;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
+
import java.util.Date;
/**
diff --git a/core/java/android/security/keymaster/KeymasterIntArgument.java b/core/java/android/security/keymaster/KeymasterIntArgument.java
index 4aadce4..01d38c7 100644
--- a/core/java/android/security/keymaster/KeymasterIntArgument.java
+++ b/core/java/android/security/keymaster/KeymasterIntArgument.java
@@ -16,7 +16,7 @@
package android.security.keymaster;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
/**
diff --git a/core/java/android/security/keymaster/KeymasterLongArgument.java b/core/java/android/security/keymaster/KeymasterLongArgument.java
index bc2255e..3ac27cc 100644
--- a/core/java/android/security/keymaster/KeymasterLongArgument.java
+++ b/core/java/android/security/keymaster/KeymasterLongArgument.java
@@ -16,7 +16,7 @@
package android.security.keymaster;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
/**
diff --git a/core/java/android/security/keymaster/OperationResult.java b/core/java/android/security/keymaster/OperationResult.java
index c278eb3..b4e155a 100644
--- a/core/java/android/security/keymaster/OperationResult.java
+++ b/core/java/android/security/keymaster/OperationResult.java
@@ -16,7 +16,7 @@
package android.security.keymaster;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/android/security/net/config/RootTrustManager.java b/core/java/android/security/net/config/RootTrustManager.java
index d8936d9..58dc4ba 100644
--- a/core/java/android/security/net/config/RootTrustManager.java
+++ b/core/java/android/security/net/config/RootTrustManager.java
@@ -16,15 +16,16 @@
package android.security.net.config;
+import android.compat.annotation.UnsupportedAppUsage;
+
import java.net.Socket;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.List;
-import android.annotation.UnsupportedAppUsage;
-import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
import javax.net.ssl.X509ExtendedTrustManager;
/**
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index d53e62a..d827b30 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.content.IntentSender;
import android.os.Parcel;
import android.os.Parcelable;
@@ -56,6 +57,10 @@
* all datasets pairs that have that view's {@link AutofillId}. Then, when the user selects a
* dataset from the UI, all views in that dataset are autofilled.
*
+ * <p>If both the current Input Method and autofill service supports inline suggestions, the Dataset
+ * can be shown by the keyboard as a suggestion. To use this feature, the Dataset should contain
+ * an {@link InlinePresentation} representing how the inline suggestion UI will be rendered.
+ *
* <a name="Authentication"></a>
* <h3>Dataset authentication</h3>
*
@@ -99,8 +104,10 @@
private final ArrayList<AutofillId> mFieldIds;
private final ArrayList<AutofillValue> mFieldValues;
private final ArrayList<RemoteViews> mFieldPresentations;
+ private final ArrayList<InlinePresentation> mFieldInlinePresentations;
private final ArrayList<DatasetFieldFilter> mFieldFilters;
private final RemoteViews mPresentation;
+ @Nullable private final InlinePresentation mInlinePresentation;
private final IntentSender mAuthentication;
@Nullable String mId;
@@ -108,8 +115,10 @@
mFieldIds = builder.mFieldIds;
mFieldValues = builder.mFieldValues;
mFieldPresentations = builder.mFieldPresentations;
+ mFieldInlinePresentations = builder.mFieldInlinePresentations;
mFieldFilters = builder.mFieldFilters;
mPresentation = builder.mPresentation;
+ mInlinePresentation = builder.mInlinePresentation;
mAuthentication = builder.mAuthentication;
mId = builder.mId;
}
@@ -132,6 +141,13 @@
/** @hide */
@Nullable
+ public InlinePresentation getFieldInlinePresentation(int index) {
+ final InlinePresentation inlinePresentation = mFieldInlinePresentations.get(index);
+ return inlinePresentation != null ? inlinePresentation : mInlinePresentation;
+ }
+
+ /** @hide */
+ @Nullable
public DatasetFieldFilter getFilter(int index) {
return mFieldFilters.get(index);
}
@@ -165,7 +181,9 @@
}
if (mFieldPresentations != null) {
builder.append(", fieldPresentations=").append(mFieldPresentations.size());
-
+ }
+ if (mFieldInlinePresentations != null) {
+ builder.append(", fieldInlinePresentations=").append(mFieldInlinePresentations.size());
}
if (mFieldFilters != null) {
builder.append(", fieldFilters=").append(mFieldFilters.size());
@@ -173,6 +191,9 @@
if (mPresentation != null) {
builder.append(", hasPresentation");
}
+ if (mInlinePresentation != null) {
+ builder.append(", hasInlinePresentation");
+ }
if (mAuthentication != null) {
builder.append(", hasAuthentication");
}
@@ -198,8 +219,10 @@
private ArrayList<AutofillId> mFieldIds;
private ArrayList<AutofillValue> mFieldValues;
private ArrayList<RemoteViews> mFieldPresentations;
+ private ArrayList<InlinePresentation> mFieldInlinePresentations;
private ArrayList<DatasetFieldFilter> mFieldFilters;
private RemoteViews mPresentation;
+ @Nullable private InlinePresentation mInlinePresentation;
private IntentSender mAuthentication;
private boolean mDestroyed;
@Nullable private String mId;
@@ -208,10 +231,39 @@
* Creates a new builder.
*
* @param presentation The presentation used to visualize this dataset.
+ * @param inlinePresentation The {@link InlinePresentation} used to visualize this dataset
+ * as inline suggestions. If the dataset supports inline suggestions,
+ * this should not be null.
*/
- public Builder(@NonNull RemoteViews presentation) {
+ public Builder(@NonNull RemoteViews presentation,
+ @NonNull InlinePresentation inlinePresentation) {
Preconditions.checkNotNull(presentation, "presentation must be non-null");
mPresentation = presentation;
+ mInlinePresentation = inlinePresentation;
+ }
+
+ /**
+ * Creates a new builder.
+ *
+ * @param presentation The presentation used to visualize this dataset.
+ */
+ public Builder(@NonNull RemoteViews presentation) {
+ this(presentation, null);
+ }
+
+ /**
+ * Creates a new builder.
+ *
+ * <p>Only called by augmented autofill.
+ *
+ * @param inlinePresentation The {@link InlinePresentation} used to visualize this dataset
+ * as inline suggestions. If the dataset supports inline suggestions,
+ * this should not be null.
+ * @hide
+ */
+ @SystemApi
+ public Builder(@NonNull InlinePresentation inlinePresentation) {
+ mInlinePresentation = inlinePresentation;
}
/**
@@ -325,7 +377,7 @@
*/
public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value) {
throwIfDestroyed();
- setLifeTheUniverseAndEverything(id, value, null, null);
+ setLifeTheUniverseAndEverything(id, value, null, null, null);
return this;
}
@@ -353,7 +405,7 @@
@NonNull RemoteViews presentation) {
throwIfDestroyed();
Preconditions.checkNotNull(presentation, "presentation cannot be null");
- setLifeTheUniverseAndEverything(id, value, presentation, null);
+ setLifeTheUniverseAndEverything(id, value, presentation, null, null);
return this;
}
@@ -389,7 +441,7 @@
throwIfDestroyed();
Preconditions.checkState(mPresentation != null,
"Dataset presentation not set on constructor");
- setLifeTheUniverseAndEverything(id, value, null, new DatasetFieldFilter(filter));
+ setLifeTheUniverseAndEverything(id, value, null, null, new DatasetFieldFilter(filter));
return this;
}
@@ -424,13 +476,119 @@
@Nullable Pattern filter, @NonNull RemoteViews presentation) {
throwIfDestroyed();
Preconditions.checkNotNull(presentation, "presentation cannot be null");
- setLifeTheUniverseAndEverything(id, value, presentation,
+ setLifeTheUniverseAndEverything(id, value, presentation, null,
+ new DatasetFieldFilter(filter));
+ return this;
+ }
+
+ /**
+ * Sets the value of a field, using a custom {@link RemoteViews presentation} to
+ * visualize it and an {@link InlinePresentation} to visualize it as an inline suggestion.
+ *
+ * <p><b>Note:</b> If the dataset requires authentication but the service knows its text
+ * value it's easier to filter by calling
+ * {@link #setValue(AutofillId, AutofillValue, RemoteViews)} and using the value to filter.
+ *
+ * @param id id returned by {@link
+ * android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
+ * @param value the value to be autofilled. Pass {@code null} if you do not have the value
+ * but the target view is a logical part of the dataset. For example, if
+ * the dataset needs authentication and you have no access to the value.
+ * @param presentation the presentation used to visualize this field.
+ * @param inlinePresentation The {@link InlinePresentation} used to visualize this dataset
+ * as inline suggestions. If the dataset supports inline suggestions,
+ * this should not be null.
+ *
+ * @return this builder.
+ */
+ public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
+ @NonNull RemoteViews presentation, @NonNull InlinePresentation inlinePresentation) {
+ throwIfDestroyed();
+ Preconditions.checkNotNull(presentation, "presentation cannot be null");
+ Preconditions.checkNotNull(inlinePresentation, "inlinePresentation cannot be null");
+ setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation, null);
+ return this;
+ }
+
+ /**
+ * Sets the value of a field, using a custom {@link RemoteViews presentation} to
+ * visualize it and a <a href="#Filtering">explicit filter</a>, and an
+ * {@link InlinePresentation} to visualize it as an inline suggestion.
+ *
+ * <p>This method is typically used when the dataset requires authentication and the service
+ * does not know its value but wants to hide the dataset after the user enters a minimum
+ * number of characters. For example, if the dataset represents a credit card number and the
+ * service does not want to show the "Tap to authenticate" message until the user tapped
+ * 4 digits, in which case the filter would be {@code Pattern.compile("\\d.{4,}")}.
+ *
+ * <p><b>Note:</b> If the dataset requires authentication but the service knows its text
+ * value it's easier to filter by calling
+ * {@link #setValue(AutofillId, AutofillValue, RemoteViews)} and using the value to filter.
+ *
+ * @param id id returned by {@link
+ * android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
+ * @param value the value to be autofilled. Pass {@code null} if you do not have the value
+ * but the target view is a logical part of the dataset. For example, if
+ * the dataset needs authentication and you have no access to the value.
+ * @param filter regex used to determine if the dataset should be shown in the autofill UI;
+ * when {@code null}, it disables filtering on that dataset (this is the recommended
+ * approach when {@code value} is not {@code null} and field contains sensitive data
+ * such as passwords).
+ * @param presentation the presentation used to visualize this field.
+ * @param inlinePresentation The {@link InlinePresentation} used to visualize this dataset
+ * as inline suggestions. If the dataset supports inline suggestions, this
+ * should not be null.
+ *
+ * @return this builder.
+ */
+ public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
+ @Nullable Pattern filter, @NonNull RemoteViews presentation,
+ @NonNull InlinePresentation inlinePresentation) {
+ throwIfDestroyed();
+ Preconditions.checkNotNull(presentation, "presentation cannot be null");
+ Preconditions.checkNotNull(inlinePresentation, "inlinePresentation cannot be null");
+ setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation,
+ new DatasetFieldFilter(filter));
+ return this;
+ }
+
+ /**
+ * Sets the value of a field with an <a href="#Filtering">explicit filter</a>, and using an
+ * {@link InlinePresentation} to visualize it as an inline suggestion.
+ *
+ * <p>Only called by augmented autofill.
+ *
+ * @param id id returned by {@link
+ * android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
+ * @param value the value to be autofilled. Pass {@code null} if you do not have the value
+ * but the target view is a logical part of the dataset. For example, if
+ * the dataset needs authentication and you have no access to the value.
+ * @param filter regex used to determine if the dataset should be shown in the autofill UI;
+ * when {@code null}, it disables filtering on that dataset (this is the recommended
+ * approach when {@code value} is not {@code null} and field contains sensitive data
+ * such as passwords).
+ * @param inlinePresentation The {@link InlinePresentation} used to visualize this dataset
+ * as inline suggestions. If the dataset supports inline suggestions, this
+ * should not be null.
+ *
+ * @return this builder.
+ *
+ * @hide
+ */
+ @SystemApi
+ public @NonNull Builder setInlinePresentation(@NonNull AutofillId id,
+ @Nullable AutofillValue value, @Nullable Pattern filter,
+ @NonNull InlinePresentation inlinePresentation) {
+ throwIfDestroyed();
+ Preconditions.checkNotNull(inlinePresentation, "inlinePresentation cannot be null");
+ setLifeTheUniverseAndEverything(id, value, null, inlinePresentation,
new DatasetFieldFilter(filter));
return this;
}
private void setLifeTheUniverseAndEverything(@NonNull AutofillId id,
@Nullable AutofillValue value, @Nullable RemoteViews presentation,
+ @Nullable InlinePresentation inlinePresentation,
@Nullable DatasetFieldFilter filter) {
Preconditions.checkNotNull(id, "id cannot be null");
if (mFieldIds != null) {
@@ -438,6 +596,7 @@
if (existingIdx >= 0) {
mFieldValues.set(existingIdx, value);
mFieldPresentations.set(existingIdx, presentation);
+ mFieldInlinePresentations.set(existingIdx, inlinePresentation);
mFieldFilters.set(existingIdx, filter);
return;
}
@@ -445,11 +604,13 @@
mFieldIds = new ArrayList<>();
mFieldValues = new ArrayList<>();
mFieldPresentations = new ArrayList<>();
+ mFieldInlinePresentations = new ArrayList<>();
mFieldFilters = new ArrayList<>();
}
mFieldIds.add(id);
mFieldValues.add(value);
mFieldPresentations.add(presentation);
+ mFieldInlinePresentations.add(inlinePresentation);
mFieldFilters.add(filter);
}
@@ -460,7 +621,8 @@
*
* @throws IllegalStateException if no field was set (through
* {@link #setValue(AutofillId, AutofillValue)} or
- * {@link #setValue(AutofillId, AutofillValue, RemoteViews)}).
+ * {@link #setValue(AutofillId, AutofillValue, RemoteViews)} or
+ * {@link #setValue(AutofillId, AutofillValue, RemoteViews, InlinePresentation)}).
*
* @return The built dataset.
*/
@@ -492,38 +654,49 @@
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeParcelable(mPresentation, flags);
+ parcel.writeParcelable(mInlinePresentation, flags);
parcel.writeTypedList(mFieldIds, flags);
parcel.writeTypedList(mFieldValues, flags);
parcel.writeTypedList(mFieldPresentations, flags);
+ parcel.writeTypedList(mFieldInlinePresentations, flags);
parcel.writeTypedList(mFieldFilters, flags);
parcel.writeParcelable(mAuthentication, flags);
parcel.writeString(mId);
}
- public static final @android.annotation.NonNull Creator<Dataset> CREATOR = new Creator<Dataset>() {
+ public static final @NonNull Creator<Dataset> CREATOR = new Creator<Dataset>() {
@Override
public Dataset createFromParcel(Parcel parcel) {
// Always go through the builder to ensure the data ingested by
// the system obeys the contract of the builder to avoid attacks
// using specially crafted parcels.
final RemoteViews presentation = parcel.readParcelable(null);
- final Builder builder = (presentation == null)
- ? new Builder()
- : new Builder(presentation);
+ final InlinePresentation inlinePresentation = parcel.readParcelable(null);
+ final Builder builder = presentation == null
+ ? new Builder(inlinePresentation)
+ : inlinePresentation == null
+ ? new Builder(presentation)
+ : new Builder(presentation, inlinePresentation);
final ArrayList<AutofillId> ids =
parcel.createTypedArrayList(AutofillId.CREATOR);
final ArrayList<AutofillValue> values =
parcel.createTypedArrayList(AutofillValue.CREATOR);
final ArrayList<RemoteViews> presentations =
parcel.createTypedArrayList(RemoteViews.CREATOR);
+ final ArrayList<InlinePresentation> inlinePresentations =
+ parcel.createTypedArrayList(InlinePresentation.CREATOR);
final ArrayList<DatasetFieldFilter> filters =
parcel.createTypedArrayList(DatasetFieldFilter.CREATOR);
+ final int inlinePresentationsSize = inlinePresentations.size();
for (int i = 0; i < ids.size(); i++) {
final AutofillId id = ids.get(i);
final AutofillValue value = values.get(i);
final RemoteViews fieldPresentation = presentations.get(i);
+ final InlinePresentation fieldInlinePresentation =
+ i < inlinePresentationsSize ? inlinePresentations.get(i) : null;
final DatasetFieldFilter filter = filters.get(i);
- builder.setLifeTheUniverseAndEverything(id, value, fieldPresentation, filter);
+ builder.setLifeTheUniverseAndEverything(id, value, fieldPresentation,
+ fieldInlinePresentation, filter);
}
builder.setAuthentication(parcel.readParcelable(null));
builder.setId(parcel.readString());
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 02a6390..d51e4ca 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -25,7 +25,6 @@
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.app.Activity;
-import android.app.slice.Slice;
import android.content.IntentSender;
import android.content.pm.ParceledListSlice;
import android.os.Bundle;
@@ -87,7 +86,7 @@
private int mRequestId;
private final @Nullable UserData mUserData;
private final @Nullable int[] mCancelIds;
- private final @Nullable ParceledListSlice<Slice> mInlineSuggestionSlices;
+ private final boolean mSupportsInlineSuggestions;
private FillResponse(@NonNull Builder builder) {
mDatasets = (builder.mDatasets != null) ? new ParceledListSlice<>(builder.mDatasets) : null;
@@ -105,8 +104,7 @@
mRequestId = INVALID_REQUEST_ID;
mUserData = builder.mUserData;
mCancelIds = builder.mCancelIds;
- mInlineSuggestionSlices = (builder.mInlineSuggestionSlices != null)
- ? new ParceledListSlice<>(builder.mInlineSuggestionSlices) : null;
+ mSupportsInlineSuggestions = builder.mSupportsInlineSuggestions;
}
/** @hide */
@@ -200,8 +198,8 @@
}
/** @hide */
- public List<Slice> getInlineSuggestionSlices() {
- return (mInlineSuggestionSlices != null) ? mInlineSuggestionSlices.getList() : null;
+ public boolean supportsInlineSuggestions() {
+ return mSupportsInlineSuggestions;
}
/**
@@ -224,7 +222,7 @@
private boolean mDestroyed;
private UserData mUserData;
private int[] mCancelIds;
- private ArrayList<Slice> mInlineSuggestionSlices;
+ private boolean mSupportsInlineSuggestions;
/**
* Triggers a custom UI before before autofilling the screen with any data set in this
@@ -580,20 +578,6 @@
}
/**
- * TODO(b/137800469): add javadoc
- */
- @NonNull
- public Builder addInlineSuggestionSlice(@NonNull Slice inlineSuggestionSlice) {
- throwIfDestroyed();
- throwIfAuthenticationCalled();
- if (mInlineSuggestionSlices == null) {
- mInlineSuggestionSlices = new ArrayList<>();
- }
- mInlineSuggestionSlices.add(inlineSuggestionSlice);
- return this;
- }
-
- /**
* Builds a new {@link FillResponse} instance.
*
* @throws IllegalStateException if any of the following conditions occur:
@@ -624,6 +608,14 @@
throw new IllegalStateException(
"must add at least 1 dataset when using header or footer");
}
+
+ for (final Dataset dataset : mDatasets) {
+ if (dataset.getFieldInlinePresentation(0) != null) {
+ mSupportsInlineSuggestions = true;
+ break;
+ }
+ }
+
mDestroyed = true;
return new FillResponse(this);
}
@@ -694,9 +686,6 @@
if (mCancelIds != null) {
builder.append(", mCancelIds=").append(mCancelIds.length);
}
- if (mInlineSuggestionSlices != null) {
- builder.append(", inlinedSuggestions=").append(mInlineSuggestionSlices.getList());
- }
return builder.append("]").toString();
}
@@ -725,7 +714,6 @@
parcel.writeParcelableArray(mFieldClassificationIds, flags);
parcel.writeInt(mFlags);
parcel.writeIntArray(mCancelIds);
- parcel.writeParcelable(mInlineSuggestionSlices, flags);
parcel.writeInt(mRequestId);
}
@@ -781,16 +769,6 @@
final int[] cancelIds = parcel.createIntArray();
builder.setCancelTargetIds(cancelIds);
- final ParceledListSlice<Slice> parceledInlineSuggestionSlices =
- parcel.readParcelable(null);
- if (parceledInlineSuggestionSlices != null) {
- final List<Slice> inlineSuggestionSlices = parceledInlineSuggestionSlices.getList();
- final int size = inlineSuggestionSlices.size();
- for (int i = 0; i < size; i++) {
- builder.addInlineSuggestionSlice(inlineSuggestionSlices.get(i));
- }
- }
-
final FillResponse response = builder.build();
response.setRequestId(parcel.readInt());
diff --git a/core/java/android/service/autofill/InlinePresentation.aidl b/core/java/android/service/autofill/InlinePresentation.aidl
new file mode 100644
index 0000000..eeddcc0
--- /dev/null
+++ b/core/java/android/service/autofill/InlinePresentation.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.autofill;
+
+parcelable InlinePresentation;
diff --git a/core/java/android/service/autofill/InlinePresentation.java b/core/java/android/service/autofill/InlinePresentation.java
new file mode 100644
index 0000000..1568fb3
--- /dev/null
+++ b/core/java/android/service/autofill/InlinePresentation.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.autofill;
+
+import android.annotation.NonNull;
+import android.app.slice.Slice;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.inline.InlinePresentationSpec;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * Wrapper class holding a {@link Slice} and an {@link InlinePresentationSpec} for rendering UI
+ * for an Inline Suggestion.
+ */
+@DataClass(
+ genToString = true,
+ genHiddenConstDefs = true,
+ genEqualsHashCode = true)
+public final class InlinePresentation implements Parcelable {
+
+ private final @NonNull Slice mSlice;
+
+ private final @NonNull InlinePresentationSpec mInlinePresentationSpec;
+
+
+
+ // Code below generated by codegen v1.0.14.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/autofill/InlinePresentation.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @DataClass.Generated.Member
+ public InlinePresentation(
+ @NonNull Slice slice,
+ @NonNull InlinePresentationSpec inlinePresentationSpec) {
+ this.mSlice = slice;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mSlice);
+ this.mInlinePresentationSpec = inlinePresentationSpec;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mInlinePresentationSpec);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull Slice getSlice() {
+ return mSlice;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull InlinePresentationSpec getInlinePresentationSpec() {
+ return mInlinePresentationSpec;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "InlinePresentation { " +
+ "slice = " + mSlice + ", " +
+ "inlinePresentationSpec = " + mInlinePresentationSpec +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@android.annotation.Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(InlinePresentation other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ InlinePresentation that = (InlinePresentation) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && java.util.Objects.equals(mSlice, that.mSlice)
+ && java.util.Objects.equals(mInlinePresentationSpec, that.mInlinePresentationSpec);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + java.util.Objects.hashCode(mSlice);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mInlinePresentationSpec);
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeTypedObject(mSlice, flags);
+ dest.writeTypedObject(mInlinePresentationSpec, flags);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ InlinePresentation(@NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ Slice slice = (Slice) in.readTypedObject(Slice.CREATOR);
+ InlinePresentationSpec inlinePresentationSpec = (InlinePresentationSpec) in.readTypedObject(InlinePresentationSpec.CREATOR);
+
+ this.mSlice = slice;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mSlice);
+ this.mInlinePresentationSpec = inlinePresentationSpec;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mInlinePresentationSpec);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<InlinePresentation> CREATOR
+ = new Parcelable.Creator<InlinePresentation>() {
+ @Override
+ public InlinePresentation[] newArray(int size) {
+ return new InlinePresentation[size];
+ }
+
+ @Override
+ public InlinePresentation createFromParcel(@NonNull Parcel in) {
+ return new InlinePresentation(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1578081082387L,
+ codegenVersion = "1.0.14",
+ sourceFile = "frameworks/base/core/java/android/service/autofill/InlinePresentation.java",
+ inputSignatures = "private final @android.annotation.NonNull android.app.slice.Slice mSlice\nprivate final @android.annotation.NonNull android.view.inline.InlinePresentationSpec mInlinePresentationSpec\nclass InlinePresentation extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genEqualsHashCode=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 38de794..de4a551 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -21,9 +21,9 @@
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.UnsupportedAppUsage;
import android.app.AlarmManager;
import android.app.Service;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.graphics.drawable.ColorDrawable;
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index c04ac59..80d054b 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -22,7 +22,6 @@
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
import android.app.ActivityManager;
import android.app.INotificationManager;
import android.app.Notification;
@@ -33,6 +32,7 @@
import android.app.Person;
import android.app.Service;
import android.companion.CompanionDeviceManager;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index b8378a3..389040c 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -17,11 +17,10 @@
package android.service.notification;
import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.Person;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index dedc3b7..3f9462c 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -20,11 +20,11 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
-import android.annotation.UnsupportedAppUsage;
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index cf56eae..67925bf 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -19,7 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 0de17ca..36e057f 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -18,8 +18,8 @@
import android.annotation.NonNull;
import android.annotation.SdkConstant;
-import android.annotation.UnsupportedAppUsage;
import android.app.Service;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
diff --git a/core/java/android/service/vr/VrListenerService.java b/core/java/android/service/vr/VrListenerService.java
index 3c38495..2758ace 100644
--- a/core/java/android/service/vr/VrListenerService.java
+++ b/core/java/android/service/vr/VrListenerService.java
@@ -18,9 +18,9 @@
import android.annotation.NonNull;
import android.annotation.SdkConstant;
-import android.annotation.UnsupportedAppUsage;
import android.app.ActivityManager;
import android.app.Service;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
diff --git a/core/java/android/service/wallpaper/Android.bp b/core/java/android/service/wallpaper/Android.bp
index aa6123f..ffbdb03 100644
--- a/core/java/android/service/wallpaper/Android.bp
+++ b/core/java/android/service/wallpaper/Android.bp
@@ -6,6 +6,8 @@
"I*.aidl",
],
+ libs: ["unsupportedappusage"],
+
// Enforce that the library is built against java 8 so that there are
// no compatibility issues with launcher
java_version: "1.8",
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index e50d6c2..9a76a1b 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -20,11 +20,11 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
import android.app.Service;
import android.app.WallpaperColors;
import android.app.WallpaperInfo;
import android.app.WallpaperManager;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 172495f..c861fa7 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -21,7 +21,7 @@
import android.annotation.RawRes;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
diff --git a/core/java/android/speech/tts/TtsEngines.java b/core/java/android/speech/tts/TtsEngines.java
index 2fab404..a8aea7c 100644
--- a/core/java/android/speech/tts/TtsEngines.java
+++ b/core/java/android/speech/tts/TtsEngines.java
@@ -18,7 +18,7 @@
import static android.provider.Settings.Secure.getString;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index 7841f99..e08a06a 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -21,7 +21,7 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
@@ -369,6 +369,21 @@
@RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 0x20000000;
+ /**
+ * Listen for Registration Failures.
+ *
+ * Listen for indications that a registration procedure has failed in either the CS or PS
+ * domain. This indication does not necessarily indicate a change of service state, which should
+ * be tracked via {@link #LISTEN_SERVICE_STATE}.
+ *
+ * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
+ * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @see #onRegistrationFailed()
+ */
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+ public static final int LISTEN_REGISTRATION_FAILURE = 0x40000000;
+
/*
* Subscription used to listen to the phone state changes
* @hide
@@ -932,6 +947,38 @@
}
/**
+ * Report that Registration or a Location/Routing/Tracking Area update has failed.
+ *
+ * <p>Indicate whenever a registration procedure, including a location, routing, or tracking
+ * area update fails. This includes procedures that do not necessarily result in a change of
+ * the modem's registration status. If the modem's registration status changes, that is
+ * reflected in the onNetworkStateChanged() and subsequent get{Voice/Data}RegistrationState().
+ *
+ * <p>Because registration failures are ephemeral, this callback is not sticky.
+ * Registrants will not receive the most recent past value when registering.
+ *
+ * @param cellIdentity the CellIdentity, which must include the globally unique identifier
+ * for the cell (for example, all components of the CGI or ECGI).
+ * @param chosenPlmn a 5 or 6 digit alphanumeric PLMN (MCC|MNC) among those broadcast by the
+ * cell that was chosen for the failed registration attempt.
+ * @param domain DOMAIN_CS, DOMAIN_PS or both in case of a combined procedure.
+ * @param causeCode the primary failure cause code of the procedure.
+ * For GSM/UMTS (MM), values are in TS 24.008 Sec 10.5.95
+ * For GSM/UMTS (GMM), values are in TS 24.008 Sec 10.5.147
+ * For LTE (EMM), cause codes are TS 24.301 Sec 9.9.3.9
+ * For NR (5GMM), cause codes are TS 24.501 Sec 9.11.3.2
+ * Integer.MAX_VALUE if this value is unused.
+ * @param additionalCauseCode the cause code of any secondary/combined procedure if appropriate.
+ * For UMTS, if a combined attach succeeds for PS only, then the GMM cause code shall be
+ * included as an additionalCauseCode. For LTE (ESM), cause codes are in
+ * TS 24.301 9.9.4.4. Integer.MAX_VALUE if this value is unused.
+ */
+ public void onRegistrationFailed(@NonNull CellIdentity cellIdentity, @NonNull String chosenPlmn,
+ @NetworkRegistrationInfo.Domain int domain, int causeCode, int additionalCauseCode) {
+ // default implementation empty
+ }
+
+ /**
* The callback methods need to be called on the handler thread where
* this object was created. If the binder did that for us it'd be nice.
*
@@ -1203,6 +1250,18 @@
() -> psl.onImsCallDisconnectCauseChanged(disconnectCause)));
}
+
+ public void onRegistrationFailed(@NonNull CellIdentity cellIdentity,
+ @NonNull String chosenPlmn, @NetworkRegistrationInfo.Domain int domain,
+ int causeCode, int additionalCauseCode) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onRegistrationFailed(
+ cellIdentity, chosenPlmn, domain, causeCode, additionalCauseCode)));
+ // default implementation empty
+ }
}
diff --git a/core/java/android/telephony/Rlog.java b/core/java/android/telephony/Rlog.java
index cdab2dc..2afdd33 100644
--- a/core/java/android/telephony/Rlog.java
+++ b/core/java/android/telephony/Rlog.java
@@ -16,12 +16,11 @@
package android.telephony;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.text.TextUtils;
-import android.util.Log;
-
-import android.annotation.UnsupportedAppUsage;
import android.util.Base64;
+import android.util.Log;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 1b2feda..9387a2c 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -24,6 +24,7 @@
import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.telephony.Annotation.ApnType;
import android.telephony.Annotation.CallState;
import android.telephony.Annotation.DataActivityType;
import android.telephony.Annotation.DataFailureCause;
@@ -352,7 +353,7 @@
* @param subId for which data connection state changed.
* @param slotIndex for which data connections state changed. Can be derived from subId except
* when subId is invalid.
- * @param apnType the APN type that triggered this update
+ * @param apnType the apn type bitmask, defined with {@code ApnSetting#TYPE_*} flags.
* @param preciseState the PreciseDataConnectionState
*
* @see android.telephony.PreciseDataConnection
@@ -360,7 +361,7 @@
* @hide
*/
public void notifyDataConnectionForSubscriber(int slotIndex, int subId,
- String apnType, PreciseDataConnectionState preciseState) {
+ @ApnType int apnType, PreciseDataConnectionState preciseState) {
try {
sRegistry.notifyDataConnectionForSubscriber(
slotIndex, subId, apnType, preciseState);
@@ -553,13 +554,13 @@
* @param subId for which data connection failed.
* @param slotIndex for which data conenction failed. Can be derived from subId except when
* subId is invalid.
- * @param apnType the apnType, "ims" for IMS APN, "emergency" for EMERGENCY APN.
+ * @param apnType the apn type bitmask, defined with {@code ApnSetting#TYPE_*} flags.
* @param apn the APN {@link ApnSetting#getApnName()} of this data connection.
* @param failCause data fail cause.
*
* @hide
*/
- public void notifyPreciseDataConnectionFailed(int subId, int slotIndex, String apnType,
+ public void notifyPreciseDataConnectionFailed(int subId, int slotIndex, @ApnType int apnType,
String apn, @DataFailureCause int failCause) {
try {
sRegistry.notifyPreciseDataConnectionFailed(slotIndex, subId, apnType, apn, failCause);
@@ -614,9 +615,9 @@
* Notify call disconnect causes which contains {@link DisconnectCause} and {@link
* android.telephony.PreciseDisconnectCause}.
*
- * @param subId for which call disconnected.
* @param slotIndex for which call disconnected. Can be derived from subId except when subId is
* invalid.
+ * @param subId for which call disconnected.
* @param cause {@link DisconnectCause} for the disconnected call.
* @param preciseCause {@link android.telephony.PreciseDisconnectCause} for the disconnected
* call.
@@ -675,4 +676,36 @@
}
}
+
+ /**
+ * Report that Registration or a Location/Routing/Tracking Area update has failed.
+ *
+ * @param slotIndex for which call disconnected. Can be derived from subId except when subId is
+ * invalid.
+ * @param subId for which cellinfo changed.
+ * @param cellIdentity the CellIdentity, which must include the globally unique identifier
+ * for the cell (for example, all components of the CGI or ECGI).
+ * @param chosenPlmn a 5 or 6 digit alphanumeric PLMN (MCC|MNC) among those broadcast by the
+ * cell that was chosen for the failed registration attempt.
+ * @param domain DOMAIN_CS, DOMAIN_PS or both in case of a combined procedure.
+ * @param causeCode the primary failure cause code of the procedure.
+ * For GSM/UMTS (MM), values are in TS 24.008 Sec 10.5.95
+ * For GSM/UMTS (GMM), values are in TS 24.008 Sec 10.5.147
+ * For LTE (EMM), cause codes are TS 24.301 Sec 9.9.3.9
+ * For NR (5GMM), cause codes are TS 24.501 Sec 9.11.3.2
+ * Integer.MAX_VALUE if this value is unused.
+ * @param additionalCauseCode the cause code of any secondary/combined procedure if appropriate.
+ * For UMTS, if a combined attach succeeds for PS only, then the GMM cause code shall be
+ * included as an additionalCauseCode. For LTE (ESM), cause codes are in
+ * TS 24.301 9.9.4.4. Integer.MAX_VALUE if this value is unused.
+ */
+ public void notifyRegistrationFailed(int slotIndex, int subId,
+ @NonNull CellIdentity cellIdentity, @NonNull String chosenPlmn,
+ @NetworkRegistrationInfo.Domain int domain, int causeCode, int additionalCauseCode) {
+ try {
+ sRegistry.notifyRegistrationFailed(slotIndex, subId, cellIdentity,
+ chosenPlmn, domain, causeCode, additionalCauseCode);
+ } catch (RemoteException ex) {
+ }
+ }
}
diff --git a/core/java/android/telephony/WapPushManagerConnector.java b/core/java/android/telephony/WapPushManagerConnector.java
new file mode 100644
index 0000000..a9df506
--- /dev/null
+++ b/core/java/android/telephony/WapPushManagerConnector.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import com.android.internal.telephony.IWapPushManager;
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * APIs for platform to connect to the WAP push manager service.
+ *
+ * <p>To start connection, {@link #bindToWapPushManagerService} should be called.
+ *
+ * <p>Upon completion {@link #unbindWapPushManagerService} should be called to unbind the service.
+ *
+ * @hide
+ */
+@SystemApi
+public final class WapPushManagerConnector {
+ private final Context mContext;
+
+ private volatile WapPushManagerConnection mConnection;
+ private volatile IWapPushManager mWapPushManager;
+ private String mWapPushManagerPackage;
+
+ /**
+ * The {@link android.content.Intent} that must be declared as handled by the
+ * WAP push manager service.
+ * @hide
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE =
+ "com.android.internal.telephony.IWapPushManager";
+
+ /** @hide */
+ @IntDef(flag = true, prefix = {"RESULT_"}, value = {
+ RESULT_MESSAGE_HANDLED,
+ RESULT_APP_QUERY_FAILED,
+ RESULT_SIGNATURE_NO_MATCH,
+ RESULT_INVALID_RECEIVER_NAME,
+ RESULT_EXCEPTION_CAUGHT,
+ RESULT_FURTHER_PROCESSING,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ProcessMessageResult{}
+
+ /** {@link #processMessage} return value: Message is handled. */
+ public static final int RESULT_MESSAGE_HANDLED = 0x1;
+ /** {@link #processMessage} return value: Application ID or content type was not found. */
+ public static final int RESULT_APP_QUERY_FAILED = 0x2;
+ /** {@link #processMessage} return value: Receiver application signature check failed. */
+ public static final int RESULT_SIGNATURE_NO_MATCH = 0x4;
+ /** {@link #processMessage} return value: Receiver application was not found. */
+ public static final int RESULT_INVALID_RECEIVER_NAME = 0x8;
+ /** {@link #processMessage} return value: Unknown exception. */
+ public static final int RESULT_EXCEPTION_CAUGHT = 0x10;
+ /** {@link #processMessage} return value: further processing needed. */
+ public static final int RESULT_FURTHER_PROCESSING = 0x8000;
+
+ /** The application package name of the WAP push manager service. */
+ private static final String SERVICE_PACKAGE = "com.android.smspush";
+
+ public WapPushManagerConnector(@NonNull Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Binds to the WAP push manager service. This method should be called exactly once.
+ *
+ * @return {@code true} upon successfully binding to a service, {@code false} otherwise
+ */
+ public boolean bindToWapPushManagerService() {
+ Preconditions.checkState(mConnection == null);
+
+ Intent intent = new Intent(SERVICE_INTERFACE);
+ ComponentName component = intent.resolveSystemService(mContext.getPackageManager(), 0);
+ intent.setComponent(component);
+ mConnection = new WapPushManagerConnection();
+ if (component != null
+ && mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) {
+ mWapPushManagerPackage = component.getPackageName();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns the package name of WAP push manager service application connected to,
+ * or {@code null} if not connected.
+ */
+ @Nullable
+ public String getConnectedWapPushManagerServicePackage() {
+ return mWapPushManagerPackage;
+ }
+
+ /**
+ * Processes WAP push message and triggers the {@code intent}.
+ *
+ * @see RESULT_MESSAGE_HANDLED
+ * @see RESULT_APP_QUERY_FAILED
+ * @see RESULT_SIGNATURE_NO_MATCH
+ * @see RESULT_INVALID_RECEIVER_NAME
+ * @see RESULT_EXCEPTION_CAUGHT
+ * @see RESULT_FURTHER_PROCESSING
+ */
+ @ProcessMessageResult
+ public int processMessage(
+ @NonNull String applicationId, @NonNull String contentType, @NonNull Intent intent) {
+ try {
+ return mWapPushManager.processMessage(applicationId, contentType, intent);
+ } catch (NullPointerException | RemoteException e) {
+ return RESULT_EXCEPTION_CAUGHT;
+ }
+ }
+
+ /**
+ * Unbinds the WAP push manager service. This method should be called exactly once.
+ */
+ public void unbindWapPushManagerService() {
+ Preconditions.checkNotNull(mConnection);
+
+ mContext.unbindService(mConnection);
+ mConnection = null;
+ }
+
+ private class WapPushManagerConnection implements ServiceConnection {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ // Because we have bound to an explicit
+ // service that is running in our own process, we can
+ // cast its IBinder to a concrete class and directly access it.
+ mWapPushManager = IWapPushManager.Stub.asInterface(service);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mWapPushManager = null;
+ }
+
+ @Override
+ public void onNullBinding(ComponentName name) {
+ onServiceDisconnected(name);
+ }
+
+ @Override
+ public void onBindingDied(ComponentName name) {
+ onServiceDisconnected(name);
+ }
+ }
+}
diff --git a/core/java/android/text/AndroidBidi.java b/core/java/android/text/AndroidBidi.java
index bb7fb44..f1e2181 100644
--- a/core/java/android/text/AndroidBidi.java
+++ b/core/java/android/text/AndroidBidi.java
@@ -16,7 +16,7 @@
package android.text;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.icu.lang.UCharacter;
import android.icu.lang.UCharacterDirection;
import android.icu.lang.UProperty;
diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java
index cf6987c..3ee1a90 100644
--- a/core/java/android/text/BoringLayout.java
+++ b/core/java/android/text/BoringLayout.java
@@ -16,7 +16,7 @@
package android.text;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 32982f9..c60d446 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -20,7 +20,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Build;
diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java
index 19c55d5..b5688a4 100644
--- a/core/java/android/text/FontConfig.java
+++ b/core/java/android/text/FontConfig.java
@@ -21,7 +21,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.fonts.FontVariationAxis;
import android.net.Uri;
diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java
index 18f8db2..55af087 100644
--- a/core/java/android/text/Html.java
+++ b/core/java/android/text/Html.java
@@ -16,9 +16,9 @@
package android.text;
-import android.annotation.UnsupportedAppUsage;
import android.app.ActivityThread;
import android.app.Application;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Typeface;
diff --git a/core/java/android/text/InputFilter.java b/core/java/android/text/InputFilter.java
index a9a7b2f..96e7bd0 100644
--- a/core/java/android/text/InputFilter.java
+++ b/core/java/android/text/InputFilter.java
@@ -17,7 +17,7 @@
package android.text;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import com.android.internal.util.Preconditions;
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 2c2c295..8a4497a 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -18,7 +18,7 @@
import android.annotation.IntDef;
import android.annotation.IntRange;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
diff --git a/core/java/android/text/Selection.java b/core/java/android/text/Selection.java
index 68199a4..57c1d23 100644
--- a/core/java/android/text/Selection.java
+++ b/core/java/android/text/Selection.java
@@ -17,7 +17,7 @@
package android.text;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import java.text.BreakIterator;
diff --git a/core/java/android/text/SpanSet.java b/core/java/android/text/SpanSet.java
index 362825a..81bdd65 100644
--- a/core/java/android/text/SpanSet.java
+++ b/core/java/android/text/SpanSet.java
@@ -16,7 +16,8 @@
package android.text;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
+
import java.lang.reflect.Array;
import java.util.Arrays;
diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java
index 0d29da0..ac27e3d 100644
--- a/core/java/android/text/SpannableStringBuilder.java
+++ b/core/java/android/text/SpannableStringBuilder.java
@@ -17,7 +17,7 @@
package android.text;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.BaseCanvas;
import android.graphics.Paint;
import android.util.Log;
diff --git a/core/java/android/text/SpannableStringInternal.java b/core/java/android/text/SpannableStringInternal.java
index a986633..b85ef76 100644
--- a/core/java/android/text/SpannableStringInternal.java
+++ b/core/java/android/text/SpannableStringInternal.java
@@ -16,7 +16,7 @@
package android.text;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 69cfc03..85e2d98 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -20,7 +20,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Paint;
import android.graphics.text.LineBreaker;
import android.os.Build;
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 1c50d73..3c51fa7 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -19,7 +19,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.FontMetricsInt;
diff --git a/core/java/android/text/TextPaint.java b/core/java/android/text/TextPaint.java
index d5aad33..73825b1 100644
--- a/core/java/android/text/TextPaint.java
+++ b/core/java/android/text/TextPaint.java
@@ -18,7 +18,7 @@
import android.annotation.ColorInt;
import android.annotation.Px;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Paint;
/**
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 5bda867..df4ead1 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -24,7 +24,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.PluralsRes;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources;
import android.icu.lang.UCharacter;
diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java
index 3c8de94..0863a81 100755
--- a/core/java/android/text/format/DateFormat.java
+++ b/core/java/android/text/format/DateFormat.java
@@ -17,9 +17,8 @@
package android.text.format;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
-import android.os.UserHandle;
import android.provider.Settings;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index 0c27923..ce676e0 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -16,7 +16,7 @@
package android.text.format;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java
index d7baa10..17d3ae4 100644
--- a/core/java/android/text/format/Formatter.java
+++ b/core/java/android/text/format/Formatter.java
@@ -18,7 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources;
import android.icu.text.MeasureFormat;
diff --git a/core/java/android/text/method/AllCapsTransformationMethod.java b/core/java/android/text/method/AllCapsTransformationMethod.java
index 5a7c98d..305b056 100644
--- a/core/java/android/text/method/AllCapsTransformationMethod.java
+++ b/core/java/android/text/method/AllCapsTransformationMethod.java
@@ -17,7 +17,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.graphics.Rect;
import android.text.Spanned;
diff --git a/core/java/android/text/method/HideReturnsTransformationMethod.java b/core/java/android/text/method/HideReturnsTransformationMethod.java
index 440a4b1..40ce871 100644
--- a/core/java/android/text/method/HideReturnsTransformationMethod.java
+++ b/core/java/android/text/method/HideReturnsTransformationMethod.java
@@ -16,7 +16,7 @@
package android.text.method;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
/**
diff --git a/core/java/android/text/method/LinkMovementMethod.java b/core/java/android/text/method/LinkMovementMethod.java
index a0c44a8..c544c41 100644
--- a/core/java/android/text/method/LinkMovementMethod.java
+++ b/core/java/android/text/method/LinkMovementMethod.java
@@ -16,7 +16,7 @@
package android.text.method;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.text.Layout;
import android.text.NoCopySpan;
diff --git a/core/java/android/text/method/MetaKeyKeyListener.java b/core/java/android/text/method/MetaKeyKeyListener.java
index ec7ed34b..d1d7c96 100644
--- a/core/java/android/text/method/MetaKeyKeyListener.java
+++ b/core/java/android/text/method/MetaKeyKeyListener.java
@@ -16,7 +16,7 @@
package android.text.method;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.text.Editable;
import android.text.NoCopySpan;
import android.text.Spannable;
diff --git a/core/java/android/text/method/PasswordTransformationMethod.java b/core/java/android/text/method/PasswordTransformationMethod.java
index c96fc5d..53553be 100644
--- a/core/java/android/text/method/PasswordTransformationMethod.java
+++ b/core/java/android/text/method/PasswordTransformationMethod.java
@@ -16,7 +16,7 @@
package android.text.method;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Rect;
import android.os.Build;
import android.os.Handler;
diff --git a/core/java/android/text/method/TransformationMethod2.java b/core/java/android/text/method/TransformationMethod2.java
index 0bf401a..8d5ec24 100644
--- a/core/java/android/text/method/TransformationMethod2.java
+++ b/core/java/android/text/method/TransformationMethod2.java
@@ -15,7 +15,7 @@
*/
package android.text.method;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
/**
* TransformationMethod2 extends the TransformationMethod interface
diff --git a/core/java/android/text/method/WordIterator.java b/core/java/android/text/method/WordIterator.java
index 313567a..d766186 100644
--- a/core/java/android/text/method/WordIterator.java
+++ b/core/java/android/text/method/WordIterator.java
@@ -17,7 +17,7 @@
package android.text.method;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.icu.lang.UCharacter;
import android.icu.lang.UProperty;
import android.icu.text.BreakIterator;
diff --git a/core/java/android/text/style/BulletSpan.java b/core/java/android/text/style/BulletSpan.java
index 9b1dfbf..ad61788 100644
--- a/core/java/android/text/style/BulletSpan.java
+++ b/core/java/android/text/style/BulletSpan.java
@@ -21,7 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Px;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.os.Build;
diff --git a/core/java/android/text/style/DynamicDrawableSpan.java b/core/java/android/text/style/DynamicDrawableSpan.java
index 1a508a1..f37e423 100644
--- a/core/java/android/text/style/DynamicDrawableSpan.java
+++ b/core/java/android/text/style/DynamicDrawableSpan.java
@@ -22,7 +22,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
diff --git a/core/java/android/text/style/EasyEditSpan.java b/core/java/android/text/style/EasyEditSpan.java
index bfb2873..b23c2b7 100644
--- a/core/java/android/text/style/EasyEditSpan.java
+++ b/core/java/android/text/style/EasyEditSpan.java
@@ -17,8 +17,8 @@
package android.text.style;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
import android.app.PendingIntent;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.text.ParcelableSpan;
import android.text.TextUtils;
diff --git a/core/java/android/text/style/ImageSpan.java b/core/java/android/text/style/ImageSpan.java
index 98f58be..ec55aeb 100644
--- a/core/java/android/text/style/ImageSpan.java
+++ b/core/java/android/text/style/ImageSpan.java
@@ -19,7 +19,7 @@
import android.annotation.DrawableRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
diff --git a/core/java/android/text/style/SpellCheckSpan.java b/core/java/android/text/style/SpellCheckSpan.java
index 6ffde38..e8ec3c6 100644
--- a/core/java/android/text/style/SpellCheckSpan.java
+++ b/core/java/android/text/style/SpellCheckSpan.java
@@ -16,7 +16,7 @@
package android.text.style;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.text.ParcelableSpan;
import android.text.TextUtils;
diff --git a/core/java/android/text/style/SuggestionRangeSpan.java b/core/java/android/text/style/SuggestionRangeSpan.java
index d958dde..2b04a7a 100644
--- a/core/java/android/text/style/SuggestionRangeSpan.java
+++ b/core/java/android/text/style/SuggestionRangeSpan.java
@@ -16,7 +16,7 @@
package android.text.style;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.text.ParcelableSpan;
import android.text.TextPaint;
diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java
index c000ae3..be01bfb 100644
--- a/core/java/android/text/style/SuggestionSpan.java
+++ b/core/java/android/text/style/SuggestionSpan.java
@@ -19,7 +19,7 @@
import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java
index 993bbe8..2aca36a 100644
--- a/core/java/android/text/util/Linkify.java
+++ b/core/java/android/text/util/Linkify.java
@@ -19,7 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.telephony.PhoneNumberUtils;
import android.telephony.TelephonyManager;
diff --git a/core/java/android/transition/ChangeBounds.java b/core/java/android/transition/ChangeBounds.java
index de182da..59a05ac 100644
--- a/core/java/android/transition/ChangeBounds.java
+++ b/core/java/android/transition/ChangeBounds.java
@@ -22,7 +22,7 @@
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.RectEvaluator;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
diff --git a/core/java/android/transition/Scene.java b/core/java/android/transition/Scene.java
index 8d4db54..52a97e3 100644
--- a/core/java/android/transition/Scene.java
+++ b/core/java/android/transition/Scene.java
@@ -18,7 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Build;
import android.util.SparseArray;
diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java
index 0feab4d..e511584 100644
--- a/core/java/android/transition/Transition.java
+++ b/core/java/android/transition/Transition.java
@@ -20,7 +20,7 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.TimeInterpolator;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Path;
diff --git a/core/java/android/transition/TransitionManager.java b/core/java/android/transition/TransitionManager.java
index 0e5252e..1b0612e 100644
--- a/core/java/android/transition/TransitionManager.java
+++ b/core/java/android/transition/TransitionManager.java
@@ -17,7 +17,7 @@
package android.transition;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.util.ArrayMap;
import android.util.Log;
diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java
index 3f44e48..4cf0a36 100644
--- a/core/java/android/util/ArrayMap.java
+++ b/core/java/android/util/ArrayMap.java
@@ -16,7 +16,7 @@
package android.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import com.android.internal.util.ArrayUtils;
diff --git a/core/java/android/util/ArraySet.java b/core/java/android/util/ArraySet.java
index 4dda709..7f652ba 100644
--- a/core/java/android/util/ArraySet.java
+++ b/core/java/android/util/ArraySet.java
@@ -18,7 +18,7 @@
import android.annotation.Nullable;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import libcore.util.EmptyArray;
diff --git a/core/java/android/util/Base64.java b/core/java/android/util/Base64.java
index ecc0c9c..92abd7c 100644
--- a/core/java/android/util/Base64.java
+++ b/core/java/android/util/Base64.java
@@ -16,7 +16,8 @@
package android.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
+
import java.io.UnsupportedEncodingException;
/**
diff --git a/core/java/android/util/Base64OutputStream.java b/core/java/android/util/Base64OutputStream.java
index 230a3a5..48fadeb 100644
--- a/core/java/android/util/Base64OutputStream.java
+++ b/core/java/android/util/Base64OutputStream.java
@@ -16,7 +16,8 @@
package android.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
+
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
diff --git a/core/java/android/util/DebugUtils.java b/core/java/android/util/DebugUtils.java
index bc5edf8..6d5e830 100644
--- a/core/java/android/util/DebugUtils.java
+++ b/core/java/android/util/DebugUtils.java
@@ -16,7 +16,7 @@
package android.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import java.io.PrintWriter;
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index 451a669..9f6065e 100755
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -16,7 +16,7 @@
package android.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.SystemProperties;
diff --git a/core/java/android/util/EventLog.java b/core/java/android/util/EventLog.java
index 65d825a..c9dc05b 100644
--- a/core/java/android/util/EventLog.java
+++ b/core/java/android/util/EventLog.java
@@ -17,7 +17,7 @@
package android.util;
import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import java.io.BufferedReader;
import java.io.FileReader;
diff --git a/core/java/android/util/IconDrawableFactory.java b/core/java/android/util/IconDrawableFactory.java
index d86ebf3..721e6b3 100644
--- a/core/java/android/util/IconDrawableFactory.java
+++ b/core/java/android/util/IconDrawableFactory.java
@@ -15,8 +15,8 @@
*/
package android.util;
-import android.annotation.UnsupportedAppUsage;
import android.annotation.UserIdInt;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageItemInfo;
diff --git a/core/java/android/util/LocalLog.java b/core/java/android/util/LocalLog.java
index 6a6bccf..fda5e0d 100644
--- a/core/java/android/util/LocalLog.java
+++ b/core/java/android/util/LocalLog.java
@@ -16,7 +16,7 @@
package android.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.SystemClock;
import java.io.FileDescriptor;
diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java
index 50779031..f324113 100644
--- a/core/java/android/util/Log.java
+++ b/core/java/android/util/Log.java
@@ -19,7 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.DeadSystemException;
import com.android.internal.os.RuntimeInit;
@@ -387,6 +387,26 @@
public static native int println_native(int bufID, int priority, String tag, String msg);
/**
+ * Send a log message to the "radio" log buffer, which can be dumped with
+ * {@code adb logcat -b radio}.
+ *
+ * <p>Only the telephony mainline module should use it.
+ *
+ * <p>Note ART will protect {@code MODULE_LIBRARIES} system APIs from regular app code.
+ *
+ * @param priority Log priority.
+ * @param tag Used to identify the source of a log message. It usually identifies
+ * the class or activity where the log call occurs.
+ * @param message The message you would like logged.
+ * @hide
+ */
+ // @SystemApi(client= SystemApi.Client.MODULE_LIBRARIES) // TODO Uncomment once http://ag/9956147 is in.
+ public static int logToRadioBuffer(@Level int priority, @Nullable String tag,
+ @Nullable String message) {
+ return println_native(LOG_ID_RADIO, priority, tag, message);
+ }
+
+ /**
* Return the maximum payload the log daemon accepts without truncation.
* @return LOGGER_ENTRY_MAX_PAYLOAD.
*/
diff --git a/core/java/android/util/LogWriter.java b/core/java/android/util/LogWriter.java
index b062ace..a674ae1 100644
--- a/core/java/android/util/LogWriter.java
+++ b/core/java/android/util/LogWriter.java
@@ -16,7 +16,8 @@
package android.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
+
import java.io.Writer;
/** @hide */
diff --git a/core/java/android/util/LongArray.java b/core/java/android/util/LongArray.java
index 6f4aa52..93bcd6b 100644
--- a/core/java/android/util/LongArray.java
+++ b/core/java/android/util/LongArray.java
@@ -17,7 +17,7 @@
package android.util;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
diff --git a/core/java/android/util/LongSparseLongArray.java b/core/java/android/util/LongSparseLongArray.java
index a0edd04..c05dd9d 100644
--- a/core/java/android/util/LongSparseLongArray.java
+++ b/core/java/android/util/LongSparseLongArray.java
@@ -16,7 +16,7 @@
package android.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import com.android.internal.util.ArrayUtils;
diff --git a/core/java/android/util/LruCache.java b/core/java/android/util/LruCache.java
index 3cbf727d..3f7fdd8 100644
--- a/core/java/android/util/LruCache.java
+++ b/core/java/android/util/LruCache.java
@@ -16,7 +16,7 @@
package android.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import java.util.LinkedHashMap;
import java.util.Map;
diff --git a/core/java/android/util/MathUtils.java b/core/java/android/util/MathUtils.java
index 0eeef70..971e161 100644
--- a/core/java/android/util/MathUtils.java
+++ b/core/java/android/util/MathUtils.java
@@ -16,7 +16,7 @@
package android.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Rect;
/**
diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java
index da566c9..fa994ba 100644
--- a/core/java/android/util/NtpTrustedTime.java
+++ b/core/java/android/util/NtpTrustedTime.java
@@ -16,7 +16,7 @@
package android.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
@@ -25,6 +25,7 @@
import android.net.NetworkInfo;
import android.net.SntpClient;
import android.os.SystemClock;
+import android.os.TimestampedValue;
import android.provider.Settings;
import android.text.TextUtils;
@@ -175,4 +176,21 @@
public long getCachedNtpTimeReference() {
return mCachedNtpElapsedRealtime;
}
+
+ /**
+ * Returns the combination of {@link #getCachedNtpTime()} and {@link
+ * #getCachedNtpTimeReference()} as a {@link TimestampedValue}. This method is useful when
+ * passing the time to another component that will adjust for elapsed time.
+ *
+ * @throws IllegalStateException if there is no cached value
+ */
+ public TimestampedValue<Long> getCachedNtpTimeSignal() {
+ if (!mHasCache) {
+ throw new IllegalStateException("Missing authoritative time source");
+ }
+ if (LOGD) Log.d(TAG, "getCachedNtpTimeSignal() cache hit");
+
+ return new TimestampedValue<>(mCachedNtpElapsedRealtime, mCachedNtpTime);
+ }
+
}
diff --git a/core/java/android/util/PathParser.java b/core/java/android/util/PathParser.java
index 5342d5d..1e5ec0b 100644
--- a/core/java/android/util/PathParser.java
+++ b/core/java/android/util/PathParser.java
@@ -14,7 +14,7 @@
package android.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Path;
import dalvik.annotation.optimization.FastNative;
diff --git a/core/java/android/util/Pools.java b/core/java/android/util/Pools.java
index e242fe5..7ae32441 100644
--- a/core/java/android/util/Pools.java
+++ b/core/java/android/util/Pools.java
@@ -16,7 +16,7 @@
package android.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
/**
* Helper class for crating pools of objects. An example use looks like this:
diff --git a/core/java/android/util/Rational.java b/core/java/android/util/Rational.java
index 39e8b14..5930000 100644
--- a/core/java/android/util/Rational.java
+++ b/core/java/android/util/Rational.java
@@ -15,9 +15,10 @@
*/
package android.util;
-import static com.android.internal.util.Preconditions.*;
+import static com.android.internal.util.Preconditions.checkNotNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
+
import java.io.IOException;
import java.io.InvalidObjectException;
diff --git a/core/java/android/util/RecurrenceRule.java b/core/java/android/util/RecurrenceRule.java
index 9c60228..a570e5e 100644
--- a/core/java/android/util/RecurrenceRule.java
+++ b/core/java/android/util/RecurrenceRule.java
@@ -16,7 +16,7 @@
package android.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/android/util/Singleton.java b/core/java/android/util/Singleton.java
index 15c6b5b..92646b4 100644
--- a/core/java/android/util/Singleton.java
+++ b/core/java/android/util/Singleton.java
@@ -16,7 +16,7 @@
package android.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
/**
* Singleton helper class for lazily initialization.
diff --git a/core/java/android/util/Slog.java b/core/java/android/util/Slog.java
index a85120f..2c8bbbf 100644
--- a/core/java/android/util/Slog.java
+++ b/core/java/android/util/Slog.java
@@ -16,7 +16,7 @@
package android.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
/**
diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java
index d3367c1..dae760f 100644
--- a/core/java/android/util/SparseArray.java
+++ b/core/java/android/util/SparseArray.java
@@ -16,7 +16,7 @@
package android.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
diff --git a/core/java/android/util/SparseBooleanArray.java b/core/java/android/util/SparseBooleanArray.java
index d6e0e53..846df39 100644
--- a/core/java/android/util/SparseBooleanArray.java
+++ b/core/java/android/util/SparseBooleanArray.java
@@ -16,7 +16,7 @@
package android.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
diff --git a/core/java/android/util/SparseIntArray.java b/core/java/android/util/SparseIntArray.java
index 1ca1717..d4f6685 100644
--- a/core/java/android/util/SparseIntArray.java
+++ b/core/java/android/util/SparseIntArray.java
@@ -16,7 +16,7 @@
package android.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 74cff20..8439f5a 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -19,7 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.SystemClock;
diff --git a/core/java/android/util/TrustedTime.java b/core/java/android/util/TrustedTime.java
index c78665d..1360f87 100644
--- a/core/java/android/util/TrustedTime.java
+++ b/core/java/android/util/TrustedTime.java
@@ -16,7 +16,7 @@
package android.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
/**
* Interface that provides trusted time information, possibly coming from an NTP
diff --git a/core/java/android/util/XmlPullAttributes.java b/core/java/android/util/XmlPullAttributes.java
index 32fe16f..d83b355 100644
--- a/core/java/android/util/XmlPullAttributes.java
+++ b/core/java/android/util/XmlPullAttributes.java
@@ -16,13 +16,12 @@
package android.util;
-import org.xmlpull.v1.XmlPullParser;
-
-import android.annotation.UnsupportedAppUsage;
-import android.util.AttributeSet;
+import android.compat.annotation.UnsupportedAppUsage;
import com.android.internal.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
+
/**
* Provides an implementation of AttributeSet on top of an XmlPullParser.
*/
diff --git a/core/java/android/view/AccessibilityIterators.java b/core/java/android/view/AccessibilityIterators.java
index 5f9bf39..bee04f4 100644
--- a/core/java/android/view/AccessibilityIterators.java
+++ b/core/java/android/view/AccessibilityIterators.java
@@ -16,7 +16,7 @@
package android.view;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.Configuration;
import java.text.BreakIterator;
diff --git a/core/java/android/view/ActionMode.java b/core/java/android/view/ActionMode.java
index 6b200e1..d9f9d03 100644
--- a/core/java/android/view/ActionMode.java
+++ b/core/java/android/view/ActionMode.java
@@ -19,10 +19,9 @@
import android.annotation.StringRes;
import android.annotation.TestApi;
+import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Rect;
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
/**
* Represents a contextual mode of the user interface. Action modes can be used to provide
* alternative interaction modes and replace parts of the normal UI until finished.
diff --git a/core/java/android/view/ActionProvider.java b/core/java/android/view/ActionProvider.java
index cd7e67e..e1be0fe 100644
--- a/core/java/android/view/ActionProvider.java
+++ b/core/java/android/view/ActionProvider.java
@@ -16,7 +16,7 @@
package android.view;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.util.Log;
diff --git a/core/java/android/view/AppTransitionAnimationSpec.java b/core/java/android/view/AppTransitionAnimationSpec.java
index 4c0ed52..330061f 100644
--- a/core/java/android/view/AppTransitionAnimationSpec.java
+++ b/core/java/android/view/AppTransitionAnimationSpec.java
@@ -1,6 +1,6 @@
package android.view;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.GraphicBuffer;
import android.graphics.Rect;
import android.os.Parcel;
diff --git a/core/java/android/view/BatchedInputEventReceiver.java b/core/java/android/view/BatchedInputEventReceiver.java
index 61ccac9..95b2c70 100644
--- a/core/java/android/view/BatchedInputEventReceiver.java
+++ b/core/java/android/view/BatchedInputEventReceiver.java
@@ -16,7 +16,7 @@
package android.view;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Looper;
/**
diff --git a/core/java/android/view/IDisplayWindowInsetsController.aidl b/core/java/android/view/IDisplayWindowInsetsController.aidl
new file mode 100644
index 0000000..429c3ae
--- /dev/null
+++ b/core/java/android/view/IDisplayWindowInsetsController.aidl
@@ -0,0 +1,48 @@
+/**
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
+
+/**
+ * Singular controller of insets to use when there isn't another obvious controller available.
+ * Specifically, this will take over insets control in multi-window.
+ * @hide
+ */
+oneway interface IDisplayWindowInsetsController {
+
+ /**
+ * @see IWindow#insetsChanged
+ */
+ void insetsChanged(in InsetsState insetsState);
+
+ /**
+ * @see IWindow#insetsControlChanged
+ */
+ void insetsControlChanged(in InsetsState insetsState, in InsetsSourceControl[] activeControls);
+
+ /**
+ * @see IWindow#showInsets
+ */
+ void showInsets(int types, boolean fromIme);
+
+ /**
+ * @see IWindow#hideInsets
+ */
+ void hideInsets(int types, boolean fromIme);
+}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 9496827..993bdc4 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -35,6 +35,7 @@
import android.view.IApplicationToken;
import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.IDockedStackListener;
+import android.view.IDisplayWindowInsetsController;
import android.view.IDisplayWindowListener;
import android.view.IDisplayFoldListener;
import android.view.IDisplayWindowRotationController;
@@ -49,6 +50,7 @@
import android.view.IWindowSessionCallback;
import android.view.KeyEvent;
import android.view.InputEvent;
+import android.view.InsetsState;
import android.view.MagnificationSpec;
import android.view.MotionEvent;
import android.view.InputChannel;
@@ -711,4 +713,16 @@
* @return true if the display was successfully mirrored.
*/
boolean mirrorDisplay(int displayId, out SurfaceControl outSurfaceControl);
+
+ /**
+ * When in multi-window mode, the provided displayWindowInsetsController will control insets
+ * animations.
+ */
+ void setDisplayWindowInsetsController(
+ int displayId, in IDisplayWindowInsetsController displayWindowInsetsController);
+
+ /**
+ * Called when a remote process modifies insets on a display window container.
+ */
+ void modifyDisplayWindowInsets(int displayId, in InsetsState state);
}
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index 1a33ea9..324d562 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -41,6 +41,7 @@
public InsetsSource(@InternalInsetsType int type) {
mType = type;
mFrame = new Rect();
+ mVisible = InsetsState.getDefaultVisibility(type);
}
public InsetsSource(InsetsSource other) {
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index e3fed3a..ae1e579 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -19,10 +19,15 @@
import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_IME;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
+import static android.view.WindowInsets.Type.IME;
import static android.view.WindowInsets.Type.MANDATORY_SYSTEM_GESTURES;
import static android.view.WindowInsets.Type.SIZE;
import static android.view.WindowInsets.Type.SYSTEM_GESTURES;
+import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.indexOf;
+import static android.view.WindowInsets.Type.systemBars;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
import android.annotation.IntDef;
import android.annotation.Nullable;
@@ -156,11 +161,10 @@
&& source.getType() != ITYPE_IME;
boolean skipSystemBars = ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_FULL
&& (type == ITYPE_STATUS_BAR || type == ITYPE_NAVIGATION_BAR);
- boolean skipIme = source.getType() == ITYPE_IME
- && (legacySoftInputMode & LayoutParams.SOFT_INPUT_ADJUST_RESIZE) == 0;
boolean skipLegacyTypes = ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_NONE
- && (toPublicType(type) & Type.compatSystemInsets()) != 0;
- if (skipSystemBars || skipIme || skipLegacyTypes || skipNonImeInImeMode) {
+ && (type == ITYPE_STATUS_BAR || type == ITYPE_NAVIGATION_BAR
+ || type == ITYPE_IME);
+ if (skipSystemBars || skipLegacyTypes || skipNonImeInImeMode) {
typeVisibilityMap[indexOf(toPublicType(type))] = source.isVisible();
continue;
}
@@ -175,8 +179,11 @@
typeMaxInsetsMap, null /* typeSideMap */, null /* typeVisibilityMap */);
}
}
+ final int softInputAdjustMode = legacySoftInputMode & SOFT_INPUT_MASK_ADJUST;
return new WindowInsets(typeInsetsMap, typeMaxInsetsMap, typeVisibilityMap, isScreenRound,
- alwaysConsumeSystemBars, cutout);
+ alwaysConsumeSystemBars, cutout, softInputAdjustMode == SOFT_INPUT_ADJUST_RESIZE
+ ? systemBars() | ime()
+ : systemBars());
}
private void processSource(InsetsSource source, Rect relativeFrame, boolean ignoreVisibility,
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index c3c7b95..0068476 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -1541,6 +1541,8 @@
@FastNative
private static native float nativeGetAxisValue(long nativePtr,
int axis, int pointerIndex, int historyPos);
+ @FastNative
+ private static native void nativeTransform(long nativePtr, Matrix matrix);
// -------------- @CriticalNative ----------------------
@@ -1614,8 +1616,6 @@
@CriticalNative
private static native void nativeScale(long nativePtr, float scale);
- @CriticalNative
- private static native void nativeTransform(long nativePtr, long matrix);
private MotionEvent() {
}
@@ -3210,7 +3210,7 @@
throw new IllegalArgumentException("matrix must not be null");
}
- nativeTransform(mNativePtr, matrix.native_instance);
+ nativeTransform(mNativePtr, matrix);
}
/**
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 52ea2b2..1782544 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -379,7 +379,7 @@
* This gets called on a RenderThread worker thread, so members accessed here must
* be protected by a lock.
*/
- final boolean useBLAST = ViewRootImpl.USE_BLAST_BUFFERQUEUE;
+ final boolean useBLAST = WindowManagerGlobal.USE_BLAST_ADAPTER;
viewRoot.registerRtFrameCallback(frame -> {
try {
final SurfaceControl.Transaction t = useBLAST ?
@@ -1107,7 +1107,7 @@
private void applySurfaceTransforms(SurfaceControl surface, SurfaceControl.Transaction t,
Rect position, long frameNumber) {
- if (frameNumber > 0 && ViewRootImpl.USE_BLAST_BUFFERQUEUE == false) {
+ if (frameNumber > 0 && !WindowManagerGlobal.USE_BLAST_ADAPTER) {
final ViewRootImpl viewRoot = getViewRootImpl();
t.deferTransactionUntilSurface(surface, viewRoot.mSurface,
@@ -1125,7 +1125,7 @@
}
private void setParentSpaceRectangle(Rect position, long frameNumber) {
- final boolean useBLAST = ViewRootImpl.USE_BLAST_BUFFERQUEUE;
+ final boolean useBLAST = WindowManagerGlobal.USE_BLAST_ADAPTER;
final ViewRootImpl viewRoot = getViewRootImpl();
final SurfaceControl.Transaction t = useBLAST ? viewRoot.getBLASTSyncTransaction() :
mRtTransaction;
@@ -1186,7 +1186,7 @@
@Override
public void positionLost(long frameNumber) {
- boolean useBLAST = ViewRootImpl.USE_BLAST_BUFFERQUEUE;
+ boolean useBLAST = WindowManagerGlobal.USE_BLAST_ADAPTER;
if (DEBUG) {
Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d",
System.identityHashCode(this), frameNumber));
@@ -1524,7 +1524,7 @@
@Override
public void invalidate(boolean invalidateCache) {
super.invalidate(invalidateCache);
- if (ViewRootImpl.USE_BLAST_BUFFERQUEUE == false) {
+ if (!WindowManagerGlobal.USE_BLAST_ADAPTER) {
return;
}
final ViewRootImpl viewRoot = getViewRootImpl();
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 522ff9a3..bf8dc65 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -192,11 +192,6 @@
private static final boolean MT_RENDERER_AVAILABLE = true;
/**
- * @hide
- */
- public static final boolean USE_BLAST_BUFFERQUEUE = false;
-
- /**
* If set to 2, the view system will switch from using rectangles retrieved from window to
* dispatch to the view hierarchy to using {@link InsetsController}, that derives the insets
* directly from the full configuration, enabling richer information about the insets state, as
@@ -1312,7 +1307,7 @@
}
mWindowAttributes.privateFlags |= compatibleWindowFlag;
- if (USE_BLAST_BUFFERQUEUE) {
+ if (WindowManagerGlobal.USE_BLAST_ADAPTER) {
mWindowAttributes.privateFlags =
WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST;
}
@@ -7273,7 +7268,7 @@
mPendingStableInsets, mPendingBackDropFrame, mPendingDisplayCutout,
mPendingMergedConfiguration, mSurfaceControl, mTempInsets);
if (mSurfaceControl.isValid()) {
- if (USE_BLAST_BUFFERQUEUE == false) {
+ if (!WindowManagerGlobal.USE_BLAST_ADAPTER) {
mSurface.copyFrom(mSurfaceControl);
} else {
mSurface.transferFrom(getOrCreateBLASTSurface(
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index a9cc50f..9df131d 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -27,8 +27,8 @@
import static android.view.WindowInsets.Type.SYSTEM_GESTURES;
import static android.view.WindowInsets.Type.TAPPABLE_ELEMENT;
import static android.view.WindowInsets.Type.all;
-import static android.view.WindowInsets.Type.compatSystemInsets;
import static android.view.WindowInsets.Type.indexOf;
+import static android.view.WindowInsets.Type.systemBars;
import android.annotation.IntDef;
import android.annotation.IntRange;
@@ -87,6 +87,8 @@
private final boolean mStableInsetsConsumed;
private final boolean mDisplayCutoutConsumed;
+ private final int mCompatInsetTypes;
+
/**
* Since new insets may be added in the future that existing apps couldn't
* know about, this fully empty constant shouldn't be made available to apps
@@ -112,7 +114,7 @@
boolean isRound, boolean alwaysConsumeSystemBars, DisplayCutout displayCutout) {
this(createCompatTypeMap(systemWindowInsetsRect), createCompatTypeMap(stableInsetsRect),
createCompatVisibilityMap(createCompatTypeMap(systemWindowInsetsRect)),
- isRound, alwaysConsumeSystemBars, displayCutout);
+ isRound, alwaysConsumeSystemBars, displayCutout, systemBars());
}
/**
@@ -131,7 +133,7 @@
@Nullable Insets[] typeMaxInsetsMap,
boolean[] typeVisibilityMap,
boolean isRound,
- boolean alwaysConsumeSystemBars, DisplayCutout displayCutout) {
+ boolean alwaysConsumeSystemBars, DisplayCutout displayCutout, int compatInsetTypes) {
mSystemWindowInsetsConsumed = typeInsetsMap == null;
mTypeInsetsMap = mSystemWindowInsetsConsumed
? new Insets[SIZE]
@@ -145,6 +147,7 @@
mTypeVisibilityMap = typeVisibilityMap;
mIsRound = isRound;
mAlwaysConsumeSystemBars = alwaysConsumeSystemBars;
+ mCompatInsetTypes = compatInsetTypes;
mDisplayCutoutConsumed = displayCutout == null;
mDisplayCutout = (mDisplayCutoutConsumed || displayCutout.isEmpty())
@@ -160,7 +163,8 @@
this(src.mSystemWindowInsetsConsumed ? null : src.mTypeInsetsMap,
src.mStableInsetsConsumed ? null : src.mTypeMaxInsetsMap,
src.mTypeVisibilityMap, src.mIsRound,
- src.mAlwaysConsumeSystemBars, displayCutoutCopyConstructorArgument(src));
+ src.mAlwaysConsumeSystemBars, displayCutoutCopyConstructorArgument(src),
+ src.mCompatInsetTypes);
}
private static DisplayCutout displayCutoutCopyConstructorArgument(WindowInsets w) {
@@ -211,7 +215,8 @@
/** @hide */
@UnsupportedAppUsage
public WindowInsets(Rect systemWindowInsets) {
- this(createCompatTypeMap(systemWindowInsets), null, new boolean[SIZE], false, false, null);
+ this(createCompatTypeMap(systemWindowInsets), null, new boolean[SIZE], false, false, null,
+ systemBars());
}
/**
@@ -280,7 +285,7 @@
*/
@NonNull
public Insets getSystemWindowInsets() {
- return getInsets(mTypeInsetsMap, compatSystemInsets());
+ return getInsets(mTypeInsetsMap, mCompatInsetTypes);
}
/**
@@ -439,7 +444,8 @@
mStableInsetsConsumed ? null : mTypeMaxInsetsMap,
mTypeVisibilityMap,
mIsRound, mAlwaysConsumeSystemBars,
- null /* displayCutout */);
+ null /* displayCutout */,
+ mCompatInsetTypes);
}
@@ -485,7 +491,8 @@
return new WindowInsets(null, mStableInsetsConsumed ? null : mTypeMaxInsetsMap,
mTypeVisibilityMap,
mIsRound, mAlwaysConsumeSystemBars,
- displayCutoutCopyConstructorArgument(this));
+ displayCutoutCopyConstructorArgument(this),
+ mCompatInsetTypes);
}
// TODO(b/119190588): replace @code with @link below
@@ -555,7 +562,7 @@
*/
@NonNull
public Insets getStableInsets() {
- return getInsets(mTypeMaxInsetsMap, compatSystemInsets());
+ return getInsets(mTypeMaxInsetsMap, mCompatInsetTypes);
}
/**
@@ -733,7 +740,8 @@
public WindowInsets consumeStableInsets() {
return new WindowInsets(mSystemWindowInsetsConsumed ? null : mTypeInsetsMap, null,
mTypeVisibilityMap, mIsRound, mAlwaysConsumeSystemBars,
- displayCutoutCopyConstructorArgument(this));
+ displayCutoutCopyConstructorArgument(this),
+ mCompatInsetTypes);
}
/**
@@ -817,7 +825,8 @@
? null
: mDisplayCutout == null
? DisplayCutout.NO_CUTOUT
- : mDisplayCutout.inset(left, top, right, bottom));
+ : mDisplayCutout.inset(left, top, right, bottom),
+ mCompatInsetTypes);
}
@Override
@@ -1134,7 +1143,8 @@
public WindowInsets build() {
return new WindowInsets(mSystemInsetsConsumed ? null : mTypeInsetsMap,
mStableInsetsConsumed ? null : mTypeMaxInsetsMap, mTypeVisibilityMap,
- mIsRound, mAlwaysConsumeSystemBars, mDisplayCutout);
+ mIsRound, mAlwaysConsumeSystemBars, mDisplayCutout,
+ systemBars());
}
}
@@ -1271,15 +1281,6 @@
}
/**
- * @return Inset types representing the list of bars that traditionally were denoted as
- * system insets.
- * @hide
- */
- static @InsetsType int compatSystemInsets() {
- return STATUS_BARS | NAVIGATION_BARS | IME;
- }
-
- /**
* @return All inset types combined.
*
* TODO: Figure out if this makes sense at all, mixing e.g {@link #systemGestures()} and
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 9578002..7d5564e 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -57,6 +57,12 @@
private static final String TAG = "WindowManager";
/**
+ * This flag controls whether ViewRootImpl will utilize the Blast Adapter
+ * to send buffer updates to SurfaceFlinger
+ */
+ public static final boolean USE_BLAST_ADAPTER = false;
+
+ /**
* The user is navigating with keys (not the touch screen), so
* navigational focus should be shown.
*/
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index 1e5a3b0..cf494ae 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -63,6 +63,8 @@
* which is what clients use to communicate with the input method.
*/
public interface InputMethod {
+ /** @hide **/
+ public static final String TAG = "InputMethod";
/**
* This is the interface name that a service implementing an input
* method should say that it supports -- that is, this is the action it
@@ -111,6 +113,10 @@
/**
* Called to notify the IME that Autofill Frameworks requested an inline suggestions request.
*
+ * @param componentName {@link ComponentName} of current app/activity.
+ * @param autofillId {@link AutofillId} of currently focused field.
+ * @param cb {@link IInlineSuggestionsRequestCallback} used to pass back the request object.
+ *
* @hide
*/
default void onCreateInlineSuggestionsRequest(ComponentName componentName,
@@ -118,7 +124,7 @@
try {
cb.onInlineSuggestionsUnsupported();
} catch (RemoteException e) {
- Log.w("InputMethod", "RemoteException calling onInlineSuggestionsUnsupported: " + e);
+ Log.w(TAG, "Failed to call onInlineSuggestionsUnsupported.", e);
}
}
diff --git a/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java b/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java
index 7dbcbf9..28cb80d 100644
--- a/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java
+++ b/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java
@@ -22,6 +22,7 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.metrics.LogMaker;
+import android.os.Build;
import android.util.Log;
import android.view.textclassifier.TextClassification;
import android.view.textclassifier.TextClassifier;
@@ -101,7 +102,8 @@
private boolean mSmartSelectionTriggered;
private String mModelName;
- @UnsupportedAppUsage(trackingBug = 136637107)
+ @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
+ publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
public SmartSelectionEventTracker(@NonNull Context context, @WidgetType int widgetType) {
mWidgetType = widgetType;
mWidgetVersion = null;
@@ -120,7 +122,8 @@
*
* @param event the selection event
*/
- @UnsupportedAppUsage(trackingBug = 136637107)
+ @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
+ publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
public void logEvent(@NonNull SelectionEvent event) {
Objects.requireNonNull(event);
@@ -444,7 +447,8 @@
*
* @param start the word index of the selected word
*/
- @UnsupportedAppUsage(trackingBug = 136637107)
+ @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
+ publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
public static SelectionEvent selectionStarted(int start) {
return new SelectionEvent(
start, start + 1, EventType.SELECTION_STARTED,
@@ -458,7 +462,8 @@
* @param start the start word (inclusive) index of the selection
* @param end the end word (exclusive) index of the selection
*/
- @UnsupportedAppUsage(trackingBug = 136637107)
+ @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
+ publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
public static SelectionEvent selectionModified(int start, int end) {
return new SelectionEvent(
start, end, EventType.SELECTION_MODIFIED,
@@ -474,7 +479,8 @@
* @param classification the TextClassification object returned by the TextClassifier that
* classified the selected text
*/
- @UnsupportedAppUsage(trackingBug = 136637107)
+ @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
+ publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
public static SelectionEvent selectionModified(
int start, int end, @NonNull TextClassification classification) {
final String entityType = classification.getEntityCount() > 0
@@ -494,7 +500,8 @@
* @param selection the TextSelection object returned by the TextClassifier for the
* specified selection
*/
- @UnsupportedAppUsage(trackingBug = 136637107)
+ @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
+ publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
public static SelectionEvent selectionModified(
int start, int end, @NonNull TextSelection selection) {
final boolean smartSelection = getSourceClassifier(selection.getId())
@@ -523,7 +530,8 @@
* @param end the end word (exclusive) index of the selection
* @param actionType the action that was performed on the selection
*/
- @UnsupportedAppUsage(trackingBug = 136637107)
+ @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
+ publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
public static SelectionEvent selectionAction(
int start, int end, @ActionType int actionType) {
return new SelectionEvent(
@@ -541,7 +549,8 @@
* @param classification the TextClassification object returned by the TextClassifier that
* classified the selected text
*/
- @UnsupportedAppUsage(trackingBug = 136637107)
+ @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
+ publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
public static SelectionEvent selectionAction(
int start, int end, @ActionType int actionType,
@NonNull TextClassification classification) {
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index 547aad6..5820f4b 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -19,7 +19,7 @@
import android.annotation.DrawableRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
diff --git a/core/java/android/widget/CursorAdapter.java b/core/java/android/widget/CursorAdapter.java
index e250f63..600a34c 100644
--- a/core/java/android/widget/CursorAdapter.java
+++ b/core/java/android/widget/CursorAdapter.java
@@ -16,8 +16,8 @@
package android.widget;
-import android.annotation.UnsupportedAppUsage;
import android.annotation.WorkerThread;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources;
import android.database.ContentObserver;
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 0c593be..c1c1a6e 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -19,8 +19,8 @@
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
import android.annotation.Widget;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
diff --git a/core/java/android/widget/DatePickerSpinnerDelegate.java b/core/java/android/widget/DatePickerSpinnerDelegate.java
index 5f15110..096e6ea 100644
--- a/core/java/android/widget/DatePickerSpinnerDelegate.java
+++ b/core/java/android/widget/DatePickerSpinnerDelegate.java
@@ -16,7 +16,7 @@
package android.widget;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
diff --git a/core/java/android/widget/DateTimeView.java b/core/java/android/widget/DateTimeView.java
index 2864ad0..20a53c0 100644
--- a/core/java/android/widget/DateTimeView.java
+++ b/core/java/android/widget/DateTimeView.java
@@ -21,8 +21,8 @@
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.text.format.DateUtils.YEAR_IN_MILLIS;
-import android.annotation.UnsupportedAppUsage;
import android.app.ActivityThread;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java
index fa0af78..32f3acd 100644
--- a/core/java/android/widget/EdgeEffect.java
+++ b/core/java/android/widget/EdgeEffect.java
@@ -18,7 +18,7 @@
import android.annotation.ColorInt;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.BlendMode;
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index f6aaa6f..a0cf534 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -21,10 +21,10 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
import android.app.RemoteAction;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ClipData;
import android.content.ClipData.Item;
import android.content.Context;
@@ -1462,7 +1462,11 @@
return false;
}
- void onTouchEvent(MotionEvent event) {
+ /**
+ * Handles touch events on an editable text view, implementing cursor movement, selection, etc.
+ */
+ @VisibleForTesting
+ public void onTouchEvent(MotionEvent event) {
final boolean filterOutEvent = shouldFilterOutTouchEvent(event);
mLastButtonState = event.getButtonState();
if (filterOutEvent) {
@@ -2424,7 +2428,9 @@
return mSelectionControllerEnabled;
}
- private InsertionPointCursorController getInsertionController() {
+ /** Returns the controller for the insertion cursor. */
+ @VisibleForTesting
+ public @Nullable InsertionPointCursorController getInsertionController() {
if (!mInsertionControllerEnabled) {
return null;
}
@@ -2439,8 +2445,9 @@
return mInsertionPointCursorController;
}
- @Nullable
- SelectionModifierCursorController getSelectionController() {
+ /** Returns the controller for selection. */
+ @VisibleForTesting
+ public @Nullable SelectionModifierCursorController getSelectionController() {
if (!mSelectionControllerEnabled) {
return null;
}
@@ -5723,11 +5730,16 @@
}
}
- class InsertionPointCursorController implements CursorController {
+ /** Controller for the insertion cursor. */
+ @VisibleForTesting
+ public class InsertionPointCursorController implements CursorController {
private InsertionHandleView mHandle;
private boolean mIsDraggingCursor;
public void onTouchEvent(MotionEvent event) {
+ if (getSelectionController().isCursorBeingModified()) {
+ return;
+ }
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mIsDraggingCursor = false;
@@ -5738,7 +5750,8 @@
} else if (FLAG_ENABLE_CURSOR_DRAG
&& mTextView.getLayout() != null
&& mTextView.isFocused()
- && mTouchState.isMovedEnoughForDrag()) {
+ && mTouchState.isMovedEnoughForDrag()
+ && !mTouchState.isDragCloseToVertical()) {
startCursorDrag(event);
}
break;
@@ -5899,7 +5912,9 @@
}
}
- class SelectionModifierCursorController implements CursorController {
+ /** Controller for selection. */
+ @VisibleForTesting
+ public class SelectionModifierCursorController implements CursorController {
// The cursor controller handles, lazily created when shown.
private SelectionHandleView mStartHandle;
private SelectionHandleView mEndHandle;
diff --git a/core/java/android/widget/EditorTouchState.java b/core/java/android/widget/EditorTouchState.java
index 3798d00..d53099d 100644
--- a/core/java/android/widget/EditorTouchState.java
+++ b/core/java/android/widget/EditorTouchState.java
@@ -56,6 +56,7 @@
private boolean mMultiTapInSameArea;
private boolean mMovedEnoughForDrag;
+ private boolean mIsDragCloseToVertical;
public float getLastDownX() {
return mLastDownX;
@@ -94,6 +95,10 @@
return mMovedEnoughForDrag;
}
+ public boolean isDragCloseToVertical() {
+ return mIsDragCloseToVertical;
+ }
+
/**
* Updates the state based on the new event.
*/
@@ -129,6 +134,7 @@
mLastDownX = event.getX();
mLastDownY = event.getY();
mMovedEnoughForDrag = false;
+ mIsDragCloseToVertical = false;
} else if (action == MotionEvent.ACTION_UP) {
if (TextView.DEBUG_CURSOR) {
logCursor("EditorTouchState", "ACTION_UP");
@@ -137,9 +143,24 @@
mLastUpY = event.getY();
mLastUpMillis = event.getEventTime();
mMovedEnoughForDrag = false;
+ mIsDragCloseToVertical = false;
} else if (action == MotionEvent.ACTION_MOVE) {
- mMovedEnoughForDrag = !isDistanceWithin(mLastDownX, mLastDownY,
- event.getX(), event.getY(), config.getScaledTouchSlop());
+ if (!mMovedEnoughForDrag) {
+ float deltaX = event.getX() - mLastDownX;
+ float deltaY = event.getY() - mLastDownY;
+ float deltaXSquared = deltaX * deltaX;
+ float distanceSquared = (deltaXSquared) + (deltaY * deltaY);
+ int touchSlop = config.getScaledTouchSlop();
+ mMovedEnoughForDrag = distanceSquared > touchSlop * touchSlop;
+ if (mMovedEnoughForDrag) {
+ // If the direction of the swipe motion is within 30 degrees of vertical, it is
+ // considered a vertical drag. We don't actually have to compute the angle to
+ // implement the check though. When the angle is exactly 30 degrees from
+ // vertical, 2*deltaX = distance. When the angle is less than 30 degrees from
+ // vertical, 2*deltaX < distance.
+ mIsDragCloseToVertical = (4 * deltaXSquared) <= distanceSquared;
+ }
+ }
}
}
diff --git a/core/java/android/widget/ExpandableListView.java b/core/java/android/widget/ExpandableListView.java
index cae91fc..bdfb550 100644
--- a/core/java/android/widget/ExpandableListView.java
+++ b/core/java/android/widget/ExpandableListView.java
@@ -18,7 +18,7 @@
import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java
index 2c09185..0c0b349 100644
--- a/core/java/android/widget/FastScroller.java
+++ b/core/java/android/widget/FastScroller.java
@@ -23,7 +23,7 @@
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.annotation.StyleRes;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
diff --git a/core/java/android/widget/Filter.java b/core/java/android/widget/Filter.java
index 16f4ee2..06e6a5a 100644
--- a/core/java/android/widget/Filter.java
+++ b/core/java/android/widget/Filter.java
@@ -16,7 +16,7 @@
package android.widget;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index 69da911..92e9a96 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -20,7 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StyleRes;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java
index 64192aa..8c0061d 100644
--- a/core/java/android/widget/Gallery.java
+++ b/core/java/android/widget/Gallery.java
@@ -17,8 +17,8 @@
package android.widget;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
import android.annotation.Widget;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index 8cda47d..f132197 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -31,7 +31,7 @@
import static java.lang.Math.min;
import android.annotation.IntDef;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index 4e39a55..4a5e95e 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -18,7 +18,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
diff --git a/core/java/android/widget/HeaderViewListAdapter.java b/core/java/android/widget/HeaderViewListAdapter.java
index 10d50b8..eda7580 100644
--- a/core/java/android/widget/HeaderViewListAdapter.java
+++ b/core/java/android/widget/HeaderViewListAdapter.java
@@ -16,7 +16,7 @@
package android.widget;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.database.DataSetObserver;
import android.view.View;
import android.view.ViewGroup;
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index ec685f5..b33660a 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -18,7 +18,7 @@
import android.annotation.ColorInt;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index d62b979..c2077384 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -20,7 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.ColorStateList;
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index a83e826..a796ba5 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -19,7 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index eb7a75b..8595fec 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -20,7 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StyleRes;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
import android.database.DataSetObserver;
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 3a197e2..79ec680 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -19,7 +19,7 @@
import android.annotation.IdRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
diff --git a/core/java/android/widget/MediaController.java b/core/java/android/widget/MediaController.java
index 65925b4..9c9baf3 100644
--- a/core/java/android/widget/MediaController.java
+++ b/core/java/android/widget/MediaController.java
@@ -16,7 +16,7 @@
package android.widget;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.PixelFormat;
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 882e81a..e9e0c14 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -23,8 +23,8 @@
import android.annotation.IntRange;
import android.annotation.Px;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
import android.annotation.Widget;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
diff --git a/core/java/android/widget/OverScroller.java b/core/java/android/widget/OverScroller.java
index e7a96be..1c33d80 100644
--- a/core/java/android/widget/OverScroller.java
+++ b/core/java/android/widget/OverScroller.java
@@ -16,7 +16,7 @@
package android.widget;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.hardware.SensorManager;
import android.util.Log;
diff --git a/core/java/android/widget/PopupMenu.java b/core/java/android/widget/PopupMenu.java
index b0c0c12..0ce9646 100644
--- a/core/java/android/widget/PopupMenu.java
+++ b/core/java/android/widget/PopupMenu.java
@@ -18,7 +18,7 @@
import android.annotation.MenuRes;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.view.Gravity;
import android.view.Menu;
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 3779779..bf696f5 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -18,13 +18,12 @@
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static android.view.WindowManager.LayoutParams
- .PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.PixelFormat;
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 5cc0e0e..733a775 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -21,7 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Px;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
diff --git a/core/java/android/widget/QuickContactBadge.java b/core/java/android/widget/QuickContactBadge.java
index c1a217c..ea39f6d 100644
--- a/core/java/android/widget/QuickContactBadge.java
+++ b/core/java/android/widget/QuickContactBadge.java
@@ -16,7 +16,7 @@
package android.widget;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.AsyncQueryHandler;
import android.content.ContentResolver;
import android.content.Context;
diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java
index 90bc0a3..71ccb59 100644
--- a/core/java/android/widget/RadioGroup.java
+++ b/core/java/android/widget/RadioGroup.java
@@ -19,7 +19,7 @@
import android.annotation.IdRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
diff --git a/core/java/android/widget/RatingBar.java b/core/java/android/widget/RatingBar.java
index 3cf3d91..f946fe6 100644
--- a/core/java/android/widget/RatingBar.java
+++ b/core/java/android/widget/RatingBar.java
@@ -16,7 +16,7 @@
package android.widget;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.shapes.RectShape;
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index 253b6e1..d085eda 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -19,7 +19,7 @@
import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.ResourceId;
import android.content.res.TypedArray;
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index a6304b1..d86766e 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -22,7 +22,6 @@
import android.annotation.LayoutRes;
import android.annotation.NonNull;
import android.annotation.StyleRes;
-import android.annotation.UnsupportedAppUsage;
import android.app.Activity;
import android.app.ActivityOptions;
import android.app.ActivityThread;
@@ -30,6 +29,7 @@
import android.app.PendingIntent;
import android.app.RemoteInput;
import android.appwidget.AppWidgetHostView;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index efc5eb3..e58f08a 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -19,11 +19,11 @@
import static android.widget.RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID;
import static android.widget.RemoteViews.EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND;
-import android.annotation.UnsupportedAppUsage;
import android.annotation.WorkerThread;
import android.app.IServiceConnection;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetManager;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
diff --git a/core/java/android/widget/ScrollBarDrawable.java b/core/java/android/widget/ScrollBarDrawable.java
index a5d3e45..80e17b5 100644
--- a/core/java/android/widget/ScrollBarDrawable.java
+++ b/core/java/android/widget/ScrollBarDrawable.java
@@ -18,7 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.PixelFormat;
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 1cc8ff6..3847d6b 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -18,7 +18,7 @@
import android.annotation.ColorInt;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
diff --git a/core/java/android/widget/Scroller.java b/core/java/android/widget/Scroller.java
index 229eaf0..6ed5b7e 100644
--- a/core/java/android/widget/Scroller.java
+++ b/core/java/android/widget/Scroller.java
@@ -16,7 +16,7 @@
package android.widget;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.hardware.SensorManager;
import android.os.Build;
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index 89d9e97..15959c2 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -19,10 +19,10 @@
import static android.widget.SuggestionsAdapter.getColumnString;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
import android.app.PendingIntent;
import android.app.SearchManager;
import android.app.SearchableInfo;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
diff --git a/core/java/android/widget/SeekBar.java b/core/java/android/widget/SeekBar.java
index e8cf1e8..5676881 100644
--- a/core/java/android/widget/SeekBar.java
+++ b/core/java/android/widget/SeekBar.java
@@ -16,7 +16,7 @@
package android.widget;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.util.AttributeSet;
import android.view.accessibility.AccessibilityNodeInfo;
diff --git a/core/java/android/widget/SimpleAdapter.java b/core/java/android/widget/SimpleAdapter.java
index 15e1ffa..404b817 100644
--- a/core/java/android/widget/SimpleAdapter.java
+++ b/core/java/android/widget/SimpleAdapter.java
@@ -18,7 +18,7 @@
import android.annotation.IdRes;
import android.annotation.LayoutRes;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources;
import android.net.Uri;
diff --git a/core/java/android/widget/SimpleCursorAdapter.java b/core/java/android/widget/SimpleCursorAdapter.java
index 77fe5d1..6277d5b 100644
--- a/core/java/android/widget/SimpleCursorAdapter.java
+++ b/core/java/android/widget/SimpleCursorAdapter.java
@@ -16,7 +16,7 @@
package android.widget;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
diff --git a/core/java/android/widget/SlidingDrawer.java b/core/java/android/widget/SlidingDrawer.java
index 2ab2b24..9a4cfa1 100644
--- a/core/java/android/widget/SlidingDrawer.java
+++ b/core/java/android/widget/SlidingDrawer.java
@@ -17,7 +17,7 @@
package android.widget;
import android.R;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index 92fcea3..46fc09f 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -19,9 +19,9 @@
import android.annotation.DrawableRes;
import android.annotation.Nullable;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
import android.annotation.Widget;
import android.app.AlertDialog;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index fbd29ba..e1fd776 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -21,7 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StyleRes;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
diff --git a/core/java/android/widget/TabHost.java b/core/java/android/widget/TabHost.java
index 45e635ebe..a9a35115 100644
--- a/core/java/android/widget/TabHost.java
+++ b/core/java/android/widget/TabHost.java
@@ -18,8 +18,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
import android.app.LocalActivityManager;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
diff --git a/core/java/android/widget/TabWidget.java b/core/java/android/widget/TabWidget.java
index bd0d039..36abf3a 100644
--- a/core/java/android/widget/TabWidget.java
+++ b/core/java/android/widget/TabWidget.java
@@ -18,7 +18,7 @@
import android.annotation.DrawableRes;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index 2ae4389..5731e50 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -21,8 +21,8 @@
import android.annotation.NonNull;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
import android.app.ActivityManager;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 43d9895..32d3fef 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -37,11 +37,11 @@
import android.annotation.Size;
import android.annotation.StringRes;
import android.annotation.StyleRes;
-import android.annotation.UnsupportedAppUsage;
import android.annotation.XmlRes;
import android.app.Activity;
import android.app.PendingIntent;
import android.app.assist.AssistStructure;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.ClipboardManager;
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index 8a5d531..51b1847 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -20,8 +20,8 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
import android.annotation.Widget;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
import android.icu.util.Calendar;
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 8f2133f..9bdb4c1 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -20,9 +20,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringRes;
-import android.annotation.UnsupportedAppUsage;
import android.app.INotificationManager;
import android.app.ITransientNotification;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index a21fb41..ea3f06b 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -24,8 +24,8 @@
import android.annotation.StringRes;
import android.annotation.StyleRes;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
import android.app.ActionBar;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java
index 40b0f13..36dafd5 100644
--- a/core/java/android/widget/VideoView.java
+++ b/core/java/android/widget/VideoView.java
@@ -17,8 +17,8 @@
package android.widget;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
import android.app.AlertDialog;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Resources;
diff --git a/core/java/android/widget/ViewAnimator.java b/core/java/android/widget/ViewAnimator.java
index d36f343..90f61ca 100644
--- a/core/java/android/widget/ViewAnimator.java
+++ b/core/java/android/widget/ViewAnimator.java
@@ -18,7 +18,7 @@
import android.annotation.AnimRes;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
diff --git a/core/java/android/widget/ViewFlipper.java b/core/java/android/widget/ViewFlipper.java
index b962298..2df9a78 100644
--- a/core/java/android/widget/ViewFlipper.java
+++ b/core/java/android/widget/ViewFlipper.java
@@ -17,7 +17,7 @@
package android.widget;
import android.annotation.IntRange;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
diff --git a/core/java/android/widget/ZoomControls.java b/core/java/android/widget/ZoomControls.java
index 7a5b7e8..f435533 100644
--- a/core/java/android/widget/ZoomControls.java
+++ b/core/java/android/widget/ZoomControls.java
@@ -16,8 +16,8 @@
package android.widget;
-import android.annotation.UnsupportedAppUsage;
import android.annotation.Widget;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
diff --git a/core/java/com/android/ims/internal/uce/common/CapInfo.java b/core/java/com/android/ims/internal/uce/common/CapInfo.java
index a9847ba..c45a3a4 100644
--- a/core/java/com/android/ims/internal/uce/common/CapInfo.java
+++ b/core/java/com/android/ims/internal/uce/common/CapInfo.java
@@ -16,10 +16,9 @@
package com.android.ims.internal.uce.common;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
-import android.util.Log;
/** Class for capability discovery information.
* @hide */
diff --git a/core/java/com/android/ims/internal/uce/common/StatusCode.java b/core/java/com/android/ims/internal/uce/common/StatusCode.java
index 7250eee..7f69493 100644
--- a/core/java/com/android/ims/internal/uce/common/StatusCode.java
+++ b/core/java/com/android/ims/internal/uce/common/StatusCode.java
@@ -16,10 +16,9 @@
package com.android.ims.internal.uce.common;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
-import android.util.Log;
/** Class for UCE status codes.
diff --git a/core/java/com/android/ims/internal/uce/common/UceLong.java b/core/java/com/android/ims/internal/uce/common/UceLong.java
index 7207899..bf51447 100644
--- a/core/java/com/android/ims/internal/uce/common/UceLong.java
+++ b/core/java/com/android/ims/internal/uce/common/UceLong.java
@@ -16,10 +16,9 @@
package com.android.ims.internal.uce.common;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
-import android.util.Log;
/** Simple object wrapper for a long type.
diff --git a/core/java/com/android/ims/internal/uce/options/OptionsCapInfo.java b/core/java/com/android/ims/internal/uce/options/OptionsCapInfo.java
index bcb9f2d..1da5a24 100644
--- a/core/java/com/android/ims/internal/uce/options/OptionsCapInfo.java
+++ b/core/java/com/android/ims/internal/uce/options/OptionsCapInfo.java
@@ -15,11 +15,11 @@
*/
package com.android.ims.internal.uce.options;
-import android.annotation.UnsupportedAppUsage;
-import com.android.ims.internal.uce.common.CapInfo;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
-import android.util.Log;
+
+import com.android.ims.internal.uce.common.CapInfo;
/** @hide */
public class OptionsCapInfo implements Parcelable {
diff --git a/core/java/com/android/ims/internal/uce/options/OptionsCmdId.java b/core/java/com/android/ims/internal/uce/options/OptionsCmdId.java
index 14c64ac..401ca2f 100644
--- a/core/java/com/android/ims/internal/uce/options/OptionsCmdId.java
+++ b/core/java/com/android/ims/internal/uce/options/OptionsCmdId.java
@@ -17,7 +17,7 @@
package com.android.ims.internal.uce.options;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/com/android/ims/internal/uce/options/OptionsCmdStatus.java b/core/java/com/android/ims/internal/uce/options/OptionsCmdStatus.java
index 4af3e6e..70a7a84 100644
--- a/core/java/com/android/ims/internal/uce/options/OptionsCmdStatus.java
+++ b/core/java/com/android/ims/internal/uce/options/OptionsCmdStatus.java
@@ -16,13 +16,13 @@
package com.android.ims.internal.uce.options;
-import com.android.ims.internal.uce.common.StatusCode;
-import com.android.ims.internal.uce.common.CapInfo;
-
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.ims.internal.uce.common.CapInfo;
+import com.android.ims.internal.uce.common.StatusCode;
+
/** @hide */
public class OptionsCmdStatus implements Parcelable {
diff --git a/core/java/com/android/ims/internal/uce/options/OptionsSipResponse.java b/core/java/com/android/ims/internal/uce/options/OptionsSipResponse.java
index c5f333d..5afddf0 100644
--- a/core/java/com/android/ims/internal/uce/options/OptionsSipResponse.java
+++ b/core/java/com/android/ims/internal/uce/options/OptionsSipResponse.java
@@ -16,7 +16,7 @@
package com.android.ims.internal.uce.options;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/com/android/ims/internal/uce/presence/PresCapInfo.java b/core/java/com/android/ims/internal/uce/presence/PresCapInfo.java
index 745df5b..1a3a028 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresCapInfo.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresCapInfo.java
@@ -16,12 +16,12 @@
package com.android.ims.internal.uce.presence;
-import com.android.ims.internal.uce.common.CapInfo;
-
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.ims.internal.uce.common.CapInfo;
+
/** @hide */
public class PresCapInfo implements Parcelable {
diff --git a/core/java/com/android/ims/internal/uce/presence/PresCmdId.java b/core/java/com/android/ims/internal/uce/presence/PresCmdId.java
index 41020ec..fba0c77 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresCmdId.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresCmdId.java
@@ -16,7 +16,7 @@
package com.android.ims.internal.uce.presence;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/com/android/ims/internal/uce/presence/PresCmdStatus.java b/core/java/com/android/ims/internal/uce/presence/PresCmdStatus.java
index ff8069c..fbc64b8 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresCmdStatus.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresCmdStatus.java
@@ -16,12 +16,12 @@
package com.android.ims.internal.uce.presence;
-import com.android.ims.internal.uce.common.StatusCode;
-
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.ims.internal.uce.common.StatusCode;
+
/** @hide */
public class PresCmdStatus implements Parcelable{
diff --git a/core/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java b/core/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java
index 87193e3..a50a22f 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java
@@ -16,7 +16,7 @@
package com.android.ims.internal.uce.presence;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/com/android/ims/internal/uce/presence/PresResInfo.java b/core/java/com/android/ims/internal/uce/presence/PresResInfo.java
index 237c999..af9b056 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresResInfo.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresResInfo.java
@@ -16,7 +16,7 @@
package com.android.ims.internal.uce.presence;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java b/core/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java
index 29699ea..9f37251 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java
@@ -16,9 +16,10 @@
package com.android.ims.internal.uce.presence;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
+
import java.util.Arrays;
/** @hide */
diff --git a/core/java/com/android/ims/internal/uce/presence/PresRlmiInfo.java b/core/java/com/android/ims/internal/uce/presence/PresRlmiInfo.java
index ab46e4b..65b9fdb 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresRlmiInfo.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresRlmiInfo.java
@@ -16,7 +16,7 @@
package com.android.ims.internal.uce.presence;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/com/android/ims/internal/uce/presence/PresServiceInfo.java b/core/java/com/android/ims/internal/uce/presence/PresServiceInfo.java
index 83ba722..5eafa0f 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresServiceInfo.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresServiceInfo.java
@@ -16,7 +16,7 @@
package com.android.ims.internal.uce.presence;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/com/android/ims/internal/uce/presence/PresSipResponse.java b/core/java/com/android/ims/internal/uce/presence/PresSipResponse.java
index 5e42592..45b02f3 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresSipResponse.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresSipResponse.java
@@ -16,7 +16,7 @@
package com.android.ims.internal.uce.presence;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/com/android/ims/internal/uce/presence/PresSubscriptionState.java b/core/java/com/android/ims/internal/uce/presence/PresSubscriptionState.java
index bee928c..ab1e17c 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresSubscriptionState.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresSubscriptionState.java
@@ -16,7 +16,7 @@
package com.android.ims.internal.uce.presence;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/com/android/ims/internal/uce/presence/PresTupleInfo.java b/core/java/com/android/ims/internal/uce/presence/PresTupleInfo.java
index 7a47786..3608eb6a 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresTupleInfo.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresTupleInfo.java
@@ -16,7 +16,7 @@
package com.android.ims.internal.uce.presence;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
index 33aa665..f9ba34e 100644
--- a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
+++ b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
@@ -15,14 +15,20 @@
*/
package com.android.internal.app;
+import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
+import static android.view.accessibility.AccessibilityManager.ShortcutType;
+
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.view.LayoutInflater;
@@ -30,12 +36,17 @@
import android.view.ViewGroup;
import android.view.Window;
import android.view.accessibility.AccessibilityManager;
+import android.widget.AdapterView;
import android.widget.BaseAdapter;
+import android.widget.FrameLayout;
import android.widget.ImageView;
+import android.widget.Switch;
import android.widget.TextView;
import com.android.internal.R;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -48,40 +59,74 @@
private static final String MAGNIFICATION_COMPONENT_ID =
"com.android.server.accessibility.MagnificationController";
- private AccessibilityButtonTarget mMagnificationTarget = null;
-
- private List<AccessibilityButtonTarget> mTargets = null;
-
+ private int mShortcutType;
+ private List<AccessibilityButtonTarget> mTargets = new ArrayList<>();
+ private List<AccessibilityButtonTarget> mReadyToBeDisabledTargets = new ArrayList<>();
private AlertDialog mAlertDialog;
+ private TargetAdapter mTargetAdapter;
+
+ /**
+ * Annotation for different accessibilityService fragment UI type.
+ *
+ * {@code LEGACY} for displaying appearance aligned with sdk version Q accessibility service
+ * page, but only hardware shortcut allowed and under service in version Q or early.
+ * {@code INVISIBLE} for displaying appearance without switch bar.
+ * {@code INTUITIVE} for displaying appearance with version R accessibility design.
+ * {@code BOUNCE} for displaying appearance with pop-up action.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ AccessibilityServiceFragmentType.LEGACY,
+ AccessibilityServiceFragmentType.INVISIBLE,
+ AccessibilityServiceFragmentType.INTUITIVE,
+ AccessibilityServiceFragmentType.BOUNCE,
+ })
+ public @interface AccessibilityServiceFragmentType {
+ int LEGACY = 0;
+ int INVISIBLE = 1;
+ int INTUITIVE = 2;
+ int BOUNCE = 3;
+ }
+
+ /**
+ * Annotation for different shortcut menu mode.
+ *
+ * {@code LAUNCH} for clicking list item to trigger the service callback.
+ * {@code EDIT} for clicking list item and save button to disable the service.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ ShortcutMenuMode.LAUNCH,
+ ShortcutMenuMode.EDIT,
+ })
+ public @interface ShortcutMenuMode {
+ int LAUNCH = 0;
+ int EDIT = 1;
+ }
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final TypedArray theme = getTheme().obtainStyledAttributes(android.R.styleable.Theme);
- if (!theme.getBoolean(android.R.styleable.Theme_windowNoTitle, /* defValue= */false)) {
+ if (!theme.getBoolean(android.R.styleable.Theme_windowNoTitle, /* defValue= */ false)) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
}
- // TODO(b/146815874): Will replace it with white list services
- mMagnificationTarget = new AccessibilityButtonTarget(this, MAGNIFICATION_COMPONENT_ID,
- R.string.accessibility_magnification_chooser_text,
- R.drawable.ic_accessibility_magnification);
-
- // TODO(b/146815544): Will use shortcut type or button type to get the corresponding
- // services
- mTargets = getServiceAccessibilityButtonTargets(this);
- if (Settings.Secure.getInt(getContentResolver(),
- Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, 0) == 1) {
- mTargets.add(mMagnificationTarget);
- }
+ mShortcutType = getIntent().getIntExtra(AccessibilityManager.EXTRA_SHORTCUT_TYPE,
+ ACCESSIBILITY_BUTTON);
+ mTargets.addAll(getServiceTargets(this, mShortcutType));
// TODO(b/146815548): Will add title to separate which one type
+ mTargetAdapter = new TargetAdapter(mTargets);
mAlertDialog = new AlertDialog.Builder(this)
- .setAdapter(new TargetAdapter(),
- (dialog, position) -> onTargetSelected(mTargets.get(position)))
+ .setAdapter(mTargetAdapter, /* listener= */ null)
+ .setPositiveButton(
+ getString(R.string.edit_accessibility_shortcut_menu_button),
+ /* listener= */ null)
.setOnDismissListener(dialog -> finish())
.create();
+ mAlertDialog.setOnShowListener(dialog -> updateDialogListeners());
mAlertDialog.show();
}
@@ -91,41 +136,95 @@
super.onDestroy();
}
- private static List<AccessibilityButtonTarget> getServiceAccessibilityButtonTargets(
- @NonNull Context context) {
- AccessibilityManager ams = (AccessibilityManager) context.getSystemService(
+ /**
+ * Gets the corresponding fragment type of a given accessibility service.
+ *
+ * @param accessibilityServiceInfo The accessibilityService's info.
+ * @return int from {@link AccessibilityServiceFragmentType}.
+ */
+ private static @AccessibilityServiceFragmentType int getAccessibilityServiceFragmentType(
+ AccessibilityServiceInfo accessibilityServiceInfo) {
+ final int targetSdk = accessibilityServiceInfo.getResolveInfo()
+ .serviceInfo.applicationInfo.targetSdkVersion;
+ final boolean requestA11yButton = (accessibilityServiceInfo.flags
+ & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
+
+ if (targetSdk <= Build.VERSION_CODES.Q) {
+ return AccessibilityServiceFragmentType.LEGACY;
+ }
+ return requestA11yButton
+ ? AccessibilityServiceFragmentType.INVISIBLE
+ : AccessibilityServiceFragmentType.INTUITIVE;
+ }
+
+ private static List<AccessibilityButtonTarget> getServiceTargets(@NonNull Context context,
+ @ShortcutType int shortcutType) {
+ final AccessibilityManager ams = (AccessibilityManager) context.getSystemService(
Context.ACCESSIBILITY_SERVICE);
- List<AccessibilityServiceInfo> services = ams.getEnabledAccessibilityServiceList(
- AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
- if (services == null) {
+ final List<AccessibilityServiceInfo> installedServices =
+ ams.getInstalledAccessibilityServiceList();
+ if (installedServices == null) {
return Collections.emptyList();
}
- ArrayList<AccessibilityButtonTarget> targets = new ArrayList<>(services.size());
- for (AccessibilityServiceInfo info : services) {
+ final List<AccessibilityButtonTarget> targets = new ArrayList<>(installedServices.size());
+ for (AccessibilityServiceInfo info : installedServices) {
if ((info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0) {
targets.add(new AccessibilityButtonTarget(context, info));
}
}
+ final List<String> requiredTargets = ams.getAccessibilityShortcutTargets(shortcutType);
+ targets.removeIf(target -> !requiredTargets.contains(target.getId()));
+
+ // TODO(b/146815874): Will replace it with white list services.
+ if (Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, 0) == 1) {
+ final AccessibilityButtonTarget magnificationTarget = new AccessibilityButtonTarget(
+ context,
+ MAGNIFICATION_COMPONENT_ID,
+ R.string.accessibility_magnification_chooser_text,
+ R.drawable.ic_accessibility_magnification,
+ AccessibilityServiceFragmentType.INTUITIVE);
+ targets.add(magnificationTarget);
+ }
+
return targets;
}
- private void onTargetSelected(AccessibilityButtonTarget target) {
- Settings.Secure.putString(getContentResolver(),
- Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, target.getId());
- finish();
+ private static class ViewHolder {
+ ImageView mIconView;
+ TextView mLabelView;
+ FrameLayout mItemContainer;
+ ImageView mViewItem;
+ Switch mSwitchItem;
}
- private class TargetAdapter extends BaseAdapter {
+ private static class TargetAdapter extends BaseAdapter {
+ @ShortcutMenuMode
+ private int mShortcutMenuMode = ShortcutMenuMode.LAUNCH;
+ private List<AccessibilityButtonTarget> mButtonTargets;
+
+ TargetAdapter(List<AccessibilityButtonTarget> targets) {
+ this.mButtonTargets = targets;
+ }
+
+ void setShortcutMenuMode(int shortcutMenuMode) {
+ mShortcutMenuMode = shortcutMenuMode;
+ }
+
+ int getShortcutMenuMode() {
+ return mShortcutMenuMode;
+ }
+
@Override
public int getCount() {
- return mTargets.size();
+ return mButtonTargets.size();
}
@Override
public Object getItem(int position) {
- return null;
+ return mButtonTargets.get(position);
}
@Override
@@ -135,36 +234,110 @@
@Override
public View getView(int position, View convertView, ViewGroup parent) {
- LayoutInflater inflater = AccessibilityButtonChooserActivity.this.getLayoutInflater();
- View root = inflater.inflate(R.layout.accessibility_button_chooser_item, parent, false);
- final AccessibilityButtonTarget target = mTargets.get(position);
- ImageView iconView = root.findViewById(R.id.accessibility_button_target_icon);
- TextView labelView = root.findViewById(R.id.accessibility_button_target_label);
- iconView.setImageDrawable(target.getDrawable());
- labelView.setText(target.getLabel());
+ final Context context = parent.getContext();
+ ViewHolder holder;
+ if (convertView == null) {
+ convertView = LayoutInflater.from(context).inflate(
+ R.layout.accessibility_button_chooser_item, parent, /* attachToRoot= */
+ false);
+ holder = new ViewHolder();
+ holder.mIconView = convertView.findViewById(R.id.accessibility_button_target_icon);
+ holder.mLabelView = convertView.findViewById(
+ R.id.accessibility_button_target_label);
+ holder.mItemContainer = convertView.findViewById(
+ R.id.accessibility_button_target_item_container);
+ holder.mViewItem = convertView.findViewById(
+ R.id.accessibility_button_target_view_item);
+ holder.mSwitchItem = convertView.findViewById(
+ R.id.accessibility_button_target_switch_item);
+ convertView.setTag(holder);
+ } else {
+ holder = (ViewHolder) convertView.getTag();
+ }
- // TODO(b/146815874): Need to get every service status to update UI
- return root;
+ final AccessibilityButtonTarget target = mButtonTargets.get(position);
+ holder.mIconView.setImageDrawable(target.getDrawable());
+ holder.mLabelView.setText(target.getLabel());
+
+ updateActionItem(context, holder, target);
+
+ return convertView;
+ }
+
+ private void updateActionItem(@NonNull Context context,
+ @NonNull ViewHolder holder, AccessibilityButtonTarget target) {
+
+ switch (target.getFragmentType()) {
+ case AccessibilityServiceFragmentType.LEGACY:
+ case AccessibilityServiceFragmentType.INVISIBLE:
+ updateLegacyOrInvisibleActionItemVisibility(context, holder);
+ break;
+ case AccessibilityServiceFragmentType.INTUITIVE:
+ updateIntuitiveActionItemVisibility(context, holder, target);
+ break;
+ case AccessibilityServiceFragmentType.BOUNCE:
+ updateBounceActionItemVisibility(context, holder);
+ break;
+ default:
+ throw new IllegalStateException("Unexpected fragment type");
+ }
+ }
+
+ private void updateLegacyOrInvisibleActionItemVisibility(@NonNull Context context,
+ @NonNull ViewHolder holder) {
+ final boolean isEditMenuMode = mShortcutMenuMode == ShortcutMenuMode.EDIT;
+
+ holder.mViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
+ holder.mViewItem.setVisibility(View.VISIBLE);
+ holder.mSwitchItem.setVisibility(View.GONE);
+ holder.mItemContainer.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE);
+ }
+
+ private void updateIntuitiveActionItemVisibility(@NonNull Context context,
+ @NonNull ViewHolder holder, AccessibilityButtonTarget target) {
+ final boolean isEditMenuMode = mShortcutMenuMode == ShortcutMenuMode.EDIT;
+
+ holder.mViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
+ holder.mViewItem.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE);
+ holder.mSwitchItem.setVisibility(isEditMenuMode ? View.GONE : View.VISIBLE);
+ holder.mSwitchItem.setChecked(!isEditMenuMode && isServiceEnabled(context, target));
+ holder.mItemContainer.setVisibility(View.VISIBLE);
+ }
+
+ private void updateBounceActionItemVisibility(@NonNull Context context,
+ @NonNull ViewHolder holder) {
+ final boolean isEditMenuMode = mShortcutMenuMode == ShortcutMenuMode.EDIT;
+
+ holder.mViewItem.setImageDrawable(
+ isEditMenuMode ? context.getDrawable(R.drawable.ic_delete_item)
+ : context.getDrawable(R.drawable.ic_open_in_new));
+ holder.mViewItem.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE);
+ holder.mSwitchItem.setVisibility(View.GONE);
+ holder.mItemContainer.setVisibility(View.VISIBLE);
}
}
private static class AccessibilityButtonTarget {
- public String mId;
- public CharSequence mLabel;
- public Drawable mDrawable;
- // TODO(b/146815874): Will add fragment type and related functions
- public AccessibilityButtonTarget(@NonNull Context context,
+ private String mId;
+ private CharSequence mLabel;
+ private Drawable mDrawable;
+ @AccessibilityServiceFragmentType
+ private int mFragmentType;
+
+ AccessibilityButtonTarget(@NonNull Context context,
@NonNull AccessibilityServiceInfo serviceInfo) {
this.mId = serviceInfo.getComponentName().flattenToString();
this.mLabel = serviceInfo.getResolveInfo().loadLabel(context.getPackageManager());
this.mDrawable = serviceInfo.getResolveInfo().loadIcon(context.getPackageManager());
+ this.mFragmentType = getAccessibilityServiceFragmentType(serviceInfo);
}
- public AccessibilityButtonTarget(Context context, @NonNull String id, int labelResId,
- int iconRes) {
+ AccessibilityButtonTarget(Context context, @NonNull String id, int labelResId,
+ int iconRes, @AccessibilityServiceFragmentType int fragmentType) {
this.mId = id;
this.mLabel = context.getText(labelResId);
this.mDrawable = context.getDrawable(iconRes);
+ this.mFragmentType = fragmentType;
}
public String getId() {
@@ -178,5 +351,105 @@
public Drawable getDrawable() {
return mDrawable;
}
+
+ public int getFragmentType() {
+ return mFragmentType;
+ }
+ }
+
+ private static boolean isServiceEnabled(@NonNull Context context,
+ AccessibilityButtonTarget target) {
+ final AccessibilityManager ams = (AccessibilityManager) context.getSystemService(
+ Context.ACCESSIBILITY_SERVICE);
+ final List<AccessibilityServiceInfo> enabledServices =
+ ams.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
+
+ for (AccessibilityServiceInfo info : enabledServices) {
+ final String id = info.getComponentName().flattenToString();
+ if (id.equals(target.getId())) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private void onTargetSelected(AdapterView<?> parent, View view, int position, long id) {
+ Settings.Secure.putString(getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
+ mTargets.get(position).getId());
+ // TODO(b/146969684): notify accessibility button clicked.
+ mAlertDialog.dismiss();
+ }
+
+ private void onTargetDeleted(AdapterView<?> parent, View view, int position, long id) {
+ // TODO(b/147027236): Will discuss with UX designer what UX behavior about deleting item
+ // is good for user.
+ mReadyToBeDisabledTargets.add(mTargets.get(position));
+ mTargets.remove(position);
+ mTargetAdapter.notifyDataSetChanged();
+ }
+
+ private void onCancelButtonClicked() {
+ resetAndUpdateTargets();
+
+ mTargetAdapter.setShortcutMenuMode(ShortcutMenuMode.LAUNCH);
+ mTargetAdapter.notifyDataSetChanged();
+
+ mAlertDialog.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.GONE);
+ mAlertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setText(
+ getString(R.string.edit_accessibility_shortcut_menu_button));
+
+ updateDialogListeners();
+ }
+
+ private void onEditButtonClicked() {
+ mTargetAdapter.setShortcutMenuMode(ShortcutMenuMode.EDIT);
+ mTargetAdapter.notifyDataSetChanged();
+
+ mAlertDialog.getButton(DialogInterface.BUTTON_NEGATIVE).setText(
+ getString(R.string.cancel_accessibility_shortcut_menu_button));
+ mAlertDialog.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.VISIBLE);
+ mAlertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setText(
+ getString(R.string.save_accessibility_shortcut_menu_button));
+
+ updateDialogListeners();
+ }
+
+ private void onSaveButtonClicked() {
+ disableTargets();
+ resetAndUpdateTargets();
+
+ mTargetAdapter.setShortcutMenuMode(ShortcutMenuMode.LAUNCH);
+ mTargetAdapter.notifyDataSetChanged();
+
+ mAlertDialog.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.GONE);
+ mAlertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setText(
+ getString(R.string.edit_accessibility_shortcut_menu_button));
+
+ updateDialogListeners();
+ }
+
+ private void updateDialogListeners() {
+ final boolean isEditMenuMode =
+ mTargetAdapter.getShortcutMenuMode() == ShortcutMenuMode.EDIT;
+
+ mAlertDialog.getButton(DialogInterface.BUTTON_NEGATIVE).setOnClickListener(
+ view -> onCancelButtonClicked());
+ mAlertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(
+ isEditMenuMode ? view -> onSaveButtonClicked() : view -> onEditButtonClicked());
+ mAlertDialog.getListView().setOnItemClickListener(
+ isEditMenuMode ? this::onTargetDeleted : this::onTargetSelected);
+ }
+
+ private void disableTargets() {
+ for (AccessibilityButtonTarget service : mReadyToBeDisabledTargets) {
+ // TODO(b/146967898): disable services.
+ }
+ }
+
+ private void resetAndUpdateTargets() {
+ mTargets.clear();
+ mTargets.addAll(getServiceTargets(this, mShortcutType));
}
}
diff --git a/core/java/com/android/internal/app/AlertActivity.java b/core/java/com/android/internal/app/AlertActivity.java
index 7307de5..cfbb273 100644
--- a/core/java/com/android/internal/app/AlertActivity.java
+++ b/core/java/com/android/internal/app/AlertActivity.java
@@ -16,9 +16,9 @@
package com.android.internal.app;
-import android.annotation.UnsupportedAppUsage;
import android.app.Activity;
import android.app.Dialog;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.KeyEvent;
diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java
index 0fd05c1..553721d7 100644
--- a/core/java/com/android/internal/app/AlertController.java
+++ b/core/java/com/android/internal/app/AlertController.java
@@ -18,14 +18,11 @@
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import com.android.internal.R;
-
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
import android.app.AlertDialog;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.DialogInterface;
-import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.database.Cursor;
import android.graphics.drawable.Drawable;
@@ -33,7 +30,6 @@
import android.os.Message;
import android.text.Layout;
import android.text.TextUtils;
-import android.text.method.LinkMovementMethod;
import android.text.method.MovementMethod;
import android.util.AttributeSet;
import android.util.TypedValue;
@@ -46,7 +42,6 @@
import android.view.ViewParent;
import android.view.ViewStub;
import android.view.Window;
-import android.view.WindowInsets;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
@@ -63,6 +58,8 @@
import android.widget.SimpleCursorAdapter;
import android.widget.TextView;
+import com.android.internal.R;
+
import java.lang.ref.WeakReference;
public class AlertController {
diff --git a/core/java/com/android/internal/app/AssistUtils.java b/core/java/com/android/internal/app/AssistUtils.java
index f848309..22a2564 100644
--- a/core/java/com/android/internal/app/AssistUtils.java
+++ b/core/java/com/android/internal/app/AssistUtils.java
@@ -17,7 +17,7 @@
package com.android.internal.app;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 06e96d5..b6b548c 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -26,7 +26,6 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.prediction.AppPredictionContext;
@@ -34,6 +33,7 @@
import android.app.prediction.AppPredictor;
import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetEvent;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.ComponentName;
@@ -2434,11 +2434,6 @@
FooterViewHolder(View itemView) {
super(itemView);
}
-
- public void setHeight(int height) {
- itemView.setLayoutParams(
- new RecyclerView.LayoutParams(LayoutParams.MATCH_PARENT, height));
- }
}
/**
@@ -2471,7 +2466,7 @@
private boolean mLayoutRequested = false;
- private FooterViewHolder mFooterViewHolder;
+ private int mFooterHeight = 0;
private static final int VIEW_TYPE_DIRECT_SHARE = 0;
private static final int VIEW_TYPE_NORMAL = 1;
@@ -2490,9 +2485,6 @@
mChooserListAdapter = wrappedAdapter;
mLayoutInflater = LayoutInflater.from(ChooserActivity.this);
- mFooterViewHolder = new FooterViewHolder(
- new Space(ChooserActivity.this.getApplicationContext()));
-
mShowAzLabelIfPoss = getNumSheetExpansions() < NUM_EXPANSIONS_TO_HIDE_AZ_LABEL;
wrappedAdapter.registerDataSetObserver(new DataSetObserver() {
@@ -2511,7 +2503,7 @@
}
public void setFooterHeight(int height) {
- mFooterViewHolder.setHeight(height);
+ mFooterHeight = height;
}
/**
@@ -2614,7 +2606,10 @@
case VIEW_TYPE_CALLER_AND_RANK:
return createItemGroupViewHolder(viewType, parent);
case VIEW_TYPE_FOOTER:
- return mFooterViewHolder;
+ Space sp = new Space(parent.getContext());
+ sp.setLayoutParams(new RecyclerView.LayoutParams(
+ LayoutParams.MATCH_PARENT, mFooterHeight));
+ return new FooterViewHolder(sp);
default:
// Since we catch all possible viewTypes above, no chance this is being called.
return null;
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 4e764fc..dabaf5a 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -58,10 +58,12 @@
List<AppOpsManager.PackageOps> getPackagesForOps(in int[] ops);
@UnsupportedAppUsage
List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, in int[] ops);
- void getHistoricalOps(int uid, String packageName, in List<String> ops, long beginTimeMillis,
- long endTimeMillis, int flags, in RemoteCallback callback);
- void getHistoricalOpsFromDiskRaw(int uid, String packageName, in List<String> ops,
- long beginTimeMillis, long endTimeMillis, int flags, in RemoteCallback callback);
+ void getHistoricalOps(int uid, String packageName, String featureId, in List<String> ops,
+ int filter, long beginTimeMillis, long endTimeMillis, int flags,
+ in RemoteCallback callback);
+ void getHistoricalOpsFromDiskRaw(int uid, String packageName, String featureId,
+ in List<String> ops, int filter, long beginTimeMillis, long endTimeMillis, int flags,
+ in RemoteCallback callback);
void offsetHistory(long duration);
void setHistoryParameters(int mode, long baseSnapshotInterval, int compressionStep);
void addHistoricalOps(in AppOpsManager.HistoricalOps ops);
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index a5f055f..9488e4f 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -20,12 +20,12 @@
import android.annotation.Nullable;
import android.annotation.StringRes;
-import android.annotation.UnsupportedAppUsage;
import android.app.Activity;
import android.app.ActivityTaskManager;
import android.app.ActivityThread;
import android.app.AppGlobals;
import android.app.admin.DevicePolicyManager;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.IPackageManager;
diff --git a/core/java/com/android/internal/app/LocaleHelper.java b/core/java/com/android/internal/app/LocaleHelper.java
index e3d07aa..d418770e 100644
--- a/core/java/com/android/internal/app/LocaleHelper.java
+++ b/core/java/com/android/internal/app/LocaleHelper.java
@@ -17,7 +17,7 @@
package com.android.internal.app;
import android.annotation.IntRange;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.icu.text.ListFormatter;
import android.icu.util.ULocale;
import android.os.LocaleList;
diff --git a/core/java/com/android/internal/app/LocalePicker.java b/core/java/com/android/internal/app/LocalePicker.java
index 7517424..3343593f 100644
--- a/core/java/com/android/internal/app/LocalePicker.java
+++ b/core/java/com/android/internal/app/LocalePicker.java
@@ -16,13 +16,11 @@
package com.android.internal.app;
-import com.android.internal.R;
-
-import android.annotation.UnsupportedAppUsage;
import android.app.ActivityManager;
import android.app.IActivityManager;
import android.app.ListFragment;
import android.app.backup.BackupManager;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -38,11 +36,13 @@
import android.widget.ListView;
import android.widget.TextView;
+import com.android.internal.R;
+
import java.text.Collator;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
-import java.util.ArrayList;
public class LocalePicker extends ListFragment {
private static final String TAG = "LocalePicker";
diff --git a/core/java/com/android/internal/app/LocaleStore.java b/core/java/com/android/internal/app/LocaleStore.java
index 49f77e1..1c5ca59 100644
--- a/core/java/com/android/internal/app/LocaleStore.java
+++ b/core/java/com/android/internal/app/LocaleStore.java
@@ -16,7 +16,7 @@
package com.android.internal.app;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.LocaleList;
import android.provider.Settings;
diff --git a/core/java/com/android/internal/app/NetInitiatedActivity.java b/core/java/com/android/internal/app/NetInitiatedActivity.java
index 9a802a9..89aa770 100644
--- a/core/java/com/android/internal/app/NetInitiatedActivity.java
+++ b/core/java/com/android/internal/app/NetInitiatedActivity.java
@@ -16,19 +16,19 @@
package com.android.internal.app;
-import android.annotation.UnsupportedAppUsage;
import android.app.AlertDialog;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
+import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
-import android.widget.Toast;
import android.util.Log;
-import android.location.LocationManager;
+import android.widget.Toast;
import com.android.internal.R;
import com.android.internal.location.GpsNetInitiatedHandler;
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 8d3c572..b2417b2 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -24,7 +24,6 @@
import android.annotation.Nullable;
import android.annotation.StringRes;
import android.annotation.UiThread;
-import android.annotation.UnsupportedAppUsage;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
@@ -32,6 +31,7 @@
import android.app.VoiceInteractor.PickOptionRequest;
import android.app.VoiceInteractor.PickOptionRequest.Option;
import android.app.VoiceInteractor.Prompt;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
diff --git a/core/java/com/android/internal/app/WindowDecorActionBar.java b/core/java/com/android/internal/app/WindowDecorActionBar.java
index e71ee66..0cd1202 100644
--- a/core/java/com/android/internal/app/WindowDecorActionBar.java
+++ b/core/java/com/android/internal/app/WindowDecorActionBar.java
@@ -16,28 +16,17 @@
package com.android.internal.app;
-import com.android.internal.R;
-import com.android.internal.view.ActionBarPolicy;
-import com.android.internal.view.menu.MenuBuilder;
-import com.android.internal.view.menu.MenuPopupHelper;
-import com.android.internal.view.menu.SubMenuBuilder;
-import com.android.internal.widget.ActionBarContainer;
-import com.android.internal.widget.ActionBarContextView;
-import com.android.internal.widget.ActionBarOverlayLayout;
-import com.android.internal.widget.DecorToolbar;
-import com.android.internal.widget.ScrollingTabContainerView;
-
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
-import android.annotation.UnsupportedAppUsage;
import android.app.ActionBar;
import android.app.Activity;
import android.app.Dialog;
import android.app.FragmentTransaction;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -58,6 +47,17 @@
import android.widget.SpinnerAdapter;
import android.widget.Toolbar;
+import com.android.internal.R;
+import com.android.internal.view.ActionBarPolicy;
+import com.android.internal.view.menu.MenuBuilder;
+import com.android.internal.view.menu.MenuPopupHelper;
+import com.android.internal.view.menu.SubMenuBuilder;
+import com.android.internal.widget.ActionBarContainer;
+import com.android.internal.widget.ActionBarContextView;
+import com.android.internal.widget.ActionBarOverlayLayout;
+import com.android.internal.widget.DecorToolbar;
+import com.android.internal.widget.ScrollingTabContainerView;
+
import java.lang.ref.WeakReference;
import java.util.ArrayList;
diff --git a/core/java/com/android/internal/compat/AndroidBuildClassifier.java b/core/java/com/android/internal/compat/AndroidBuildClassifier.java
new file mode 100644
index 0000000..0b937fa
--- /dev/null
+++ b/core/java/com/android/internal/compat/AndroidBuildClassifier.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.compat;
+
+import android.os.Build;
+
+/**
+ * Platform private class for determining the type of Android build installed.
+ *
+ */
+public class AndroidBuildClassifier {
+
+ public boolean isDebuggableBuild() {
+ return Build.IS_DEBUGGABLE;
+ }
+
+ public boolean isFinalBuild() {
+ return "REL".equals(Build.VERSION.CODENAME);
+ }
+}
diff --git a/core/java/com/android/internal/compat/IOverrideValidator.aidl b/core/java/com/android/internal/compat/IOverrideValidator.aidl
new file mode 100644
index 0000000..add4708
--- /dev/null
+++ b/core/java/com/android/internal/compat/IOverrideValidator.aidl
@@ -0,0 +1,38 @@
+/*
+ * 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.compat;
+
+import android.content.pm.ApplicationInfo;
+
+import com.android.internal.compat.OverrideAllowedState;
+
+/**
+ * Platform private API for determining whether a changeId can be overridden.
+ *
+ * {@hide}
+ */
+interface IOverrideValidator
+{
+ /**
+ * Validation function.
+ * @param changeId id of the change to be toggled on or off.
+ * @param packageName package of the app for which the change should be overridden.
+ * @return {@link OverrideAllowedState} specifying whether the change can be overridden for
+ * the given package or a reason why not.
+ */
+ OverrideAllowedState getOverrideAllowedState(long changeId, String packageName);
+}
diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl
index 7dcb12c..4c203d3 100644
--- a/core/java/com/android/internal/compat/IPlatformCompat.aidl
+++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl
@@ -17,6 +17,7 @@
package com.android.internal.compat;
import android.content.pm.ApplicationInfo;
+import com.android.internal.compat.IOverrideValidator;
import java.util.Map;
parcelable CompatibilityChangeConfig;
@@ -195,4 +196,9 @@
* @return An array of {@link CompatChangeInfo} known to the service.
*/
CompatibilityChangeInfo[] listAllChanges();
+
+ /**
+ * Get an instance that can determine whether a changeid can be overridden for a package name.
+ */
+ IOverrideValidator getOverrideValidator();
}
diff --git a/core/java/com/android/internal/compat/OverrideAllowedState.aidl b/core/java/com/android/internal/compat/OverrideAllowedState.aidl
new file mode 100644
index 0000000..10ceac7
--- /dev/null
+++ b/core/java/com/android/internal/compat/OverrideAllowedState.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.compat;
+
+parcelable OverrideAllowedState;
\ No newline at end of file
diff --git a/core/java/com/android/internal/compat/OverrideAllowedState.java b/core/java/com/android/internal/compat/OverrideAllowedState.java
new file mode 100644
index 0000000..56216c2
--- /dev/null
+++ b/core/java/com/android/internal/compat/OverrideAllowedState.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.compat;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This class contains all the possible override allowed states.
+ */
+public final class OverrideAllowedState implements Parcelable {
+ @IntDef({
+ ALLOWED,
+ DISABLED_NOT_DEBUGGABLE,
+ DISABLED_NON_TARGET_SDK,
+ DISABLED_TARGET_SDK_TOO_HIGH,
+ PACKAGE_DOES_NOT_EXIST
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface State {
+ }
+
+ /**
+ * Change can be overridden.
+ */
+ public static final int ALLOWED = 0;
+ /**
+ * Change cannot be overridden, due to the app not being debuggable.
+ */
+ public static final int DISABLED_NOT_DEBUGGABLE = 1;
+ /**
+ * Change cannot be overridden, due to the build being non-debuggable and the change being
+ * non-targetSdk.
+ */
+ public static final int DISABLED_NON_TARGET_SDK = 2;
+ /**
+ * Change cannot be overridden, due to the app's targetSdk being above the change's targetSdk.
+ */
+ public static final int DISABLED_TARGET_SDK_TOO_HIGH = 3;
+ /**
+ * Package does not exist.
+ */
+ public static final int PACKAGE_DOES_NOT_EXIST = 4;
+
+ @State
+ public final int state;
+ public final int appTargetSdk;
+ public final int changeIdTargetSdk;
+
+ private OverrideAllowedState(Parcel parcel) {
+ state = parcel.readInt();
+ appTargetSdk = parcel.readInt();
+ changeIdTargetSdk = parcel.readInt();
+ }
+
+ public OverrideAllowedState(@State int state, int appTargetSdk, int changeIdTargetSdk) {
+ this.state = state;
+ this.appTargetSdk = appTargetSdk;
+ this.changeIdTargetSdk = changeIdTargetSdk;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(state);
+ out.writeInt(appTargetSdk);
+ out.writeInt(changeIdTargetSdk);
+ }
+
+ /**
+ * Enforces the policy for overriding compat changes.
+ *
+ * @param changeId the change id that was attempted to be overridden.
+ * @param packageName the package for which the attempt was made.
+ * @throws SecurityException if the policy forbids this operation.
+ */
+ public void enforce(long changeId, String packageName)
+ throws SecurityException {
+ switch (state) {
+ case ALLOWED:
+ return;
+ case DISABLED_NOT_DEBUGGABLE:
+ throw new SecurityException(
+ "Cannot override a change on a non-debuggable app and user build.");
+ case DISABLED_NON_TARGET_SDK:
+ throw new SecurityException(
+ "Cannot override a default enabled/disabled change on a user build.");
+ case DISABLED_TARGET_SDK_TOO_HIGH:
+ throw new SecurityException(String.format(
+ "Cannot override %1$d for %2$s because the app's targetSdk (%3$d) is "
+ + "above the change's targetSdk threshold (%4$d)",
+ changeId, packageName, appTargetSdk, changeIdTargetSdk));
+ case PACKAGE_DOES_NOT_EXIST:
+ throw new SecurityException(String.format(
+ "Cannot override %1$d for %2$s because the package does not exist, and "
+ + "the change is targetSdk gated.",
+ changeId, packageName));
+ }
+ }
+
+ public static final @NonNull
+ Parcelable.Creator<OverrideAllowedState> CREATOR =
+ new Parcelable.Creator<OverrideAllowedState>() {
+ public OverrideAllowedState createFromParcel(Parcel parcel) {
+ OverrideAllowedState info = new OverrideAllowedState(parcel);
+ return info;
+ }
+
+ public OverrideAllowedState[] newArray(int size) {
+ return new OverrideAllowedState[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof OverrideAllowedState)) {
+ return false;
+ }
+ OverrideAllowedState otherState = (OverrideAllowedState) obj;
+ return state == otherState.state
+ && appTargetSdk == otherState.appTargetSdk
+ && changeIdTargetSdk == otherState.changeIdTargetSdk;
+ }
+}
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java
index 6b76a0f..3682b7b 100644
--- a/core/java/com/android/internal/content/PackageMonitor.java
+++ b/core/java/com/android/internal/content/PackageMonitor.java
@@ -16,8 +16,8 @@
package com.android.internal.content;
-import android.annotation.UnsupportedAppUsage;
import android.app.Activity;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
diff --git a/core/java/com/android/internal/content/ReferrerIntent.java b/core/java/com/android/internal/content/ReferrerIntent.java
index 6d05f7e..6af03dd 100644
--- a/core/java/com/android/internal/content/ReferrerIntent.java
+++ b/core/java/com/android/internal/content/ReferrerIntent.java
@@ -16,7 +16,7 @@
package com.android.internal.content;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Intent;
import android.os.Parcel;
diff --git a/core/java/com/android/internal/database/SortCursor.java b/core/java/com/android/internal/database/SortCursor.java
index 7fe809e..230a9b87 100644
--- a/core/java/com/android/internal/database/SortCursor.java
+++ b/core/java/com/android/internal/database/SortCursor.java
@@ -16,7 +16,7 @@
package com.android.internal.database;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.database.AbstractCursor;
import android.database.Cursor;
import android.database.DataSetObserver;
diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java
index f916cf6..ed04fd8 100644
--- a/core/java/com/android/internal/logging/MetricsLogger.java
+++ b/core/java/com/android/internal/logging/MetricsLogger.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.logging;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.metrics.LogMaker;
import android.os.Build;
diff --git a/core/java/com/android/internal/net/LegacyVpnInfo.java b/core/java/com/android/internal/net/LegacyVpnInfo.java
index 2ad9759..4eb7ded 100644
--- a/core/java/com/android/internal/net/LegacyVpnInfo.java
+++ b/core/java/com/android/internal/net/LegacyVpnInfo.java
@@ -16,8 +16,8 @@
package com.android.internal.net;
-import android.annotation.UnsupportedAppUsage;
import android.app.PendingIntent;
+import android.compat.annotation.UnsupportedAppUsage;
import android.net.NetworkInfo;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java
index e6be549..f5a19fe 100644
--- a/core/java/com/android/internal/net/VpnConfig.java
+++ b/core/java/com/android/internal/net/VpnConfig.java
@@ -16,8 +16,8 @@
package com.android.internal.net;
-import android.annotation.UnsupportedAppUsage;
import android.app.PendingIntent;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
diff --git a/core/java/com/android/internal/net/VpnProfile.java b/core/java/com/android/internal/net/VpnProfile.java
index 940cc36..4bb012a 100644
--- a/core/java/com/android/internal/net/VpnProfile.java
+++ b/core/java/com/android/internal/net/VpnProfile.java
@@ -16,10 +16,9 @@
package com.android.internal.net;
-import android.annotation.UnsupportedAppUsage;
-import android.os.Build;
+import android.compat.annotation.UnsupportedAppUsage;
import android.net.ProxyInfo;
-import android.net.Uri;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
diff --git a/core/java/com/android/internal/os/AndroidPrintStream.java b/core/java/com/android/internal/os/AndroidPrintStream.java
index fe23411..a6e41ff 100644
--- a/core/java/com/android/internal/os/AndroidPrintStream.java
+++ b/core/java/com/android/internal/os/AndroidPrintStream.java
@@ -16,7 +16,7 @@
package com.android.internal.os;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.util.Log;
/**
diff --git a/core/java/com/android/internal/os/BaseCommand.java b/core/java/com/android/internal/os/BaseCommand.java
index 3d027c5..32442f0 100644
--- a/core/java/com/android/internal/os/BaseCommand.java
+++ b/core/java/com/android/internal/os/BaseCommand.java
@@ -17,7 +17,7 @@
package com.android.internal.os;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.BasicShellCommandHandler;
import java.io.PrintStream;
diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java
index b1fc369..b3ea118 100644
--- a/core/java/com/android/internal/os/BatterySipper.java
+++ b/core/java/com/android/internal/os/BatterySipper.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.os;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.BatteryStats.Uid;
import java.util.List;
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index e85508e..b131ab8 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -16,7 +16,7 @@
package com.android.internal.os;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index d0a83c4..b3548b8 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -21,10 +21,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
import android.app.ActivityManager;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.bluetooth.UidTraffic;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
diff --git a/core/java/com/android/internal/os/BinderInternal.java b/core/java/com/android/internal/os/BinderInternal.java
index 4901e1f..95c36ca 100644
--- a/core/java/com/android/internal/os/BinderInternal.java
+++ b/core/java/com/android/internal/os/BinderInternal.java
@@ -17,7 +17,7 @@
package com.android.internal.os;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
diff --git a/core/java/com/android/internal/os/ClassLoaderFactory.java b/core/java/com/android/internal/os/ClassLoaderFactory.java
index d323498..a18943c 100644
--- a/core/java/com/android/internal/os/ClassLoaderFactory.java
+++ b/core/java/com/android/internal/os/ClassLoaderFactory.java
@@ -16,7 +16,7 @@
package com.android.internal.os;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Trace;
import dalvik.system.DelegateLastClassLoader;
diff --git a/core/java/com/android/internal/os/FuseAppLoop.java b/core/java/com/android/internal/os/FuseAppLoop.java
index a22615b..ab0cc30 100644
--- a/core/java/com/android/internal/os/FuseAppLoop.java
+++ b/core/java/com/android/internal/os/FuseAppLoop.java
@@ -18,17 +18,19 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
-import android.os.ProxyFileDescriptorCallback;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelFileDescriptor;
+import android.os.ProxyFileDescriptorCallback;
import android.system.ErrnoException;
import android.system.OsConstants;
import android.util.Log;
import android.util.SparseArray;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
+
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
diff --git a/core/java/com/android/internal/os/HandlerCaller.java b/core/java/com/android/internal/os/HandlerCaller.java
index c8bfa1b..a11c815 100644
--- a/core/java/com/android/internal/os/HandlerCaller.java
+++ b/core/java/com/android/internal/os/HandlerCaller.java
@@ -16,7 +16,7 @@
package com.android.internal.os;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index 0faf962..3db8f4e 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -17,7 +17,7 @@
package com.android.internal.os;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
diff --git a/core/java/com/android/internal/os/ProcessCpuTracker.java b/core/java/com/android/internal/os/ProcessCpuTracker.java
index dcf8d28..5e3c5df 100644
--- a/core/java/com/android/internal/os/ProcessCpuTracker.java
+++ b/core/java/com/android/internal/os/ProcessCpuTracker.java
@@ -16,9 +16,15 @@
package com.android.internal.os;
-import static android.os.Process.*;
+import static android.os.Process.PROC_COMBINE;
+import static android.os.Process.PROC_OUT_FLOAT;
+import static android.os.Process.PROC_OUT_LONG;
+import static android.os.Process.PROC_OUT_STRING;
+import static android.os.Process.PROC_PARENS;
+import static android.os.Process.PROC_SPACE_TERM;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.CpuUsageProto;
import android.os.Process;
import android.os.StrictMode;
import android.os.SystemClock;
@@ -26,10 +32,12 @@
import android.system.Os;
import android.system.OsConstants;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.util.FastPrintWriter;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
@@ -735,6 +743,63 @@
return mWorkingProcs.get(index);
}
+ /** Dump cpuinfo in protobuf format. */
+ public final void dumpProto(FileDescriptor fd) {
+ final long now = SystemClock.uptimeMillis();
+ final ProtoOutputStream proto = new ProtoOutputStream(fd);
+ final long currentLoadToken = proto.start(CpuUsageProto.CURRENT_LOAD);
+ proto.write(CpuUsageProto.Load.LOAD1, mLoad1);
+ proto.write(CpuUsageProto.Load.LOAD5, mLoad5);
+ proto.write(CpuUsageProto.Load.LOAD15, mLoad15);
+ proto.end(currentLoadToken);
+
+ proto.write(CpuUsageProto.NOW, now);
+ proto.write(CpuUsageProto.LAST_SAMPLE_TIME, mLastSampleTime);
+ proto.write(CpuUsageProto.CURRENT_SAMPLE_TIME, mCurrentSampleTime);
+ proto.write(CpuUsageProto.LAST_SAMPLE_REAL_TIME, mLastSampleRealTime);
+ proto.write(CpuUsageProto.CURRENT_SAMPLE_REAL_TIME, mCurrentSampleRealTime);
+ proto.write(CpuUsageProto.LAST_SAMPLE_WALL_TIME, mLastSampleWallTime);
+ proto.write(CpuUsageProto.CURRENT_SAMPLE_WALL_TIME, mCurrentSampleWallTime);
+
+ proto.write(CpuUsageProto.TOTAL_USER_TIME, mRelUserTime);
+ proto.write(CpuUsageProto.TOTAL_SYSTEM_TIME, mRelSystemTime);
+ proto.write(CpuUsageProto.TOTAL_IOWAIT_TIME, mRelIoWaitTime);
+ proto.write(CpuUsageProto.TOTAL_IRQ_TIME, mRelIrqTime);
+ proto.write(CpuUsageProto.TOTAL_SOFT_IRQ_TIME, mRelSoftIrqTime);
+ proto.write(CpuUsageProto.TOTAL_IDLE_TIME, mRelIdleTime);
+ final int totalTime = mRelUserTime + mRelSystemTime + mRelIoWaitTime
+ + mRelIrqTime + mRelSoftIrqTime + mRelIdleTime;
+ proto.write(CpuUsageProto.TOTAL_TIME, totalTime);
+
+ for (Stats st : mWorkingProcs) {
+ dumpProcessCpuProto(proto, st, null);
+ if (!st.removed && st.workingThreads != null) {
+ for (Stats tst : st.workingThreads) {
+ dumpProcessCpuProto(proto, tst, st);
+ }
+ }
+ }
+ proto.flush();
+ }
+
+ private static void dumpProcessCpuProto(ProtoOutputStream proto, Stats st, Stats proc) {
+ long statToken = proto.start(CpuUsageProto.PROCESSES);
+ proto.write(CpuUsageProto.Stat.UID, st.uid);
+ proto.write(CpuUsageProto.Stat.PID, st.pid);
+ proto.write(CpuUsageProto.Stat.NAME, st.name);
+ proto.write(CpuUsageProto.Stat.ADDED, st.added);
+ proto.write(CpuUsageProto.Stat.REMOVED, st.removed);
+ proto.write(CpuUsageProto.Stat.UPTIME, st.rel_uptime);
+ proto.write(CpuUsageProto.Stat.USER_TIME, st.rel_utime);
+ proto.write(CpuUsageProto.Stat.SYSTEM_TIME, st.rel_stime);
+ proto.write(CpuUsageProto.Stat.MINOR_FAULTS, st.rel_minfaults);
+ proto.write(CpuUsageProto.Stat.MAJOR_FAULTS, st.rel_majfaults);
+ if (proc != null) {
+ proto.write(CpuUsageProto.Stat.PARENT_PID, proc.pid);
+ }
+ proto.end(statToken);
+ }
+
final public String printCurrentLoad() {
StringWriter sw = new StringWriter();
PrintWriter pw = new FastPrintWriter(sw, false, 128);
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index fa823c4..179828c 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -16,10 +16,10 @@
package com.android.internal.os;
-import android.annotation.UnsupportedAppUsage;
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.ApplicationErrorReport;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.type.DefaultMimeMapFactory;
import android.os.Build;
import android.os.DeadObjectException;
diff --git a/core/java/com/android/internal/os/SomeArgs.java b/core/java/com/android/internal/os/SomeArgs.java
index d78bfac..003f610 100644
--- a/core/java/com/android/internal/os/SomeArgs.java
+++ b/core/java/com/android/internal/os/SomeArgs.java
@@ -16,7 +16,7 @@
package com.android.internal.os;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
/**
* Helper class for passing more arguments though a message
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 9c6a288..c91c661 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -23,7 +23,7 @@
import static com.android.internal.os.ZygoteConnectionConstants.CONNECTION_TIMEOUT_MILLIS;
import static com.android.internal.os.ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.ApplicationInfo;
import android.net.Credentials;
import android.net.LocalSocket;
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 49b4cf8..7b6262b 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -19,8 +19,8 @@
import static android.system.OsConstants.S_IRWXG;
import static android.system.OsConstants.S_IRWXO;
-import android.annotation.UnsupportedAppUsage;
import android.app.ApplicationLoaders;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.SharedLibraryInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
diff --git a/core/java/com/android/internal/os/ZygoteSecurityException.java b/core/java/com/android/internal/os/ZygoteSecurityException.java
index 7e50cb8..8111483 100644
--- a/core/java/com/android/internal/os/ZygoteSecurityException.java
+++ b/core/java/com/android/internal/os/ZygoteSecurityException.java
@@ -16,7 +16,7 @@
package com.android.internal.os;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
/**
* Exception thrown when a security policy is violated.
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 29b148c..0170978 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -48,8 +48,8 @@
import android.animation.ObjectAnimator;
import android.annotation.Nullable;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
import android.app.WindowConfiguration;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
diff --git a/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java b/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java
index 791c2d7..f07f667 100644
--- a/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java
+++ b/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java
@@ -16,9 +16,9 @@
package com.android.internal.policy;
-import android.annotation.UnsupportedAppUsage;
import android.app.KeyguardManager;
import android.app.SearchManager;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
@@ -33,7 +33,6 @@
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.View;
-import com.android.internal.policy.PhoneWindow;
/**
* @hide
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 9aa56f0..88a9cb0 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -32,10 +32,10 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
import android.app.ActivityManager;
import android.app.KeyguardManager;
import android.app.SearchManager;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
diff --git a/core/java/com/android/internal/preference/YesNoPreference.java b/core/java/com/android/internal/preference/YesNoPreference.java
index 46d14a1..f878054 100644
--- a/core/java/com/android/internal/preference/YesNoPreference.java
+++ b/core/java/com/android/internal/preference/YesNoPreference.java
@@ -16,7 +16,7 @@
package com.android.internal.preference;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Parcel;
diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 6c7e3dc..6fd271c 100644
--- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -62,4 +62,6 @@
void onOutgoingEmergencySms(in EmergencyNumber sentEmergencyNumber);
void onCallDisconnectCauseChanged(in int disconnectCause, in int preciseDisconnectCause);
void onImsCallDisconnectCauseChanged(in ImsReasonInfo imsReasonInfo);
+ void onRegistrationFailed(in CellIdentity cellIdentity,
+ String chosenPlmn, int domain, int causeCode, int additionalCauseCode);
}
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 4e40503..8e97ae1 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -63,7 +63,7 @@
void notifyDataActivity(int state);
void notifyDataActivityForSubscriber(in int subId, int state);
void notifyDataConnectionForSubscriber(
- int phoneId, int subId, String apnType, in PreciseDataConnectionState preciseState);
+ int phoneId, int subId, int apnType, in PreciseDataConnectionState preciseState);
@UnsupportedAppUsage
void notifyDataConnectionFailed(String apnType);
// Uses CellIdentity which is Parcelable here; will convert to CellLocation in client.
@@ -75,7 +75,7 @@
int foregroundCallState, int backgroundCallState);
void notifyDisconnectCause(int phoneId, int subId, int disconnectCause,
int preciseDisconnectCause);
- void notifyPreciseDataConnectionFailed(int phoneId, int subId, String apnType, String apn,
+ void notifyPreciseDataConnectionFailed(int phoneId, int subId, int apnType, String apn,
int failCause);
void notifyCellInfoForSubscriber(in int subId, in List<CellInfo> cellInfo);
void notifySrvccStateChanged(in int subId, in int lteState);
@@ -97,4 +97,6 @@
void notifyCallQualityChanged(in CallQuality callQuality, int phoneId, int subId,
int callNetworkType);
void notifyImsDisconnectCause(int subId, in ImsReasonInfo imsReasonInfo);
+ void notifyRegistrationFailed(int slotIndex, int subId, in CellIdentity cellIdentity,
+ String chosenPlmn, int domain, int causeCode, int additionalCauseCode);
}
diff --git a/telephony/java/com/android/internal/telephony/IWapPushManager.aidl b/core/java/com/android/internal/telephony/IWapPushManager.aidl
similarity index 82%
rename from telephony/java/com/android/internal/telephony/IWapPushManager.aidl
rename to core/java/com/android/internal/telephony/IWapPushManager.aidl
index 1c3df6533..9f6851b 100644
--- a/telephony/java/com/android/internal/telephony/IWapPushManager.aidl
+++ b/core/java/com/android/internal/telephony/IWapPushManager.aidl
@@ -18,6 +18,7 @@
import android.content.Intent;
+/** @hide */
interface IWapPushManager {
/**
* Processes WAP push message and triggers the receiver application registered
@@ -26,11 +27,10 @@
int processMessage(String app_id, String content_type, in Intent intent);
/**
- * Add receiver application into the application ID table.
- * Returns true if inserting the information is successfull. Inserting the duplicated
+ * Adds receiver application into the application ID table.
+ * Returns true if inserting the information is successful. Inserting duplicated
* record in the application ID table is not allowed. Use update/delete method.
*/
- @UnsupportedAppUsage
boolean addPackage(String x_app_id, String content_type,
String package_name, String class_name,
int app_type, boolean need_signature, boolean further_processing);
@@ -39,17 +39,14 @@
* Updates receiver application that is last added.
* Returns true if updating the information is successfull.
*/
- @UnsupportedAppUsage
boolean updatePackage(String x_app_id, String content_type,
String package_name, String class_name,
int app_type, boolean need_signature, boolean further_processing);
/**
- * Delites receiver application information.
+ * Deletes receiver application information.
* Returns true if deleting is successfull.
*/
- @UnsupportedAppUsage
boolean deletePackage(String x_app_id, String content_type,
- String package_name, String class_name);
+ String package_name, String class_name);
}
-
diff --git a/core/java/com/android/internal/telephony/WapPushManagerParams.java b/core/java/com/android/internal/telephony/WapPushManagerParams.java
new file mode 100644
index 0000000..eafb8f1
--- /dev/null
+++ b/core/java/com/android/internal/telephony/WapPushManagerParams.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+
+import android.telephony.WapPushManagerConnector;
+
+/**
+ * WapPushManager constant value definitions.
+ * @hide
+ */
+public class WapPushManagerParams {
+ /**
+ * Application type activity
+ */
+ public static final int APP_TYPE_ACTIVITY = 0;
+
+ /**
+ * Application type service
+ */
+ public static final int APP_TYPE_SERVICE = 1;
+
+ /**
+ * Process Message return value
+ * Message is handled
+ */
+ public static final int MESSAGE_HANDLED = WapPushManagerConnector.RESULT_MESSAGE_HANDLED;
+
+ /**
+ * Process Message return value
+ * Application ID or content type was not found in the application ID table
+ */
+ public static final int APP_QUERY_FAILED = WapPushManagerConnector.RESULT_APP_QUERY_FAILED;
+
+ /**
+ * Process Message return value
+ * Receiver application signature check failed
+ */
+ public static final int SIGNATURE_NO_MATCH = WapPushManagerConnector.RESULT_SIGNATURE_NO_MATCH;
+
+ /**
+ * Process Message return value
+ * Receiver application was not found
+ */
+ public static final int INVALID_RECEIVER_NAME =
+ WapPushManagerConnector.RESULT_INVALID_RECEIVER_NAME;
+
+ /**
+ * Process Message return value
+ * Unknown exception
+ */
+ public static final int EXCEPTION_CAUGHT = WapPushManagerConnector.RESULT_EXCEPTION_CAUGHT;
+
+ /**
+ * Process Message return value
+ * Need further processing after WapPushManager message processing
+ */
+ public static final int FURTHER_PROCESSING = WapPushManagerConnector.RESULT_FURTHER_PROCESSING;
+}
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index c7ec2cd..cd69a02 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -18,7 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.util.ArraySet;
import dalvik.system.VMRuntime;
diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java
index 121ae1a..7e3c171 100644
--- a/core/java/com/android/internal/util/AsyncChannel.java
+++ b/core/java/com/android/internal/util/AsyncChannel.java
@@ -16,7 +16,7 @@
package com.android.internal.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
diff --git a/core/java/com/android/internal/util/BitwiseInputStream.java b/core/java/com/android/internal/util/BitwiseInputStream.java
index 6ff67e9..ffae3ce 100644
--- a/core/java/com/android/internal/util/BitwiseInputStream.java
+++ b/core/java/com/android/internal/util/BitwiseInputStream.java
@@ -16,7 +16,7 @@
package com.android.internal.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
/**
* An object that provides bitwise incremental read access to a byte array.
diff --git a/core/java/com/android/internal/util/BitwiseOutputStream.java b/core/java/com/android/internal/util/BitwiseOutputStream.java
index cdd6f17..9f41508 100644
--- a/core/java/com/android/internal/util/BitwiseOutputStream.java
+++ b/core/java/com/android/internal/util/BitwiseOutputStream.java
@@ -16,7 +16,7 @@
package com.android.internal.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
/**
* An object that provides bitwise incremental write access to a byte array.
diff --git a/core/java/com/android/internal/util/CharSequences.java b/core/java/com/android/internal/util/CharSequences.java
index 6b6c43c..82ef200 100644
--- a/core/java/com/android/internal/util/CharSequences.java
+++ b/core/java/com/android/internal/util/CharSequences.java
@@ -16,7 +16,7 @@
package com.android.internal.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
/**
* {@link CharSequence} utility methods.
diff --git a/core/java/com/android/internal/util/FastMath.java b/core/java/com/android/internal/util/FastMath.java
index 35efe70..b7dbee5 100644
--- a/core/java/com/android/internal/util/FastMath.java
+++ b/core/java/com/android/internal/util/FastMath.java
@@ -16,7 +16,7 @@
package com.android.internal.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
/**
* Fast and loose math routines.
diff --git a/core/java/com/android/internal/util/FastPrintWriter.java b/core/java/com/android/internal/util/FastPrintWriter.java
index 981fbaa..63124de 100644
--- a/core/java/com/android/internal/util/FastPrintWriter.java
+++ b/core/java/com/android/internal/util/FastPrintWriter.java
@@ -16,7 +16,7 @@
package com.android.internal.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.util.Log;
import android.util.Printer;
diff --git a/core/java/com/android/internal/util/FastXmlSerializer.java b/core/java/com/android/internal/util/FastXmlSerializer.java
index 9f76aeb..94e07a8 100644
--- a/core/java/com/android/internal/util/FastXmlSerializer.java
+++ b/core/java/com/android/internal/util/FastXmlSerializer.java
@@ -16,9 +16,10 @@
package com.android.internal.util;
+import android.compat.annotation.UnsupportedAppUsage;
+
import org.xmlpull.v1.XmlSerializer;
-import android.annotation.UnsupportedAppUsage;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
diff --git a/core/java/com/android/internal/util/GrowingArrayUtils.java b/core/java/com/android/internal/util/GrowingArrayUtils.java
index 9f56366..597fe6b 100644
--- a/core/java/com/android/internal/util/GrowingArrayUtils.java
+++ b/core/java/com/android/internal/util/GrowingArrayUtils.java
@@ -16,7 +16,7 @@
package com.android.internal.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
/**
* A helper class that aims to provide comparable growth performance to ArrayList, but on primitive
diff --git a/core/java/com/android/internal/util/HexDump.java b/core/java/com/android/internal/util/HexDump.java
index 6ffc928..ad88dd6 100644
--- a/core/java/com/android/internal/util/HexDump.java
+++ b/core/java/com/android/internal/util/HexDump.java
@@ -17,7 +17,7 @@
package com.android.internal.util;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
public class HexDump
{
diff --git a/core/java/com/android/internal/util/IState.java b/core/java/com/android/internal/util/IState.java
index eb66e2c..07837bf 100644
--- a/core/java/com/android/internal/util/IState.java
+++ b/core/java/com/android/internal/util/IState.java
@@ -16,7 +16,7 @@
package com.android.internal.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Message;
/**
diff --git a/core/java/com/android/internal/util/IndentingPrintWriter.java b/core/java/com/android/internal/util/IndentingPrintWriter.java
index 03a555e..34c6a05 100644
--- a/core/java/com/android/internal/util/IndentingPrintWriter.java
+++ b/core/java/com/android/internal/util/IndentingPrintWriter.java
@@ -16,7 +16,8 @@
package com.android.internal.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
+
import java.io.PrintWriter;
import java.io.Writer;
import java.util.Arrays;
diff --git a/core/java/com/android/internal/util/JournaledFile.java b/core/java/com/android/internal/util/JournaledFile.java
index 065cc5b2..a9d8f72 100644
--- a/core/java/com/android/internal/util/JournaledFile.java
+++ b/core/java/com/android/internal/util/JournaledFile.java
@@ -16,7 +16,7 @@
package com.android.internal.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import java.io.File;
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index 27c2478..67cfc3a 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -121,7 +121,11 @@
}
public static boolean isEnabled(Context ctx) {
- return Build.IS_DEBUGGABLE && getInstance(ctx).mEnabled;
+ return getInstance(ctx).isEnabled();
+ }
+
+ public boolean isEnabled() {
+ return Build.IS_DEBUGGABLE && mEnabled;
}
/**
diff --git a/core/java/com/android/internal/util/MemInfoReader.java b/core/java/com/android/internal/util/MemInfoReader.java
index 580c2fa..5de77d9 100644
--- a/core/java/com/android/internal/util/MemInfoReader.java
+++ b/core/java/com/android/internal/util/MemInfoReader.java
@@ -16,7 +16,7 @@
package com.android.internal.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Debug;
import android.os.StrictMode;
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
index 3fff5c2..5bc96d8 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -18,7 +18,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.text.TextUtils;
import java.util.Collection;
diff --git a/core/java/com/android/internal/util/State.java b/core/java/com/android/internal/util/State.java
index 3c61e03..636378e 100644
--- a/core/java/com/android/internal/util/State.java
+++ b/core/java/com/android/internal/util/State.java
@@ -16,7 +16,7 @@
package com.android.internal.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Message;
/**
diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java
index 6c217e5..0c24065 100644
--- a/core/java/com/android/internal/util/StateMachine.java
+++ b/core/java/com/android/internal/util/StateMachine.java
@@ -16,7 +16,7 @@
package com.android.internal.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java
index 8799e3d..c1be33a 100644
--- a/core/java/com/android/internal/util/XmlUtils.java
+++ b/core/java/com/android/internal/util/XmlUtils.java
@@ -16,10 +16,10 @@
package com.android.internal.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
import android.graphics.Bitmap.CompressFormat;
+import android.graphics.BitmapFactory;
import android.net.Uri;
import android.text.TextUtils;
import android.util.ArrayMap;
diff --git a/core/java/com/android/internal/view/ActionBarPolicy.java b/core/java/com/android/internal/view/ActionBarPolicy.java
index d18c35e..d16cb43 100644
--- a/core/java/com/android/internal/view/ActionBarPolicy.java
+++ b/core/java/com/android/internal/view/ActionBarPolicy.java
@@ -16,15 +16,15 @@
package com.android.internal.view;
-import com.android.internal.R;
-
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.os.Build;
+import com.android.internal.R;
+
/**
* Allows components to query for various configuration policy decisions
* about how the action bar should lay out and behave on the current device.
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 2ac2975..5dd3389b 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -16,6 +16,7 @@
package com.android.internal.view;
+import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.input.InputManager;
@@ -34,8 +35,6 @@
import com.android.internal.os.IResultReceiver;
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
import java.io.IOException;
public class BaseIWindow extends IWindow.Stub {
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index ececba1..6278d4a3 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -18,7 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java
index 1b133d2..a5964b5 100644
--- a/core/java/com/android/internal/view/InputBindResult.java
+++ b/core/java/com/android/internal/view/InputBindResult.java
@@ -20,7 +20,7 @@
import android.annotation.IntDef;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java
index 0c057ea..a41048c 100644
--- a/core/java/com/android/internal/view/InputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/InputConnectionWrapper.java
@@ -19,7 +19,7 @@
import android.annotation.AnyThread;
import android.annotation.BinderThread;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.inputmethodservice.AbstractInputMethodService;
import android.os.Bundle;
import android.os.Handler;
diff --git a/core/java/com/android/internal/view/WindowManagerPolicyThread.java b/core/java/com/android/internal/view/WindowManagerPolicyThread.java
index b009a2d..6d691fc 100644
--- a/core/java/com/android/internal/view/WindowManagerPolicyThread.java
+++ b/core/java/com/android/internal/view/WindowManagerPolicyThread.java
@@ -16,7 +16,7 @@
package com.android.internal.view;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Looper;
/**
diff --git a/core/java/com/android/internal/view/menu/ActionMenu.java b/core/java/com/android/internal/view/menu/ActionMenu.java
index 977c1f6..6482629 100644
--- a/core/java/com/android/internal/view/menu/ActionMenu.java
+++ b/core/java/com/android/internal/view/menu/ActionMenu.java
@@ -16,10 +16,7 @@
package com.android.internal.view.menu;
-import java.util.ArrayList;
-import java.util.List;
-
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -30,6 +27,9 @@
import android.view.MenuItem;
import android.view.SubMenu;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* @hide
*/
diff --git a/core/java/com/android/internal/view/menu/ActionMenuItem.java b/core/java/com/android/internal/view/menu/ActionMenuItem.java
index ed253d5..bd8bcb9 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuItem.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuItem.java
@@ -17,7 +17,7 @@
package com.android.internal.view.menu;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
import android.content.res.ColorStateList;
diff --git a/core/java/com/android/internal/view/menu/ActionMenuItemView.java b/core/java/com/android/internal/view/menu/ActionMenuItemView.java
index eb94db3..7622b93 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuItemView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuItemView.java
@@ -16,7 +16,7 @@
package com.android.internal.view.menu;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
diff --git a/core/java/com/android/internal/view/menu/ContextMenuBuilder.java b/core/java/com/android/internal/view/menu/ContextMenuBuilder.java
index 3d3aceb..a9f5e47 100644
--- a/core/java/com/android/internal/view/menu/ContextMenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/ContextMenuBuilder.java
@@ -16,7 +16,7 @@
package com.android.internal.view.menu;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.IBinder;
diff --git a/core/java/com/android/internal/view/menu/IconMenuItemView.java b/core/java/com/android/internal/view/menu/IconMenuItemView.java
index 3d888d3..539c71e 100644
--- a/core/java/com/android/internal/view/menu/IconMenuItemView.java
+++ b/core/java/com/android/internal/view/menu/IconMenuItemView.java
@@ -16,13 +16,12 @@
package com.android.internal.view.menu;
-import com.android.internal.view.menu.MenuBuilder.ItemInvoker;
-
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.text.Layout;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.Gravity;
@@ -30,7 +29,8 @@
import android.view.View;
import android.view.ViewDebug;
import android.widget.TextView;
-import android.text.Layout;
+
+import com.android.internal.view.menu.MenuBuilder.ItemInvoker;
/**
* The item view for each item in the {@link IconMenuView}.
diff --git a/core/java/com/android/internal/view/menu/IconMenuView.java b/core/java/com/android/internal/view/menu/IconMenuView.java
index 6f26434..9e240db 100644
--- a/core/java/com/android/internal/view/menu/IconMenuView.java
+++ b/core/java/com/android/internal/view/menu/IconMenuView.java
@@ -16,9 +16,7 @@
package com.android.internal.view.menu;
-import com.android.internal.view.menu.MenuBuilder.ItemInvoker;
-
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -29,10 +27,12 @@
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.KeyEvent;
+import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
-import android.view.LayoutInflater;
+
+import com.android.internal.view.menu.MenuBuilder.ItemInvoker;
import java.util.ArrayList;
diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java
index 0e07ca7..b31ae38b 100644
--- a/core/java/com/android/internal/view/menu/MenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/MenuBuilder.java
@@ -18,7 +18,7 @@
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
diff --git a/core/java/com/android/internal/view/menu/MenuDialogHelper.java b/core/java/com/android/internal/view/menu/MenuDialogHelper.java
index 88d0a03..d02b8f6 100644
--- a/core/java/com/android/internal/view/menu/MenuDialogHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuDialogHelper.java
@@ -16,9 +16,9 @@
package com.android.internal.view.menu;
-import android.annotation.UnsupportedAppUsage;
import android.app.AlertDialog;
import android.app.Dialog;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.DialogInterface;
import android.os.IBinder;
import android.view.KeyEvent;
diff --git a/core/java/com/android/internal/view/menu/MenuItemImpl.java b/core/java/com/android/internal/view/menu/MenuItemImpl.java
index 994a9c1..218f518 100644
--- a/core/java/com/android/internal/view/menu/MenuItemImpl.java
+++ b/core/java/com/android/internal/view/menu/MenuItemImpl.java
@@ -16,10 +16,8 @@
package com.android.internal.view.menu;
-import com.android.internal.view.menu.MenuView.ItemView;
-
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
@@ -39,6 +37,8 @@
import android.view.ViewDebug;
import android.widget.LinearLayout;
+import com.android.internal.view.menu.MenuView.ItemView;
+
/**
* @hide
*/
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
index d00108e..bac6025 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -20,7 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StyleRes;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
diff --git a/core/java/com/android/internal/view/menu/MenuPresenter.java b/core/java/com/android/internal/view/menu/MenuPresenter.java
index c5df8ad..35b8fef 100644
--- a/core/java/com/android/internal/view/menu/MenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/MenuPresenter.java
@@ -18,7 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Parcelable;
import android.view.ViewGroup;
diff --git a/core/java/com/android/internal/view/menu/MenuView.java b/core/java/com/android/internal/view/menu/MenuView.java
index 67a5530..a31c820 100644
--- a/core/java/com/android/internal/view/menu/MenuView.java
+++ b/core/java/com/android/internal/view/menu/MenuView.java
@@ -16,10 +16,7 @@
package com.android.internal.view.menu;
-import com.android.internal.view.menu.MenuBuilder;
-import com.android.internal.view.menu.MenuItemImpl;
-
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.drawable.Drawable;
/**
diff --git a/core/java/com/android/internal/view/menu/SubMenuBuilder.java b/core/java/com/android/internal/view/menu/SubMenuBuilder.java
index cf6d974..6eb215e 100644
--- a/core/java/com/android/internal/view/menu/SubMenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/SubMenuBuilder.java
@@ -16,7 +16,7 @@
package com.android.internal.view.menu;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.Menu;
diff --git a/core/java/com/android/internal/widget/AbsActionBarView.java b/core/java/com/android/internal/widget/AbsActionBarView.java
index 9ccee7f..0f0c1a3 100644
--- a/core/java/com/android/internal/widget/AbsActionBarView.java
+++ b/core/java/com/android/internal/widget/AbsActionBarView.java
@@ -15,26 +15,25 @@
*/
package com.android.internal.widget;
-import com.android.internal.R;
-
-import android.util.TypedValue;
-import android.view.ContextThemeWrapper;
-import android.view.MotionEvent;
-import android.widget.ActionMenuPresenter;
-import android.widget.ActionMenuView;
-
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.ContextThemeWrapper;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
+import android.widget.ActionMenuPresenter;
+import android.widget.ActionMenuView;
+
+import com.android.internal.R;
public abstract class AbsActionBarView extends ViewGroup {
private static final TimeInterpolator sAlphaInterpolator = new DecelerateInterpolator();
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index 78ed53f..051526e 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -15,13 +15,7 @@
*/
package com.android.internal.widget;
-import com.android.internal.R;
-
-import android.widget.ActionMenuPresenter;
-import android.widget.ActionMenuView;
-import com.android.internal.view.menu.MenuBuilder;
-
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
@@ -32,9 +26,14 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
+import android.widget.ActionMenuPresenter;
+import android.widget.ActionMenuView;
import android.widget.LinearLayout;
import android.widget.TextView;
+import com.android.internal.R;
+import com.android.internal.view.menu.MenuBuilder;
+
/**
* @hide
*/
diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
index e9e3cda..aca0b71 100644
--- a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
+++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
@@ -18,7 +18,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
@@ -41,6 +41,7 @@
import android.view.WindowInsets;
import android.widget.OverScroller;
import android.widget.Toolbar;
+
import com.android.internal.view.menu.MenuPresenter;
/**
diff --git a/core/java/com/android/internal/widget/AlertDialogLayout.java b/core/java/com/android/internal/widget/AlertDialogLayout.java
index 7a01749..d879b6d 100644
--- a/core/java/com/android/internal/widget/AlertDialogLayout.java
+++ b/core/java/com/android/internal/widget/AlertDialogLayout.java
@@ -18,8 +18,8 @@
import android.annotation.AttrRes;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
import android.annotation.StyleRes;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
diff --git a/core/java/com/android/internal/widget/ButtonBarLayout.java b/core/java/com/android/internal/widget/ButtonBarLayout.java
index 0ca6743..ff13107 100644
--- a/core/java/com/android/internal/widget/ButtonBarLayout.java
+++ b/core/java/com/android/internal/widget/ButtonBarLayout.java
@@ -16,7 +16,7 @@
package com.android.internal.widget;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
diff --git a/core/java/com/android/internal/widget/CachingIconView.java b/core/java/com/android/internal/widget/CachingIconView.java
index 35bff6d..74ad815 100644
--- a/core/java/com/android/internal/widget/CachingIconView.java
+++ b/core/java/com/android/internal/widget/CachingIconView.java
@@ -18,7 +18,7 @@
import android.annotation.DrawableRes;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Bitmap;
diff --git a/core/java/com/android/internal/widget/DialogTitle.java b/core/java/com/android/internal/widget/DialogTitle.java
index 405436c..0bfd684 100644
--- a/core/java/com/android/internal/widget/DialogTitle.java
+++ b/core/java/com/android/internal/widget/DialogTitle.java
@@ -16,7 +16,7 @@
package com.android.internal.widget;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
import android.text.Layout;
diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java
index 2b648e9..ff3543c8 100644
--- a/core/java/com/android/internal/widget/EditableInputConnection.java
+++ b/core/java/com/android/internal/widget/EditableInputConnection.java
@@ -16,7 +16,7 @@
package com.android.internal.widget;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Bundle;
import android.text.Editable;
import android.text.method.KeyListener;
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 13cc98b..de02eb0 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -88,4 +88,5 @@
in byte[] recoveryKeyBlob,
in List<WrappedApplicationKey> applicationKeys);
void closeSession(in String sessionId);
+ boolean hasSecureLockScreen();
}
diff --git a/core/java/com/android/internal/widget/LinearLayoutWithDefaultTouchRecepient.java b/core/java/com/android/internal/widget/LinearLayoutWithDefaultTouchRecepient.java
index cc7911d..9ef9f69 100644
--- a/core/java/com/android/internal/widget/LinearLayoutWithDefaultTouchRecepient.java
+++ b/core/java/com/android/internal/widget/LinearLayoutWithDefaultTouchRecepient.java
@@ -16,12 +16,12 @@
package com.android.internal.widget;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
-import android.view.View;
import android.view.MotionEvent;
+import android.view.View;
import android.widget.LinearLayout;
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index cff39f1..f37a468 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -25,15 +25,14 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
import android.app.admin.DevicePolicyManager;
import android.app.admin.PasswordMetrics;
import android.app.trust.IStrongAuthTracker;
import android.app.trust.TrustManager;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.AsyncTask;
import android.os.Handler;
@@ -56,10 +55,10 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
-import com.google.android.collect.Lists;
-
import libcore.util.HexEncoding;
+import com.google.android.collect.Lists;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.security.MessageDigest;
@@ -88,7 +87,6 @@
*/
public static final long FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS = 1000L;
-
/**
* This dictates when we start telling the user that continued failed attempts will wipe
* their device.
@@ -1601,6 +1599,11 @@
public static final int STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN = 0x20;
/**
+ * Strong authentication is required to prepare for unattended upgrade.
+ */
+ public static final int STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE = 0x40;
+
+ /**
* Strong auth flags that do not prevent biometric methods from being accepted as auth.
* If any other flags are set, biometric authentication is disabled.
*/
@@ -1735,8 +1738,11 @@
*/
public boolean hasSecureLockScreen() {
if (mHasSecureLockScreen == null) {
- mHasSecureLockScreen = Boolean.valueOf(mContext.getPackageManager()
- .hasSystemFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN));
+ try {
+ mHasSecureLockScreen = Boolean.valueOf(getLockSettings().hasSecureLockScreen());
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
}
return mHasSecureLockScreen.booleanValue();
}
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 74a0aa3..4ddc782 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -19,7 +19,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
diff --git a/core/java/com/android/internal/widget/LockSettingsInternal.java b/core/java/com/android/internal/widget/LockSettingsInternal.java
index dd05576..90a18ef 100644
--- a/core/java/com/android/internal/widget/LockSettingsInternal.java
+++ b/core/java/com/android/internal/widget/LockSettingsInternal.java
@@ -77,4 +77,34 @@
* @return the user password metrics.
*/
public abstract @Nullable PasswordMetrics getUserPasswordMetrics(int userHandle);
+
+ /**
+ * Prepare for reboot escrow. This triggers the strong auth to be required. After the escrow
+ * is complete as indicated by calling to the listener registered with {@link
+ * #setRebootEscrowListener}, then {@link #armRebootEscrow()} should be called before
+ * rebooting to apply the update.
+ */
+ public abstract void prepareRebootEscrow();
+
+ /**
+ * Registers a listener for when the RebootEscrow HAL has stored its data needed for rebooting
+ * for an OTA.
+ *
+ * @see RebootEscrowListener
+ * @param listener
+ */
+ public abstract void setRebootEscrowListener(RebootEscrowListener listener);
+
+ /**
+ * Requests that any data needed for rebooting is cleared from the RebootEscrow HAL.
+ */
+ public abstract void clearRebootEscrow();
+
+ /**
+ * Should be called immediately before rebooting for an update. This depends on {@link
+ * #prepareRebootEscrow()} having been called and the escrow completing.
+ *
+ * @return true if the arming worked
+ */
+ public abstract boolean armRebootEscrow();
}
diff --git a/core/java/com/android/internal/widget/LockscreenCredential.java b/core/java/com/android/internal/widget/LockscreenCredential.java
index 9b87dd2..55f30fb 100644
--- a/core/java/com/android/internal/widget/LockscreenCredential.java
+++ b/core/java/com/android/internal/widget/LockscreenCredential.java
@@ -308,7 +308,7 @@
@Override
public int hashCode() {
- // Effective Java — Item 9
+ // Effective Java — Always override hashCode when you override equals
return (17 + mType) * 31 + mCredential.hashCode();
}
diff --git a/core/java/com/android/internal/widget/NumericTextView.java b/core/java/com/android/internal/widget/NumericTextView.java
index d215670..c8f9011 100644
--- a/core/java/com/android/internal/widget/NumericTextView.java
+++ b/core/java/com/android/internal/widget/NumericTextView.java
@@ -16,7 +16,7 @@
package com.android.internal.widget;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index 37046af..dc8d57a 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -16,7 +16,7 @@
package com.android.internal.widget;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
diff --git a/core/java/com/android/internal/widget/PreferenceImageView.java b/core/java/com/android/internal/widget/PreferenceImageView.java
index 02a0b8d..43b6b5a 100644
--- a/core/java/com/android/internal/widget/PreferenceImageView.java
+++ b/core/java/com/android/internal/widget/PreferenceImageView.java
@@ -16,7 +16,7 @@
package com.android.internal.widget;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ImageView;
diff --git a/core/java/com/android/internal/widget/RebootEscrowListener.java b/core/java/com/android/internal/widget/RebootEscrowListener.java
new file mode 100644
index 0000000..1654532
--- /dev/null
+++ b/core/java/com/android/internal/widget/RebootEscrowListener.java
@@ -0,0 +1,32 @@
+/*
+ * 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.widget;
+
+/**
+ * Private API to be notified about reboot escrow events.
+ *
+ * {@hide}
+ */
+public interface RebootEscrowListener {
+ /**
+ * Called when the preparation status has changed. When {@code prepared} is {@code true} the
+ * user has entered their lock screen knowledge factor (LSKF) and the HAL has confirmed that
+ * it is ready to retrieve the secret after a reboot. When {@code prepared} is {@code false}
+ * then those conditions are not true.
+ */
+ void onPreparedForReboot(boolean prepared);
+}
diff --git a/core/java/com/android/internal/widget/RecyclerView.java b/core/java/com/android/internal/widget/RecyclerView.java
index b66a7b4..43a227a 100644
--- a/core/java/com/android/internal/widget/RecyclerView.java
+++ b/core/java/com/android/internal/widget/RecyclerView.java
@@ -20,7 +20,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
import android.database.Observable;
diff --git a/core/java/com/android/internal/widget/ScrollBarUtils.java b/core/java/com/android/internal/widget/ScrollBarUtils.java
index 982e315..3e9d697 100644
--- a/core/java/com/android/internal/widget/ScrollBarUtils.java
+++ b/core/java/com/android/internal/widget/ScrollBarUtils.java
@@ -16,7 +16,7 @@
package com.android.internal.widget;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
public class ScrollBarUtils {
diff --git a/core/java/com/android/internal/widget/ScrollingTabContainerView.java b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
index 5d48ab9..aa0b0bb 100644
--- a/core/java/com/android/internal/widget/ScrollingTabContainerView.java
+++ b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
@@ -15,13 +15,11 @@
*/
package com.android.internal.widget;
-import com.android.internal.view.ActionBarPolicy;
-
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
-import android.annotation.UnsupportedAppUsage;
import android.app.ActionBar;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.drawable.Drawable;
@@ -42,6 +40,8 @@
import android.widget.Spinner;
import android.widget.TextView;
+import com.android.internal.view.ActionBarPolicy;
+
/**
* This widget implements the dynamic action bar tab behavior that can change
* across different configurations or circumstances.
diff --git a/core/java/com/android/internal/widget/SlidingTab.java b/core/java/com/android/internal/widget/SlidingTab.java
index 4b5d624..5e6f3a4 100644
--- a/core/java/com/android/internal/widget/SlidingTab.java
+++ b/core/java/com/android/internal/widget/SlidingTab.java
@@ -16,7 +16,7 @@
package com.android.internal.widget;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -34,12 +34,12 @@
import android.view.ViewGroup;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
+import android.view.animation.Animation.AnimationListener;
import android.view.animation.LinearInterpolator;
import android.view.animation.TranslateAnimation;
-import android.view.animation.Animation.AnimationListener;
import android.widget.ImageView;
-import android.widget.TextView;
import android.widget.ImageView.ScaleType;
+import android.widget.TextView;
import com.android.internal.R;
diff --git a/core/java/com/android/internal/widget/TextViewInputDisabler.java b/core/java/com/android/internal/widget/TextViewInputDisabler.java
index 8d8f0fe..57806eb 100644
--- a/core/java/com/android/internal/widget/TextViewInputDisabler.java
+++ b/core/java/com/android/internal/widget/TextViewInputDisabler.java
@@ -16,7 +16,7 @@
package com.android.internal.widget;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.text.InputFilter;
import android.text.Spanned;
import android.widget.TextView;
diff --git a/core/java/com/android/internal/widget/ViewPager.java b/core/java/com/android/internal/widget/ViewPager.java
index 7d36b02..c8a86d1 100644
--- a/core/java/com/android/internal/widget/ViewPager.java
+++ b/core/java/com/android/internal/widget/ViewPager.java
@@ -18,7 +18,7 @@
import android.annotation.DrawableRes;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
diff --git a/core/java/com/android/server/ResettableTimeout.java b/core/java/com/android/server/ResettableTimeout.java
index 64083f7..511af94 100644
--- a/core/java/com/android/server/ResettableTimeout.java
+++ b/core/java/com/android/server/ResettableTimeout.java
@@ -16,10 +16,9 @@
package com.android.server;
-import android.os.SystemClock;
-
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.ConditionVariable;
+import android.os.SystemClock;
/**
* Utility class that you can call on with a timeout, and get called back
diff --git a/core/java/com/android/server/net/BaseNetworkObserver.java b/core/java/com/android/server/net/BaseNetworkObserver.java
index e1a10a5..2a9c0b4 100644
--- a/core/java/com/android/server/net/BaseNetworkObserver.java
+++ b/core/java/com/android/server/net/BaseNetworkObserver.java
@@ -16,7 +16,7 @@
package com.android.server.net;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.net.INetworkManagementEventObserver;
import android.net.LinkAddress;
import android.net.RouteInfo;
diff --git a/core/java/com/android/server/net/NetlinkTracker.java b/core/java/com/android/server/net/NetlinkTracker.java
index 647fb5b..b57397f 100644
--- a/core/java/com/android/server/net/NetlinkTracker.java
+++ b/core/java/com/android/server/net/NetlinkTracker.java
@@ -16,7 +16,7 @@
package com.android.server.net;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.RouteInfo;
diff --git a/core/java/com/google/android/collect/Lists.java b/core/java/com/google/android/collect/Lists.java
index 8f6594a..585847d 100644
--- a/core/java/com/google/android/collect/Lists.java
+++ b/core/java/com/google/android/collect/Lists.java
@@ -16,7 +16,8 @@
package com.google.android.collect;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
+
import java.util.ArrayList;
import java.util.Collections;
diff --git a/core/java/com/google/android/collect/Maps.java b/core/java/com/google/android/collect/Maps.java
index 6ba3320..cd4c128 100644
--- a/core/java/com/google/android/collect/Maps.java
+++ b/core/java/com/google/android/collect/Maps.java
@@ -16,7 +16,7 @@
package com.google.android.collect;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.util.ArrayMap;
import java.util.HashMap;
diff --git a/core/java/com/google/android/collect/Sets.java b/core/java/com/google/android/collect/Sets.java
index 09b5e51..c67a88a 100644
--- a/core/java/com/google/android/collect/Sets.java
+++ b/core/java/com/google/android/collect/Sets.java
@@ -16,7 +16,7 @@
package com.google.android.collect;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.util.ArraySet;
import java.util.Collections;
diff --git a/core/java/com/google/android/util/AbstractMessageParser.java b/core/java/com/google/android/util/AbstractMessageParser.java
index f11e6b2..0da7607 100644
--- a/core/java/com/google/android/util/AbstractMessageParser.java
+++ b/core/java/com/google/android/util/AbstractMessageParser.java
@@ -16,7 +16,7 @@
package com.google.android.util;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import java.util.ArrayList;
import java.util.HashMap;
diff --git a/core/java/org/apache/http/conn/ssl/AbstractVerifier.java b/core/java/org/apache/http/conn/ssl/AbstractVerifier.java
index 36d6e22..2848ad7 100644
--- a/core/java/org/apache/http/conn/ssl/AbstractVerifier.java
+++ b/core/java/org/apache/http/conn/ssl/AbstractVerifier.java
@@ -31,7 +31,7 @@
package org.apache.http.conn.ssl;
-import java.util.regex.Pattern;
+import android.compat.annotation.UnsupportedAppUsage;
import java.io.IOException;
import java.security.cert.Certificate;
@@ -43,10 +43,10 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
-import java.util.logging.Logger;
import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
-import android.annotation.UnsupportedAppUsage;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
diff --git a/core/java/org/apache/http/conn/ssl/SSLSocketFactory.java b/core/java/org/apache/http/conn/ssl/SSLSocketFactory.java
index b2e8b5e..ffae757 100644
--- a/core/java/org/apache/http/conn/ssl/SSLSocketFactory.java
+++ b/core/java/org/apache/http/conn/ssl/SSLSocketFactory.java
@@ -31,20 +31,14 @@
package org.apache.http.conn.ssl;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+
import org.apache.http.conn.scheme.HostNameResolver;
import org.apache.http.conn.scheme.LayeredSocketFactory;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
-import android.annotation.UnsupportedAppUsage;
-import android.os.Build;
-import javax.net.ssl.HttpsURLConnection;
-import javax.net.ssl.KeyManager;
-import javax.net.ssl.KeyManagerFactory;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLSocket;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.TrustManagerFactory;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
@@ -57,6 +51,14 @@
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+
/**
* Layered socket factory for TLS/SSL connections, based on JSSE.
*.
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 98ce8b0..6e6746f 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -345,8 +345,9 @@
srcs: [
"android/graphics/apex/android_bitmap.cpp",
- "android/graphics/apex/android_region.cpp",
+ "android/graphics/apex/android_matrix.cpp",
"android/graphics/apex/android_paint.cpp",
+ "android/graphics/apex/android_region.cpp",
"android_graphics_Canvas.cpp",
"android_graphics_ColorSpace.cpp",
diff --git a/core/jni/android/graphics/Matrix.h b/core/jni/android/graphics/Matrix.h
index 11c9e72..fe90d2e 100644
--- a/core/jni/android/graphics/Matrix.h
+++ b/core/jni/android/graphics/Matrix.h
@@ -23,7 +23,7 @@
namespace android {
/* Gets the underlying SkMatrix from a Matrix object. */
-extern SkMatrix* android_graphics_Matrix_getSkMatrix(JNIEnv* env, jobject matrixObj);
+SkMatrix* android_graphics_Matrix_getSkMatrix(JNIEnv* env, jobject matrixObj);
} // namespace android
diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index 6095ffa..f5e2a52 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -5,7 +5,7 @@
#include "SkShader.h"
#include "SkBlendMode.h"
#include "core_jni_helpers.h"
-#include "src/shaders/SkRTShader.h"
+#include "include/effects/SkRuntimeEffect.h"
#include <jni.h>
@@ -214,14 +214,14 @@
///////////////////////////////////////////////////////////////////////////////////////////////
static jlong RuntimeShader_create(JNIEnv* env, jobject, jlong shaderFactory, jlong matrixPtr,
- jbyteArray inputs, jlong colorSpaceHandle) {
- SkRuntimeShaderFactory* factory = reinterpret_cast<SkRuntimeShaderFactory*>(shaderFactory);
+ jbyteArray inputs, jlong colorSpaceHandle, jboolean isOpaque) {
+ SkRuntimeEffect* effect = reinterpret_cast<SkRuntimeEffect*>(shaderFactory);
AutoJavaByteArray arInputs(env, inputs);
sk_sp<SkData> fData;
fData = SkData::MakeWithCopy(arInputs.ptr(), arInputs.length());
const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
- sk_sp<SkShader> shader = factory->make(fData, matrix);
+ sk_sp<SkShader> shader = effect->makeShader(fData, nullptr, 0, matrix, isOpaque == JNI_TRUE);
ThrowIAE_IfNull(env, shader);
return reinterpret_cast<jlong>(shader.release());
@@ -229,24 +229,22 @@
///////////////////////////////////////////////////////////////////////////////////////////////
-static jlong RuntimeShader_createShaderFactory(JNIEnv* env, jobject, jstring sksl,
- jboolean isOpaque) {
+static jlong RuntimeShader_createShaderFactory(JNIEnv* env, jobject, jstring sksl) {
ScopedUtfChars strSksl(env, sksl);
- SkRuntimeShaderFactory* shaderFactory = new SkRuntimeShaderFactory(SkString(strSksl.c_str()),
- isOpaque == JNI_TRUE);
- ThrowIAE_IfNull(env, shaderFactory);
+ sk_sp<SkRuntimeEffect> effect = std::get<0>(SkRuntimeEffect::Make(SkString(strSksl.c_str())));
+ ThrowIAE_IfNull(env, effect);
- return reinterpret_cast<jlong>(shaderFactory);
+ return reinterpret_cast<jlong>(effect.release());
}
///////////////////////////////////////////////////////////////////////////////////////////////
-static void RuntimeShader_delete(SkRuntimeShaderFactory* shaderFactory) {
- delete shaderFactory;
+static void Effect_safeUnref(SkRuntimeEffect* effect) {
+ SkSafeUnref(effect);
}
static jlong RuntimeShader_getNativeFinalizer(JNIEnv*, jobject) {
- return static_cast<jlong>(reinterpret_cast<uintptr_t>(&RuntimeShader_delete));
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Effect_safeUnref));
}
///////////////////////////////////////////////////////////////////////////////////////////////
@@ -282,8 +280,8 @@
static const JNINativeMethod gRuntimeShaderMethods[] = {
{ "nativeGetFinalizer", "()J", (void*)RuntimeShader_getNativeFinalizer },
- { "nativeCreate", "(JJ[BJ)J", (void*)RuntimeShader_create },
- { "nativeCreateShaderFactory", "(Ljava/lang/String;Z)J",
+ { "nativeCreate", "(JJ[BJZ)J", (void*)RuntimeShader_create },
+ { "nativeCreateShaderFactory", "(Ljava/lang/String;)J",
(void*)RuntimeShader_createShaderFactory },
};
diff --git a/core/jni/android/graphics/apex/android_matrix.cpp b/core/jni/android/graphics/apex/android_matrix.cpp
new file mode 100644
index 0000000..309360d
--- /dev/null
+++ b/core/jni/android/graphics/apex/android_matrix.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android/graphics/matrix.h"
+#include "Matrix.h"
+
+bool AMatrix_getContents(JNIEnv* env, jobject matrixObj, float values[9]) {
+ static_assert(SkMatrix::kMScaleX == 0, "SkMatrix unexpected index");
+ static_assert(SkMatrix::kMSkewX == 1, "SkMatrix unexpected index");
+ static_assert(SkMatrix::kMTransX == 2, "SkMatrix unexpected index");
+ static_assert(SkMatrix::kMSkewY == 3, "SkMatrix unexpected index");
+ static_assert(SkMatrix::kMScaleY == 4, "SkMatrix unexpected index");
+ static_assert(SkMatrix::kMTransY == 5, "SkMatrix unexpected index");
+ static_assert(SkMatrix::kMPersp0 == 6, "SkMatrix unexpected index");
+ static_assert(SkMatrix::kMPersp1 == 7, "SkMatrix unexpected index");
+ static_assert(SkMatrix::kMPersp2 == 8, "SkMatrix unexpected index");
+
+ SkMatrix* m = android::android_graphics_Matrix_getSkMatrix(env, matrixObj);
+ if (m != nullptr) {
+ m->get9(values);
+ return true;
+ }
+ return false;
+}
diff --git a/core/jni/android/graphics/apex/include/android/graphics/matrix.h b/core/jni/android/graphics/apex/include/android/graphics/matrix.h
new file mode 100644
index 0000000..4039cd1
--- /dev/null
+++ b/core/jni/android/graphics/apex/include/android/graphics/matrix.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_GRAPHICS_MATRIX_H
+#define ANDROID_GRAPHICS_MATRIX_H
+
+#include <jni.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/**
+ * Returns an array of floats that represents the 3x3 matrix of the java object.
+ * @param values The 9 values of the 3x3 matrix in the following order.
+ * values[0] = scaleX values[1] = skewX values[2] = transX
+ * values[3] = skewY values[4] = scaleY values[5] = transY
+ * values[6] = persp0 values[7] = persp1 values[8] = persp2
+ * @return true if the values param was populated and false otherwise.
+
+ */
+bool AMatrix_getContents(JNIEnv* env, jobject matrixObj, float values[9]);
+
+__END_DECLS
+
+#endif // ANDROID_GRAPHICS_MATRIX_H
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 08aa1d9..ba7fe7f 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -24,9 +24,7 @@
#include <linux/tcp.h>
#include <net/if.h>
#include <netinet/ether.h>
-#include <netinet/icmp6.h>
#include <netinet/ip.h>
-#include <netinet/ip6.h>
#include <netinet/udp.h>
#include <android_runtime/AndroidRuntime.h>
@@ -102,98 +100,6 @@
}
}
-static void android_net_utils_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd,
- jint ifIndex)
-{
- static const int kLinkLocalHopLimit = 255;
-
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
-
- // Set an ICMPv6 filter that only passes Router Solicitations.
- struct icmp6_filter rs_only;
- ICMP6_FILTER_SETBLOCKALL(&rs_only);
- ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &rs_only);
- socklen_t len = sizeof(rs_only);
- if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &rs_only, len) != 0) {
- jniThrowExceptionFmt(env, "java/net/SocketException",
- "setsockopt(ICMP6_FILTER): %s", strerror(errno));
- return;
- }
-
- // Most/all of the rest of these options can be set via Java code, but
- // because we're here on account of setting an icmp6_filter go ahead
- // and do it all natively for now.
- //
- // TODO: Consider moving these out to Java.
-
- // Set the multicast hoplimit to 255 (link-local only).
- int hops = kLinkLocalHopLimit;
- len = sizeof(hops);
- if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, len) != 0) {
- jniThrowExceptionFmt(env, "java/net/SocketException",
- "setsockopt(IPV6_MULTICAST_HOPS): %s", strerror(errno));
- return;
- }
-
- // Set the unicast hoplimit to 255 (link-local only).
- hops = kLinkLocalHopLimit;
- len = sizeof(hops);
- if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hops, len) != 0) {
- jniThrowExceptionFmt(env, "java/net/SocketException",
- "setsockopt(IPV6_UNICAST_HOPS): %s", strerror(errno));
- return;
- }
-
- // Explicitly disable multicast loopback.
- int off = 0;
- len = sizeof(off);
- if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, len) != 0) {
- jniThrowExceptionFmt(env, "java/net/SocketException",
- "setsockopt(IPV6_MULTICAST_LOOP): %s", strerror(errno));
- return;
- }
-
- // Specify the IPv6 interface to use for outbound multicast.
- len = sizeof(ifIndex);
- if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifIndex, len) != 0) {
- jniThrowExceptionFmt(env, "java/net/SocketException",
- "setsockopt(IPV6_MULTICAST_IF): %s", strerror(errno));
- return;
- }
-
- // Additional options to be considered:
- // - IPV6_TCLASS
- // - IPV6_RECVPKTINFO
- // - IPV6_RECVHOPLIMIT
-
- // Bind to [::].
- const struct sockaddr_in6 sin6 = {
- .sin6_family = AF_INET6,
- .sin6_port = 0,
- .sin6_flowinfo = 0,
- .sin6_addr = IN6ADDR_ANY_INIT,
- .sin6_scope_id = 0,
- };
- auto sa = reinterpret_cast<const struct sockaddr *>(&sin6);
- len = sizeof(sin6);
- if (bind(fd, sa, len) != 0) {
- jniThrowExceptionFmt(env, "java/net/SocketException",
- "bind(IN6ADDR_ANY): %s", strerror(errno));
- return;
- }
-
- // Join the all-routers multicast group, ff02::2%index.
- struct ipv6_mreq all_rtrs = {
- .ipv6mr_multiaddr = {{{0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2}}},
- .ipv6mr_interface = ifIndex,
- };
- len = sizeof(all_rtrs);
- if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &all_rtrs, len) != 0) {
- jniThrowExceptionFmt(env, "java/net/SocketException",
- "setsockopt(IPV6_JOIN_GROUP): %s", strerror(errno));
- return;
- }
-}
static jboolean android_net_utils_bindProcessToNetwork(JNIEnv *env, jobject thiz, jint netId)
{
@@ -370,7 +276,6 @@
{ "attachDropAllBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDropAllBPFFilter },
{ "detachBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_detachBPFFilter },
{ "getTcpRepairWindow", "(Ljava/io/FileDescriptor;)Landroid/net/TcpRepairWindow;", (void*) android_net_utils_getTcpRepairWindow },
- { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_setupRaSocket },
{ "resNetworkSend", "(I[BII)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkSend },
{ "resNetworkQuery", "(ILjava/lang/String;III)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkQuery },
{ "resNetworkResult", "(Ljava/io/FileDescriptor;)Landroid/net/DnsResolver$DnsResponse;", (void*) android_net_utils_resNetworkResult },
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 4314eb6..566c385 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -237,7 +237,7 @@
return err;
}
-static void load_maps(int pid, stats_t* stats, bool* foundSwapPss)
+static bool load_maps(int pid, stats_t* stats, bool* foundSwapPss)
{
*foundSwapPss = false;
uint64_t prev_end = 0;
@@ -407,17 +407,19 @@
}
};
- meminfo::ForEachVmaFromFile(smaps_path, vma_scan);
+ return meminfo::ForEachVmaFromFile(smaps_path, vma_scan);
}
-static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz,
+static jboolean android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz,
jint pid, jobject object)
{
bool foundSwapPss;
stats_t stats[_NUM_HEAP];
memset(&stats, 0, sizeof(stats));
- load_maps(pid, stats, &foundSwapPss);
+ if (!load_maps(pid, stats, &foundSwapPss)) {
+ return JNI_FALSE;
+ }
struct graphics_memory_pss graphics_mem;
if (read_memtrack_memory(pid, &graphics_mem) == 0) {
@@ -462,7 +464,7 @@
jint* otherArray = (jint*)env->GetPrimitiveArrayCritical(otherIntArray, 0);
if (otherArray == NULL) {
- return;
+ return JNI_FALSE;
}
int j=0;
@@ -479,6 +481,7 @@
}
env->ReleasePrimitiveArrayCritical(otherIntArray, otherArray, 0);
+ return JNI_TRUE;
}
static void android_os_Debug_getDirtyPages(JNIEnv *env, jobject clazz, jobject object)
@@ -508,6 +511,8 @@
rss += stats.rss;
swapPss = stats.swap_pss;
pss += swapPss; // Also in swap, those pages would be accounted as Pss without SWAP
+ } else {
+ return 0;
}
if (outUssSwapPssRss != NULL) {
@@ -866,7 +871,7 @@
(void*) android_os_Debug_getNativeHeapFreeSize },
{ "getMemoryInfo", "(Landroid/os/Debug$MemoryInfo;)V",
(void*) android_os_Debug_getDirtyPages },
- { "getMemoryInfo", "(ILandroid/os/Debug$MemoryInfo;)V",
+ { "getMemoryInfo", "(ILandroid/os/Debug$MemoryInfo;)Z",
(void*) android_os_Debug_getDirtyPagesPid },
{ "getPss", "()J",
(void*) android_os_Debug_getPss },
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index 3531cf2..7daefd3 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -62,7 +62,7 @@
void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) override;
void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
- int32_t configId) override;
+ int32_t configId, nsecs_t vsyncPeriod) override;
};
@@ -118,24 +118,23 @@
mMessageQueue->raiseAndClearException(env, "dispatchHotplug");
}
-void NativeDisplayEventReceiver::dispatchConfigChanged(nsecs_t timestamp,
- PhysicalDisplayId displayId,
- int32_t configId) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+void NativeDisplayEventReceiver::dispatchConfigChanged(
+ nsecs_t timestamp, PhysicalDisplayId displayId, int32_t configId, nsecs_t) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
- ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
- if (receiverObj.get()) {
- ALOGV("receiver %p ~ Invoking config changed handler.", this);
- env->CallVoidMethod(receiverObj.get(),
- gDisplayEventReceiverClassInfo.dispatchConfigChanged,
- timestamp, displayId, configId);
- ALOGV("receiver %p ~ Returned from config changed handler.", this);
- }
+ ScopedLocalRef<jobject> receiverObj(env,
+ jniGetReferent(env, mReceiverWeakGlobal));
+ if (receiverObj.get()) {
+ ALOGV("receiver %p ~ Invoking config changed handler.", this);
+ env->CallVoidMethod(receiverObj.get(),
+ gDisplayEventReceiverClassInfo.dispatchConfigChanged,
+ timestamp, displayId, configId);
+ ALOGV("receiver %p ~ Returned from config changed handler.", this);
+ }
- mMessageQueue->raiseAndClearException(env, "dispatchConfigChanged");
+ mMessageQueue->raiseAndClearException(env, "dispatchConfigChanged");
}
-
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
jobject messageQueueObj, jint vsyncSource, jint configChanged) {
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index c75c54b..feb9fe3 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -18,7 +18,7 @@
#include <nativehelper/JNIHelp.h>
-#include <SkMatrix.h>
+#include <android/graphics/matrix.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/Log.h>
#include <utils/Log.h>
@@ -571,6 +571,15 @@
}
}
+static void android_view_MotionEvent_nativeTransform(JNIEnv* env, jclass clazz,
+ jlong nativePtr, jobject matrixObj) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+
+ float m[9];
+ AMatrix_getContents(env, matrixObj, m);
+ event->transform(m);
+}
+
// ----------------- @CriticalNative ------------------------------
static jlong android_view_MotionEvent_nativeCopy(jlong destNativePtr, jlong sourceNativePtr,
@@ -745,24 +754,6 @@
event->scale(scale);
}
-static void android_view_MotionEvent_nativeTransform(jlong nativePtr, jlong matrixPtr) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr);
-
- static_assert(SkMatrix::kMScaleX == 0, "SkMatrix unexpected index");
- static_assert(SkMatrix::kMSkewX == 1, "SkMatrix unexpected index");
- static_assert(SkMatrix::kMTransX == 2, "SkMatrix unexpected index");
- static_assert(SkMatrix::kMSkewY == 3, "SkMatrix unexpected index");
- static_assert(SkMatrix::kMScaleY == 4, "SkMatrix unexpected index");
- static_assert(SkMatrix::kMTransY == 5, "SkMatrix unexpected index");
- static_assert(SkMatrix::kMPersp0 == 6, "SkMatrix unexpected index");
- static_assert(SkMatrix::kMPersp1 == 7, "SkMatrix unexpected index");
- static_assert(SkMatrix::kMPersp2 == 8, "SkMatrix unexpected index");
- float m[9];
- matrix->get9(m);
- event->transform(m);
-}
-
// ----------------------------------------------------------------------------
static const JNINativeMethod gMotionEventMethods[] = {
@@ -810,6 +801,9 @@
{ "nativeGetAxisValue",
"(JIII)F",
(void*)android_view_MotionEvent_nativeGetAxisValue },
+ { "nativeTransform",
+ "(JLandroid/graphics/Matrix;)V",
+ (void*)android_view_MotionEvent_nativeTransform },
// --------------- @CriticalNative ------------------
@@ -912,9 +906,6 @@
{ "nativeScale",
"(JF)V",
(void*)android_view_MotionEvent_nativeScale },
- { "nativeTransform",
- "(JJ)V",
- (void*)android_view_MotionEvent_nativeTransform },
};
int register_android_view_MotionEvent(JNIEnv* env) {
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index df5b02c..d17d0a4 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -746,7 +746,13 @@
const std::string pass_through_source = StringPrintf("/mnt/pass_through/%d", user_id);
bool isFuse = GetBoolProperty(kPropFuse, false);
- PrepareDir(user_source, DEFAULT_DATA_DIR_PERMISSION, AID_ROOT, AID_ROOT, fail_fn);
+ // Shell is neither AID_ROOT nor AID_EVERYBODY. Since it equally needs 'execute' access to
+ // /mnt/user/0 to 'adb shell ls /sdcard' for instance, we set the uid bit of /mnt/user/0 to
+ // AID_SHELL. This gives shell access along with apps running as group everybody (user 0 apps)
+ // These bits should be consistent with what is set in vold in
+ // Utils#MountUserFuse on FUSE volume mount
+ PrepareDir(user_source, 0710, user_id ? AID_ROOT : AID_SHELL,
+ multiuser_get_uid(user_id, AID_EVERYBODY), fail_fn);
if (isFuse) {
if (mount_mode == MOUNT_EXTERNAL_PASS_THROUGH || mount_mode ==
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 5624f45..082a289 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -33,27 +33,27 @@
// Static whitelist of open paths that the zygote is allowed to keep open.
static const char* kPathWhitelist[] = {
- "/apex/com.android.appsearch/javalib/framework-appsearch.jar",
- "/apex/com.android.conscrypt/javalib/conscrypt.jar",
- "/apex/com.android.ipsec/javalib/ike.jar",
- "/apex/com.android.media/javalib/updatable-media.jar",
- "/apex/com.android.mediaprovider/javalib/framework-mediaprovider.jar",
- "/apex/com.android.os.statsd/javalib/framework-statsd.jar",
- "/apex/com.android.sdkext/javalib/framework-sdkext.jar",
- "/apex/com.android.wifi/javalib/framework-wifi.jar",
- "/apex/com.android.tethering/javalib/framework-tethering.jar",
- "/dev/null",
- "/dev/socket/zygote",
- "/dev/socket/zygote_secondary",
- "/dev/socket/usap_pool_primary",
- "/dev/socket/usap_pool_secondary",
- "/dev/socket/webview_zygote",
- "/dev/socket/heapprofd",
- "/sys/kernel/debug/tracing/trace_marker",
- "/system/framework/framework-res.apk",
- "/dev/urandom",
- "/dev/ion",
- "/dev/dri/renderD129", // Fixes b/31172436
+ "/apex/com.android.appsearch/javalib/framework-appsearch.jar",
+ "/apex/com.android.conscrypt/javalib/conscrypt.jar",
+ "/apex/com.android.ipsec/javalib/ike.jar",
+ "/apex/com.android.media/javalib/updatable-media.jar",
+ "/apex/com.android.mediaprovider/javalib/framework-mediaprovider.jar",
+ "/apex/com.android.os.statsd/javalib/framework-statsd.jar",
+ "/apex/com.android.sdkext/javalib/framework-sdkextensions.jar",
+ "/apex/com.android.wifi/javalib/framework-wifi.jar",
+ "/apex/com.android.tethering/javalib/framework-tethering.jar",
+ "/dev/null",
+ "/dev/socket/zygote",
+ "/dev/socket/zygote_secondary",
+ "/dev/socket/usap_pool_primary",
+ "/dev/socket/usap_pool_secondary",
+ "/dev/socket/webview_zygote",
+ "/dev/socket/heapprofd",
+ "/sys/kernel/debug/tracing/trace_marker",
+ "/system/framework/framework-res.apk",
+ "/dev/urandom",
+ "/dev/ion",
+ "/dev/dri/renderD129", // Fixes b/31172436
};
static const char kFdPath[] = "/proc/self/fd";
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index d5a3b5e..b83b31c 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -702,6 +702,12 @@
// CATEGORY: SETTINGS
// OS: R
ACTION_DASHBOARD_VISIBLE_TIME = 1729;
+
+ // ACTION: Allow "Access all files" for an app
+ APP_SPECIAL_PERMISSION_MANAGE_EXT_STRG_ALLOW = 1730;
+
+ // ACTION: Deny "Access all files" for an app
+ APP_SPECIAL_PERMISSION_MANAGE_EXT_STRG_DENY = 1731;
}
/**
@@ -2542,4 +2548,9 @@
// OS: R
FUELGAUGE_BATTERY_SHARE = 1821;
+ // OPEN: Settings -> Apps & Notifications -> Special App Access
+ // CATEGORY: SETTINGS
+ // OS: R
+ MANAGE_EXTERNAL_STORAGE = 1822;
+
}
diff --git a/core/proto/android/os/cpu_usage.proto b/core/proto/android/os/cpu_usage.proto
new file mode 100644
index 0000000..ac98900
--- /dev/null
+++ b/core/proto/android/os/cpu_usage.proto
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+option java_multiple_files = true;
+
+import "frameworks/base/core/proto/android/privacy.proto";
+
+package android.os;
+
+message CpuUsageProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ message Load {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional float load1 = 1;
+ optional float load5 = 2;
+ optional float load15 = 3;
+ }
+ optional Load current_load = 1;
+
+ optional int64 now = 2;
+ optional int64 last_sample_time = 3;
+ optional int64 current_sample_time = 4;
+ optional int64 last_sample_real_time = 5;
+ optional int64 current_sample_real_time = 6;
+ optional int64 last_sample_wall_time = 7;
+ optional int64 current_sample_wall_time = 8;
+
+ optional int32 total_user_time = 9;
+ optional int32 total_system_time = 10;
+ optional int32 total_iowait_time = 11;
+ optional int32 total_irq_time = 12;
+ optional int32 total_soft_irq_time = 13;
+ optional int32 total_idle_time = 14;
+ optional int32 total_time = 15;
+
+ message Stat {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional int32 uid = 1;
+ optional int32 pid = 2;
+ optional string name = 3;
+ optional bool added = 4;
+ optional bool removed = 5;
+ optional int32 uptime = 6;
+ optional int32 user_time = 7;
+ optional int32 system_time = 8;
+ optional int32 minor_faults = 9;
+ optional int32 major_faults = 10;
+ optional int32 parent_pid = 11;
+ }
+ repeated Stat processes = 16;
+
+ // Next tag: 17
+}
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 6cf9424..8f9c041 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -21,6 +21,7 @@
import "frameworks/base/core/proto/android/os/batterytype.proto";
import "frameworks/base/core/proto/android/os/cpufreq.proto";
import "frameworks/base/core/proto/android/os/cpuinfo.proto";
+import "frameworks/base/core/proto/android/os/cpu_usage.proto";
import "frameworks/base/core/proto/android/os/data.proto";
import "frameworks/base/core/proto/android/os/header.proto";
import "frameworks/base/core/proto/android/os/kernelwake.proto";
@@ -469,6 +470,11 @@
(section).args = "dropbox --proto SubsystemRestart"
];
+ optional CpuUsageProto process_cpu_usage = 3047 [
+ (section).type = SECTION_DUMPSYS,
+ (section).args = "cpuinfo --proto"
+ ];
+
// Reserved for OEMs.
extensions 50000 to 100000;
}
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index a98d7db..d5384a1 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -115,7 +115,7 @@
option (android.msg_privacy).dest = DEST_EXPLICIT;
optional SettingProto backup_agent_timeout_parameters = 1;
- optional SettingProto backup_multi_user_enabled = 2;
+ reserved 2; // Used to be backup_multi_user_enabled which was never used
}
optional Backup backup = 146;
diff --git a/core/proto/android/server/animationadapter.proto b/core/proto/android/server/animationadapter.proto
index 70627ed..c6925f4 100644
--- a/core/proto/android/server/animationadapter.proto
+++ b/core/proto/android/server/animationadapter.proto
@@ -50,6 +50,7 @@
optional WindowAnimationSpecProto window = 1;
optional MoveAnimationSpecProto move = 2;
optional AlphaAnimationSpecProto alpha = 3;
+ optional RotationAnimationSpecProto rotate = 4;
}
/* represents WindowAnimationSpec */
@@ -76,3 +77,12 @@
optional float to = 2;
optional int64 duration_ms = 3;
}
+
+/* represents RotationAnimationSpec */
+message RotationAnimationSpecProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional float start_luma = 1;
+ optional float end_luma = 2;
+ optional int64 duration_ms = 3;
+}
diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
index ee5144c..9054d54 100644
--- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto
+++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
@@ -153,4 +153,5 @@
CROSS_PROFILE_APPS_START_ACTIVITY_AS_USER = 126;
SET_AUTO_TIME = 127;
SET_AUTO_TIME_ZONE = 128;
+ SET_PACKAGES_PROTECTED = 129;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 5410cf5..ea70dcf 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -456,7 +456,6 @@
<protected-broadcast android:name="android.intent.action.internal_sim_state_changed" />
<protected-broadcast android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
<protected-broadcast android:name="android.intent.action.PRECISE_CALL_STATE" />
- <protected-broadcast android:name="android.intent.action.PRECISE_DATA_CONNECTION_STATE_CHANGED" />
<protected-broadcast android:name="android.intent.action.SUBSCRIPTION_PHONE_STATE" />
<protected-broadcast android:name="android.intent.action.USER_INFO_CHANGED" />
<protected-broadcast android:name="android.intent.action.USER_UNLOCKED" />
@@ -932,6 +931,14 @@
<permission android:name="android.permission.WRITE_OBB"
android:protectionLevel="signature|privileged" />
+ <!-- Allows an application a broad access to external storage in scoped storage.
+ Intended to be used by few apps that need to manage files on behalf of the users.
+ <p>Protection level: signature|appop
+ <p>This protection level is temporary and will most likely be changed to |preinstalled -->
+ <permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:protectionLevel="signature|appop" />
+
<!-- ====================================================================== -->
<!-- Permissions for accessing the device location -->
<!-- ====================================================================== -->
@@ -1615,6 +1622,12 @@
<permission android:name="android.permission.REQUEST_NETWORK_SCORES"
android:protectionLevel="signature|setup" />
+ <!-- @SystemApi @hide Allows applications to toggle airplane mode.
+ <p>Not for use by third-party or privileged applications.
+ -->
+ <permission android:name="android.permission.NETWORK_AIRPLANE_MODE"
+ android:protectionLevel="signature" />
+
<!-- Allows network stack services (Connectivity and Wifi) to coordinate
<p>Not for use by third-party or privileged applications.
@SystemApi
@@ -2332,13 +2345,9 @@
<permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"
android:protectionLevel="signature|installer|telephony" />
- <!-- @SystemApi Allows an application to start its own activities, but on a different profile
- associated with the user. For example, an application running on the main profile of a user
- can start an activity on a managed profile of that user.
- This permission is not available to third party applications.
- @hide -->
+ <!-- Allows interaction across profiles in the same profile group. -->
<permission android:name="android.permission.INTERACT_ACROSS_PROFILES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|appop|documenter|wellbeing" />
<!-- @SystemApi @hide Allows an application to call APIs that allow it to query and manage
users on the device. This permission is not available to
@@ -2534,17 +2543,17 @@
<permission android:name="android.permission.READ_WALLPAPER_INTERNAL"
android:protectionLevel="signature|privileged" />
- <!-- ============================================ -->
- <!-- Permissions for changing the system clock -->
- <!-- ============================================ -->
+ <!-- ===================================================== -->
+ <!-- Permissions for changing the system clock / time zone -->
+ <!-- ===================================================== -->
<eat-comment />
- <!-- Allows applications to set the system time.
- <p>Not for use by third-party applications. -->
+ <!-- Allows applications to set the system time directly.
+ <p>Not for use by third-party applications. -->
<permission android:name="android.permission.SET_TIME"
android:protectionLevel="signature|privileged" />
- <!-- Allows applications to set the system time zone.
+ <!-- Allows applications to set the system time zone directly.
<p>Not for use by third-party applications.
-->
<permission android:name="android.permission.SET_TIME_ZONE"
@@ -2552,6 +2561,20 @@
android:description="@string/permdesc_setTimeZone"
android:protectionLevel="signature|privileged" />
+ <!-- Allows telephony to suggest the time / time zone.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.SUGGEST_PHONE_TIME_AND_ZONE"
+ android:protectionLevel="signature|telephony" />
+
+ <!-- Allows applications like settings to suggest the user's manually chosen time / time zone.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.SUGGEST_MANUAL_TIME_AND_ZONE"
+ android:protectionLevel="signature" />
+
<!-- ==================================================== -->
<!-- Permissions related to changing status bar -->
<!-- ==================================================== -->
diff --git a/core/res/res/anim/screen_rotate_0_enter.xml b/core/res/res/anim/screen_rotate_0_enter.xml
index 93cf365..629be7e 100644
--- a/core/res/res/anim/screen_rotate_0_enter.xml
+++ b/core/res/res/anim/screen_rotate_0_enter.xml
@@ -1,25 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/*
-** Copyright 2010, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT 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.
+ -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false">
- <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
- android:interpolator="@interpolator/decelerate_quint"
- android:duration="@android:integer/config_shortAnimTime" />
+ android:shareInterpolator="false">
+ <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+ android:interpolator="@interpolator/screen_rotation_alpha_in"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:duration="@android:integer/config_screen_rotation_fade_in" />
</set>
diff --git a/core/res/res/anim/screen_rotate_0_exit.xml b/core/res/res/anim/screen_rotate_0_exit.xml
index 37d5a411..fa046a0 100644
--- a/core/res/res/anim/screen_rotate_0_exit.xml
+++ b/core/res/res/anim/screen_rotate_0_exit.xml
@@ -1,22 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/*
-** Copyright 2010, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT 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.
+ -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false">
+ android:shareInterpolator="false">
+ <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+ android:interpolator="@interpolator/screen_rotation_alpha_out"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:duration="@android:integer/config_screen_rotation_fade_out" />
</set>
diff --git a/core/res/res/anim/screen_rotate_0_frame.xml b/core/res/res/anim/screen_rotate_0_frame.xml
deleted file mode 100644
index 5ea9bf8..0000000
--- a/core/res/res/anim/screen_rotate_0_frame.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false">
- <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
- android:interpolator="@interpolator/decelerate_quint"
- android:duration="@android:integer/config_shortAnimTime" />
-</set>
diff --git a/core/res/res/anim/screen_rotate_180_enter.xml b/core/res/res/anim/screen_rotate_180_enter.xml
index 688a8d5..889a615 100644
--- a/core/res/res/anim/screen_rotate_180_enter.xml
+++ b/core/res/res/anim/screen_rotate_180_enter.xml
@@ -18,11 +18,11 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false">
+ android:shareInterpolator="false">
<rotate android:fromDegrees="180" android:toDegrees="0"
- android:pivotX="50%" android:pivotY="50%"
- android:interpolator="@interpolator/decelerate_quint"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:duration="@android:integer/config_mediumAnimTime" />
+ android:pivotX="50%" android:pivotY="50%"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@interpolator/fast_out_slow_in"
+ android:duration="@android:integer/config_screen_rotation_total_180" />
</set>
diff --git a/core/res/res/anim/screen_rotate_180_exit.xml b/core/res/res/anim/screen_rotate_180_exit.xml
index 58a1868..766fcfa 100644
--- a/core/res/res/anim/screen_rotate_180_exit.xml
+++ b/core/res/res/anim/screen_rotate_180_exit.xml
@@ -18,11 +18,11 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false">
+ android:shareInterpolator="false">
<rotate android:fromDegrees="0" android:toDegrees="-180"
- android:pivotX="50%" android:pivotY="50%"
- android:interpolator="@interpolator/decelerate_quint"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:duration="@android:integer/config_mediumAnimTime" />
+ android:pivotX="50%" android:pivotY="50%"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@interpolator/fast_out_slow_in"
+ android:duration="@android:integer/config_screen_rotation_total_180" />
</set>
\ No newline at end of file
diff --git a/core/res/res/anim/screen_rotate_alpha.xml b/core/res/res/anim/screen_rotate_alpha.xml
index c49ef9c..2cac982 100644
--- a/core/res/res/anim/screen_rotate_alpha.xml
+++ b/core/res/res/anim/screen_rotate_alpha.xml
@@ -20,8 +20,8 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
- android:interpolator="@interpolator/decelerate_quint"
+ android:interpolator="@interpolator/screen_rotation_alpha_out"
android:fillEnabled="true"
android:fillBefore="true" android:fillAfter="true"
- android:duration="@android:integer/config_mediumAnimTime" />
+ android:duration="@android:integer/config_screen_rotation_fade_out" />
</set>
diff --git a/core/res/res/anim/screen_rotate_minus_90_enter.xml b/core/res/res/anim/screen_rotate_minus_90_enter.xml
index b16d5fc..87fd25e 100644
--- a/core/res/res/anim/screen_rotate_minus_90_enter.xml
+++ b/core/res/res/anim/screen_rotate_minus_90_enter.xml
@@ -18,19 +18,17 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false">
- <!-- Version for two-phase anim
+ android:shareInterpolator="false">
<rotate android:fromDegrees="-90" android:toDegrees="0"
- android:pivotX="50%" android:pivotY="50%"
- android:interpolator="@interpolator/decelerate_quint"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:duration="@android:integer/config_longAnimTime" />
- -->
- <rotate android:fromDegrees="-90" android:toDegrees="0"
- android:pivotX="50%" android:pivotY="50%"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/decelerate_quint"
- android:duration="@android:integer/config_mediumAnimTime" />
+ android:pivotX="50%" android:pivotY="50%"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@interpolator/fast_out_slow_in"
+ android:duration="@android:integer/config_screen_rotation_total_90" />
+ <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@interpolator/screen_rotation_alpha_in"
+ android:startOffset="@android:integer/config_screen_rotation_fade_in_delay"
+ android:duration="@android:integer/config_screen_rotation_fade_in" />
</set>
diff --git a/core/res/res/anim/screen_rotate_minus_90_exit.xml b/core/res/res/anim/screen_rotate_minus_90_exit.xml
index 0927dd3..c3aee14 100644
--- a/core/res/res/anim/screen_rotate_minus_90_exit.xml
+++ b/core/res/res/anim/screen_rotate_minus_90_exit.xml
@@ -18,26 +18,16 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false">
- <!-- Version for two-phase animation
+ android:shareInterpolator="false">
<rotate android:fromDegrees="0" android:toDegrees="90"
- android:pivotX="50%" android:pivotY="50%"
- android:interpolator="@interpolator/decelerate_quint"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:duration="@android:integer/config_longAnimTime" />
- -->
- <scale android:fromXScale="100%" android:toXScale="100%p"
- android:fromYScale="100%" android:toYScale="100%p"
- android:pivotX="50%" android:pivotY="50%"
- android:interpolator="@interpolator/decelerate_quint"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:duration="@android:integer/config_mediumAnimTime" />
- <rotate android:fromDegrees="0" android:toDegrees="90"
- android:pivotX="50%" android:pivotY="50%"
- android:interpolator="@interpolator/decelerate_quint"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:duration="@android:integer/config_mediumAnimTime" />
+ android:pivotX="50%" android:pivotY="50%"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@interpolator/fast_out_slow_in"
+ android:duration="@android:integer/config_screen_rotation_total_90" />
+ <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@interpolator/screen_rotation_alpha_out"
+ android:duration="@android:integer/config_screen_rotation_fade_out" />
</set>
diff --git a/core/res/res/anim/screen_rotate_plus_90_enter.xml b/core/res/res/anim/screen_rotate_plus_90_enter.xml
index 86a8d24..8849db4 100644
--- a/core/res/res/anim/screen_rotate_plus_90_enter.xml
+++ b/core/res/res/anim/screen_rotate_plus_90_enter.xml
@@ -18,19 +18,16 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false">
- <!-- Version for two-phase animation
+ android:shareInterpolator="false">
<rotate android:fromDegrees="90" android:toDegrees="0"
- android:pivotX="50%" android:pivotY="50%"
- android:interpolator="@interpolator/decelerate_quint"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:duration="@android:integer/config_longAnimTime" />
- -->
- <rotate android:fromDegrees="90" android:toDegrees="0"
- android:pivotX="50%" android:pivotY="50%"
- android:interpolator="@interpolator/decelerate_quint"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:duration="@android:integer/config_mediumAnimTime" />
+ android:pivotX="50%" android:pivotY="50%"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@interpolator/fast_out_slow_in"
+ android:duration="@android:integer/config_screen_rotation_total_90" />
+ <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+ android:fillEnabled="true"
+ android:interpolator="@interpolator/screen_rotation_alpha_in"
+ android:startOffset="@android:integer/config_screen_rotation_fade_in_delay"
+ android:duration="@android:integer/config_screen_rotation_fade_in" />
</set>
diff --git a/core/res/res/anim/screen_rotate_plus_90_exit.xml b/core/res/res/anim/screen_rotate_plus_90_exit.xml
index fd786f9..de84c3b 100644
--- a/core/res/res/anim/screen_rotate_plus_90_exit.xml
+++ b/core/res/res/anim/screen_rotate_plus_90_exit.xml
@@ -18,26 +18,16 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false">
- <!-- Version for two-phase animation
+ android:shareInterpolator="false">
<rotate android:fromDegrees="0" android:toDegrees="-90"
- android:pivotX="50%" android:pivotY="50%"
- android:interpolator="@interpolator/decelerate_quint"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:duration="@android:integer/config_longAnimTime" />
- -->
- <scale android:fromXScale="100%" android:toXScale="100%p"
- android:fromYScale="100%" android:toYScale="100%p"
- android:pivotX="50%" android:pivotY="50%"
- android:interpolator="@interpolator/decelerate_quint"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:duration="@android:integer/config_mediumAnimTime" />
- <rotate android:fromDegrees="0" android:toDegrees="-90"
- android:pivotX="50%" android:pivotY="50%"
- android:interpolator="@interpolator/decelerate_quint"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:duration="@android:integer/config_mediumAnimTime" />
+ android:pivotX="50%" android:pivotY="50%"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@interpolator/fast_out_slow_in"
+ android:duration="@android:integer/config_screen_rotation_total_90" />
+ <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+ android:interpolator="@interpolator/screen_rotation_alpha_out"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:duration="@android:integer/config_screen_rotation_fade_out" />
</set>
diff --git a/core/res/res/drawable/ic_delete_item.xml b/core/res/res/drawable/ic_delete_item.xml
new file mode 100644
index 0000000..8a398a4
--- /dev/null
+++ b/core/res/res/drawable/ic_delete_item.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"
+ android:fillColor="#FFFFFF"/>
+</vector>
diff --git a/core/res/res/drawable/ic_open_in_new.xml b/core/res/res/drawable/ic_open_in_new.xml
new file mode 100644
index 0000000..67378c8
--- /dev/null
+++ b/core/res/res/drawable/ic_open_in_new.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:pathData="M19,19H5V5h7V3H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2v-7h-2v7zM14,3v2h3.59l-9.83,9.83 1.41,1.41L19,6.41V10h2V3h-7z"
+ android:fillColor="#FFFFFF"/>
+</vector>
diff --git a/core/res/res/interpolator/screen_rotation_alpha_in.xml b/core/res/res/interpolator/screen_rotation_alpha_in.xml
new file mode 100644
index 0000000..9c566a7
--- /dev/null
+++ b/core/res/res/interpolator/screen_rotation_alpha_in.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:controlX1="0.15"
+ android:controlY1="0.45"
+ android:controlX2="0.33"
+ android:controlY2="1"/>
diff --git a/core/res/res/interpolator/screen_rotation_alpha_out.xml b/core/res/res/interpolator/screen_rotation_alpha_out.xml
new file mode 100644
index 0000000..73a37d4
--- /dev/null
+++ b/core/res/res/interpolator/screen_rotation_alpha_out.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:controlX1="0.57"
+ android:controlY1="0"
+ android:controlX2="0.71"
+ android:controlY2=".43"/>
diff --git a/core/res/res/layout/accessibility_button_chooser_item.xml b/core/res/res/layout/accessibility_button_chooser_item.xml
index fddca5a..1edd2cd 100644
--- a/core/res/res/layout/accessibility_button_chooser_item.xml
+++ b/core/res/res/layout/accessibility_button_chooser_item.xml
@@ -21,10 +21,10 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
- android:paddingBottom="12dp"
- android:paddingEnd="16dp"
android:paddingStart="16dp"
- android:paddingTop="12dp">
+ android:paddingEnd="16dp"
+ android:paddingTop="12dp"
+ android:paddingBottom="12dp">
<ImageView
android:id="@+id/accessibility_button_target_icon"
@@ -39,5 +39,26 @@
android:layout_marginStart="8dp"
android:layout_weight="1"
android:textColor="?attr/textColorPrimary"/>
+
+ <FrameLayout
+ android:id="@+id/accessibility_button_target_item_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minWidth="56dp">
+
+ <ImageView
+ android:id="@+id/accessibility_button_target_view_item"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"/>
+
+ <Switch android:id="@+id/accessibility_button_target_switch_item"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:background="@null"
+ android:clickable="false"
+ android:focusable="false"/>
+ </FrameLayout>
</LinearLayout>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 00c0d30..68708d35 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -440,7 +440,7 @@
<string name="permlab_callPhone" msgid="1798582257194643320">"اتصال مباشر بأرقام الهواتف"</string>
<string name="permdesc_callPhone" msgid="5439809516131609109">"للسماح للتطبيق بطلب أرقام هاتفية بدون تدخل منك. وقد يؤدي ذلك إلى تحمل رسوم غير متوقعة أو إجراء مكالمات غير متوقعة. ومن الجدير بالذكر أن ذلك لا يتيح للتطبيق الاتصال بأرقام الطوارئ. وقد تؤدي التطبيقات الضارة إلى تحملك تكاليف مالية من خلال إجراء مكالمات بدون موافقة منك."</string>
<string name="permlab_accessImsCallService" msgid="442192920714863782">"الوصول إلى خدمة الاتصال عبر الرسائل الفورية"</string>
- <string name="permdesc_accessImsCallService" msgid="6328551241649687162">"للسماح للتطبيق باستخدام خدمة الرسائل الفورية لإجراء المكالمات دون تدخل منك."</string>
+ <string name="permdesc_accessImsCallService" msgid="6328551241649687162">"للسماح للتطبيق باستخدام خدمة الرسائل الفورية لإجراء المكالمات بدون تدخل منك."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"قراءة حالة الهاتف والهوية"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"للسماح للتطبيق بالدخول إلى ميزات الهاتف في الجهاز. ويتيح هذا الإذن للتطبيق تحديد رقم الهاتف ومعرّفات الجهاز، وما إذا كانت هناك مكالمة نشطة والرقم البعيد الذي تم الاتصال به في المكالمة."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"توجيه المكالمات من خلال النظام"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 6928a14..612854c 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1855,11 +1855,9 @@
<string name="usb_mtp_launch_notification_title" msgid="774319638256707227">"<xliff:g id="PRODUCT_NAME">%1$s</xliff:g> से कनेक्ट किया गया"</string>
<string name="usb_mtp_launch_notification_description" msgid="6942535713629852684">"फ़ाइलें देखने के लिए टैप करें"</string>
<string name="pin_target" msgid="8036028973110156895">"पिन करें"</string>
- <!-- no translation found for pin_specific_target (7824671240625957415) -->
- <skip />
+ <string name="pin_specific_target" msgid="7824671240625957415">"<xliff:g id="LABEL">%1$s</xliff:g> को पिन करें"</string>
<string name="unpin_target" msgid="3963318576590204447">"अनपिन करें"</string>
- <!-- no translation found for unpin_specific_target (3859828252160908146) -->
- <skip />
+ <string name="unpin_specific_target" msgid="3859828252160908146">"<xliff:g id="LABEL">%1$s</xliff:g> को अनपिन करें"</string>
<string name="app_info" msgid="6113278084877079851">"ऐप्लिकेशन की जानकारी"</string>
<string name="negative_duration" msgid="1938335096972945232">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="demo_starting_message" msgid="6577581216125805905">"डेमो प्रारंभ हो रहा है…"</string>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9073a02..245aed1 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -147,6 +147,24 @@
<integer name="config_activityShortDur">150</integer>
<integer name="config_activityDefaultDur">220</integer>
+ <!-- Fade out time for screen rotation -->
+ <integer name="config_screen_rotation_fade_out">116</integer>
+
+ <!-- Fade in time for screen rotation -->
+ <integer name="config_screen_rotation_fade_in">233</integer>
+
+ <!-- Fade in delay time for screen rotation -->
+ <integer name="config_screen_rotation_fade_in_delay">100</integer>
+
+ <!-- Total time for 90 degree screen rotation animations -->
+ <integer name="config_screen_rotation_total_90">333</integer>
+
+ <!-- Total time for 180 degree screen rotation animation -->
+ <integer name="config_screen_rotation_total_180">433</integer>
+
+ <!-- Total time for the rotation background color transition -->
+ <integer name="config_screen_rotation_color_transition">200</integer>
+
<!-- The duration (in milliseconds) of the tooltip show/hide animations. -->
<integer name="config_tooltipAnimTime">150</integer>
@@ -2093,9 +2111,6 @@
<!-- Number of times to try again with the shorter interval, before backing
off until the normal polling interval. A value < 0 indicates infinite. -->
<integer name="config_ntpRetry">3</integer>
- <!-- If the time difference is greater than this threshold in milliseconds,
- then update the time. -->
- <integer name="config_ntpThreshold">5000</integer>
<!-- Timeout to wait for NTP server response in milliseconds. -->
<integer name="config_ntpTimeout">5000</integer>
@@ -4287,4 +4302,12 @@
<!-- Whether or not to use assistant stream volume separately from music volume -->
<bool name="config_useAssistantVolume">false</bool>
+
+ <!-- Whether to use a custom Bugreport handling. When true, ACTION_CUSTOM_BUGREPORT_REQUESTED
+ intent is broadcasted on bugreporting chord (instead of the default full bugreport
+ generation). -->
+ <bool name="config_customBugreport">false</bool>
+
+ <!-- Class name of the custom country detector to be used. -->
+ <string name="config_customCountryDetector" translatable="false">com.android.server.location.ComprehensiveCountryDetector</string>
</resources>
diff --git a/core/res/res/values/cross_profile_apps.xml b/core/res/res/values/cross_profile_apps.xml
new file mode 100644
index 0000000..ab6f20d
--- /dev/null
+++ b/core/res/res/values/cross_profile_apps.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<resources>
+ <!--
+ A collection of apps that have been pre-approved for cross-profile communication.
+ These will not require admin consent, but will still require user consent during provisioning.
+ -->
+ <string-array translatable="false" name="cross_profile_apps">
+ </string-array>
+</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index d12d069..817ccde 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3007,6 +3007,7 @@
<public name="preferMinimalPostProcessing"/>
<public name="featureId" />
<public name="supportsInlineSuggestions" />
+ <public name="crossProfile" />
</public-group>
<public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index ed744ba..66267d1 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4354,6 +4354,18 @@
You can change the feature in Settings > Accessibility.
</string>
+ <!-- Text in button that edit the accessibility shortcut menu. [CHAR LIMIT=100] -->
+ <string name="accessibility_shortcut_menu_button">Empty</string>
+
+ <!-- Text in button that edit the accessibility shortcut menu. [CHAR LIMIT=100] -->
+ <string name="edit_accessibility_shortcut_menu_button">Edit</string>
+
+ <!-- Text in button that save the accessibility shortcut menu changed status. [CHAR LIMIT=100] -->
+ <string name="save_accessibility_shortcut_menu_button">Save</string>
+
+ <!-- Text in button that cancel the accessibility shortcut menu changed status. [CHAR LIMIT=100] -->
+ <string name="cancel_accessibility_shortcut_menu_button">Cancel</string>
+
<!-- Text in button that turns off the accessibility shortcut -->
<string name="disable_accessibility_shortcut">Turn off Shortcut</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a00296c..cdbf14b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -434,7 +434,6 @@
<java-symbol type="integer" name="config_ntpPollingInterval" />
<java-symbol type="integer" name="config_ntpPollingIntervalShorter" />
<java-symbol type="integer" name="config_ntpRetry" />
- <java-symbol type="integer" name="config_ntpThreshold" />
<java-symbol type="integer" name="config_ntpTimeout" />
<java-symbol type="integer" name="config_shortPressOnPowerBehavior" />
<java-symbol type="integer" name="config_toastDefaultGravity" />
@@ -1259,6 +1258,7 @@
<java-symbol type="array" name="vendor_disallowed_apps_managed_user" />
<java-symbol type="array" name="vendor_disallowed_apps_managed_profile" />
<java-symbol type="array" name="vendor_disallowed_apps_managed_device" />
+ <java-symbol type="array" name="cross_profile_apps" />
<java-symbol type="drawable" name="default_wallpaper" />
<java-symbol type="drawable" name="default_lock_wallpaper" />
@@ -1805,7 +1805,6 @@
<!-- From services -->
<java-symbol type="anim" name="screen_rotate_0_enter" />
<java-symbol type="anim" name="screen_rotate_0_exit" />
- <java-symbol type="anim" name="screen_rotate_0_frame" />
<java-symbol type="anim" name="screen_rotate_180_enter" />
<java-symbol type="anim" name="screen_rotate_180_exit" />
<java-symbol type="anim" name="screen_rotate_180_frame" />
@@ -1981,6 +1980,7 @@
<java-symbol type="integer" name="config_virtualKeyQuietTimeMillis" />
<java-symbol type="integer" name="config_brightness_ramp_rate_fast" />
<java-symbol type="integer" name="config_brightness_ramp_rate_slow" />
+ <java-symbol type="integer" name="config_screen_rotation_color_transition" />
<java-symbol type="layout" name="am_compat_mode_dialog" />
<java-symbol type="layout" name="launch_warning" />
<java-symbol type="layout" name="safe_mode" />
@@ -3207,10 +3207,19 @@
<java-symbol type="layout" name="accessibility_button_chooser_item" />
<java-symbol type="id" name="accessibility_button_target_icon" />
<java-symbol type="id" name="accessibility_button_target_label" />
+ <java-symbol type="id" name="accessibility_button_target_item_container" />
+ <java-symbol type="id" name="accessibility_button_target_view_item" />
+ <java-symbol type="id" name="accessibility_button_target_switch_item" />
<java-symbol type="string" name="accessibility_magnification_chooser_text" />
+ <java-symbol type="string" name="edit_accessibility_shortcut_menu_button" />
+ <java-symbol type="string" name="save_accessibility_shortcut_menu_button" />
+ <java-symbol type="string" name="cancel_accessibility_shortcut_menu_button" />
<java-symbol type="drawable" name="ic_accessibility_magnification" />
+ <java-symbol type="drawable" name="ic_delete_item" />
+ <java-symbol type="drawable" name="ic_open_in_new" />
+
<!-- com.android.internal.widget.RecyclerView -->
<java-symbol type="id" name="item_touch_helper_previous_elevation"/>
<java-symbol type="dimen" name="item_touch_helper_max_drag_scroll_per_frame"/>
@@ -3596,6 +3605,8 @@
<java-symbol type="bool" name="config_maskMainBuiltInDisplayCutout" />
+ <java-symbol type="string" name="config_customCountryDetector" />
+
<!-- For Foldables -->
<java-symbol type="bool" name="config_lidControlsDisplayFold" />
<java-symbol type="string" name="config_foldedArea" />
diff --git a/core/tests/InstantAppResolverTests/Android.bp b/core/tests/InstantAppResolverTests/Android.bp
new file mode 100644
index 0000000..7b01010
--- /dev/null
+++ b/core/tests/InstantAppResolverTests/Android.bp
@@ -0,0 +1,32 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+android_test {
+ name: "FrameworksInstantAppResolverTests",
+ srcs: [ "src/**/*.kt" ],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+ platform_apis: true,
+ static_libs: [
+ "androidx.test.ext.junit",
+ "androidx.test.rules",
+ "mockito-target-minus-junit4",
+ "truth-prebuilt",
+ ],
+ test_suites: ["device-tests"],
+}
diff --git a/core/tests/InstantAppResolverTests/AndroidManifest.xml b/core/tests/InstantAppResolverTests/AndroidManifest.xml
new file mode 100644
index 0000000..f95978b
--- /dev/null
+++ b/core/tests/InstantAppResolverTests/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?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="android.app.instantapp.resolver.test"
+ >
+
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:label="InstantAppResolverTests"
+ android:targetPackage="android.app.instantapp.resolver.test"
+ />
+
+</manifest>
diff --git a/core/tests/InstantAppResolverTests/AndroidTest.xml b/core/tests/InstantAppResolverTests/AndroidTest.xml
new file mode 100644
index 0000000..fcc6344
--- /dev/null
+++ b/core/tests/InstantAppResolverTests/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<configuration description="Test module config for InstantAppResolverTests">
+ <option name="test-tag" value="InstantAppResolverTests" />
+
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="FrameworksInstantAppResolverTests.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="android.app.instantapp.resolver.test" />
+ </test>
+</configuration>
diff --git a/core/tests/InstantAppResolverTests/src/android/app/instantapp/resolver/test/ResolverServiceMethodFallbackTest.kt b/core/tests/InstantAppResolverTests/src/android/app/instantapp/resolver/test/ResolverServiceMethodFallbackTest.kt
new file mode 100644
index 0000000..2a17ef2
--- /dev/null
+++ b/core/tests/InstantAppResolverTests/src/android/app/instantapp/resolver/test/ResolverServiceMethodFallbackTest.kt
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.instantapp.resolver.test
+
+import android.app.InstantAppResolverService
+import android.app.InstantAppResolverService.InstantAppResolutionCallback
+import android.content.Intent
+import android.content.pm.InstantAppRequestInfo
+import android.net.Uri
+import android.os.Bundle
+import android.os.IRemoteCallback
+import android.os.UserHandle
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.ExpectedException
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.mockito.Answers
+import org.mockito.Mock
+import org.mockito.Mockito.doNothing
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+import java.util.UUID
+import kotlin.random.Random
+
+private typealias Method = InstantAppResolverService.(InstantAppRequestInfo) -> Unit
+
+@Suppress("max-line-length")
+@RunWith(Parameterized::class)
+class ResolverServiceMethodFallbackTest @Suppress("UNUSED_PARAMETER") constructor(
+ private val version: Int,
+ private val methodList: List<Method>,
+ private val info: InstantAppRequestInfo,
+ // Remaining only used to print human-readable test name
+ name: String,
+ isWebIntent: Boolean
+) {
+
+ companion object {
+ // Since the resolution callback class is final, mock the IRemoteCallback and have it throw
+ // a unique exception to indicate it was called.
+ class TestRemoteCallbackException : Exception()
+
+ private val testIntentWeb = Intent(Intent.ACTION_VIEW,
+ Uri.parse("https://${this::class.java.canonicalName}.com"))
+ private val testIntentNotWeb = Intent(Intent.ACTION_VIEW,
+ Uri.parse("content://${this::class.java.canonicalName}"))
+
+ private val testRemoteCallback = object : IRemoteCallback {
+ override fun sendResult(data: Bundle?) = throw TestRemoteCallbackException()
+ override fun asBinder() = throw UnsupportedOperationException()
+ }
+ private val testResolutionCallback = InstantAppResolutionCallback(0, testRemoteCallback)
+ private val testArray = IntArray(10) { Random.nextInt() }
+ private val testToken = UUID.randomUUID().toString()
+ private val testUser = UserHandle(Integer.MAX_VALUE)
+ private val testInfoWeb = InstantAppRequestInfo(testIntentWeb, testArray, testUser,
+ false, testToken)
+ private val testInfoNotWeb = InstantAppRequestInfo(testIntentNotWeb, testArray, testUser,
+ false, testToken)
+
+ // Each section defines methods versions with later definitions falling back to
+ // earlier definitions. Each block receives an [InstantAppResolverService] and invokes
+ // the appropriate version with the test data defined above.
+ private val infoOne: Method = { onGetInstantAppResolveInfo(testArray, testToken,
+ testResolutionCallback) }
+ private val infoTwo: Method = { onGetInstantAppResolveInfo(it.intent, testArray, testToken,
+ testResolutionCallback) }
+ private val infoThree: Method = { onGetInstantAppResolveInfo(it.intent, testArray, testUser,
+ testToken, testResolutionCallback) }
+ private val infoFour: Method = { onGetInstantAppResolveInfo(it, testResolutionCallback) }
+
+ private val filterOne: Method = { onGetInstantAppIntentFilter(testArray, testToken,
+ testResolutionCallback) }
+ private val filterTwo: Method = { onGetInstantAppIntentFilter(it.intent, testArray,
+ testToken, testResolutionCallback) }
+ private val filterThree: Method = { onGetInstantAppIntentFilter(it.intent, testArray,
+ testUser, testToken, testResolutionCallback) }
+ private val filterFour: Method = { onGetInstantAppIntentFilter(it, testResolutionCallback) }
+
+ private val infoList = listOf(infoOne, infoTwo, infoThree, infoFour)
+ private val filterList = listOf(filterOne, filterTwo, filterThree, filterFour)
+
+ @JvmStatic
+ @Parameterized.Parameters(name = "{3} version {0}, isWeb = {4}")
+ fun parameters(): Array<Array<*>> {
+ // Sanity check that web intent logic hasn't changed
+ assertThat(testInfoWeb.intent.isWebIntent).isTrue()
+ assertThat(testInfoNotWeb.intent.isWebIntent).isFalse()
+
+ // Declare all the possible params
+ val versions = Array(5) { it }
+ val methods = arrayOf("ResolveInfo" to infoList, "IntentFilter" to filterList)
+ val infos = arrayOf(testInfoWeb, testInfoNotWeb)
+
+ // FlatMap params into every possible combination
+ return infos.flatMap { info ->
+ methods.flatMap { (name, methods) ->
+ versions.map { version ->
+ arrayOf(version, methods, info, name, info.intent.isWebIntent)
+ }
+ }
+ }.toTypedArray()
+ }
+ }
+
+ @field:Mock(answer = Answers.CALLS_REAL_METHODS)
+ lateinit var mockService: InstantAppResolverService
+
+ @get:Rule
+ val expectedException = ExpectedException.none()
+
+ @Before
+ fun setUpMocks() {
+ MockitoAnnotations.initMocks(this)
+ }
+
+ @Test
+ fun onGetInstantApp() {
+ if (version == 0) {
+ // No version of the API was implemented, so expect terminal case
+ if (info.intent.isWebIntent) {
+ // If web intent, terminal is total failure
+ expectedException.expect(IllegalStateException::class.java)
+ } else {
+ // Otherwise, terminal is a fail safe by calling [testRemoteCallback]
+ expectedException.expect(TestRemoteCallbackException::class.java)
+ }
+ } else if (version < 2 && !info.intent.isWebIntent) {
+ // Starting from v2, if resolving a non-web intent and a v2+ method isn't implemented,
+ // it fails safely by calling [testRemoteCallback]
+ expectedException.expect(TestRemoteCallbackException::class.java)
+ }
+
+ // Version 1 is the first method (index 0)
+ val methodIndex = version - 1
+
+ // Implement a method if necessary
+ methodList.getOrNull(methodIndex)?.invoke(doNothing().`when`(mockService), info)
+
+ // Call the latest API
+ methodList.last().invoke(mockService, info)
+
+ // Check all methods before implemented method are never called
+ (0 until methodIndex).forEach {
+ methodList[it].invoke(verify(mockService, never()), info)
+ }
+
+ // Check all methods from implemented method are called
+ (methodIndex until methodList.size).forEach {
+ methodList[it].invoke(verify(mockService), info)
+ }
+
+ verifyNoMoreInteractions(mockService)
+ }
+}
diff --git a/core/tests/coretests/src/android/app/timedetector/ManualTimeSuggestionTest.java b/core/tests/coretests/src/android/app/timedetector/ManualTimeSuggestionTest.java
index de6f8f7..750ffa1 100644
--- a/core/tests/coretests/src/android/app/timedetector/ManualTimeSuggestionTest.java
+++ b/core/tests/coretests/src/android/app/timedetector/ManualTimeSuggestionTest.java
@@ -22,7 +22,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
-import android.util.TimestampedValue;
+import android.os.TimestampedValue;
import org.junit.Test;
diff --git a/core/tests/coretests/src/android/app/timedetector/NetworkTimeSuggestionTest.java b/core/tests/coretests/src/android/app/timedetector/NetworkTimeSuggestionTest.java
new file mode 100644
index 0000000..b88c36f
--- /dev/null
+++ b/core/tests/coretests/src/android/app/timedetector/NetworkTimeSuggestionTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.timedetector;
+
+import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable;
+import static android.app.timezonedetector.ParcelableTestSupport.roundTripParcelable;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.os.TimestampedValue;
+
+import org.junit.Test;
+
+public class NetworkTimeSuggestionTest {
+
+ private static final TimestampedValue<Long> ARBITRARY_TIME =
+ new TimestampedValue<>(1111L, 2222L);
+
+ @Test
+ public void testEquals() {
+ NetworkTimeSuggestion one = new NetworkTimeSuggestion(ARBITRARY_TIME);
+ assertEquals(one, one);
+
+ NetworkTimeSuggestion two = new NetworkTimeSuggestion(ARBITRARY_TIME);
+ assertEquals(one, two);
+ assertEquals(two, one);
+
+ TimestampedValue<Long> differentTime = new TimestampedValue<>(
+ ARBITRARY_TIME.getReferenceTimeMillis() + 1,
+ ARBITRARY_TIME.getValue());
+ NetworkTimeSuggestion three = new NetworkTimeSuggestion(differentTime);
+ assertNotEquals(one, three);
+ assertNotEquals(three, one);
+
+ // DebugInfo must not be considered in equals().
+ one.addDebugInfo("Debug info 1");
+ two.addDebugInfo("Debug info 2");
+ assertEquals(one, two);
+ }
+
+ @Test
+ public void testParcelable() {
+ NetworkTimeSuggestion suggestion = new NetworkTimeSuggestion(ARBITRARY_TIME);
+ assertRoundTripParcelable(suggestion);
+
+ // DebugInfo should also be stored (but is not checked by equals()
+ suggestion.addDebugInfo("This is debug info");
+ NetworkTimeSuggestion rtSuggestion = roundTripParcelable(suggestion);
+ assertEquals(suggestion.getDebugInfo(), rtSuggestion.getDebugInfo());
+ }
+}
diff --git a/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java b/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java
index bee270e..ba29a97 100644
--- a/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java
+++ b/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java
@@ -22,7 +22,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
-import android.util.TimestampedValue;
+import android.os.TimestampedValue;
import org.junit.Test;
diff --git a/core/tests/coretests/src/android/os/ExternalVibrationTest.java b/core/tests/coretests/src/android/os/ExternalVibrationTest.java
new file mode 100644
index 0000000..3b872d5
--- /dev/null
+++ b/core/tests/coretests/src/android/os/ExternalVibrationTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.Mockito.mock;
+
+import android.media.AudioAttributes;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ExternalVibrationTest {
+ @Test
+ public void testSerialization() {
+ AudioAttributes audio = new AudioAttributes.Builder().build();
+ IExternalVibrationController controller = mock(IExternalVibrationController.class);
+ ExternalVibration original = new ExternalVibration(
+ 123, // uid
+ "pkg",
+ audio,
+ controller);
+ Parcel p = Parcel.obtain();
+ original.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ ExternalVibration restored = ExternalVibration.CREATOR.createFromParcel(p);
+ assertEquals(original, restored);
+ }
+}
+
diff --git a/core/tests/coretests/src/android/util/TimestampedValueTest.java b/core/tests/coretests/src/android/os/TimestampedValueTest.java
similarity index 98%
rename from core/tests/coretests/src/android/util/TimestampedValueTest.java
rename to core/tests/coretests/src/android/os/TimestampedValueTest.java
index 6fc2400..f36d9e6 100644
--- a/core/tests/coretests/src/android/util/TimestampedValueTest.java
+++ b/core/tests/coretests/src/android/os/TimestampedValueTest.java
@@ -14,14 +14,12 @@
* limitations under the License.
*/
-package android.util;
+package android.os;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.fail;
-import android.os.Parcel;
-
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
diff --git a/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
index ae835e4..84c42db 100644
--- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java
+++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
@@ -20,6 +20,8 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.testng.Assert.assertThrows;
+
import android.content.ContentResolver;
import android.os.Bundle;
import android.platform.test.annotations.Presubmit;
@@ -44,9 +46,11 @@
private static final String KEY = "key1";
private static final String KEY2 = "key2";
private static final String KEY3 = "key3";
+ private static final String KEY4 = "key4";
private static final String VALUE = "value1";
private static final String VALUE2 = "value2";
private static final String VALUE3 = "value3";
+ private static final String NULL_VALUE = "null";
@After
public void cleanUp() {
@@ -561,6 +565,78 @@
assertThat(properties.getFloat("key5", 0f)).isEqualTo(floatValue);
}
+ @Test
+ public void banNamespaceProperties() throws DeviceConfig.BadConfigException {
+ // Given namespace will be permanently banned, thus it needs to be different every time
+ final String namespaceToBan = NAMESPACE + System.currentTimeMillis();
+ Properties properties = new Properties.Builder(namespaceToBan).setString(KEY, VALUE)
+ .setString(KEY4, NULL_VALUE).build();
+ // Set namespace properties
+ DeviceConfig.setProperties(properties);
+ // Ban namespace with related properties
+ DeviceConfig.resetToDefaults(Settings.RESET_MODE_PACKAGE_DEFAULTS, namespaceToBan);
+ // Verify given namespace properties are banned
+ assertThrows(DeviceConfig.BadConfigException.class,
+ () -> DeviceConfig.setProperties(properties));
+ // Modify properties and verify we can set them
+ Properties modifiedProperties = new Properties.Builder(namespaceToBan).setString(KEY, VALUE)
+ .setString(KEY4, NULL_VALUE).setString(KEY2, VALUE2).build();
+ DeviceConfig.setProperties(modifiedProperties);
+ modifiedProperties = DeviceConfig.getProperties(namespaceToBan);
+ assertThat(modifiedProperties.getKeyset()).containsExactly(KEY, KEY2, KEY4);
+ assertThat(modifiedProperties.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE);
+ assertThat(modifiedProperties.getString(KEY2, DEFAULT_VALUE)).isEqualTo(VALUE2);
+ // Since value is null DEFAULT_VALUE should be returned
+ assertThat(modifiedProperties.getString(KEY4, DEFAULT_VALUE)).isEqualTo(DEFAULT_VALUE);
+ }
+
+ @Test
+ public void banEntireDeviceConfig() throws DeviceConfig.BadConfigException {
+ // Given namespaces will be permanently banned, thus they need to be different every time
+ final String namespaceToBan1 = NAMESPACE + System.currentTimeMillis();
+ final String namespaceToBan2 = NAMESPACE + System.currentTimeMillis() + 1;
+
+ // Set namespaces properties
+ Properties properties1 = new Properties.Builder(namespaceToBan1).setString(KEY, VALUE)
+ .setString(KEY4, NULL_VALUE).build();
+ DeviceConfig.setProperties(properties1);
+ Properties properties2 = new Properties.Builder(namespaceToBan2).setString(KEY2, VALUE2)
+ .setString(KEY4, NULL_VALUE).build();
+ DeviceConfig.setProperties(properties2);
+
+ // Ban entire DeviceConfig
+ DeviceConfig.resetToDefaults(Settings.RESET_MODE_PACKAGE_DEFAULTS, null);
+
+ // Verify given namespace properties are banned
+ assertThrows(DeviceConfig.BadConfigException.class,
+ () -> DeviceConfig.setProperties(properties1));
+ assertThrows(DeviceConfig.BadConfigException.class,
+ () -> DeviceConfig.setProperties(properties2));
+
+ // Modify properties and verify we can set them
+ Properties modifiedProperties1 = new Properties.Builder(namespaceToBan1).setString(KEY,
+ VALUE)
+ .setString(KEY4, NULL_VALUE).setString(KEY2, VALUE2).build();
+ DeviceConfig.setProperties(modifiedProperties1);
+ modifiedProperties1 = DeviceConfig.getProperties(namespaceToBan1);
+ assertThat(modifiedProperties1.getKeyset()).containsExactly(KEY, KEY2, KEY4);
+ assertThat(modifiedProperties1.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE);
+ assertThat(modifiedProperties1.getString(KEY2, DEFAULT_VALUE)).isEqualTo(VALUE2);
+ // Since value is null DEFAULT_VALUE should be returned
+ assertThat(modifiedProperties1.getString(KEY4, DEFAULT_VALUE)).isEqualTo(DEFAULT_VALUE);
+
+ Properties modifiedProperties2 = new Properties.Builder(namespaceToBan2).setString(KEY,
+ VALUE)
+ .setString(KEY4, NULL_VALUE).setString(KEY2, VALUE2).build();
+ DeviceConfig.setProperties(modifiedProperties1);
+ modifiedProperties2 = DeviceConfig.getProperties(namespaceToBan1);
+ assertThat(modifiedProperties2.getKeyset()).containsExactly(KEY, KEY2, KEY4);
+ assertThat(modifiedProperties2.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE);
+ assertThat(modifiedProperties2.getString(KEY2, DEFAULT_VALUE)).isEqualTo(VALUE2);
+ // Since value is null DEFAULT_VALUE should be returned
+ assertThat(modifiedProperties2.getString(KEY4, DEFAULT_VALUE)).isEqualTo(DEFAULT_VALUE);
+ }
+
// TODO(mpape): resolve b/142727848 and re-enable listener tests
// @Test
// public void onPropertiesChangedListener_setPropertyCallback() throws InterruptedException {
diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
index 2cf6ff3..7af833b 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
@@ -102,8 +102,12 @@
@Test
public void testShow() {
+
+ // Insets source starts out visible
+ mConsumer.hide();
mConsumer.show();
assertTrue("Consumer should be visible", mConsumer.isVisible());
+ verify(mSpyInsetsSource).setVisible(eq(false));
verify(mSpyInsetsSource).setVisible(eq(true));
}
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index 6062088..fa2ffcca 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -23,6 +23,7 @@
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.WindowInsets.Type.ime;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static org.junit.Assert.assertEquals;
@@ -116,14 +117,18 @@
@Test
public void testCalculateInsets_imeIgnoredWithoutAdjustResize() {
- mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
- mState.getSource(ITYPE_STATUS_BAR).setVisible(true);
- mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300));
- mState.getSource(ITYPE_IME).setVisible(true);
- WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
- DisplayCutout.NO_CUTOUT, null, null, 0, null);
- assertEquals(0, insets.getSystemWindowInsetBottom());
- assertTrue(insets.isVisible(ime()));
+ try (final InsetsModeSession session =
+ new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) {
+ mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(ITYPE_STATUS_BAR).setVisible(true);
+ mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300));
+ mState.getSource(ITYPE_IME).setVisible(true);
+ WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
+ DisplayCutout.NO_CUTOUT, null, null, SOFT_INPUT_ADJUST_NOTHING, null);
+ assertEquals(0, insets.getSystemWindowInsetBottom());
+ assertEquals(100, insets.getInsets(ime()).bottom);
+ assertTrue(insets.isVisible(ime()));
+ }
}
@Test
diff --git a/core/tests/coretests/src/android/view/WindowInsetsTest.java b/core/tests/coretests/src/android/view/WindowInsetsTest.java
index 8c7b28a..e5a4f6d 100644
--- a/core/tests/coretests/src/android/view/WindowInsetsTest.java
+++ b/core/tests/coretests/src/android/view/WindowInsetsTest.java
@@ -63,7 +63,8 @@
b.setInsets(navigationBars(), Insets.of(0, 0, 0, 100));
b.setInsets(ime(), Insets.of(0, 0, 0, 300));
WindowInsets insets = b.build();
- assertEquals(300, insets.getSystemWindowInsets().bottom);
+ assertEquals(100, insets.getSystemWindowInsets().bottom);
+ assertEquals(300, insets.getInsets(ime()).bottom);
}
// TODO: Move this to CTS once API made public
diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
index b4f2c91..f497db2 100644
--- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
+++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
@@ -16,15 +16,23 @@
package android.widget;
+import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
import static android.widget.espresso.TextViewActions.dragOnText;
import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex;
+import static android.widget.espresso.TextViewAssertions.hasSelection;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.replaceText;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static org.hamcrest.Matchers.emptyString;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
import android.app.Activity;
import android.app.Instrumentation;
+import android.view.MotionEvent;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -70,107 +78,247 @@
onView(withId(R.id.textview)).perform(replaceText(text));
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));
- // Drag left to right. The cursor should end up at the position where the finger is lifted.
+ // Swipe left to right to drag the cursor. The cursor should end up at the position where
+ // the finger is lifted.
onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("llo"), text.indexOf("!")));
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(11));
- // Drag right to left. The cursor should end up at the position where the finger is lifted.
+ // Swipe right to left to drag the cursor. The cursor should end up at the position where
+ // the finger is lifted.
onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("!"), text.indexOf("llo")));
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(2));
}
@Test
public void testCursorDrag_horizontal_whenTextViewContentsLargerThanScreen() throws Throwable {
- String text = "Hello world!"
- + Strings.repeat("\n", 500) + "012345middle"
- + Strings.repeat("\n", 10) + "012345last";
+ String text = "Hello world!\n\n"
+ + Strings.repeat("Bla\n\n", 200) + "Bye";
onView(withId(R.id.textview)).perform(replaceText(text));
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));
- // Drag left to right. The cursor should end up at the position where the finger is lifted.
+ // Swipe left to right to drag the cursor. The cursor should end up at the position where
+ // the finger is lifted.
onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("llo"), text.indexOf("!")));
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(11));
- // Drag right to left. The cursor should end up at the position where the finger is lifted.
+ // Swipe right to left to drag the cursor. The cursor should end up at the position where
+ // the finger is lifted.
onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("!"), text.indexOf("llo")));
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(2));
}
@Test
+ public void testCursorDrag_diagonal_whenTextViewContentsFitOnScreen() throws Throwable {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 1; i <= 9; i++) {
+ sb.append("line").append(i).append("\n");
+ }
+ String text = sb.toString();
+ onView(withId(R.id.textview)).perform(replaceText(text));
+ onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));
+
+ // Swipe along a diagonal path. This should drag the cursor.
+ onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("2")));
+ onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("2")));
+
+ // Swipe along a steeper diagonal path. This should still drag the cursor.
+ onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("3")));
+ onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("3")));
+
+ // Swipe right-down along a very steep diagonal path. This should not drag the cursor.
+ // Normally this would trigger a scroll, but since the full view fits on the screen there
+ // is nothing to scroll and the gesture will trigger a selection drag.
+ onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("7")));
+ onView(withId(R.id.textview)).check(hasSelection(not(emptyString())));
+
+ // Swipe right-up along a very steep diagonal path. This should not drag the cursor.
+ // Normally this would trigger a scroll, but since the full view fits on the screen there
+ // is nothing to scroll and the gesture will trigger a selection drag.
+ int index = text.indexOf("line9");
+ onView(withId(R.id.textview)).perform(clickOnTextAtIndex(index));
+ onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(index));
+ onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line7"), text.indexOf("1")));
+ onView(withId(R.id.textview)).check(hasSelection(not(emptyString())));
+ }
+
+ @Test
public void testCursorDrag_diagonal_whenTextViewContentsLargerThanScreen() throws Throwable {
StringBuilder sb = new StringBuilder();
for (int i = 1; i <= 9; i++) {
sb.append("line").append(i).append("\n");
}
- sb.append(Strings.repeat("0123456789\n\n", 500)).append("Last line");
+ sb.append(Strings.repeat("0123456789\n", 400)).append("Last");
String text = sb.toString();
onView(withId(R.id.textview)).perform(replaceText(text));
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));
- // Drag along a diagonal path.
+ // Swipe along a diagonal path. This should drag the cursor.
onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("2")));
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("2")));
- // Drag along a steeper diagonal path.
- onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("9")));
- onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("9")));
+ // Swipe along a steeper diagonal path. This should still drag the cursor.
+ onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("3")));
+ onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("3")));
- // Drag along an almost vertical path.
- // TODO(b/145833335): Consider whether this should scroll instead of dragging the cursor.
- onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("ne1"), text.indexOf("9")));
- onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("9")));
+ // Swipe right-down along a very steep diagonal path. This should not drag the cursor.
+ // Normally this would trigger a scroll up, but since the view is already at the top there
+ // is nothing to scroll and the gesture will trigger a selection drag.
+ onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("7")));
+ onView(withId(R.id.textview)).check(hasSelection(not(emptyString())));
- // Drag along a vertical path from line 1 to line 9.
- // TODO(b/145833335): Consider whether this should scroll instead of dragging the cursor.
- onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("e1"), text.indexOf("e9")));
- onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("e9")));
-
- // Drag along a vertical path from line 9 to line 1.
- // TODO(b/145833335): Consider whether this should scroll instead of dragging the cursor.
- onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("e9"), text.indexOf("e1")));
- onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("e1")));
+ // Swipe right-up along a very steep diagonal path. This should not drag the cursor. This
+ // will trigger a downward scroll and the cursor position will not change.
+ int index = text.indexOf("line9");
+ onView(withId(R.id.textview)).perform(clickOnTextAtIndex(index));
+ onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(index));
+ onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line7"), text.indexOf("1")));
+ onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(index));
}
@Test
public void testCursorDrag_vertical_whenTextViewContentsFitOnScreen() throws Throwable {
- String text = "012first\n\n" + Strings.repeat("012345\n\n", 10) + "012last";
+ String text = "012345_aaa\n"
+ + "0123456789\n"
+ + "012345_bbb\n"
+ + "0123456789\n"
+ + "012345_ccc\n"
+ + "0123456789\n"
+ + "012345_ddd";
onView(withId(R.id.textview)).perform(replaceText(text));
+
+ // Swipe up vertically. This should not drag the cursor. Since there's also nothing to
+ // scroll, the gesture will trigger a selection drag.
+ onView(withId(R.id.textview)).perform(clickOnTextAtIndex(0));
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));
+ onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("bbb"), text.indexOf("aaa")));
+ onView(withId(R.id.textview)).check(hasSelection(not(emptyString())));
- // Drag down. Since neither the TextView nor its container require scrolling, the cursor
- // drag should execute and the cursor should end up at the position where the finger is
- // lifted.
- onView(withId(R.id.textview)).perform(
- dragOnText(text.indexOf("first"), text.indexOf("last")));
- onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length() - 4));
-
- // Drag up. Since neither the TextView nor its container require scrolling, the cursor
- // drag should execute and the cursor should end up at the position where the finger is
- // lifted.
- onView(withId(R.id.textview)).perform(
- dragOnText(text.indexOf("last"), text.indexOf("first")));
- onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(3));
+ // Swipe down vertically. This should not drag the cursor. Since there's also nothing to
+ // scroll, the gesture will trigger a selection drag.
+ onView(withId(R.id.textview)).perform(clickOnTextAtIndex(0));
+ onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));
+ onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("ccc"), text.indexOf("ddd")));
+ onView(withId(R.id.textview)).check(hasSelection(not(emptyString())));
}
@Test
public void testCursorDrag_vertical_whenTextViewContentsLargerThanScreen() throws Throwable {
- String text = "012345first\n\n"
- + Strings.repeat("0123456789\n\n", 10) + "012345middle"
- + Strings.repeat("0123456789\n\n", 500) + "012345last";
+ String text = "012345_aaa\n"
+ + "0123456789\n"
+ + "012345_bbb\n"
+ + "0123456789\n"
+ + "012345_ccc\n"
+ + "0123456789\n"
+ + "012345_ddd\n"
+ + Strings.repeat("0123456789\n", 400) + "012345_zzz";
onView(withId(R.id.textview)).perform(replaceText(text));
- int initialCursorPosition = 0;
+ onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.indexOf("ddd")));
+ int initialCursorPosition = text.indexOf("ddd");
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(initialCursorPosition));
- // Drag up.
- // TODO(b/145833335): Consider whether this should scroll instead of dragging the cursor.
- onView(withId(R.id.textview)).perform(
- dragOnText(text.indexOf("middle"), text.indexOf("first")));
- onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("first")));
+ // Swipe up vertically. This should trigger a downward scroll.
+ onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("bbb"), text.indexOf("aaa")));
+ onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(initialCursorPosition));
- // Drag down.
- // TODO(b/145833335): Consider whether this should scroll instead of dragging the cursor.
- onView(withId(R.id.textview)).perform(
- dragOnText(text.indexOf("first"), text.indexOf("middle")));
- onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("middle")));
+ // Swipe down vertically. This should trigger an upward scroll.
+ onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("ccc"), text.indexOf("ddd")));
+ onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(initialCursorPosition));
+ }
+
+ @Test
+ public void testEditor_onTouchEvent_cursorDrag() throws Throwable {
+ String text = "testEditor_onTouchEvent_cursorDrag";
+ onView(withId(R.id.textview)).perform(replaceText(text));
+ onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));
+
+ TextView tv = mActivity.findViewById(R.id.textview);
+ Editor editor = tv.getEditorForTesting();
+
+ // Simulate a tap-and-drag gesture. This should trigger a cursor drag.
+ long event1Time = 1001;
+ MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
+ mActivity.runOnUiThread(() -> editor.onTouchEvent(event1));
+ mInstrumentation.waitForIdleSync();
+ assertFalse(editor.getInsertionController().isCursorBeingModified());
+ assertFalse(editor.getSelectionController().isCursorBeingModified());
+
+ long event2Time = 1002;
+ MotionEvent event2 = moveEvent(event1Time, event2Time, 21f, 30f);
+ mActivity.runOnUiThread(() -> editor.onTouchEvent(event2));
+ mInstrumentation.waitForIdleSync();
+ assertFalse(editor.getInsertionController().isCursorBeingModified());
+ assertFalse(editor.getSelectionController().isCursorBeingModified());
+
+ long event3Time = 1003;
+ MotionEvent event3 = moveEvent(event3Time, event3Time, 120f, 30f);
+ mActivity.runOnUiThread(() -> editor.onTouchEvent(event3));
+ mInstrumentation.waitForIdleSync();
+ assertTrue(editor.getInsertionController().isCursorBeingModified());
+ assertFalse(editor.getSelectionController().isCursorBeingModified());
+
+ long event4Time = 1004;
+ MotionEvent event4 = upEvent(event3Time, event4Time, 120f, 30f);
+ mActivity.runOnUiThread(() -> editor.onTouchEvent(event4));
+ mInstrumentation.waitForIdleSync();
+ assertFalse(editor.getInsertionController().isCursorBeingModified());
+ assertFalse(editor.getSelectionController().isCursorBeingModified());
+ }
+
+ @Test
+ public void testEditor_onTouchEvent_selectionDrag() throws Throwable {
+ String text = "testEditor_onTouchEvent_selectionDrag";
+ onView(withId(R.id.textview)).perform(replaceText(text));
+ onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));
+
+ TextView tv = mActivity.findViewById(R.id.textview);
+ Editor editor = tv.getEditorForTesting();
+
+ // Simulate a double-tap followed by a drag. This should trigger a selection drag.
+ long event1Time = 1001;
+ MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
+ mActivity.runOnUiThread(() -> editor.onTouchEvent(event1));
+ mInstrumentation.waitForIdleSync();
+ assertFalse(editor.getInsertionController().isCursorBeingModified());
+ assertFalse(editor.getSelectionController().isCursorBeingModified());
+
+ long event2Time = 1002;
+ MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f);
+ mActivity.runOnUiThread(() -> editor.onTouchEvent(event2));
+ mInstrumentation.waitForIdleSync();
+ assertFalse(editor.getInsertionController().isCursorBeingModified());
+ assertFalse(editor.getSelectionController().isCursorBeingModified());
+
+ long event3Time = 1003;
+ MotionEvent event3 = downEvent(event3Time, event3Time, 20f, 30f);
+ mActivity.runOnUiThread(() -> editor.onTouchEvent(event3));
+ mInstrumentation.waitForIdleSync();
+ assertFalse(editor.getInsertionController().isCursorBeingModified());
+ assertTrue(editor.getSelectionController().isCursorBeingModified());
+
+ long event4Time = 1004;
+ MotionEvent event4 = moveEvent(event3Time, event4Time, 120f, 30f);
+ mActivity.runOnUiThread(() -> editor.onTouchEvent(event4));
+ mInstrumentation.waitForIdleSync();
+ assertFalse(editor.getInsertionController().isCursorBeingModified());
+ assertTrue(editor.getSelectionController().isCursorBeingModified());
+
+ long event5Time = 1005;
+ MotionEvent event5 = upEvent(event3Time, event5Time, 120f, 30f);
+ mActivity.runOnUiThread(() -> editor.onTouchEvent(event5));
+ mInstrumentation.waitForIdleSync();
+ assertFalse(editor.getInsertionController().isCursorBeingModified());
+ assertFalse(editor.getSelectionController().isCursorBeingModified());
+ }
+
+ private static MotionEvent downEvent(long downTime, long eventTime, float x, float y) {
+ return MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_DOWN, x, y, 0);
+ }
+
+ private static MotionEvent upEvent(long downTime, long eventTime, float x, float y) {
+ return MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0);
+ }
+
+ private static MotionEvent moveEvent(long downTime, long eventTime, float x, float y) {
+ return MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, x, y, 0);
}
}
diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
index 9863e38..66d84aa 100644
--- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
+++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
@@ -54,7 +54,6 @@
import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -76,7 +75,6 @@
@RunWith(AndroidJUnit4.class)
@MediumTest
@Presubmit
-@FlakyTest(bugId = 143153552)
public class ActivityThreadClientTest {
@Test
diff --git a/core/xsd/permission.xsd b/core/xsd/permission.xsd
index 9520db7..cc01a31 100644
--- a/core/xsd/permission.xsd
+++ b/core/xsd/permission.xsd
@@ -126,12 +126,12 @@
</xs:complexType>
<xs:complexType name="privapp-permissions">
<xs:sequence>
- <xs:element name="permission" maxOccurs="unbounded">
+ <xs:element name="permission" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="name" type="xs:string"/>
</xs:complexType>
</xs:element>
- <xs:element name="deny-permission" maxOccurs="unbounded">
+ <xs:element name="deny-permission" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="name" type="xs:string"/>
</xs:complexType>
@@ -141,12 +141,12 @@
</xs:complexType>
<xs:complexType name="oem-permissions">
<xs:sequence>
- <xs:element name="permission" maxOccurs="unbounded">
+ <xs:element name="permission" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="name" type="xs:string"/>
</xs:complexType>
</xs:element>
- <xs:element name="deny-permission" maxOccurs="unbounded">
+ <xs:element name="deny-permission" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="name" type="xs:string"/>
</xs:complexType>
diff --git a/data/etc/com.android.settings.xml b/data/etc/com.android.settings.xml
index ee989cca1..70b61e0 100644
--- a/data/etc/com.android.settings.xml
+++ b/data/etc/com.android.settings.xml
@@ -42,8 +42,8 @@
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
<permission name="android.permission.READ_SEARCH_INDEXABLES"/>
<permission name="android.permission.REBOOT"/>
- <permission name="android.permission.SET_TIME"/>
<permission name="android.permission.STATUS_BAR"/>
+ <permission name="android.permission.SUGGEST_MANUAL_TIME_AND_ZONE"/>
<permission name="android.permission.TETHER_PRIVILEGED"/>
<permission name="android.permission.USE_RESERVED_DISK"/>
<permission name="android.permission.USER_ACTIVITY"/>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 38a9f46..eb1d1ab 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -160,12 +160,12 @@
<permission name="android.permission.REGISTER_CALL_PROVIDER"/>
<permission name="android.permission.REGISTER_SIM_SUBSCRIPTION"/>
<permission name="android.permission.SEND_RESPOND_VIA_MESSAGE"/>
- <permission name="android.permission.SET_TIME"/>
<permission name="android.permission.SET_TIME_ZONE"/>
<permission name="android.permission.SHUTDOWN"/>
<permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/>
<permission name="android.permission.STATUS_BAR"/>
<permission name="android.permission.STOP_APP_SWITCHES"/>
+ <permission name="android.permission.SUGGEST_PHONE_TIME_AND_ZONE"/>
<permission name="android.permission.UPDATE_APP_OPS_STATS"/>
<permission name="android.permission.UPDATE_DEVICE_STATS"/>
<permission name="android.permission.UPDATE_LOCK"/>
diff --git a/docs/html/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-11-ratio.png b/docs/html/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-11-ratio.png
new file mode 100644
index 0000000..b6c07656
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-11-ratio.png
Binary files differ
diff --git a/docs/html/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-169-ratio.png b/docs/html/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-169-ratio.png
new file mode 100644
index 0000000..4e975e5
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-169-ratio.png
Binary files differ
diff --git a/docs/html/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-ratio.png b/docs/html/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-ratio.png
new file mode 100644
index 0000000..a331095
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-ratio.png
Binary files differ
diff --git a/docs/html/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-square-ratio.png b/docs/html/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-square-ratio.png
new file mode 100644
index 0000000..41e6668
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-square-ratio.png
Binary files differ
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index 6e7f286..bee8d5e 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -21,7 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Canvas.VertexMode;
import android.graphics.text.MeasuredText;
import android.text.GraphicsOperations;
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index d900a42..ac094ba 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -21,8 +21,8 @@
import android.annotation.ColorLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
import android.annotation.WorkerThread;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.ResourcesImpl;
import android.hardware.HardwareBuffer;
import android.os.Build;
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index 5623a8a..bad487b 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -20,7 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.os.Trace;
diff --git a/graphics/java/android/graphics/BitmapRegionDecoder.java b/graphics/java/android/graphics/BitmapRegionDecoder.java
index 629d8c1..34eba97 100644
--- a/graphics/java/android/graphics/BitmapRegionDecoder.java
+++ b/graphics/java/android/graphics/BitmapRegionDecoder.java
@@ -15,7 +15,7 @@
package android.graphics;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.AssetManager;
import android.os.Build;
diff --git a/graphics/java/android/graphics/BitmapShader.java b/graphics/java/android/graphics/BitmapShader.java
index 198d1e7..edf53c4 100644
--- a/graphics/java/android/graphics/BitmapShader.java
+++ b/graphics/java/android/graphics/BitmapShader.java
@@ -17,7 +17,7 @@
package android.graphics;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
/**
* Shader used to draw a bitmap as a texture. The bitmap can be repeated or
diff --git a/graphics/java/android/graphics/Camera.java b/graphics/java/android/graphics/Camera.java
index cbd4ead..80a3740 100644
--- a/graphics/java/android/graphics/Camera.java
+++ b/graphics/java/android/graphics/Camera.java
@@ -16,7 +16,7 @@
package android.graphics;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
/**
* A camera instance can be used to compute 3D transformations and
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index a815f20..9a0ca3e 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -22,7 +22,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.text.MeasuredText;
import android.os.Build;
diff --git a/graphics/java/android/graphics/CanvasProperty.java b/graphics/java/android/graphics/CanvasProperty.java
index 1275e08..4263772c 100644
--- a/graphics/java/android/graphics/CanvasProperty.java
+++ b/graphics/java/android/graphics/CanvasProperty.java
@@ -16,7 +16,8 @@
package android.graphics;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
+
import com.android.internal.util.VirtualRefBasePtr;
/**
diff --git a/graphics/java/android/graphics/ColorMatrixColorFilter.java b/graphics/java/android/graphics/ColorMatrixColorFilter.java
index 0f7980c..a8b18a9 100644
--- a/graphics/java/android/graphics/ColorMatrixColorFilter.java
+++ b/graphics/java/android/graphics/ColorMatrixColorFilter.java
@@ -18,7 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
/**
* A color filter that transforms colors through a 4x5 color matrix. This filter
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index 5ad93f4..ae90995 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -17,7 +17,7 @@
package android.graphics;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.AssetManager;
import android.graphics.fonts.FontVariationAxis;
import android.text.TextUtils;
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index 21cc375..c146bbd 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -16,7 +16,7 @@
package android.graphics;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.fonts.FontVariationAxis;
import android.text.FontConfig;
import android.util.Xml;
diff --git a/graphics/java/android/graphics/GraphicBuffer.java b/graphics/java/android/graphics/GraphicBuffer.java
index 3b1fc70..99fa5ee 100644
--- a/graphics/java/android/graphics/GraphicBuffer.java
+++ b/graphics/java/android/graphics/GraphicBuffer.java
@@ -16,7 +16,7 @@
package android.graphics;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index aecef8e..83432c3 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -28,8 +28,8 @@
import android.annotation.Nullable;
import android.annotation.Px;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
import android.annotation.WorkerThread;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentResolver;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
@@ -277,6 +277,10 @@
assetFd = mResolver.openAssetFileDescriptor(mUri, "r");
}
} catch (FileNotFoundException e) {
+ // Handled below, along with the case where assetFd was set to null.
+ }
+
+ if (assetFd == null) {
// Some images cannot be opened as AssetFileDescriptors (e.g.
// bmp, ico). Open them as InputStreams.
InputStream is = mResolver.openInputStream(mUri);
@@ -286,9 +290,7 @@
return createFromStream(is, true, preferAnimation, this);
}
- if (assetFd == null) {
- throw new FileNotFoundException(mUri.toString());
- }
+
return createFromAssetFileDescriptor(assetFd, preferAnimation, this);
}
}
diff --git a/graphics/java/android/graphics/LightingColorFilter.java b/graphics/java/android/graphics/LightingColorFilter.java
index 62a890f..221dfa1 100644
--- a/graphics/java/android/graphics/LightingColorFilter.java
+++ b/graphics/java/android/graphics/LightingColorFilter.java
@@ -22,7 +22,7 @@
package android.graphics;
import android.annotation.ColorInt;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
/**
* A color filter that can be used to simulate simple lighting effects.
diff --git a/graphics/java/android/graphics/LinearGradient.java b/graphics/java/android/graphics/LinearGradient.java
index 12e63c0..3f3ad96 100644
--- a/graphics/java/android/graphics/LinearGradient.java
+++ b/graphics/java/android/graphics/LinearGradient.java
@@ -20,7 +20,7 @@
import android.annotation.ColorLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
public class LinearGradient extends Shader {
diff --git a/graphics/java/android/graphics/Matrix.java b/graphics/java/android/graphics/Matrix.java
index 22b6401..cf914c2 100644
--- a/graphics/java/android/graphics/Matrix.java
+++ b/graphics/java/android/graphics/Matrix.java
@@ -16,7 +16,7 @@
package android.graphics;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
diff --git a/graphics/java/android/graphics/Movie.java b/graphics/java/android/graphics/Movie.java
index 6f030ff..4b3924f 100644
--- a/graphics/java/android/graphics/Movie.java
+++ b/graphics/java/android/graphics/Movie.java
@@ -16,7 +16,7 @@
package android.graphics;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.AssetManager;
import android.os.Build;
diff --git a/graphics/java/android/graphics/NinePatch.java b/graphics/java/android/graphics/NinePatch.java
index c4c1eac..ff32393 100644
--- a/graphics/java/android/graphics/NinePatch.java
+++ b/graphics/java/android/graphics/NinePatch.java
@@ -16,7 +16,7 @@
package android.graphics;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
/**
* The NinePatch class permits drawing a bitmap in nine or more sections.
diff --git a/graphics/java/android/graphics/Outline.java b/graphics/java/android/graphics/Outline.java
index 1fc056c..91a60c3 100644
--- a/graphics/java/android/graphics/Outline.java
+++ b/graphics/java/android/graphics/Outline.java
@@ -19,7 +19,7 @@
import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.drawable.Drawable;
import java.lang.annotation.Retention;
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 109d863..3b58624 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -24,7 +24,7 @@
import android.annotation.Nullable;
import android.annotation.Px;
import android.annotation.Size;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.fonts.FontVariationAxis;
import android.os.Build;
import android.os.LocaleList;
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index 7282d52..1362fd8 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -20,7 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java
index 8d12cbf..390d3d4 100644
--- a/graphics/java/android/graphics/Picture.java
+++ b/graphics/java/android/graphics/Picture.java
@@ -17,7 +17,7 @@
package android.graphics;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import java.io.InputStream;
import java.io.OutputStream;
diff --git a/graphics/java/android/graphics/PorterDuff.java b/graphics/java/android/graphics/PorterDuff.java
index bc1f66f..1275cb9 100644
--- a/graphics/java/android/graphics/PorterDuff.java
+++ b/graphics/java/android/graphics/PorterDuff.java
@@ -16,7 +16,7 @@
package android.graphics;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
/**
* <p>This class contains the list of alpha compositing and blending modes
diff --git a/graphics/java/android/graphics/PorterDuffColorFilter.java b/graphics/java/android/graphics/PorterDuffColorFilter.java
index cc2d3a8..50ecb62 100644
--- a/graphics/java/android/graphics/PorterDuffColorFilter.java
+++ b/graphics/java/android/graphics/PorterDuffColorFilter.java
@@ -18,7 +18,7 @@
import android.annotation.ColorInt;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
/**
* A color filter that can be used to tint the source pixels using a single
diff --git a/graphics/java/android/graphics/RadialGradient.java b/graphics/java/android/graphics/RadialGradient.java
index acbe3da..96b7b9a 100644
--- a/graphics/java/android/graphics/RadialGradient.java
+++ b/graphics/java/android/graphics/RadialGradient.java
@@ -20,7 +20,7 @@
import android.annotation.ColorLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
public class RadialGradient extends Shader {
@UnsupportedAppUsage
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index 9e1946c..081b851 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -19,7 +19,7 @@
import android.annotation.CheckResult;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
diff --git a/graphics/java/android/graphics/Region.java b/graphics/java/android/graphics/Region.java
index ec7f7a0..d8d9641 100644
--- a/graphics/java/android/graphics/Region.java
+++ b/graphics/java/android/graphics/Region.java
@@ -17,7 +17,7 @@
package android.graphics;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Pools.SynchronizedPool;
diff --git a/graphics/java/android/graphics/RuntimeShader.java b/graphics/java/android/graphics/RuntimeShader.java
index 613ce90..5a3f2a9 100644
--- a/graphics/java/android/graphics/RuntimeShader.java
+++ b/graphics/java/android/graphics/RuntimeShader.java
@@ -34,6 +34,7 @@
}
private byte[] mUniforms;
+ private boolean mIsOpaque;
/**
* Current native shader factory instance.
@@ -56,7 +57,8 @@
ColorSpace colorSpace) {
super(colorSpace);
mUniforms = uniforms;
- mNativeInstanceRuntimeShaderFactory = nativeCreateShaderFactory(sksl, isOpaque);
+ mIsOpaque = isOpaque;
+ mNativeInstanceRuntimeShaderFactory = nativeCreateShaderFactory(sksl);
NoImagePreloadHolder.sRegistry.registerNativeAllocation(this,
mNativeInstanceRuntimeShaderFactory);
}
@@ -75,13 +77,13 @@
@Override
long createNativeInstance(long nativeMatrix) {
return nativeCreate(mNativeInstanceRuntimeShaderFactory, nativeMatrix, mUniforms,
- colorSpace().getNativeInstance());
+ colorSpace().getNativeInstance(), mIsOpaque);
}
private static native long nativeCreate(long shaderFactory, long matrix, byte[] inputs,
- long colorSpaceHandle);
+ long colorSpaceHandle, boolean isOpaque);
- private static native long nativeCreateShaderFactory(String sksl, boolean isOpaque);
+ private static native long nativeCreateShaderFactory(String sksl);
private static native long nativeGetFinalizer();
}
diff --git a/graphics/java/android/graphics/Shader.java b/graphics/java/android/graphics/Shader.java
index 3050d1d..5335aa4 100644
--- a/graphics/java/android/graphics/Shader.java
+++ b/graphics/java/android/graphics/Shader.java
@@ -20,7 +20,7 @@
import android.annotation.ColorLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import libcore.util.NativeAllocationRegistry;
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index 99f440d..697daa8 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -17,7 +17,7 @@
package android.graphics;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
diff --git a/graphics/java/android/graphics/SweepGradient.java b/graphics/java/android/graphics/SweepGradient.java
index 667f45a..0852004 100644
--- a/graphics/java/android/graphics/SweepGradient.java
+++ b/graphics/java/android/graphics/SweepGradient.java
@@ -20,7 +20,7 @@
import android.annotation.ColorLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
public class SweepGradient extends Shader {
@UnsupportedAppUsage
diff --git a/graphics/java/android/graphics/TableMaskFilter.java b/graphics/java/android/graphics/TableMaskFilter.java
index d81c491..204f970 100644
--- a/graphics/java/android/graphics/TableMaskFilter.java
+++ b/graphics/java/android/graphics/TableMaskFilter.java
@@ -16,7 +16,7 @@
package android.graphics;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
/**
* @hide
diff --git a/graphics/java/android/graphics/TemporaryBuffer.java b/graphics/java/android/graphics/TemporaryBuffer.java
index 0ae2c70..ef3f7f7 100644
--- a/graphics/java/android/graphics/TemporaryBuffer.java
+++ b/graphics/java/android/graphics/TemporaryBuffer.java
@@ -16,7 +16,8 @@
package android.graphics;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
+
import com.android.internal.util.ArrayUtils;
/**
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 6d20ec3..a2dd9a8 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -25,7 +25,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.AssetManager;
import android.graphics.fonts.Font;
import android.graphics.fonts.FontFamily;
diff --git a/graphics/java/android/graphics/Xfermode.java b/graphics/java/android/graphics/Xfermode.java
index 6f4adfd..e79fb76 100644
--- a/graphics/java/android/graphics/Xfermode.java
+++ b/graphics/java/android/graphics/Xfermode.java
@@ -21,7 +21,7 @@
package android.graphics;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
/**
* Xfermode is the base class for objects that are called to implement custom
diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
index 82f5870..d894600 100644
--- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
@@ -19,7 +19,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
diff --git a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
index b29fd4d..686f146 100644
--- a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
@@ -18,23 +18,23 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
+import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.Resources.Theme;
+import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.TypedValue;
-import android.os.SystemClock;
+
+import com.android.internal.R;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
-import com.android.internal.R;
-
/**
* @hide
*/
diff --git a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
index 11a46c4..06159d8 100644
--- a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
@@ -20,7 +20,7 @@
import android.animation.TimeInterpolator;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 66947da..1acf6c5 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -25,9 +25,9 @@
import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
import android.app.ActivityThread;
import android.app.Application;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo.Config;
import android.content.res.ColorStateList;
import android.content.res.Resources;
diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java
index 57764c2..8c3fa44 100644
--- a/graphics/java/android/graphics/drawable/AnimationDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java
@@ -16,20 +16,20 @@
package android.graphics.drawable;
-import com.android.internal.R;
+import android.annotation.NonNull;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
+import android.content.res.TypedArray;
+import android.os.SystemClock;
+import android.util.AttributeSet;
-import java.io.IOException;
+import com.android.internal.R;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.Resources.Theme;
-import android.os.SystemClock;
-import android.util.AttributeSet;
+import java.io.IOException;
/**
* An object used to create frame-by-frame animations, defined by a series of
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index e4aa774..4e768c9 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -17,7 +17,7 @@
package android.graphics.drawable;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo.Config;
import android.content.res.ColorStateList;
import android.content.res.Resources;
diff --git a/graphics/java/android/graphics/drawable/ClipDrawable.java b/graphics/java/android/graphics/drawable/ClipDrawable.java
index 31fdb02..69ed9b4 100644
--- a/graphics/java/android/graphics/drawable/ClipDrawable.java
+++ b/graphics/java/android/graphics/drawable/ClipDrawable.java
@@ -16,21 +16,23 @@
package android.graphics.drawable;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.Gravity;
+
import com.android.internal.R;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.Resources.Theme;
-import android.graphics.*;
-import android.view.Gravity;
-import android.util.AttributeSet;
-
import java.io.IOException;
/**
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java
index c6515ef..feb6101 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java
@@ -176,7 +176,7 @@
KeyStore keyStore, IBinder operationToken) {
KeyStoreCryptoOperationStreamer streamer = new KeyStoreCryptoOperationChunkedStreamer(
new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
- keyStore, operationToken));
+ keyStore, operationToken), 0);
if (isEncrypting()) {
return streamer;
} else {
@@ -191,7 +191,7 @@
protected final KeyStoreCryptoOperationStreamer createAdditionalAuthenticationDataStreamer(
KeyStore keyStore, IBinder operationToken) {
return new KeyStoreCryptoOperationChunkedStreamer(
- new AdditionalAuthenticationDataStream(keyStore, operationToken));
+ new AdditionalAuthenticationDataStream(keyStore, operationToken), 0);
}
@Override
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
index 5bcb34a..ccc3153 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
@@ -299,7 +299,7 @@
KeyStore keyStore, IBinder operationToken) {
return new KeyStoreCryptoOperationChunkedStreamer(
new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
- keyStore, operationToken));
+ keyStore, operationToken), 0);
}
/**
diff --git a/keystore/java/android/security/keystore/ArrayUtils.java b/keystore/java/android/security/keystore/ArrayUtils.java
index 26172d2..f519c7c 100644
--- a/keystore/java/android/security/keystore/ArrayUtils.java
+++ b/keystore/java/android/security/keystore/ArrayUtils.java
@@ -55,6 +55,34 @@
}
}
+ /**
+ * Copies a subset of the source array to the destination array.
+ * Length will be limited to the bounds of source and destination arrays.
+ * The length actually copied is returned, which will be <= length argument.
+ * @param src is the source array
+ * @param srcOffset is the offset in the source array.
+ * @param dst is the destination array.
+ * @param dstOffset is the offset in the destination array.
+ * @param length is the length to be copied from source to destination array.
+ * @return The length actually copied from source array.
+ */
+ public static int copy(byte[] src, int srcOffset, byte[] dst, int dstOffset, int length) {
+ if (dst == null || src == null) {
+ return 0;
+ }
+ if (length > dst.length - dstOffset) {
+ length = dst.length - dstOffset;
+ }
+ if (length > src.length - srcOffset) {
+ length = src.length - srcOffset;
+ }
+ if (length <= 0) {
+ return 0;
+ }
+ System.arraycopy(src, srcOffset, dst, dstOffset, length);
+ return length;
+ }
+
public static byte[] subarray(byte[] arr, int offset, int len) {
if (len == 0) {
return EmptyArray.BYTE;
diff --git a/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java b/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java
index 75bea26..2c0f40d 100644
--- a/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java
+++ b/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java
@@ -24,19 +24,20 @@
import libcore.util.EmptyArray;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.security.ProviderException;
-
/**
* Helper for streaming a crypto operation's input and output via {@link KeyStore} service's
* {@code update} and {@code finish} operations.
*
- * <p>The helper abstracts away to issues that need to be solved in most code that uses KeyStore's
+ * <p>The helper abstracts away issues that need to be solved in most code that uses KeyStore's
* update and finish operations. Firstly, KeyStore's update operation can consume only a limited
* amount of data in one go because the operations are marshalled via Binder. Secondly, the update
* operation may consume less data than provided, in which case the caller has to buffer the
- * remainder for next time. The helper exposes {@link #update(byte[], int, int) update} and
+ * remainder for next time. Thirdly, when the input is smaller than a threshold, skipping update
+ * and passing input data directly to final improves performance. This threshold is configurable;
+ * using a threshold <= 1 causes the helper act eagerly, which may be required for some types of
+ * operations (e.g. ciphers).
+ *
+ * <p>The helper exposes {@link #update(byte[], int, int) update} and
* {@link #doFinal(byte[], int, int, byte[], byte[]) doFinal} operations which can be used to
* conveniently implement various JCA crypto primitives.
*
@@ -67,240 +68,122 @@
// Binder buffer is about 1MB, but it's shared between all active transactions of the process.
// Thus, it's safer to use a much smaller upper bound.
- private static final int DEFAULT_MAX_CHUNK_SIZE = 64 * 1024;
+ private static final int DEFAULT_CHUNK_SIZE_MAX = 64 * 1024;
+ // The chunk buffer will be sent to update until its size under this threshold.
+ // This threshold should be <= the max input allowed for finish.
+ // Setting this threshold <= 1 will effectivley disable buffering between updates.
+ private static final int DEFAULT_CHUNK_SIZE_THRESHOLD = 2 * 1024;
private final Stream mKeyStoreStream;
- private final int mMaxChunkSize;
-
- private byte[] mBuffered = EmptyArray.BYTE;
- private int mBufferedOffset;
- private int mBufferedLength;
+ private final int mChunkSizeMax;
+ private final int mChunkSizeThreshold;
+ private final byte[] mChunk;
+ private int mChunkLength = 0;
private long mConsumedInputSizeBytes;
private long mProducedOutputSizeBytes;
- public KeyStoreCryptoOperationChunkedStreamer(Stream operation) {
- this(operation, DEFAULT_MAX_CHUNK_SIZE);
+ KeyStoreCryptoOperationChunkedStreamer(Stream operation) {
+ this(operation, DEFAULT_CHUNK_SIZE_THRESHOLD, DEFAULT_CHUNK_SIZE_MAX);
}
- public KeyStoreCryptoOperationChunkedStreamer(Stream operation, int maxChunkSize) {
+ KeyStoreCryptoOperationChunkedStreamer(Stream operation, int chunkSizeThreshold) {
+ this(operation, chunkSizeThreshold, DEFAULT_CHUNK_SIZE_MAX);
+ }
+
+ KeyStoreCryptoOperationChunkedStreamer(Stream operation, int chunkSizeThreshold,
+ int chunkSizeMax) {
mKeyStoreStream = operation;
- mMaxChunkSize = maxChunkSize;
+ mChunkSizeMax = chunkSizeMax;
+ if (chunkSizeThreshold <= 0) {
+ mChunkSizeThreshold = 1;
+ } else if (chunkSizeThreshold > chunkSizeMax) {
+ mChunkSizeThreshold = chunkSizeMax;
+ } else {
+ mChunkSizeThreshold = chunkSizeThreshold;
+ }
+ mChunk = new byte[mChunkSizeMax];
}
- @Override
public byte[] update(byte[] input, int inputOffset, int inputLength) throws KeyStoreException {
- if (inputLength == 0) {
+ if (inputLength == 0 || input == null) {
// No input provided
return EmptyArray.BYTE;
}
+ if (inputLength < 0 || inputOffset < 0 || (inputOffset + inputLength) > input.length) {
+ throw new KeyStoreException(KeymasterDefs.KM_ERROR_UNKNOWN_ERROR,
+ "Input offset and length out of bounds of input array");
+ }
- ByteArrayOutputStream bufferedOutput = null;
+ byte[] output = EmptyArray.BYTE;
- while (inputLength > 0) {
- byte[] chunk;
- int inputBytesInChunk;
- if ((mBufferedLength + inputLength) > mMaxChunkSize) {
- // Too much input for one chunk -- extract one max-sized chunk and feed it into the
- // update operation.
- inputBytesInChunk = mMaxChunkSize - mBufferedLength;
- chunk = ArrayUtils.concat(mBuffered, mBufferedOffset, mBufferedLength,
- input, inputOffset, inputBytesInChunk);
- } else {
- // All of available input fits into one chunk.
- if ((mBufferedLength == 0) && (inputOffset == 0)
- && (inputLength == input.length)) {
- // Nothing buffered and all of input array needs to be fed into the update
- // operation.
- chunk = input;
- inputBytesInChunk = input.length;
- } else {
- // Need to combine buffered data with input data into one array.
- inputBytesInChunk = inputLength;
- chunk = ArrayUtils.concat(mBuffered, mBufferedOffset, mBufferedLength,
- input, inputOffset, inputBytesInChunk);
+ while (inputLength > 0 || mChunkLength >= mChunkSizeThreshold) {
+ int inputConsumed = ArrayUtils.copy(input, inputOffset, mChunk, mChunkLength,
+ inputLength);
+ inputLength -= inputConsumed;
+ inputOffset += inputConsumed;
+ mChunkLength += inputConsumed;
+ mConsumedInputSizeBytes += inputConsumed;
+
+ if (mChunkLength > mChunkSizeMax) {
+ throw new KeyStoreException(KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH,
+ "Chunk size exceeded max chunk size. Max: " + mChunkSizeMax
+ + " Actual: " + mChunkLength);
+ }
+
+ if (mChunkLength >= mChunkSizeThreshold) {
+ OperationResult opResult = mKeyStoreStream.update(
+ ArrayUtils.subarray(mChunk, 0, mChunkLength));
+
+ if (opResult == null) {
+ throw new KeyStoreConnectException();
+ } else if (opResult.resultCode != KeyStore.NO_ERROR) {
+ throw KeyStore.getKeyStoreException(opResult.resultCode);
}
- }
- // Update input array references to reflect that some of its bytes are now in mBuffered.
- inputOffset += inputBytesInChunk;
- inputLength -= inputBytesInChunk;
- mConsumedInputSizeBytes += inputBytesInChunk;
-
- OperationResult opResult = mKeyStoreStream.update(chunk);
- if (opResult == null) {
- throw new KeyStoreConnectException();
- } else if (opResult.resultCode != KeyStore.NO_ERROR) {
- throw KeyStore.getKeyStoreException(opResult.resultCode);
- }
-
- if (opResult.inputConsumed == chunk.length) {
- // The whole chunk was consumed
- mBuffered = EmptyArray.BYTE;
- mBufferedOffset = 0;
- mBufferedLength = 0;
- } else if (opResult.inputConsumed <= 0) {
- // Nothing was consumed. More input needed.
- if (inputLength > 0) {
- // More input is available, but it wasn't included into the previous chunk
- // because the chunk reached its maximum permitted size.
- // Shouldn't have happened.
+ if (opResult.inputConsumed <= 0) {
+ throw new KeyStoreException(KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH,
+ "Keystore consumed 0 of " + mChunkLength + " bytes provided.");
+ } else if (opResult.inputConsumed > mChunkLength) {
throw new KeyStoreException(KeymasterDefs.KM_ERROR_UNKNOWN_ERROR,
- "Keystore consumed nothing from max-sized chunk: " + chunk.length
- + " bytes");
+ "Keystore consumed more input than provided. Provided: "
+ + mChunkLength + ", consumed: " + opResult.inputConsumed);
}
- mBuffered = chunk;
- mBufferedOffset = 0;
- mBufferedLength = chunk.length;
- } else if (opResult.inputConsumed < chunk.length) {
- // The chunk was consumed only partially -- buffer the rest of the chunk
- mBuffered = chunk;
- mBufferedOffset = opResult.inputConsumed;
- mBufferedLength = chunk.length - opResult.inputConsumed;
- } else {
- throw new KeyStoreException(KeymasterDefs.KM_ERROR_UNKNOWN_ERROR,
- "Keystore consumed more input than provided. Provided: " + chunk.length
- + ", consumed: " + opResult.inputConsumed);
- }
+ mChunkLength -= opResult.inputConsumed;
- if ((opResult.output != null) && (opResult.output.length > 0)) {
- if (inputLength + mBufferedLength > 0) {
- // More output might be produced in this loop -- buffer the current output
- if (bufferedOutput == null) {
- bufferedOutput = new ByteArrayOutputStream();
- }
- try {
- bufferedOutput.write(opResult.output);
- } catch (IOException e) {
- throw new ProviderException("Failed to buffer output", e);
- }
- } else {
- // No more output will be produced in this loop
- byte[] result;
- if (bufferedOutput == null) {
- // No previously buffered output
- result = opResult.output;
- } else {
- // There was some previously buffered output
- try {
- bufferedOutput.write(opResult.output);
- } catch (IOException e) {
- throw new ProviderException("Failed to buffer output", e);
- }
- result = bufferedOutput.toByteArray();
- }
- mProducedOutputSizeBytes += result.length;
- return result;
+ if (mChunkLength > 0) {
+ // Partialy consumed, shift chunk contents
+ ArrayUtils.copy(mChunk, opResult.inputConsumed, mChunk, 0, mChunkLength);
+ }
+
+ if ((opResult.output != null) && (opResult.output.length > 0)) {
+ // Output was produced
+ mProducedOutputSizeBytes += opResult.output.length;
+ output = ArrayUtils.concat(output, opResult.output);
}
}
}
-
- byte[] result;
- if (bufferedOutput == null) {
- // No output produced
- result = EmptyArray.BYTE;
- } else {
- result = bufferedOutput.toByteArray();
- }
- mProducedOutputSizeBytes += result.length;
- return result;
+ return output;
}
- @Override
public byte[] doFinal(byte[] input, int inputOffset, int inputLength,
byte[] signature, byte[] additionalEntropy) throws KeyStoreException {
- if (inputLength == 0) {
- // No input provided -- simplify the rest of the code
- input = EmptyArray.BYTE;
- inputOffset = 0;
- }
-
- // Flush all buffered input and provided input into keystore/keymaster.
byte[] output = update(input, inputOffset, inputLength);
- output = ArrayUtils.concat(output, flush());
+ byte[] finalChunk = ArrayUtils.subarray(mChunk, 0, mChunkLength);
+ OperationResult opResult = mKeyStoreStream.finish(finalChunk, signature, additionalEntropy);
- OperationResult opResult = mKeyStoreStream.finish(EmptyArray.BYTE, signature,
- additionalEntropy);
if (opResult == null) {
throw new KeyStoreConnectException();
} else if (opResult.resultCode != KeyStore.NO_ERROR) {
throw KeyStore.getKeyStoreException(opResult.resultCode);
}
- mProducedOutputSizeBytes += opResult.output.length;
+ // If no error, assume all input consumed
+ mConsumedInputSizeBytes += finalChunk.length;
- return ArrayUtils.concat(output, opResult.output);
- }
-
- public byte[] flush() throws KeyStoreException {
- if (mBufferedLength <= 0) {
- return EmptyArray.BYTE;
+ if ((opResult.output != null) && (opResult.output.length > 0)) {
+ mProducedOutputSizeBytes += opResult.output.length;
+ output = ArrayUtils.concat(output, opResult.output);
}
- // Keep invoking the update operation with remaining buffered data until either all of the
- // buffered data is consumed or until update fails to consume anything.
- ByteArrayOutputStream bufferedOutput = null;
- while (mBufferedLength > 0) {
- byte[] chunk = ArrayUtils.subarray(mBuffered, mBufferedOffset, mBufferedLength);
- OperationResult opResult = mKeyStoreStream.update(chunk);
- if (opResult == null) {
- throw new KeyStoreConnectException();
- } else if (opResult.resultCode != KeyStore.NO_ERROR) {
- throw KeyStore.getKeyStoreException(opResult.resultCode);
- }
-
- if (opResult.inputConsumed <= 0) {
- // Nothing was consumed. Break out of the loop to avoid an infinite loop.
- break;
- }
-
- if (opResult.inputConsumed >= chunk.length) {
- // All of the input was consumed
- mBuffered = EmptyArray.BYTE;
- mBufferedOffset = 0;
- mBufferedLength = 0;
- } else {
- // Some of the input was not consumed
- mBuffered = chunk;
- mBufferedOffset = opResult.inputConsumed;
- mBufferedLength = chunk.length - opResult.inputConsumed;
- }
-
- if (opResult.inputConsumed > chunk.length) {
- throw new KeyStoreException(KeymasterDefs.KM_ERROR_UNKNOWN_ERROR,
- "Keystore consumed more input than provided. Provided: "
- + chunk.length + ", consumed: " + opResult.inputConsumed);
- }
-
- if ((opResult.output != null) && (opResult.output.length > 0)) {
- // Some output was produced by this update operation
- if (bufferedOutput == null) {
- // No output buffered yet.
- if (mBufferedLength == 0) {
- // No more output will be produced by this flush operation
- mProducedOutputSizeBytes += opResult.output.length;
- return opResult.output;
- } else {
- // More output might be produced by this flush operation -- buffer output.
- bufferedOutput = new ByteArrayOutputStream();
- }
- }
- // Buffer the output from this update operation
- try {
- bufferedOutput.write(opResult.output);
- } catch (IOException e) {
- throw new ProviderException("Failed to buffer output", e);
- }
- }
- }
-
- if (mBufferedLength > 0) {
- throw new KeyStoreException(KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH,
- "Keystore failed to consume last "
- + ((mBufferedLength != 1) ? (mBufferedLength + " bytes") : "byte")
- + " of input");
- }
-
- byte[] result = (bufferedOutput != null) ? bufferedOutput.toByteArray() : EmptyArray.BYTE;
- mProducedOutputSizeBytes += result.length;
- return result;
+ return output;
}
@Override
diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp
index e53f3db..6d4a0c6 100644
--- a/libs/hwui/DeviceInfo.cpp
+++ b/libs/hwui/DeviceInfo.cpp
@@ -52,8 +52,8 @@
DeviceInfo::get()->mMaxTextureSize = maxTextureSize;
}
-void DeviceInfo::onDisplayConfigChanged() {
- updateDisplayInfo();
+void DeviceInfo::onRefreshRateChanged(int64_t vsyncPeriod) {
+ mVsyncPeriod = vsyncPeriod;
}
void DeviceInfo::updateDisplayInfo() {
@@ -113,10 +113,11 @@
ADisplay* primaryDisplay = mDisplays[mPhysicalDisplayIndex];
status_t status = ADisplay_getCurrentConfig(primaryDisplay, &mCurrentConfig);
LOG_ALWAYS_FATAL_IF(status, "Failed to get display config, error %d", status);
+
mWidth = ADisplayConfig_getWidth(mCurrentConfig);
mHeight = ADisplayConfig_getHeight(mCurrentConfig);
mDensity = ADisplayConfig_getDensity(mCurrentConfig);
- mRefreshRate = ADisplayConfig_getFps(mCurrentConfig);
+ mVsyncPeriod = static_cast<int64_t>(1000000000 / ADisplayConfig_getFps(mCurrentConfig));
mCompositorOffset = ADisplayConfig_getCompositorOffsetNanos(mCurrentConfig);
mAppOffset = ADisplayConfig_getAppVsyncOffsetNanos(mCurrentConfig);
}
diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h
index a420746..16a22f4 100644
--- a/libs/hwui/DeviceInfo.h
+++ b/libs/hwui/DeviceInfo.h
@@ -37,7 +37,7 @@
static int32_t getWidth() { return get()->mWidth; }
static int32_t getHeight() { return get()->mHeight; }
static float getDensity() { return get()->mDensity; }
- static float getRefreshRate() { return get()->mRefreshRate; }
+ static int64_t getVsyncPeriod() { return get()->mVsyncPeriod; }
static int64_t getCompositorOffset() { return get()->mCompositorOffset; }
static int64_t getAppOffset() { return get()->mAppOffset; }
@@ -47,7 +47,8 @@
sk_sp<SkColorSpace> getWideColorSpace() const { return mWideColorSpace; }
SkColorType getWideColorType() const { return mWideColorType; }
- void onDisplayConfigChanged();
+ // This method should be called whenever the display refresh rate changes.
+ void onRefreshRateChanged(int64_t vsyncPeriod);
private:
friend class renderthread::RenderThread;
@@ -68,7 +69,7 @@
int32_t mWidth = 1080;
int32_t mHeight = 1920;
float mDensity = 2.0;
- float mRefreshRate = 60.0;
+ int64_t mVsyncPeriod = 16666666;
int64_t mCompositorOffset = 0;
int64_t mAppOffset = 0;
};
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index 10e7160..d25fc4b 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -81,7 +81,7 @@
JankTracker::JankTracker(ProfileDataContainer* globalData) {
mGlobalData = globalData;
- nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1_s / DeviceInfo::getRefreshRate());
+ nsecs_t frameIntervalNanos = DeviceInfo::getVsyncPeriod();
nsecs_t sfOffset = DeviceInfo::getCompositorOffset();
nsecs_t offsetDelta = sfOffset - DeviceInfo::getAppOffset();
// There are two different offset cases. If the offsetDelta is positive
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index a446858..d78f641 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -34,7 +34,6 @@
#include <GrContextOptions.h>
#include <gl/GrGLInterface.h>
-#include <gui/DisplayEventReceiver.h>
#include <sys/resource.h>
#include <utils/Condition.h>
#include <utils/Log.h>
@@ -45,53 +44,43 @@
namespace uirenderer {
namespace renderthread {
-// Number of events to read at a time from the DisplayEventReceiver pipe.
-// The value should be large enough that we can quickly drain the pipe
-// using just a few large reads.
-static const size_t EVENT_BUFFER_SIZE = 100;
-
static bool gHasRenderThreadInstance = false;
static JVMAttachHook gOnStartHook = nullptr;
-class DisplayEventReceiverWrapper : public VsyncSource {
+void RenderThread::frameCallback(int64_t frameTimeNanos, void* data) {
+ RenderThread* rt = reinterpret_cast<RenderThread*>(data);
+ rt->mVsyncRequested = false;
+ if (rt->timeLord().vsyncReceived(frameTimeNanos) && !rt->mFrameCallbackTaskPending) {
+ ATRACE_NAME("queue mFrameCallbackTask");
+ rt->mFrameCallbackTaskPending = true;
+ nsecs_t runAt = (frameTimeNanos + rt->mDispatchFrameDelay);
+ rt->queue().postAt(runAt, [=]() { rt->dispatchFrameCallbacks(); });
+ }
+}
+
+void RenderThread::refreshRateCallback(int64_t vsyncPeriod, void* data) {
+ ATRACE_NAME("refreshRateCallback");
+ RenderThread* rt = reinterpret_cast<RenderThread*>(data);
+ DeviceInfo::get()->onRefreshRateChanged(vsyncPeriod);
+ rt->setupFrameInterval();
+}
+
+class ChoreographerSource : public VsyncSource {
public:
- DisplayEventReceiverWrapper(std::unique_ptr<DisplayEventReceiver>&& receiver,
- const std::function<void()>& onDisplayConfigChanged)
- : mDisplayEventReceiver(std::move(receiver))
- , mOnDisplayConfigChanged(onDisplayConfigChanged) {}
+ ChoreographerSource(RenderThread* renderThread) : mRenderThread(renderThread) {}
virtual void requestNextVsync() override {
- status_t status = mDisplayEventReceiver->requestNextVsync();
- LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "requestNextVsync failed with status: %d", status);
+ AChoreographer_postFrameCallback64(mRenderThread->mChoreographer,
+ RenderThread::frameCallback, mRenderThread);
}
- virtual nsecs_t latestVsyncEvent() override {
- DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
- nsecs_t latest = 0;
- ssize_t n;
- while ((n = mDisplayEventReceiver->getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
- for (ssize_t i = 0; i < n; i++) {
- const DisplayEventReceiver::Event& ev = buf[i];
- switch (ev.header.type) {
- case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
- latest = ev.header.timestamp;
- break;
- case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
- mOnDisplayConfigChanged();
- break;
- }
- }
- }
- if (n < 0) {
- ALOGW("Failed to get events from display event receiver, status=%d", status_t(n));
- }
- return latest;
+ virtual void drainPendingEvents() override {
+ AChoreographer_handlePendingEvents(mRenderThread->mChoreographer, mRenderThread);
}
private:
- std::unique_ptr<DisplayEventReceiver> mDisplayEventReceiver;
- std::function<void()> mOnDisplayConfigChanged;
+ RenderThread* mRenderThread;
};
class DummyVsyncSource : public VsyncSource {
@@ -99,11 +88,14 @@
DummyVsyncSource(RenderThread* renderThread) : mRenderThread(renderThread) {}
virtual void requestNextVsync() override {
- mRenderThread->queue().postDelayed(16_ms,
- [this]() { mRenderThread->drainDisplayEventQueue(); });
+ mRenderThread->queue().postDelayed(16_ms, [this]() {
+ RenderThread::frameCallback(systemTime(SYSTEM_TIME_MONOTONIC), mRenderThread);
+ });
}
- virtual nsecs_t latestVsyncEvent() override { return systemTime(SYSTEM_TIME_MONOTONIC); }
+ virtual void drainPendingEvents() override {
+ RenderThread::frameCallback(systemTime(SYSTEM_TIME_MONOTONIC), mRenderThread);
+ }
private:
RenderThread* mRenderThread;
@@ -145,29 +137,24 @@
}
RenderThread::~RenderThread() {
+ // Note that if this fatal assertion is removed then member variables must
+ // be properly destroyed.
LOG_ALWAYS_FATAL("Can't destroy the render thread");
}
-void RenderThread::initializeDisplayEventReceiver() {
- LOG_ALWAYS_FATAL_IF(mVsyncSource, "Initializing a second DisplayEventReceiver?");
+void RenderThread::initializeChoreographer() {
+ LOG_ALWAYS_FATAL_IF(mVsyncSource, "Initializing a second Choreographer?");
if (!Properties::isolatedProcess) {
- auto receiver = std::make_unique<DisplayEventReceiver>(
- ISurfaceComposer::eVsyncSourceApp,
- ISurfaceComposer::eConfigChangedDispatch);
- status_t status = receiver->initCheck();
- LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
- "Initialization of DisplayEventReceiver "
- "failed with status: %d",
- status);
+ mChoreographer = AChoreographer_create();
+ LOG_ALWAYS_FATAL_IF(mChoreographer == nullptr, "Initialization of Choreographer failed");
+ AChoreographer_registerRefreshRateCallback(mChoreographer,
+ RenderThread::refreshRateCallback, this);
// Register the FD
- mLooper->addFd(receiver->getFd(), 0, Looper::EVENT_INPUT,
- RenderThread::displayEventReceiverCallback, this);
- mVsyncSource = new DisplayEventReceiverWrapper(std::move(receiver), [this] {
- DeviceInfo::get()->onDisplayConfigChanged();
- setupFrameInterval();
- });
+ mLooper->addFd(AChoreographer_getFd(mChoreographer), 0, Looper::EVENT_INPUT,
+ RenderThread::choreographerCallback, this);
+ mVsyncSource = new ChoreographerSource(this);
} else {
mVsyncSource = new DummyVsyncSource(this);
}
@@ -175,7 +162,7 @@
void RenderThread::initThreadLocals() {
setupFrameInterval();
- initializeDisplayEventReceiver();
+ initializeChoreographer();
mEglManager = new EglManager();
mRenderState = new RenderState(*this);
mVkManager = new VulkanManager();
@@ -183,7 +170,7 @@
}
void RenderThread::setupFrameInterval() {
- nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1000000000 / DeviceInfo::getRefreshRate());
+ nsecs_t frameIntervalNanos = DeviceInfo::getVsyncPeriod();
mTimeLord.setFrameInterval(frameIntervalNanos);
mDispatchFrameDelay = static_cast<nsecs_t>(frameIntervalNanos * .25f);
}
@@ -288,7 +275,7 @@
}
}
-int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) {
+int RenderThread::choreographerCallback(int fd, int events, void* data) {
if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
ALOGE("Display event receiver pipe was closed or an error occurred. "
"events=0x%x",
@@ -302,24 +289,10 @@
events);
return 1; // keep the callback
}
+ RenderThread* rt = reinterpret_cast<RenderThread*>(data);
+ AChoreographer_handlePendingEvents(rt->mChoreographer, data);
- reinterpret_cast<RenderThread*>(data)->drainDisplayEventQueue();
-
- return 1; // keep the callback
-}
-
-void RenderThread::drainDisplayEventQueue() {
- ATRACE_CALL();
- nsecs_t vsyncEvent = mVsyncSource->latestVsyncEvent();
- if (vsyncEvent > 0) {
- mVsyncRequested = false;
- if (mTimeLord.vsyncReceived(vsyncEvent) && !mFrameCallbackTaskPending) {
- ATRACE_NAME("queue mFrameCallbackTask");
- mFrameCallbackTaskPending = true;
- nsecs_t runAt = (vsyncEvent + mDispatchFrameDelay);
- queue().postAt(runAt, [this]() { dispatchFrameCallbacks(); });
- }
- }
+ return 1;
}
void RenderThread::dispatchFrameCallbacks() {
@@ -360,7 +333,7 @@
processQueue();
if (mPendingRegistrationFrameCallbacks.size() && !mFrameCallbackTaskPending) {
- drainDisplayEventQueue();
+ mVsyncSource->drainPendingEvents();
mFrameCallbacks.insert(mPendingRegistrationFrameCallbacks.begin(),
mPendingRegistrationFrameCallbacks.end());
mPendingRegistrationFrameCallbacks.clear();
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index da79e97..8be46a6 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -19,6 +19,7 @@
#include <GrContext.h>
#include <SkBitmap.h>
+#include <apex/choreographer.h>
#include <cutils/compiler.h>
#include <thread/ThreadBase.h>
#include <utils/Looper.h>
@@ -73,10 +74,11 @@
struct VsyncSource {
virtual void requestNextVsync() = 0;
- virtual nsecs_t latestVsyncEvent() = 0;
+ virtual void drainPendingEvents() = 0;
virtual ~VsyncSource() {}
};
+class ChoreographerSource;
class DummyVsyncSource;
typedef void (*JVMAttachHook)(const char* name);
@@ -136,6 +138,7 @@
friend class DispatchFrameCallbacks;
friend class RenderProxy;
friend class DummyVsyncSource;
+ friend class ChoreographerSource;
friend class android::uirenderer::AutoBackendTextureRelease;
friend class android::uirenderer::TestUtils;
friend class android::uirenderer::WebViewFunctor;
@@ -149,13 +152,21 @@
static RenderThread& getInstance();
void initThreadLocals();
- void initializeDisplayEventReceiver();
+ void initializeChoreographer();
void setupFrameInterval();
- static int displayEventReceiverCallback(int fd, int events, void* data);
+ // Callbacks for choreographer events:
+ // choreographerCallback will call AChoreograper_handleEvent to call the
+ // corresponding callbacks for each display event type
+ static int choreographerCallback(int fd, int events, void* data);
+ // Callback that will be run on vsync ticks.
+ static void frameCallback(int64_t frameTimeNanos, void* data);
+ // Callback that will be run whenver there is a refresh rate change.
+ static void refreshRateCallback(int64_t vsyncPeriod, void* data);
void drainDisplayEventQueue();
void dispatchFrameCallbacks();
void requestVsync();
+ AChoreographer* mChoreographer;
VsyncSource* mVsyncSource;
bool mVsyncRequested;
std::set<IFrameCallback*> mFrameCallbacks;
diff --git a/media/java/android/media/IMediaRouter2Client.aidl b/media/java/android/media/IMediaRouter2Client.aidl
index 18a6428..f90c7c5 100644
--- a/media/java/android/media/IMediaRouter2Client.aidl
+++ b/media/java/android/media/IMediaRouter2Client.aidl
@@ -30,4 +30,5 @@
void notifyRoutesChanged(in List<MediaRoute2Info> routes);
void notifySessionCreated(in @nullable RouteSessionInfo sessionInfo, int requestId);
void notifySessionInfoChanged(in RouteSessionInfo sessionInfo);
+ void notifySessionReleased(in RouteSessionInfo sessionInfo);
}
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index 4b7d802..e5b62ff 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -56,6 +56,7 @@
void selectRoute(IMediaRouter2Client client, String sessionId, in MediaRoute2Info route);
void deselectRoute(IMediaRouter2Client client, String sessionId, in MediaRoute2Info route);
void transferToRoute(IMediaRouter2Client client, String sessionId, in MediaRoute2Info route);
+ void releaseSession(IMediaRouter2Client client, String sessionId);
void registerManager(IMediaRouter2Manager manager, String packageName);
void unregisterManager(IMediaRouter2Manager manager);
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 7fca03c..4cd581b 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -17,6 +17,7 @@
package android.media;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
@@ -354,8 +355,8 @@
* is less than or equal to 0.
* @see {@link #getScaledFrameAtTime(long, int, int, int, BitmapParams)}
*/
- public @Nullable Bitmap getScaledFrameAtTime(
- long timeUs, @Option int option, int dstWidth, int dstHeight) {
+ public @Nullable Bitmap getScaledFrameAtTime(long timeUs, @Option int option,
+ @IntRange(from=1) int dstWidth, @IntRange(from=1) int dstHeight) {
validate(option, dstWidth, dstHeight);
return _getFrameAtTime(timeUs, option, dstWidth, dstHeight, null);
}
@@ -400,7 +401,8 @@
* @see {@link #getScaledFrameAtTime(long, int, int, int)}
*/
public @Nullable Bitmap getScaledFrameAtTime(long timeUs, @Option int option,
- int dstWidth, int dstHeight, @NonNull BitmapParams params) {
+ @IntRange(from=1) int dstWidth, @IntRange(from=1) int dstHeight,
+ @NonNull BitmapParams params) {
validate(option, dstWidth, dstHeight);
return _getFrameAtTime(timeUs, option, dstWidth, dstHeight, params);
}
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index 506d616..4e6b4af 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -156,8 +156,6 @@
@Nullable
final Bundle mExtras;
- private final String mUniqueId;
-
MediaRoute2Info(@NonNull Builder builder) {
mId = builder.mId;
mProviderId = builder.mProviderId;
@@ -172,7 +170,6 @@
mVolumeHandling = builder.mVolumeHandling;
mDeviceType = builder.mDeviceType;
mExtras = builder.mExtras;
- mUniqueId = createUniqueId();
}
MediaRoute2Info(@NonNull Parcel in) {
@@ -189,18 +186,12 @@
mVolumeHandling = in.readInt();
mDeviceType = in.readInt();
mExtras = in.readBundle();
- mUniqueId = createUniqueId();
}
- private String createUniqueId() {
- String uniqueId = null;
- if (mProviderId != null) {
- uniqueId = toUniqueId(mProviderId, mId);
- }
- return uniqueId;
- }
-
- static String toUniqueId(String providerId, String routeId) {
+ /**
+ * @hide
+ */
+ public static String toUniqueId(String providerId, String routeId) {
return providerId + ":" + routeId;
}
@@ -251,25 +242,30 @@
}
/**
- * Gets the id of the route.
- * Use {@link #getUniqueId()} if you need a unique identifier.
+ * Gets the id of the route. The routes which are given by {@link MediaRouter2} will have
+ * unique IDs.
+ * <p>
+ * In order to ensure uniqueness in {@link MediaRouter2} side, the value of this method
+ * can be different from what was set in {@link MediaRoute2ProviderService}.
*
- * @see #getUniqueId()
+ * @see Builder#setId(String)
*/
@NonNull
public String getId() {
- return mId;
+ if (mProviderId != null) {
+ return toUniqueId(mProviderId, mId);
+ } else {
+ return mId;
+ }
}
/**
- * Gets the unique id of the route. A route obtained from
- * {@link com.android.server.media.MediaRouterService} always has a unique id.
- *
- * @return unique id of the route or null if it has no unique id.
+ * Gets the original id set by {@link Builder#setId(String)}.
+ * @hide
*/
- @Nullable
- public String getUniqueId() {
- return mUniqueId;
+ @NonNull
+ public String getOriginalId() {
+ return mId;
}
/**
@@ -499,7 +495,15 @@
}
/**
- * Sets the unique id of the route.
+ * Sets the unique id of the route. The value given here must be unique for each of your
+ * route.
+ * <p>
+ * In order to ensure uniqueness in {@link MediaRouter2} side, the value of
+ * {@link MediaRoute2Info#getId()} can be different from what was set in
+ * {@link MediaRoute2ProviderService}.
+ * </p>
+ *
+ * @see MediaRoute2Info#getId()
*/
@NonNull
public Builder setId(@NonNull String id) {
diff --git a/media/java/android/media/MediaRoute2ProviderInfo.java b/media/java/android/media/MediaRoute2ProviderInfo.java
index 7078d4a..e2f246c 100644
--- a/media/java/android/media/MediaRoute2ProviderInfo.java
+++ b/media/java/android/media/MediaRoute2ProviderInfo.java
@@ -25,6 +25,7 @@
import java.util.Arrays;
import java.util.Collection;
+import java.util.Map;
import java.util.Objects;
/**
@@ -161,14 +162,17 @@
return this;
}
mUniqueId = uniqueId;
- final int count = mRoutes.size();
- for (int i = 0; i < count; i++) {
- MediaRoute2Info route = mRoutes.valueAt(i);
- mRoutes.setValueAt(i, new MediaRoute2Info.Builder(route)
+
+ final ArrayMap<String, MediaRoute2Info> newRoutes = new ArrayMap<>();
+ for (Map.Entry<String, MediaRoute2Info> entry : mRoutes.entrySet()) {
+ MediaRoute2Info routeWithProviderId = new MediaRoute2Info.Builder(entry.getValue())
.setProviderId(mUniqueId)
- .build());
+ .build();
+ newRoutes.put(routeWithProviderId.getId(), routeWithProviderId);
}
+ mRoutes.clear();
+ mRoutes.putAll(newRoutes);
return this;
}
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 9100032..bddfa69 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -96,7 +96,7 @@
private static final String TAG = "MR2";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final Object sLock = new Object();
+ private static final Object sRouterLock = new Object();
@GuardedBy("sLock")
private static MediaRouter2 sInstance;
@@ -122,8 +122,9 @@
// TODO: Make MediaRouter2 is always connected to the MediaRouterService.
@GuardedBy("sLock")
- private Client2 mClient;
+ Client2 mClient;
+ @GuardedBy("sLock")
private Map<String, RouteSessionController> mSessionControllers = new ArrayMap<>();
private AtomicInteger mSessionCreationRequestCnt = new AtomicInteger(1);
@@ -138,7 +139,7 @@
*/
public static MediaRouter2 getInstance(@NonNull Context context) {
Objects.requireNonNull(context, "context must not be null");
- synchronized (sLock) {
+ synchronized (sRouterLock) {
if (sInstance == null) {
sInstance = new MediaRouter2(context.getApplicationContext());
}
@@ -176,9 +177,9 @@
* @hide
*/
public static boolean checkRouteListContainsRouteId(@NonNull List<MediaRoute2Info> routeList,
- @NonNull String uniqueRouteId) {
+ @NonNull String routeId) {
for (MediaRoute2Info info : routeList) {
- if (TextUtils.equals(uniqueRouteId, info.getUniqueId())) {
+ if (TextUtils.equals(routeId, info.getId())) {
return true;
}
}
@@ -210,7 +211,7 @@
return;
}
- synchronized (sLock) {
+ synchronized (sRouterLock) {
if (mClient == null) {
Client2 client = new Client2();
try {
@@ -242,7 +243,7 @@
return;
}
- synchronized (sLock) {
+ synchronized (sRouterLock) {
if (mRouteCallbackRecords.size() == 0 && mClient != null) {
try {
mMediaRouterService.unregisterClient2(mClient);
@@ -266,7 +267,7 @@
List<String> newControlCategories = new ArrayList<>(controlCategories);
- synchronized (sLock) {
+ synchronized (sRouterLock) {
mShouldUpdateRoutes = true;
// invoke callbacks due to control categories change
@@ -291,7 +292,7 @@
*/
@NonNull
public List<MediaRoute2Info> getRoutes() {
- synchronized (sLock) {
+ synchronized (sRouterLock) {
if (mShouldUpdateRoutes) {
mShouldUpdateRoutes = false;
@@ -372,7 +373,7 @@
mSessionCreationRequests.add(request);
Client2 client;
- synchronized (sLock) {
+ synchronized (sRouterLock) {
client = mClient;
}
if (client != null) {
@@ -400,7 +401,7 @@
Objects.requireNonNull(request, "request must not be null");
Client2 client;
- synchronized (sLock) {
+ synchronized (sRouterLock) {
client = mClient;
}
if (client != null) {
@@ -424,7 +425,7 @@
Objects.requireNonNull(route, "route must not be null");
Client2 client;
- synchronized (sLock) {
+ synchronized (sRouterLock) {
client = mClient;
}
if (client != null) {
@@ -448,7 +449,7 @@
Objects.requireNonNull(route, "route must not be null");
Client2 client;
- synchronized (sLock) {
+ synchronized (sRouterLock) {
client = mClient;
}
if (client != null) {
@@ -496,9 +497,9 @@
// 2) Call onRouteSelected(system_route, reason_fallback) if previously selected route
// does not exist anymore. => We may need 'boolean MediaRoute2Info#isSystemRoute()'.
List<MediaRoute2Info> addedRoutes = new ArrayList<>();
- synchronized (sLock) {
+ synchronized (sRouterLock) {
for (MediaRoute2Info route : routes) {
- mRoutes.put(route.getUniqueId(), route);
+ mRoutes.put(route.getId(), route);
if (route.supportsControlCategories(mControlCategories)) {
addedRoutes.add(route);
}
@@ -512,9 +513,9 @@
void removeRoutesOnHandler(List<MediaRoute2Info> routes) {
List<MediaRoute2Info> removedRoutes = new ArrayList<>();
- synchronized (sLock) {
+ synchronized (sRouterLock) {
for (MediaRoute2Info route : routes) {
- mRoutes.remove(route.getUniqueId());
+ mRoutes.remove(route.getId());
if (route.supportsControlCategories(mControlCategories)) {
removedRoutes.add(route);
}
@@ -528,9 +529,9 @@
void changeRoutesOnHandler(List<MediaRoute2Info> routes) {
List<MediaRoute2Info> changedRoutes = new ArrayList<>();
- synchronized (sLock) {
+ synchronized (sRouterLock) {
for (MediaRoute2Info route : routes) {
- mRoutes.put(route.getUniqueId(), route);
+ mRoutes.put(route.getId(), route);
if (route.supportsControlCategories(mControlCategories)) {
changedRoutes.add(route);
}
@@ -596,7 +597,9 @@
if (sessionInfo != null) {
RouteSessionController controller = new RouteSessionController(sessionInfo);
- mSessionControllers.put(controller.getUniqueSessionId(), controller);
+ synchronized (sRouterLock) {
+ mSessionControllers.put(controller.getUniqueSessionId(), controller);
+ }
notifySessionCreated(controller);
}
}
@@ -607,8 +610,10 @@
return;
}
- RouteSessionController matchingController = mSessionControllers.get(
- sessionInfo.getUniqueSessionId());
+ RouteSessionController matchingController;
+ synchronized (sRouterLock) {
+ matchingController = mSessionControllers.get(sessionInfo.getUniqueSessionId());
+ }
if (matchingController == null) {
Log.w(TAG, "changeSessionInfoOnHandler: Matching controller not found. uniqueSessionId="
@@ -627,6 +632,40 @@
notifySessionInfoChanged(matchingController, oldInfo, sessionInfo);
}
+ void releaseControllerOnHandler(RouteSessionInfo sessionInfo) {
+ if (sessionInfo == null) {
+ Log.w(TAG, "releaseControllerOnHandler: Ignoring null sessionInfo.");
+ return;
+ }
+
+ final String uniqueSessionId = sessionInfo.getUniqueSessionId();
+ RouteSessionController matchingController;
+ synchronized (sRouterLock) {
+ matchingController = mSessionControllers.get(uniqueSessionId);
+ }
+
+ if (matchingController == null) {
+ if (DEBUG) {
+ Log.d(TAG, "releaseControllerOnHandler: Matching controller not found. "
+ + "uniqueSessionId=" + sessionInfo.getUniqueSessionId());
+ }
+ return;
+ }
+
+ RouteSessionInfo oldInfo = matchingController.getRouteSessionInfo();
+ if (!TextUtils.equals(oldInfo.getProviderId(), sessionInfo.getProviderId())) {
+ Log.w(TAG, "releaseControllerOnHandler: Provider IDs are not matched. old="
+ + oldInfo.getProviderId() + ", new=" + sessionInfo.getProviderId());
+ return;
+ }
+
+ synchronized (sRouterLock) {
+ mSessionControllers.remove(uniqueSessionId, matchingController);
+ }
+ matchingController.release();
+ notifyControllerReleased(matchingController);
+ }
+
private void notifyRoutesAdded(List<MediaRoute2Info> routes) {
for (RouteCallbackRecord record: mRouteCallbackRecords) {
record.mExecutor.execute(
@@ -671,6 +710,13 @@
}
}
+ private void notifyControllerReleased(RouteSessionController controller) {
+ for (SessionCallbackRecord record: mSessionCallbackRecords) {
+ record.mExecutor.execute(
+ () -> record.mSessionCallback.onSessionReleased(controller));
+ }
+ }
+
/**
* Callback for receiving events about media route discovery.
*/
@@ -737,14 +783,20 @@
@NonNull RouteSessionInfo newInfo) {}
/**
- * Called when the session is released. Session can be released by the controller using
- * {@link RouteSessionController#release(boolean)}, or by the
- * {@link MediaRoute2ProviderService} itself. One can do clean-ups here.
+ * Called when the session is released by {@link MediaRoute2ProviderService}.
+ * Before this method is called, the controller would be released by the system,
+ * which means the {@link RouteSessionController#isReleased()} will always return true
+ * for the {@code controller} here.
+ * <p>
+ * Note: Calling {@link RouteSessionController#release()} will <em>NOT</em> trigger
+ * this method to be called.
*
- * TODO: When Provider#notifySessionDestroyed is introduced, add @see for the method.
+ * TODO: Add tests for checking whether this method is called.
+ * TODO: When service process dies, this should be called.
+ *
+ * @see RouteSessionController#isReleased()
*/
- public void onSessionReleased(@NonNull RouteSessionController controller, int reason,
- boolean shouldStop) {}
+ public void onSessionReleased(@NonNull RouteSessionController controller) {}
}
/**
@@ -755,7 +807,7 @@
* TODO: Need to add toString()
*/
public final class RouteSessionController {
- private final Object mLock = new Object();
+ private final Object mControllerLock = new Object();
@GuardedBy("mLock")
private RouteSessionInfo mSessionInfo;
@@ -771,7 +823,7 @@
* @return the ID of the session
*/
public int getSessionId() {
- synchronized (mLock) {
+ synchronized (mControllerLock) {
return mSessionInfo.getSessionId();
}
}
@@ -782,7 +834,7 @@
*/
@NonNull
public String getUniqueSessionId() {
- synchronized (mLock) {
+ synchronized (mControllerLock) {
return mSessionInfo.getUniqueSessionId();
}
}
@@ -792,7 +844,7 @@
*/
@NonNull
public String getControlCategory() {
- synchronized (mLock) {
+ synchronized (mControllerLock) {
return mSessionInfo.getControlCategory();
}
}
@@ -802,7 +854,7 @@
*/
@Nullable
public Bundle getControlHints() {
- synchronized (mLock) {
+ synchronized (mControllerLock) {
return mSessionInfo.getControlHints();
}
}
@@ -812,7 +864,7 @@
*/
@NonNull
public List<MediaRoute2Info> getSelectedRoutes() {
- synchronized (mLock) {
+ synchronized (mControllerLock) {
return getRoutesWithIdsLocked(mSessionInfo.getSelectedRoutes());
}
}
@@ -822,7 +874,7 @@
*/
@NonNull
public List<MediaRoute2Info> getSelectableRoutes() {
- synchronized (mLock) {
+ synchronized (mControllerLock) {
return getRoutesWithIdsLocked(mSessionInfo.getSelectableRoutes());
}
}
@@ -832,7 +884,7 @@
*/
@NonNull
public List<MediaRoute2Info> getDeselectableRoutes() {
- synchronized (mLock) {
+ synchronized (mControllerLock) {
return getRoutesWithIdsLocked(mSessionInfo.getDeselectableRoutes());
}
}
@@ -842,7 +894,7 @@
*/
@NonNull
public List<MediaRoute2Info> getTransferrableRoutes() {
- synchronized (mLock) {
+ synchronized (mControllerLock) {
return getRoutesWithIdsLocked(mSessionInfo.getTransferrableRoutes());
}
}
@@ -853,10 +905,9 @@
* Also, any operations to this instance will be ignored once released.
*
* @see #release
- * @see SessionCallback#onSessionReleased
*/
public boolean isReleased() {
- synchronized (mLock) {
+ synchronized (mControllerLock) {
return mIsReleased;
}
}
@@ -876,26 +927,32 @@
*/
public void selectRoute(@NonNull MediaRoute2Info route) {
Objects.requireNonNull(route, "route must not be null");
+ synchronized (mControllerLock) {
+ if (mIsReleased) {
+ Log.w(TAG, "selectRoute() called on released controller. Ignoring.");
+ return;
+ }
+ }
List<MediaRoute2Info> selectedRoutes = getSelectedRoutes();
- if (checkRouteListContainsRouteId(selectedRoutes, route.getUniqueId())) {
+ if (checkRouteListContainsRouteId(selectedRoutes, route.getId())) {
Log.w(TAG, "Ignoring selecting a route that is already selected. route=" + route);
return;
}
List<MediaRoute2Info> selectableRoutes = getSelectableRoutes();
- if (!checkRouteListContainsRouteId(selectableRoutes, route.getUniqueId())) {
+ if (!checkRouteListContainsRouteId(selectableRoutes, route.getId())) {
Log.w(TAG, "Ignoring selecting a non-selectable route=" + route);
return;
}
Client2 client;
- synchronized (sLock) {
+ synchronized (sRouterLock) {
client = mClient;
}
if (client != null) {
try {
- mMediaRouterService.selectRoute(mClient, getUniqueSessionId(), route);
+ mMediaRouterService.selectRoute(client, getUniqueSessionId(), route);
} catch (RemoteException ex) {
Log.e(TAG, "Unable to select route for session.", ex);
}
@@ -917,26 +974,32 @@
*/
public void deselectRoute(@NonNull MediaRoute2Info route) {
Objects.requireNonNull(route, "route must not be null");
+ synchronized (mControllerLock) {
+ if (mIsReleased) {
+ Log.w(TAG, "deselectRoute() called on released controller. Ignoring.");
+ return;
+ }
+ }
List<MediaRoute2Info> selectedRoutes = getSelectedRoutes();
- if (!checkRouteListContainsRouteId(selectedRoutes, route.getUniqueId())) {
+ if (!checkRouteListContainsRouteId(selectedRoutes, route.getId())) {
Log.w(TAG, "Ignoring deselecting a route that is not selected. route=" + route);
return;
}
List<MediaRoute2Info> deselectableRoutes = getDeselectableRoutes();
- if (!checkRouteListContainsRouteId(deselectableRoutes, route.getUniqueId())) {
+ if (!checkRouteListContainsRouteId(deselectableRoutes, route.getId())) {
Log.w(TAG, "Ignoring deselecting a non-deselectable route=" + route);
return;
}
Client2 client;
- synchronized (sLock) {
+ synchronized (sRouterLock) {
client = mClient;
}
if (client != null) {
try {
- mMediaRouterService.deselectRoute(mClient, getUniqueSessionId(), route);
+ mMediaRouterService.deselectRoute(client, getUniqueSessionId(), route);
} catch (RemoteException ex) {
Log.e(TAG, "Unable to remove route from session.", ex);
}
@@ -958,27 +1021,33 @@
*/
public void transferToRoute(@NonNull MediaRoute2Info route) {
Objects.requireNonNull(route, "route must not be null");
+ synchronized (mControllerLock) {
+ if (mIsReleased) {
+ Log.w(TAG, "transferToRoute() called on released controller. Ignoring.");
+ return;
+ }
+ }
List<MediaRoute2Info> selectedRoutes = getSelectedRoutes();
- if (checkRouteListContainsRouteId(selectedRoutes, route.getUniqueId())) {
+ if (checkRouteListContainsRouteId(selectedRoutes, route.getId())) {
Log.w(TAG, "Ignoring transferring to a route that is already added. route="
+ route);
return;
}
List<MediaRoute2Info> transferrableRoutes = getTransferrableRoutes();
- if (!checkRouteListContainsRouteId(transferrableRoutes, route.getUniqueId())) {
+ if (!checkRouteListContainsRouteId(transferrableRoutes, route.getId())) {
Log.w(TAG, "Ignoring transferring to a non-transferrable route=" + route);
return;
}
Client2 client;
- synchronized (sLock) {
+ synchronized (sRouterLock) {
client = mClient;
}
if (client != null) {
try {
- mMediaRouterService.transferToRoute(mClient, getUniqueSessionId(), route);
+ mMediaRouterService.transferToRoute(client, getUniqueSessionId(), route);
} catch (RemoteException ex) {
Log.e(TAG, "Unable to transfer to route for session.", ex);
}
@@ -986,45 +1055,57 @@
}
/**
- * Release this session.
- * Any operation on this session after calling this method will be ignored.
+ * Release this controller and corresponding session.
+ * Any operations on this controller after calling this method will be ignored.
+ * The devices that are playing media will stop playing it.
*
- * @param stopMedia Should the media that is playing on the device be stopped after this
- * session is released.
- * @see SessionCallback#onSessionReleased
+ * TODO: Add tests using {@link MediaRouter2Manager#getActiveSessions()}.
*/
- public void release(boolean stopMedia) {
- synchronized (mLock) {
+ public void release() {
+ synchronized (mControllerLock) {
if (mIsReleased) {
+ Log.w(TAG, "release() called on released controller. Ignoring.");
return;
}
mIsReleased = true;
}
- // TODO: Use stopMedia variable when the actual connection logic is implemented.
+
+ Client2 client;
+ synchronized (sRouterLock) {
+ mSessionControllers.remove(getUniqueSessionId(), this);
+ client = mClient;
+ }
+ if (client != null) {
+ try {
+ mMediaRouterService.releaseSession(client, getUniqueSessionId());
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Unable to notify of controller release", ex);
+ }
+ }
}
/**
+ * TODO: Change this to package private. (Hidden for debugging purposes)
* @hide
*/
@NonNull
public RouteSessionInfo getRouteSessionInfo() {
- synchronized (mLock) {
+ synchronized (mControllerLock) {
return mSessionInfo;
}
}
- /**
- * @hide
- */
- public void setRouteSessionInfo(@NonNull RouteSessionInfo info) {
- synchronized (mLock) {
+ void setRouteSessionInfo(@NonNull RouteSessionInfo info) {
+ synchronized (mControllerLock) {
mSessionInfo = info;
}
}
+ // TODO: This method uses two locks (mLock outside, sLock inside).
+ // Check if there is any possiblity of deadlock.
private List<MediaRoute2Info> getRoutesWithIdsLocked(List<String> routeIds) {
List<MediaRoute2Info> routes = new ArrayList<>();
- synchronized (mLock) {
+ synchronized (sRouterLock) {
for (String routeId : routeIds) {
MediaRoute2Info route = mRoutes.get(
MediaRoute2Info.toUniqueId(mSessionInfo.mProviderId, routeId));
@@ -1139,5 +1220,11 @@
mHandler.sendMessage(obtainMessage(MediaRouter2::changeSessionInfoOnHandler,
MediaRouter2.this, sessionInfo));
}
+
+ @Override
+ public void notifySessionReleased(RouteSessionInfo sessionInfo) {
+ mHandler.sendMessage(obtainMessage(MediaRouter2::releaseControllerOnHandler,
+ MediaRouter2.this, sessionInfo));
+ }
}
}
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 2e68e42..3cbbea1 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -295,7 +295,7 @@
void addRoutesOnHandler(List<MediaRoute2Info> routes) {
synchronized (mRoutesLock) {
for (MediaRoute2Info route : routes) {
- mRoutes.put(route.getUniqueId(), route);
+ mRoutes.put(route.getId(), route);
}
}
if (routes.size() > 0) {
@@ -306,7 +306,7 @@
void removeRoutesOnHandler(List<MediaRoute2Info> routes) {
synchronized (mRoutesLock) {
for (MediaRoute2Info route : routes) {
- mRoutes.remove(route.getUniqueId());
+ mRoutes.remove(route.getId());
}
}
if (routes.size() > 0) {
@@ -317,7 +317,7 @@
void changeRoutesOnHandler(List<MediaRoute2Info> routes) {
synchronized (mRoutesLock) {
for (MediaRoute2Info route : routes) {
- mRoutes.put(route.getUniqueId(), route);
+ mRoutes.put(route.getId(), route);
}
}
if (routes.size() > 0) {
diff --git a/media/java/android/media/RouteSessionInfo.java b/media/java/android/media/RouteSessionInfo.java
index b9cf15e..4a9298a 100644
--- a/media/java/android/media/RouteSessionInfo.java
+++ b/media/java/android/media/RouteSessionInfo.java
@@ -106,9 +106,47 @@
/**
* Gets non-unique session id (int) from unique session id (string).
+ * If the corresponding session id could not be generated, it will return null.
+ * @hide
*/
- public static int getSessionId(@NonNull String uniqueSessionId, @NonNull String providerId) {
- return Integer.parseInt(uniqueSessionId.substring(providerId.length() + 1));
+ @Nullable
+ public static Integer getSessionId(@NonNull String uniqueSessionId) {
+ int lastIndexOfSeparator = uniqueSessionId.lastIndexOf("/");
+ if (lastIndexOfSeparator == -1 || lastIndexOfSeparator + 1 >= uniqueSessionId.length()) {
+ return null;
+ }
+
+ String integerString = uniqueSessionId.substring(lastIndexOfSeparator + 1);
+ if (TextUtils.isEmpty(integerString)) {
+ return null;
+ }
+
+ try {
+ return Integer.parseInt(integerString);
+ } catch (NumberFormatException ex) {
+ return null;
+ }
+ }
+
+ /**
+ * Gets provider ID (string) from unique session id (string).
+ * If the corresponding provider ID could not be generated, it will return null.
+ * @hide
+ *
+ * TODO: This logic seems error-prone. Consider to use long uniqueId.
+ */
+ @Nullable
+ public static String getProviderId(@NonNull String uniqueSessionId) {
+ int lastIndexOfSeparator = uniqueSessionId.lastIndexOf("/");
+ if (lastIndexOfSeparator == -1) {
+ return null;
+ }
+
+ String result = uniqueSessionId.substring(0, lastIndexOfSeparator);
+ if (TextUtils.isEmpty(result)) {
+ return null;
+ }
+ return result;
}
/**
@@ -289,14 +327,30 @@
}
/**
- * Sets the provider id of the session.
+ * Sets the provider ID of the session.
+ * Also, calling this method will make all type of route IDs be unique by adding
+ * {@code providerId:} as a prefix. So do NOT call this method twice on same instance.
+ *
+ * @hide
*/
@NonNull
public Builder setProviderId(String providerId) {
mProviderId = providerId;
+ convertToUniqueRouteIds(providerId, mSelectedRoutes);
+ convertToUniqueRouteIds(providerId, mSelectableRoutes);
+ convertToUniqueRouteIds(providerId, mDeselectableRoutes);
+ convertToUniqueRouteIds(providerId, mTransferrableRoutes);
return this;
}
+ private void convertToUniqueRouteIds(@NonNull String providerId,
+ @NonNull List<String> routeIds) {
+ for (int i = 0; i < routeIds.size(); i++) {
+ String routeId = routeIds.get(i);
+ routeIds.set(i, MediaRoute2Info.toUniqueId(providerId, routeId));
+ }
+ }
+
/**
* Clears the selected routes.
*/
diff --git a/media/java/android/media/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java
index 4a40c62..cad5aa6 100644
--- a/media/java/android/media/audiofx/AudioEffect.java
+++ b/media/java/android/media/audiofx/AudioEffect.java
@@ -16,11 +16,18 @@
package android.media.audiofx;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.ActivityThread;
import android.compat.annotation.UnsupportedAppUsage;
+import android.media.AudioDeviceAddress;
+import android.media.AudioDeviceInfo;
+import android.media.AudioSystem;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
@@ -448,12 +455,46 @@
public AudioEffect(UUID type, UUID uuid, int priority, int audioSession)
throws IllegalArgumentException, UnsupportedOperationException,
RuntimeException {
+ this(type, uuid, priority, audioSession, null);
+ }
+
+ /**
+ * Constructs an AudioEffect attached to a particular audio device.
+ * The device does not have to be attached when the effect is created. The effect will only
+ * be applied when the device is actually selected for playback or capture.
+ * @param uuid unique identifier of a particular effect implementation.
+ * @param device the device the effect must be attached to.
+ *
+ * @throws java.lang.IllegalArgumentException
+ * @throws java.lang.UnsupportedOperationException
+ * @throws java.lang.RuntimeException
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ public AudioEffect(@NonNull UUID uuid, @NonNull AudioDeviceAddress device) {
+ this(EFFECT_TYPE_NULL, Objects.requireNonNull(uuid), 0, -2, Objects.requireNonNull(device));
+ }
+
+ private AudioEffect(UUID type, UUID uuid, int priority,
+ int audioSession, @Nullable AudioDeviceAddress device)
+ throws IllegalArgumentException, UnsupportedOperationException,
+ RuntimeException {
int[] id = new int[1];
Descriptor[] desc = new Descriptor[1];
+
+ int deviceType = AudioSystem.DEVICE_NONE;
+ String deviceAddress = "";
+ if (device != null) {
+ deviceType = AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType());
+ deviceAddress = device.getAddress();
+ }
+
// native initialization
int initResult = native_setup(new WeakReference<AudioEffect>(this),
- type.toString(), uuid.toString(), priority, audioSession, id,
- desc, ActivityThread.currentOpPackageName());
+ type.toString(), uuid.toString(), priority, audioSession,
+ deviceType, deviceAddress,
+ id, desc, ActivityThread.currentOpPackageName());
if (initResult != SUCCESS && initResult != ALREADY_EXISTS) {
Log.e(TAG, "Error code " + initResult
+ " when initializing AudioEffect.");
@@ -1293,7 +1334,8 @@
private static native final void native_init();
private native final int native_setup(Object audioeffect_this, String type,
- String uuid, int priority, int audioSession, int[] id, Object[] desc,
+ String uuid, int priority, int audioSession,
+ int deviceType, String deviceAddress, int[] id, Object[] desc,
String opPackageName);
private native final void native_finalize();
diff --git a/media/java/android/media/tv/DvbDeviceInfo.java b/media/java/android/media/tv/DvbDeviceInfo.java
index a574fe1..96c8528 100644
--- a/media/java/android/media/tv/DvbDeviceInfo.java
+++ b/media/java/android/media/tv/DvbDeviceInfo.java
@@ -16,6 +16,8 @@
package android.media.tv;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
@@ -26,10 +28,11 @@
*
* @hide
*/
+@SystemApi
public final class DvbDeviceInfo implements Parcelable {
static final String TAG = "DvbDeviceInfo";
- public static final @android.annotation.NonNull Parcelable.Creator<DvbDeviceInfo> CREATOR =
+ public static final @NonNull Parcelable.Creator<DvbDeviceInfo> CREATOR =
new Parcelable.Creator<DvbDeviceInfo>() {
@Override
public DvbDeviceInfo createFromParcel(Parcel source) {
@@ -86,7 +89,7 @@
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mAdapterId);
dest.writeInt(mDeviceId);
}
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index d22a298..854ea43 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -103,6 +103,12 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
+ @IntDef({DVB_DEVICE_DEMUX, DVB_DEVICE_DVR, DVB_DEVICE_FRONTEND})
+ public @interface DvbDeviceType {}
+
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
@IntDef({VIDEO_UNAVAILABLE_REASON_UNKNOWN, VIDEO_UNAVAILABLE_REASON_TUNING,
VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL, VIDEO_UNAVAILABLE_REASON_BUFFERING,
VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY})
@@ -1663,6 +1669,9 @@
* @return the list of {@link DvbDeviceInfo} objects representing available DVB devices.
* @hide
*/
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.DVB_DEVICE)
+ @NonNull
public List<DvbDeviceInfo> getDvbDeviceList() {
try {
return mService.getDvbDeviceList();
@@ -1676,19 +1685,24 @@
* {@link DvbDeviceInfo}
*
* @param info A {@link DvbDeviceInfo} to open a DVB device.
- * @param device A DVB device. The DVB device can be {@link #DVB_DEVICE_DEMUX},
+ * @param deviceType A DVB device type. The type can be {@link #DVB_DEVICE_DEMUX},
* {@link #DVB_DEVICE_DVR} or {@link #DVB_DEVICE_FRONTEND}.
* @return a {@link ParcelFileDescriptor} of a specified DVB device for a given
- * {@link DvbDeviceInfo}, or {@code null} if the given {@link DvbDeviceInfo} was invalid
- * or the specified DVB device was busy with a previous request.
+ * {@link DvbDeviceInfo}, or {@code null} if the given {@link DvbDeviceInfo}
+ * failed to open.
+ * @throws IllegalArgumentException if {@code deviceType} is invalid or the device is not found.
* @hide
*/
- public ParcelFileDescriptor openDvbDevice(DvbDeviceInfo info, int device) {
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.DVB_DEVICE)
+ @Nullable
+ public ParcelFileDescriptor openDvbDevice(@NonNull DvbDeviceInfo info,
+ @DvbDeviceType int deviceType) {
try {
- if (DVB_DEVICE_START > device || DVB_DEVICE_END < device) {
- throw new IllegalArgumentException("Invalid DVB device: " + device);
+ if (DVB_DEVICE_START > deviceType || DVB_DEVICE_END < deviceType) {
+ throw new IllegalArgumentException("Invalid DVB device: " + deviceType);
}
- return mService.openDvbDevice(info, device);
+ return mService.openDvbDevice(info, deviceType);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/media/java/android/media/tv/TvTrackInfo.java b/media/java/android/media/tv/TvTrackInfo.java
index ea00d6e..c17beba 100644
--- a/media/java/android/media/tv/TvTrackInfo.java
+++ b/media/java/android/media/tv/TvTrackInfo.java
@@ -61,6 +61,8 @@
private final boolean mEncrypted;
private final int mAudioChannelCount;
private final int mAudioSampleRate;
+ private final boolean mAudioDescription;
+ private final boolean mHardOfHearing;
private final int mVideoWidth;
private final int mVideoHeight;
private final float mVideoFrameRate;
@@ -70,9 +72,9 @@
private final Bundle mExtra;
private TvTrackInfo(int type, String id, String language, CharSequence description,
- boolean encrypted, int audioChannelCount, int audioSampleRate, int videoWidth,
- int videoHeight, float videoFrameRate, float videoPixelAspectRatio,
- byte videoActiveFormatDescription, Bundle extra) {
+ boolean encrypted, int audioChannelCount, int audioSampleRate, boolean audioDescription,
+ boolean hardOfHearing, int videoWidth, int videoHeight, float videoFrameRate,
+ float videoPixelAspectRatio, byte videoActiveFormatDescription, Bundle extra) {
mType = type;
mId = id;
mLanguage = language;
@@ -80,6 +82,8 @@
mEncrypted = encrypted;
mAudioChannelCount = audioChannelCount;
mAudioSampleRate = audioSampleRate;
+ mAudioDescription = audioDescription;
+ mHardOfHearing = hardOfHearing;
mVideoWidth = videoWidth;
mVideoHeight = videoHeight;
mVideoFrameRate = videoFrameRate;
@@ -96,6 +100,8 @@
mEncrypted = in.readInt() != 0;
mAudioChannelCount = in.readInt();
mAudioSampleRate = in.readInt();
+ mAudioDescription = in.readInt() != 0;
+ mHardOfHearing = in.readInt() != 0;
mVideoWidth = in.readInt();
mVideoHeight = in.readInt();
mVideoFrameRate = in.readFloat();
@@ -172,6 +178,40 @@
}
/**
+ * Returns {@code true} if the track is an audio description intended for people with visual
+ * impairment, {@code false} otherwise. Valid only for {@link #TYPE_AUDIO} tracks.
+ *
+ * <p>For example of broadcast, audio description information may be referred to broadcast
+ * standard (e.g. ISO 639 Language Descriptor of ISO/IEC 13818-1, Supplementary Audio Language
+ * Descriptor, AC-3 Descriptor, Enhanced AC-3 Descriptor, AAC Descriptor of ETSI EN 300 468).
+ *
+ * @throws IllegalStateException if not called on an audio track
+ */
+ public boolean isAudioDescription() {
+ if (mType != TYPE_AUDIO) {
+ throw new IllegalStateException("Not an audio track");
+ }
+ return mAudioDescription;
+ }
+
+ /**
+ * Returns {@code true} if the track is intended for people with hearing impairment, {@code
+ * false} otherwise. Valid only for {@link #TYPE_AUDIO} and {@link #TYPE_SUBTITLE} tracks.
+ *
+ * <p>For example of broadcast, hard of hearing information may be referred to broadcast
+ * standard (e.g. ISO 639 Language Descriptor of ISO/IEC 13818-1, Supplementary Audio Language
+ * Descriptor, AC-3 Descriptor, Enhanced AC-3 Descriptor, AAC Descriptor of ETSI EN 300 468).
+ *
+ * @throws IllegalStateException if not called on an audio track or a subtitle track
+ */
+ public boolean isHardOfHearing() {
+ if (mType != TYPE_AUDIO && mType != TYPE_SUBTITLE) {
+ throw new IllegalStateException("Not an audio or a subtitle track");
+ }
+ return mHardOfHearing;
+ }
+
+ /**
* Returns the width of the video, in the unit of pixels. Valid only for {@link #TYPE_VIDEO}
* tracks.
*
@@ -266,6 +306,8 @@
dest.writeInt(mEncrypted ? 1 : 0);
dest.writeInt(mAudioChannelCount);
dest.writeInt(mAudioSampleRate);
+ dest.writeInt(mAudioDescription ? 1 : 0);
+ dest.writeInt(mHardOfHearing ? 1 : 0);
dest.writeInt(mVideoWidth);
dest.writeInt(mVideoHeight);
dest.writeFloat(mVideoFrameRate);
@@ -296,7 +338,9 @@
switch (mType) {
case TYPE_AUDIO:
return mAudioChannelCount == obj.mAudioChannelCount
- && mAudioSampleRate == obj.mAudioSampleRate;
+ && mAudioSampleRate == obj.mAudioSampleRate
+ && mAudioDescription == obj.mAudioDescription
+ && mHardOfHearing == obj.mHardOfHearing;
case TYPE_VIDEO:
return mVideoWidth == obj.mVideoWidth
@@ -304,6 +348,9 @@
&& mVideoFrameRate == obj.mVideoFrameRate
&& mVideoPixelAspectRatio == obj.mVideoPixelAspectRatio
&& mVideoActiveFormatDescription == obj.mVideoActiveFormatDescription;
+
+ case TYPE_SUBTITLE:
+ return mHardOfHearing == obj.mHardOfHearing;
}
return true;
@@ -338,6 +385,8 @@
private boolean mEncrypted;
private int mAudioChannelCount;
private int mAudioSampleRate;
+ private boolean mAudioDescription;
+ private boolean mHardOfHearing;
private int mVideoWidth;
private int mVideoHeight;
private float mVideoFrameRate;
@@ -430,6 +479,48 @@
}
/**
+ * Sets the audio description attribute of the audio. Valid only for {@link #TYPE_AUDIO}
+ * tracks.
+ *
+ * <p>For example of broadcast, audio description information may be referred to broadcast
+ * standard (e.g. ISO 639 Language Descriptor of ISO/IEC 13818-1, Supplementary Audio
+ * Language Descriptor, AC-3 Descriptor, Enhanced AC-3 Descriptor, AAC Descriptor of ETSI EN
+ * 300 468).
+ *
+ * @param audioDescription The audio description attribute of the audio.
+ * @throws IllegalStateException if not called on an audio track
+ */
+ @NonNull
+ public Builder setAudioDescription(boolean audioDescription) {
+ if (mType != TYPE_AUDIO) {
+ throw new IllegalStateException("Not an audio track");
+ }
+ mAudioDescription = audioDescription;
+ return this;
+ }
+
+ /**
+ * Sets the hard of hearing attribute of the track. Valid only for {@link #TYPE_AUDIO} and
+ * {@link #TYPE_SUBTITLE} tracks.
+ *
+ * <p>For example of broadcast, hard of hearing information may be referred to broadcast
+ * standard (e.g. ISO 639 Language Descriptor of ISO/IEC 13818-1, Supplementary Audio
+ * Language Descriptor, AC-3 Descriptor, Enhanced AC-3 Descriptor, AAC Descriptor of ETSI EN
+ * 300 468).
+ *
+ * @param hardOfHearing The hard of hearing attribute of the track.
+ * @throws IllegalStateException if not called on an audio track or a subtitle track
+ */
+ @NonNull
+ public Builder setHardOfHearing(boolean hardOfHearing) {
+ if (mType != TYPE_AUDIO && mType != TYPE_SUBTITLE) {
+ throw new IllegalStateException("Not an audio track or a subtitle track");
+ }
+ mHardOfHearing = hardOfHearing;
+ return this;
+ }
+
+ /**
* Sets the width of the video, in the unit of pixels. Valid only for {@link #TYPE_VIDEO}
* tracks.
*
@@ -531,8 +622,9 @@
*/
public TvTrackInfo build() {
return new TvTrackInfo(mType, mId, mLanguage, mDescription, mEncrypted,
- mAudioChannelCount, mAudioSampleRate, mVideoWidth, mVideoHeight,
- mVideoFrameRate, mVideoPixelAspectRatio, mVideoActiveFormatDescription, mExtra);
+ mAudioChannelCount, mAudioSampleRate, mAudioDescription, mHardOfHearing,
+ mVideoWidth, mVideoHeight, mVideoFrameRate, mVideoPixelAspectRatio,
+ mVideoActiveFormatDescription, mExtra);
}
}
}
diff --git a/media/java/android/media/tv/tuner/DemuxCapabilities.java b/media/java/android/media/tv/tuner/DemuxCapabilities.java
new file mode 100644
index 0000000..bda166e
--- /dev/null
+++ b/media/java/android/media/tv/tuner/DemuxCapabilities.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Capabilities info for Demux.
+ * @hide
+ */
+public class DemuxCapabilities {
+ private final int mNumDemux;
+ private final int mNumRecord;
+ private final int mNumPlayback;
+ private final int mNumTsFilter;
+ private final int mNumSectionFilter;
+ private final int mNumAudioFilter;
+ private final int mNumVideoFilter;
+ private final int mNumPesFilter;
+ private final int mNumPcrFilter;
+ private final int mNumBytesInSectionFilter;
+ private final int mFilterCaps;
+ private final int[] mLinkCaps;
+
+ DemuxCapabilities(int numDemux, int numRecord, int numPlayback, int numTsFilter,
+ int numSectionFilter, int numAudioFilter, int numVideoFilter, int numPesFilter,
+ int numPcrFilter, int numBytesInSectionFilter, int filterCaps, int[] linkCaps) {
+ mNumDemux = numDemux;
+ mNumRecord = numRecord;
+ mNumPlayback = numPlayback;
+ mNumTsFilter = numTsFilter;
+ mNumSectionFilter = numSectionFilter;
+ mNumAudioFilter = numAudioFilter;
+ mNumVideoFilter = numVideoFilter;
+ mNumPesFilter = numPesFilter;
+ mNumPcrFilter = numPcrFilter;
+ mNumBytesInSectionFilter = numBytesInSectionFilter;
+ mFilterCaps = filterCaps;
+ mLinkCaps = linkCaps;
+ }
+
+ /** Gets total number of demuxes. */
+ public int getNumDemux() {
+ return mNumDemux;
+ }
+ /** Gets max number of recordings at a time. */
+ public int getNumRecord() {
+ return mNumRecord;
+ }
+ /** Gets max number of playbacks at a time. */
+ public int getNumPlayback() {
+ return mNumPlayback;
+ }
+ /** Gets number of TS filters. */
+ public int getNumTsFilter() {
+ return mNumTsFilter;
+ }
+ /** Gets number of section filters. */
+ public int getNumSectionFilter() {
+ return mNumSectionFilter;
+ }
+ /** Gets number of audio filters. */
+ public int getNumAudioFilter() {
+ return mNumAudioFilter;
+ }
+ /** Gets number of video filters. */
+ public int getNumVideoFilter() {
+ return mNumVideoFilter;
+ }
+ /** Gets number of PES filters. */
+ public int getNumPesFilter() {
+ return mNumPesFilter;
+ }
+ /** Gets number of PCR filters. */
+ public int getNumPcrFilter() {
+ return mNumPcrFilter;
+ }
+ /** Gets number of bytes in the mask of a section filter. */
+ public int getNumBytesInSectionFilter() {
+ return mNumBytesInSectionFilter;
+ }
+ /**
+ * Gets filter capabilities in bit field.
+ *
+ * The bits of the returned value is corresponding to the types in
+ * {@link TunerConstants.FilterType}.
+ */
+ public int getFilterCapabilities() {
+ return mFilterCaps;
+ }
+
+ /**
+ * Gets link capabilities.
+ *
+ * The returned array contains the same elements as the number of types in
+ * {@link TunerConstants.FilterType}.
+ * The ith element represents the filter's capability as the source for the ith type
+ */
+ public int[] getLinkCapabilities() {
+ return mLinkCaps;
+ }
+}
diff --git a/media/java/android/media/tv/tuner/FilterSettings.java b/media/java/android/media/tv/tuner/FilterConfiguration.java
similarity index 83%
rename from media/java/android/media/tv/tuner/FilterSettings.java
rename to media/java/android/media/tv/tuner/FilterConfiguration.java
index 673b3d9..b80a82a 100644
--- a/media/java/android/media/tv/tuner/FilterSettings.java
+++ b/media/java/android/media/tv/tuner/FilterConfiguration.java
@@ -22,33 +22,37 @@
import java.util.List;
/**
- * Demux Filter settings.
+ * Demux Filter configuration.
*
* @hide
*/
-public abstract class FilterSettings {
+public abstract class FilterConfiguration {
@Nullable
protected final Settings mSettings;
- protected FilterSettings(Settings settings) {
+ protected FilterConfiguration(Settings settings) {
mSettings = settings;
}
/**
- * Gets filter settings type
+ * Gets filter configuration type
*/
@FilterType
public abstract int getType();
+ public Settings getSettings() {
+ return mSettings;
+ }
+
// TODO: more builders and getters
/**
- * Filter Settings for a TS filter.
+ * Filter configuration for a TS filter.
*/
- public static class TsFilterSettings extends FilterSettings {
+ public static class TsFilterConfiguration extends FilterConfiguration {
private int mTpid;
- private TsFilterSettings(Settings settings, int tpid) {
+ private TsFilterConfiguration(Settings settings, int tpid) {
super(settings);
mTpid = tpid;
}
@@ -66,7 +70,7 @@
}
/**
- * Builder for TsFilterSettings.
+ * Builder for TsFilterConfiguration.
*/
public static class Builder {
private Settings mSettings;
@@ -89,21 +93,21 @@
}
/**
- * Builds a TsFilterSettings instance.
+ * Builds a TsFilterConfiguration instance.
*/
- public TsFilterSettings build() {
- return new TsFilterSettings(mSettings, mTpid);
+ public TsFilterConfiguration build() {
+ return new TsFilterConfiguration(mSettings, mTpid);
}
}
}
/**
- * Filter Settings for a MMTP filter.
+ * Filter configuration for a MMTP filter.
*/
- public static class MmtpFilterSettings extends FilterSettings {
+ public static class MmtpFilterConfiguration extends FilterConfiguration {
private int mMmtpPid;
- public MmtpFilterSettings(Settings settings) {
+ public MmtpFilterConfiguration(Settings settings) {
super(settings);
}
@@ -115,16 +119,16 @@
/**
- * Filter Settings for a IP filter.
+ * Filter configuration for a IP filter.
*/
- public static class IpFilterSettings extends FilterSettings {
+ public static class IpFilterConfiguration extends FilterConfiguration {
private byte[] mSrcIpAddress;
private byte[] mDstIpAddress;
private int mSrcPort;
private int mDstPort;
private boolean mPassthrough;
- public IpFilterSettings(Settings settings) {
+ public IpFilterConfiguration(Settings settings) {
super(settings);
}
@@ -136,14 +140,14 @@
/**
- * Filter Settings for a TLV filter.
+ * Filter configuration for a TLV filter.
*/
- public static class TlvFilterSettings extends FilterSettings {
+ public static class TlvFilterConfiguration extends FilterConfiguration {
private int mPacketType;
private boolean mIsCompressedIpPacket;
private boolean mPassthrough;
- public TlvFilterSettings(Settings settings) {
+ public TlvFilterConfiguration(Settings settings) {
super(settings);
}
@@ -155,13 +159,13 @@
/**
- * Filter Settings for a ALP filter.
+ * Filter configuration for a ALP filter.
*/
- public static class AlpFilterSettings extends FilterSettings {
+ public static class AlpFilterConfiguration extends FilterConfiguration {
private int mPacketType;
private int mLengthType;
- public AlpFilterSettings(Settings settings) {
+ public AlpFilterConfiguration(Settings settings) {
super(settings);
}
diff --git a/media/java/android/media/tv/tuner/FilterEvent.java b/media/java/android/media/tv/tuner/FilterEvent.java
deleted file mode 100644
index 7c165ce..0000000
--- a/media/java/android/media/tv/tuner/FilterEvent.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.tv.tuner;
-
-import android.os.NativeHandle;
-
-/**
- * Demux filter event.
- *
- * @hide
- */
-public abstract class FilterEvent {
-
- /**
- * Section event.
- */
- public static class SectionEvent extends FilterEvent {
- private int mTableId;
- private int mVersion;
- private int mSectionNum;
- private int mDataLength;
- }
-
- /**
- * Media event.
- */
- public static class MediaEvent extends FilterEvent {
- private int mStreamId;
- private boolean mIsPtsPresent;
- private long mPts;
- private int mDataLength;
- private NativeHandle mHandle;
- private boolean mIsSecureMemory;
- private int mMpuSequenceNumber;
- private boolean mIsPrivateData;
- private AudioExtraMetaData mExtraMetaData;
- }
-
- /**
- * PES event.
- */
- public static class PesEvent extends FilterEvent {
- private int mStreamId;
- private int mDataLength;
- private int mMpuSequenceNumber;
- }
-
- /**
- * TS record event.
- */
- public static class TsRecordEvent extends FilterEvent {
- private int mTpid;
- private int mIndexMask;
- private long mByteNumber;
- }
-
- /**
- * MMPT record event.
- */
- public static class MmtpRecordEvent extends FilterEvent {
- private int mScHevcIndexMask;
- private long mByteNumber;
- }
-
- /**
- * Download event.
- */
- public static class DownloadEvent extends FilterEvent {
- private int mItemId;
- private int mMpuSequenceNumber;
- private int mItemFragmentIndex;
- private int mLastItemFragmentIndex;
- private int mDataLength;
- }
-
- /**
- * IP payload event.
- */
- public static class IpPayloadEvent extends FilterEvent {
- private int mDataLength;
- }
-
- /**
- * TEMI event.
- */
- public static class TemiEvent extends FilterEvent {
- private long mPts;
- private byte mDescrTag;
- private byte[] mDescrData;
- }
-
- /**
- * Extra Meta Data from AD (Audio Descriptor) according to
- * ETSI TS 101 154 V2.1.1.
- */
- public static class AudioExtraMetaData {
- private byte mAdFade;
- private byte mAdPan;
- private byte mVersionTextTag;
- private byte mAdGainCenter;
- private byte mAdGainFront;
- private byte mAdGainSurround;
- }
-}
diff --git a/media/java/android/media/tv/tuner/FrontendCapabilities.java b/media/java/android/media/tv/tuner/FrontendCapabilities.java
new file mode 100644
index 0000000..fcfd7c8
--- /dev/null
+++ b/media/java/android/media/tv/tuner/FrontendCapabilities.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Frontend Capabilities.
+ * @hide
+ */
+public class FrontendCapabilities {
+ /** Analog Capabilities. */
+ public class Analog extends FrontendCapabilities {
+ private final int mTypeCap;
+ private final int mSifStandardCap;
+
+ Analog(int typeCap, int sifStandardCap) {
+ mTypeCap = typeCap;
+ mSifStandardCap = sifStandardCap;
+ }
+ /**
+ * Gets type capability.
+ */
+ public int getTypeCapability() {
+ return mTypeCap;
+ }
+ /** Gets SIF standard capability. */
+ public int getSifStandardCapability() {
+ return mSifStandardCap;
+ }
+ }
+
+ /** ATSC Capabilities. */
+ public class Atsc extends FrontendCapabilities {
+ private final int mModulationCap;
+
+ Atsc(int modulationCap) {
+ mModulationCap = modulationCap;
+ }
+ /** Gets modulation capability. */
+ public int getModulationCapability() {
+ return mModulationCap;
+ }
+ }
+
+ /** ATSC-3 Capabilities. */
+ public class Atsc3 extends FrontendCapabilities {
+ private final int mBandwidthCap;
+ private final int mModulationCap;
+ private final int mTimeInterleaveModeCap;
+ private final int mCodeRateCap;
+ private final int mFecCap;
+ private final int mDemodOutputFormatCap;
+
+ Atsc3(int bandwidthCap, int modulationCap, int timeInterleaveModeCap, int codeRateCap,
+ int fecCap, int demodOutputFormatCap) {
+ mBandwidthCap = bandwidthCap;
+ mModulationCap = modulationCap;
+ mTimeInterleaveModeCap = timeInterleaveModeCap;
+ mCodeRateCap = codeRateCap;
+ mFecCap = fecCap;
+ mDemodOutputFormatCap = demodOutputFormatCap;
+ }
+
+ /** Gets bandwidth capability. */
+ public int getBandwidthCapability() {
+ return mBandwidthCap;
+ }
+ /** Gets modulation capability. */
+ public int getModulationCapability() {
+ return mModulationCap;
+ }
+ /** Gets time interleave mod capability. */
+ public int getTimeInterleaveModeCapability() {
+ return mTimeInterleaveModeCap;
+ }
+ /** Gets code rate capability. */
+ public int getCodeRateCapability() {
+ return mCodeRateCap;
+ }
+ /** Gets FEC capability. */
+ public int getFecCapability() {
+ return mFecCap;
+ }
+ /** Gets demodulator output format capability. */
+ public int getDemodOutputFormatCapability() {
+ return mDemodOutputFormatCap;
+ }
+ }
+
+ /** DVBS Capabilities. */
+ public class Dvbs extends FrontendCapabilities {
+ private final int mModulationCap;
+ private final long mInnerFecCap;
+ private final int mStandard;
+
+ Dvbs(int modulationCap, long innerFecCap, int standard) {
+ mModulationCap = modulationCap;
+ mInnerFecCap = innerFecCap;
+ mStandard = standard;
+ }
+
+ /** Gets modulation capability. */
+ public int getModulationCapability() {
+ return mModulationCap;
+ }
+ /** Gets inner FEC capability. */
+ public long getInnerFecCapability() {
+ return mInnerFecCap;
+ }
+ /** Gets DVBS standard capability. */
+ public int getStandardCapability() {
+ return mStandard;
+ }
+ }
+
+ /** DVBC Capabilities. */
+ public class Dvbc extends FrontendCapabilities {
+ private final int mModulationCap;
+ private final int mFecCap;
+ private final int mAnnexCap;
+
+ Dvbc(int modulationCap, int fecCap, int annexCap) {
+ mModulationCap = modulationCap;
+ mFecCap = fecCap;
+ mAnnexCap = annexCap;
+ }
+
+ /** Gets modulation capability. */
+ public int getModulationCapability() {
+ return mModulationCap;
+ }
+ /** Gets FEC capability. */
+ public int getFecCapability() {
+ return mFecCap;
+ }
+ /** Gets annex capability. */
+ public int getAnnexCapability() {
+ return mAnnexCap;
+ }
+ }
+
+ /** DVBT Capabilities. */
+ public class Dvbt extends FrontendCapabilities {
+ private final int mTransmissionModeCap;
+ private final int mBandwidthCap;
+ private final int mConstellationCap;
+ private final int mCoderateCap;
+ private final int mHierarchyCap;
+ private final int mGuardIntervalCap;
+ private final boolean mIsT2Supported;
+ private final boolean mIsMisoSupported;
+
+ Dvbt(int transmissionModeCap, int bandwidthCap, int constellationCap, int coderateCap,
+ int hierarchyCap, int guardIntervalCap, boolean isT2Supported,
+ boolean isMisoSupported) {
+ mTransmissionModeCap = transmissionModeCap;
+ mBandwidthCap = bandwidthCap;
+ mConstellationCap = constellationCap;
+ mCoderateCap = coderateCap;
+ mHierarchyCap = hierarchyCap;
+ mGuardIntervalCap = guardIntervalCap;
+ mIsT2Supported = isT2Supported;
+ mIsMisoSupported = isMisoSupported;
+ }
+
+ /** Gets transmission mode capability. */
+ public int getTransmissionModeCapability() {
+ return mTransmissionModeCap;
+ }
+ /** Gets bandwidth capability. */
+ public int getBandwidthCapability() {
+ return mBandwidthCap;
+ }
+ /** Gets constellation capability. */
+ public int getConstellationCapability() {
+ return mConstellationCap;
+ }
+ /** Gets code rate capability. */
+ public int getCodeRateCapability() {
+ return mCoderateCap;
+ }
+ /** Gets hierarchy capability. */
+ public int getHierarchyCapability() {
+ return mHierarchyCap;
+ }
+ /** Gets guard interval capability. */
+ public int getGuardIntervalCapability() {
+ return mGuardIntervalCap;
+ }
+ /** Returns whether T2 is supported. */
+ public boolean getIsT2Supported() {
+ return mIsT2Supported;
+ }
+ /** Returns whether MISO is supported. */
+ public boolean getIsMisoSupported() {
+ return mIsMisoSupported;
+ }
+ }
+
+ /** ISDBS Capabilities. */
+ public class Isdbs extends FrontendCapabilities {
+ private final int mModulationCap;
+ private final int mCoderateCap;
+
+ Isdbs(int modulationCap, int coderateCap) {
+ mModulationCap = modulationCap;
+ mCoderateCap = coderateCap;
+ }
+
+ /** Gets modulation capability. */
+ public int getModulationCapability() {
+ return mModulationCap;
+ }
+ /** Gets code rate capability. */
+ public int getCodeRateCapability() {
+ return mCoderateCap;
+ }
+ }
+
+ /** ISDBS-3 Capabilities. */
+ public class Isdbs3 extends FrontendCapabilities {
+ private final int mModulationCap;
+ private final int mCoderateCap;
+
+ Isdbs3(int modulationCap, int coderateCap) {
+ mModulationCap = modulationCap;
+ mCoderateCap = coderateCap;
+ }
+
+ /** Gets modulation capability. */
+ public int getModulationCapability() {
+ return mModulationCap;
+ }
+ /** Gets code rate capability. */
+ public int getCodeRateCapability() {
+ return mCoderateCap;
+ }
+ }
+
+ /** ISDBC Capabilities. */
+ public class Isdbc extends FrontendCapabilities {
+ private final int mModeCap;
+ private final int mBandwidthCap;
+ private final int mModulationCap;
+ private final int mCoderateCap;
+ private final int mGuardIntervalCap;
+
+ Isdbc(int modeCap, int bandwidthCap, int modulationCap, int coderateCap,
+ int guardIntervalCap) {
+ mModeCap = modeCap;
+ mBandwidthCap = bandwidthCap;
+ mModulationCap = modulationCap;
+ mCoderateCap = coderateCap;
+ mGuardIntervalCap = guardIntervalCap;
+ }
+
+ /** Gets mode capability. */
+ public int getModeCapability() {
+ return mModeCap;
+ }
+ /** Gets bandwidth capability. */
+ public int getBandwidthCapability() {
+ return mBandwidthCap;
+ }
+ /** Gets modulation capability. */
+ public int getModulationCapability() {
+ return mModulationCap;
+ }
+ /** Gets code rate capability. */
+ public int getCodeRateCapability() {
+ return mCoderateCap;
+ }
+ /** Gets guard interval capability. */
+ public int getGuardIntervalCapability() {
+ return mGuardIntervalCap;
+ }
+ }
+}
diff --git a/media/java/android/media/tv/tuner/FrontendSettings.java b/media/java/android/media/tv/tuner/FrontendSettings.java
index 531e210..e2e9910 100644
--- a/media/java/android/media/tv/tuner/FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/FrontendSettings.java
@@ -19,8 +19,6 @@
import android.annotation.SystemApi;
import android.media.tv.tuner.TunerConstants.FrontendSettingsType;
-import java.util.List;
-
/**
* Frontend settings for tune and scan operations.
* @hide
@@ -29,7 +27,8 @@
public abstract class FrontendSettings {
private final int mFrequency;
- FrontendSettings(int frequency) {
+ /** @hide */
+ public FrontendSettings(int frequency) {
mFrequency = frequency;
}
@@ -48,282 +47,4 @@
return mFrequency;
}
- // TODO: use hal constants for enum fields
- // TODO: javaDoc
- // TODO: add builders and getters for other settings type
-
- /**
- * Frontend settings for analog.
- * @hide
- */
- public static class FrontendAnalogSettings extends FrontendSettings {
- private int mAnalogType;
- private int mSifStandard;
-
- @Override
- public int getType() {
- return TunerConstants.FRONTEND_TYPE_ANALOG;
- }
-
- public int getAnalogType() {
- return mAnalogType;
- }
-
- public int getSifStandard() {
- return mSifStandard;
- }
-
- /**
- * Creates a new builder object.
- */
- public static Builder newBuilder() {
- return new Builder();
- }
-
- private FrontendAnalogSettings(int frequency, int analogType, int sifStandard) {
- super(frequency);
- mAnalogType = analogType;
- mSifStandard = sifStandard;
- }
-
- /**
- * Builder for FrontendAnalogSettings.
- */
- public static class Builder {
- private int mFrequency;
- private int mAnalogType;
- private int mSifStandard;
-
- private Builder() {}
-
- /**
- * Sets frequency.
- */
- public Builder setFrequency(int frequency) {
- mFrequency = frequency;
- return this;
- }
-
- /**
- * Sets analog type.
- */
- public Builder setAnalogType(int analogType) {
- mAnalogType = analogType;
- return this;
- }
-
- /**
- * Sets sif standard.
- */
- public Builder setSifStandard(int sifStandard) {
- mSifStandard = sifStandard;
- return this;
- }
-
- /**
- * Builds a FrontendAnalogSettings instance.
- */
- public FrontendAnalogSettings build() {
- return new FrontendAnalogSettings(mFrequency, mAnalogType, mSifStandard);
- }
- }
- }
-
- /**
- * Frontend settings for ATSC.
- * @hide
- */
- public static class FrontendAtscSettings extends FrontendSettings {
- public int modulation;
-
- FrontendAtscSettings(int frequency) {
- super(frequency);
- }
-
- @Override
- public int getType() {
- return TunerConstants.FRONTEND_TYPE_ATSC;
- }
- }
-
- /**
- * Frontend settings for ATSC-3.
- * @hide
- */
- public static class FrontendAtsc3Settings extends FrontendSettings {
- public int bandwidth;
- public byte demodOutputFormat;
- public List<FrontendAtsc3PlpSettings> plpSettings;
-
- FrontendAtsc3Settings(int frequency) {
- super(frequency);
- }
-
- @Override
- public int getType() {
- return TunerConstants.FRONTEND_TYPE_ATSC3;
- }
- }
-
- /**
- * Frontend settings for DVBS.
- * @hide
- */
- public static class FrontendDvbsSettings extends FrontendSettings {
- public int modulation;
- public FrontendDvbsCodeRate coderate;
- public int symbolRate;
- public int rolloff;
- public int pilot;
- public int inputStreamId;
- public byte standard;
-
- FrontendDvbsSettings(int frequency) {
- super(frequency);
- }
-
- @Override
- public int getType() {
- return TunerConstants.FRONTEND_TYPE_DVBS;
- }
- }
-
- /**
- * Frontend settings for DVBC.
- * @hide
- */
- public static class FrontendDvbcSettings extends FrontendSettings {
- public int modulation;
- public long fec;
- public int symbolRate;
- public int outerFec;
- public byte annex;
- public int spectralInversion;
-
- FrontendDvbcSettings(int frequency) {
- super(frequency);
- }
-
- @Override
- public int getType() {
- return TunerConstants.FRONTEND_TYPE_DVBC;
- }
- }
-
- /**
- * Frontend settings for DVBT.
- * @hide
- */
- public static class FrontendDvbtSettings extends FrontendSettings {
- public int transmissionMode;
- public int bandwidth;
- public int constellation;
- public int hierarchy;
- public int hpCoderate;
- public int lpCoderate;
- public int guardInterval;
- public boolean isHighPriority;
- public byte standard;
- public boolean isMiso;
- public int plpMode;
- public byte plpId;
- public byte plpGroupId;
-
- FrontendDvbtSettings(int frequency) {
- super(frequency);
- }
-
- @Override
- public int getType() {
- return TunerConstants.FRONTEND_TYPE_DVBT;
- }
- }
-
- /**
- * Frontend settings for ISDBS.
- * @hide
- */
- public static class FrontendIsdbsSettings extends FrontendSettings {
- public int streamId;
- public int streamIdType;
- public int modulation;
- public int coderate;
- public int symbolRate;
- public int rolloff;
-
- FrontendIsdbsSettings(int frequency) {
- super(frequency);
- }
-
- @Override
- public int getType() {
- return TunerConstants.FRONTEND_TYPE_ISDBS;
- }
- }
-
- /**
- * Frontend settings for ISDBS-3.
- * @hide
- */
- public static class FrontendIsdbs3Settings extends FrontendSettings {
- public int streamId;
- public int streamIdType;
- public int modulation;
- public int coderate;
- public int symbolRate;
- public int rolloff;
-
- FrontendIsdbs3Settings(int frequency) {
- super(frequency);
- }
-
- @Override
- public int getType() {
- return TunerConstants.FRONTEND_TYPE_ISDBS3;
- }
- }
-
- /**
- * Frontend settings for ISDBT.
- * @hide
- */
- public static class FrontendIsdbtSettings extends FrontendSettings {
- public int modulation;
- public int bandwidth;
- public int coderate;
- public int guardInterval;
- public int serviceAreaId;
-
- FrontendIsdbtSettings(int frequency) {
- super(frequency);
- }
-
- @Override
- public int getType() {
- return TunerConstants.FRONTEND_TYPE_ISDBT;
- }
- }
-
- /**
- * PLP settings for ATSC-3.
- * @hide
- */
- public static class FrontendAtsc3PlpSettings {
- public byte plpId;
- public int modulation;
- public int interleaveMode;
- public int codeRate;
- public int fec;
- }
-
- /**
- * Code rate for DVBS.
- * @hide
- */
- public static class FrontendDvbsCodeRate {
- public long fec;
- public boolean isLinear;
- public boolean isShortFrames;
- public int bitsPer1000Symbol;
- }
}
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index b8ab7ee..43f9a89 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -21,7 +21,9 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
+import android.media.tv.tuner.FilterConfiguration.Settings;
import android.media.tv.tuner.TunerConstants.DemuxPidType;
+import android.media.tv.tuner.TunerConstants.FilterStatus;
import android.media.tv.tuner.TunerConstants.FilterSubtype;
import android.media.tv.tuner.TunerConstants.FilterType;
import android.media.tv.tuner.TunerConstants.FrontendScanType;
@@ -29,10 +31,15 @@
import android.media.tv.tuner.TunerConstants.LnbTone;
import android.media.tv.tuner.TunerConstants.LnbVoltage;
import android.media.tv.tuner.TunerConstants.Result;
+import android.media.tv.tuner.filter.FilterEvent;
+import android.media.tv.tuner.frontend.FrontendCallback;
+import android.media.tv.tuner.frontend.FrontendInfo;
+import android.media.tv.tuner.frontend.FrontendStatus;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import java.io.FileDescriptor;
import java.util.List;
/**
@@ -110,6 +117,11 @@
private native int nativeSetLnb(int lnbId);
private native int nativeSetLna(boolean enable);
private native FrontendStatus[] nativeGetFrontendStatus(int[] statusTypes);
+ private native int nativeGetAvSyncHwId(Filter filter);
+ private native long nativeGetAvSyncTime(int avSyncId);
+ private native int nativeConnectCiCam(int ciCamId);
+ private native int nativeDisconnectCiCam();
+ private native FrontendInfo nativeGetFrontendInfo(int id);
private native Filter nativeOpenFilter(int type, int subType, int bufferSize);
private native List<Integer> nativeGetLnbIds();
@@ -119,24 +131,7 @@
private native Dvr nativeOpenDvr(int type, int bufferSize);
- /**
- * Frontend Callback.
- *
- * @hide
- */
- public interface FrontendCallback {
-
- /**
- * Invoked when there is a frontend event.
- */
- void onEvent(int frontendEventType);
-
- /**
- * Invoked when there is a scan message.
- * @param msg
- */
- void onScanMessage(ScanMessage msg);
- }
+ private static native DemuxCapabilities nativeGetDemuxCapabilities();
/**
* LNB Callback.
@@ -160,19 +155,23 @@
}
/**
- * Frontend Callback.
- *
- * @hide
+ * Callback interface for receiving information from the corresponding filters.
*/
public interface FilterCallback {
/**
* Invoked when there are filter events.
+ *
+ * @param filter the corresponding filter which sent the events.
+ * @param events the filter events sent from the filter.
*/
- void onFilterEvent(FilterEvent[] events);
+ void onFilterEvent(@NonNull Filter filter, @NonNull FilterEvent[] events);
/**
* Invoked when filter status changed.
+ *
+ * @param filter the corresponding filter whose status is changed.
+ * @param status the new status of the filter.
*/
- void onFilterStatus(int status);
+ void onFilterStatusChanged(@NonNull Filter filter, @FilterStatus int status);
}
/**
@@ -218,7 +217,7 @@
case MSG_ON_FILTER_STATUS: {
Filter filter = (Filter) msg.obj;
if (filter.mCallback != null) {
- filter.mCallback.onFilterStatus(msg.arg1);
+ filter.mCallback.onFilterStatusChanged(filter, msg.arg1);
}
break;
}
@@ -351,6 +350,86 @@
return nativeGetFrontendStatus(statusTypes);
}
+ /**
+ * Gets hardware sync ID for audio and video.
+ *
+ * @param filter the filter instance for the hardware sync ID.
+ * @return the id of hardware A/V sync.
+ * @hide
+ */
+ public int getAvSyncHwId(Filter filter) {
+ return nativeGetAvSyncHwId(filter);
+ }
+ /**
+ * Gets the current timestamp for A/V sync
+ *
+ * The timestamp is maintained by hardware. The timestamp based on 90KHz, and it's format is the
+ * same as PTS (Presentation Time Stamp).
+ *
+ * @param avSyncHwId the hardware id of A/V sync.
+ * @return the current timestamp of hardware A/V sync.
+ * @hide
+ */
+ public long getAvSyncTime(int avSyncHwId) {
+ return nativeGetAvSyncTime(avSyncHwId);
+ }
+
+
+ /**
+ * Connects Conditional Access Modules (CAM) through Common Interface (CI)
+ *
+ * The demux uses the output from the frontend as the input by default, and must change to use
+ * the output from CI-CAM as the input after this call.
+ *
+ * @param ciCamId specify CI-CAM Id to connect.
+ * @return result status of the operation.
+ * @hide
+ */
+ @Result
+ public int connectCiCam(int ciCamId) {
+ return nativeConnectCiCam(ciCamId);
+ }
+
+ /**
+ * Disconnects Conditional Access Modules (CAM)
+ *
+ * The demux will use the output from the frontend as the input after this call.
+ *
+ * @return result status of the operation.
+ * @hide
+ */
+ @Result
+ public int disconnectCiCam() {
+ return nativeDisconnectCiCam();
+ }
+
+ /**
+ * Retrieve the frontend information.
+ * @hide
+ */
+ public FrontendInfo getFrontendInfo() {
+ if (mFrontend == null) {
+ throw new IllegalStateException("frontend is not initialized");
+ }
+ return nativeGetFrontendInfo(mFrontend.mId);
+ }
+
+ /**
+ * Gets frontend ID.
+ * @hide
+ */
+ public int getFrontendId() {
+ if (mFrontend == null) {
+ throw new IllegalStateException("frontend is not initialized");
+ }
+ return mFrontend.mId;
+ }
+
+ /** @hide */
+ private static DemuxCapabilities getDemuxCapabilities() {
+ return nativeGetDemuxCapabilities();
+ }
+
private List<Integer> getFrontendIds() {
mFrontendIds = nativeGetFrontendIds();
return mFrontendIds;
@@ -373,13 +452,18 @@
}
}
- /** @hide */
+ /**
+ * Tuner data filter.
+ *
+ * <p> This class is used to filter wanted data according to the filter's configuration.
+ */
public class Filter {
private long mNativeContext;
private FilterCallback mCallback;
int mId;
- private native int nativeConfigureFilter(int type, int subType, FilterSettings settings);
+ private native int nativeConfigureFilter(
+ int type, int subType, FilterConfiguration settings);
private native int nativeGetId();
private native int nativeSetDataSource(Filter source);
private native int nativeStartFilter();
@@ -404,11 +488,13 @@
*
* @param settings the settings of the filter.
* @return result status of the operation.
+ * @hide
*/
- public int configure(FilterSettings settings) {
+ public int configure(FilterConfiguration settings) {
int subType = -1;
- if (settings.mSettings != null) {
- subType = settings.mSettings.getType();
+ Settings s = settings.getSettings();
+ if (s != null) {
+ subType = s.getType();
}
return nativeConfigureFilter(settings.getType(), subType, settings);
}
@@ -417,6 +503,7 @@
* Gets the filter Id.
*
* @return the hardware resource Id for the filter.
+ * @hide
*/
public int getId() {
return nativeGetId();
@@ -433,6 +520,7 @@
* @param source the filter instance which provides data input. Switch to
* use demux as data source if the filter instance is NULL.
* @return result status of the operation.
+ * @hide
*/
public int setDataSource(@Nullable Filter source) {
return nativeSetDataSource(source);
@@ -442,6 +530,7 @@
* Starts the filter.
*
* @return result status of the operation.
+ * @hide
*/
public int start() {
return nativeStartFilter();
@@ -452,6 +541,7 @@
* Stops the filter.
*
* @return result status of the operation.
+ * @hide
*/
public int stop() {
return nativeStopFilter();
@@ -461,11 +551,13 @@
* Flushes the filter.
*
* @return result status of the operation.
+ * @hide
*/
public int flush() {
return nativeFlushFilter();
}
+ /** @hide */
public int read(@NonNull byte[] buffer, int offset, int size) {
size = Math.min(size, buffer.length - offset);
return nativeRead(buffer, offset, size);
@@ -475,6 +567,7 @@
* Release the Filter instance.
*
* @return result status of the operation.
+ * @hide
*/
public int close() {
return nativeClose();
@@ -708,6 +801,11 @@
private native int nativeStopDvr();
private native int nativeFlushDvr();
private native int nativeClose();
+ private native void nativeSetFileDescriptor(FileDescriptor fd);
+ private native int nativeRead(int size);
+ private native int nativeRead(byte[] bytes, int offset, int size);
+ private native int nativeWrite(int size);
+ private native int nativeWrite(byte[] bytes, int offset, int size);
private Dvr() {}
@@ -780,6 +878,45 @@
public int close() {
return nativeClose();
}
+
+ /**
+ * Sets file descriptor to read/write data.
+ */
+ public void setFileDescriptor(FileDescriptor fd) {
+ nativeSetFileDescriptor(fd);
+ }
+
+ /**
+ * Reads data from the file for DVR playback.
+ */
+ public int read(int size) {
+ return nativeRead(size);
+ }
+
+ /**
+ * Reads data from the buffer for DVR playback.
+ */
+ public int read(@NonNull byte[] bytes, int offset, int size) {
+ if (size + offset > bytes.length) {
+ throw new ArrayIndexOutOfBoundsException(
+ "Array length=" + bytes.length + ", offset=" + offset + ", size=" + size);
+ }
+ return nativeRead(bytes, offset, size);
+ }
+
+ /**
+ * Writes recording data to file.
+ */
+ public int write(int size) {
+ return nativeWrite(size);
+ }
+
+ /**
+ * Writes recording data to buffer.
+ */
+ public int write(@NonNull byte[] bytes, int offset, int size) {
+ return nativeWrite(bytes, offset, size);
+ }
}
private Dvr openDvr(int type, int bufferSize) {
diff --git a/media/java/android/media/tv/tuner/TunerConstants.java b/media/java/android/media/tv/tuner/TunerConstants.java
index e611431..e024432 100644
--- a/media/java/android/media/tv/tuner/TunerConstants.java
+++ b/media/java/android/media/tv/tuner/TunerConstants.java
@@ -18,151 +18,217 @@
import android.annotation.IntDef;
import android.annotation.LongDef;
+import android.annotation.SystemApi;
import android.hardware.tv.tuner.V1_0.Constants;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
+ * Constants for tuner framework.
+ *
* @hide
*/
-final class TunerConstants {
+@SystemApi
+public final class TunerConstants {
+ /** @hide */
public static final int INVALID_TS_PID = Constants.Constant.INVALID_TS_PID;
+ /** @hide */
public static final int INVALID_STREAM_ID = Constants.Constant.INVALID_STREAM_ID;
- @Retention(RetentionPolicy.SOURCE)
+ /** @hide */
@IntDef({FRONTEND_TYPE_UNDEFINED, FRONTEND_TYPE_ANALOG, FRONTEND_TYPE_ATSC, FRONTEND_TYPE_ATSC3,
FRONTEND_TYPE_DVBC, FRONTEND_TYPE_DVBS, FRONTEND_TYPE_DVBT, FRONTEND_TYPE_ISDBS,
FRONTEND_TYPE_ISDBS3, FRONTEND_TYPE_ISDBT})
+ @Retention(RetentionPolicy.SOURCE)
public @interface FrontendType {}
+ /** @hide */
public static final int FRONTEND_TYPE_UNDEFINED = Constants.FrontendType.UNDEFINED;
+ /** @hide */
public static final int FRONTEND_TYPE_ANALOG = Constants.FrontendType.ANALOG;
+ /** @hide */
public static final int FRONTEND_TYPE_ATSC = Constants.FrontendType.ATSC;
+ /** @hide */
public static final int FRONTEND_TYPE_ATSC3 = Constants.FrontendType.ATSC3;
+ /** @hide */
public static final int FRONTEND_TYPE_DVBC = Constants.FrontendType.DVBC;
+ /** @hide */
public static final int FRONTEND_TYPE_DVBS = Constants.FrontendType.DVBS;
+ /** @hide */
public static final int FRONTEND_TYPE_DVBT = Constants.FrontendType.DVBT;
+ /** @hide */
public static final int FRONTEND_TYPE_ISDBS = Constants.FrontendType.ISDBS;
+ /** @hide */
public static final int FRONTEND_TYPE_ISDBS3 = Constants.FrontendType.ISDBS3;
+ /** @hide */
public static final int FRONTEND_TYPE_ISDBT = Constants.FrontendType.ISDBT;
- @Retention(RetentionPolicy.SOURCE)
+ /** @hide */
@IntDef({FRONTEND_EVENT_TYPE_LOCKED, FRONTEND_EVENT_TYPE_NO_SIGNAL,
FRONTEND_EVENT_TYPE_LOST_LOCK})
+ @Retention(RetentionPolicy.SOURCE)
public @interface FrontendEventType {}
-
+ /** @hide */
public static final int FRONTEND_EVENT_TYPE_LOCKED = Constants.FrontendEventType.LOCKED;
+ /** @hide */
public static final int FRONTEND_EVENT_TYPE_NO_SIGNAL = Constants.FrontendEventType.NO_SIGNAL;
+ /** @hide */
public static final int FRONTEND_EVENT_TYPE_LOST_LOCK = Constants.FrontendEventType.LOST_LOCK;
- @Retention(RetentionPolicy.SOURCE)
+ /** @hide */
@IntDef({DATA_FORMAT_TS, DATA_FORMAT_PES, DATA_FORMAT_ES, DATA_FORMAT_SHV_TLV})
+ @Retention(RetentionPolicy.SOURCE)
public @interface DataFormat {}
-
+ /** @hide */
public static final int DATA_FORMAT_TS = Constants.DataFormat.TS;
+ /** @hide */
public static final int DATA_FORMAT_PES = Constants.DataFormat.PES;
+ /** @hide */
public static final int DATA_FORMAT_ES = Constants.DataFormat.ES;
+ /** @hide */
public static final int DATA_FORMAT_SHV_TLV = Constants.DataFormat.SHV_TLV;
- @Retention(RetentionPolicy.SOURCE)
+ /** @hide */
@IntDef({DEMUX_T_PID, DEMUX_MMPT_PID})
+ @Retention(RetentionPolicy.SOURCE)
public @interface DemuxPidType {}
-
+ /** @hide */
public static final int DEMUX_T_PID = 1;
+ /** @hide */
public static final int DEMUX_MMPT_PID = 2;
- @Retention(RetentionPolicy.SOURCE)
+ /** @hide */
@IntDef({FRONTEND_SETTINGS_ANALOG, FRONTEND_SETTINGS_ATSC, FRONTEND_SETTINGS_ATSC3,
FRONTEND_SETTINGS_DVBS, FRONTEND_SETTINGS_DVBC, FRONTEND_SETTINGS_DVBT,
FRONTEND_SETTINGS_ISDBS, FRONTEND_SETTINGS_ISDBS3, FRONTEND_SETTINGS_ISDBT})
+ @Retention(RetentionPolicy.SOURCE)
public @interface FrontendSettingsType {}
-
+ /** @hide */
public static final int FRONTEND_SETTINGS_ANALOG = 1;
+ /** @hide */
public static final int FRONTEND_SETTINGS_ATSC = 2;
+ /** @hide */
public static final int FRONTEND_SETTINGS_ATSC3 = 3;
+ /** @hide */
public static final int FRONTEND_SETTINGS_DVBS = 4;
+ /** @hide */
public static final int FRONTEND_SETTINGS_DVBC = 5;
+ /** @hide */
public static final int FRONTEND_SETTINGS_DVBT = 6;
+ /** @hide */
public static final int FRONTEND_SETTINGS_ISDBS = 7;
+ /** @hide */
public static final int FRONTEND_SETTINGS_ISDBS3 = 8;
+ /** @hide */
public static final int FRONTEND_SETTINGS_ISDBT = 9;
- @Retention(RetentionPolicy.SOURCE)
+ /** @hide */
@IntDef({FILTER_TYPE_TS, FILTER_TYPE_MMTP, FILTER_TYPE_IP, FILTER_TYPE_TLV, FILTER_TYPE_ALP})
+ @Retention(RetentionPolicy.SOURCE)
public @interface FilterType {}
-
+ /** @hide */
public static final int FILTER_TYPE_TS = Constants.DemuxFilterMainType.TS;
+ /** @hide */
public static final int FILTER_TYPE_MMTP = Constants.DemuxFilterMainType.MMTP;
+ /** @hide */
public static final int FILTER_TYPE_IP = Constants.DemuxFilterMainType.IP;
+ /** @hide */
public static final int FILTER_TYPE_TLV = Constants.DemuxFilterMainType.TLV;
+ /** @hide */
public static final int FILTER_TYPE_ALP = Constants.DemuxFilterMainType.ALP;
- @Retention(RetentionPolicy.SOURCE)
+ /** @hide */
@IntDef({FILTER_SUBTYPE_UNDEFINED, FILTER_SUBTYPE_SECTION, FILTER_SUBTYPE_PES,
FILTER_SUBTYPE_AUDIO, FILTER_SUBTYPE_VIDEO, FILTER_SUBTYPE_DOWNLOAD,
FILTER_SUBTYPE_RECORD, FILTER_SUBTYPE_TS, FILTER_SUBTYPE_PCR, FILTER_SUBTYPE_TEMI,
FILTER_SUBTYPE_MMPT, FILTER_SUBTYPE_NTP, FILTER_SUBTYPE_IP_PAYLOAD, FILTER_SUBTYPE_IP,
FILTER_SUBTYPE_PAYLOAD_THROUGH, FILTER_SUBTYPE_TLV, FILTER_SUBTYPE_PTP, })
+ @Retention(RetentionPolicy.SOURCE)
public @interface FilterSubtype {}
-
+ /** @hide */
public static final int FILTER_SUBTYPE_UNDEFINED = 0;
+ /** @hide */
public static final int FILTER_SUBTYPE_SECTION = 1;
+ /** @hide */
public static final int FILTER_SUBTYPE_PES = 2;
+ /** @hide */
public static final int FILTER_SUBTYPE_AUDIO = 3;
+ /** @hide */
public static final int FILTER_SUBTYPE_VIDEO = 4;
+ /** @hide */
public static final int FILTER_SUBTYPE_DOWNLOAD = 5;
+ /** @hide */
public static final int FILTER_SUBTYPE_RECORD = 6;
+ /** @hide */
public static final int FILTER_SUBTYPE_TS = 7;
+ /** @hide */
public static final int FILTER_SUBTYPE_PCR = 8;
+ /** @hide */
public static final int FILTER_SUBTYPE_TEMI = 9;
+ /** @hide */
public static final int FILTER_SUBTYPE_MMPT = 10;
+ /** @hide */
public static final int FILTER_SUBTYPE_NTP = 11;
+ /** @hide */
public static final int FILTER_SUBTYPE_IP_PAYLOAD = 12;
+ /** @hide */
public static final int FILTER_SUBTYPE_IP = 13;
+ /** @hide */
public static final int FILTER_SUBTYPE_PAYLOAD_THROUGH = 14;
+ /** @hide */
public static final int FILTER_SUBTYPE_TLV = 15;
+ /** @hide */
public static final int FILTER_SUBTYPE_PTP = 16;
+ /** @hide */
+ @IntDef({FILTER_STATUS_DATA_READY, FILTER_STATUS_LOW_WATER, FILTER_STATUS_HIGH_WATER,
+ FILTER_STATUS_OVERFLOW})
@Retention(RetentionPolicy.SOURCE)
- @IntDef({FRONTEND_SCAN_UNDEFINED, FRONTEND_SCAN_AUTO, FRONTEND_SCAN_BLIND})
- public @interface FrontendScanType {}
+ public @interface FilterStatus {}
+ /**
+ * The status of a filter that the data in the filter buffer is ready to be read.
+ */
+ public static final int FILTER_STATUS_DATA_READY = Constants.DemuxFilterStatus.DATA_READY;
+ /**
+ * The status of a filter that the amount of available data in the filter buffer is at low
+ * level.
+ *
+ * The value is set to 25 percent of the buffer size by default. It can be changed when
+ * configuring the filter.
+ */
+ public static final int FILTER_STATUS_LOW_WATER = Constants.DemuxFilterStatus.LOW_WATER;
+ /**
+ * The status of a filter that the amount of available data in the filter buffer is at high
+ * level.
+ * The value is set to 75 percent of the buffer size by default. It can be changed when
+ * configuring the filter.
+ */
+ public static final int FILTER_STATUS_HIGH_WATER = Constants.DemuxFilterStatus.HIGH_WATER;
+ /**
+ * The status of a filter that the filter buffer is full and newly filtered data is being
+ * discarded.
+ */
+ public static final int FILTER_STATUS_OVERFLOW = Constants.DemuxFilterStatus.OVERFLOW;
+
+ /** @hide */
+ @IntDef({FRONTEND_SCAN_UNDEFINED, FRONTEND_SCAN_AUTO, FRONTEND_SCAN_BLIND})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FrontendScanType {}
+ /** @hide */
public static final int FRONTEND_SCAN_UNDEFINED = Constants.FrontendScanType.SCAN_UNDEFINED;
+ /** @hide */
public static final int FRONTEND_SCAN_AUTO = Constants.FrontendScanType.SCAN_AUTO;
+ /** @hide */
public static final int FRONTEND_SCAN_BLIND = Constants.FrontendScanType.SCAN_BLIND;
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({SCAN_MESSAGE_TYPE_LOCKED, SCAN_MESSAGE_TYPE_END, SCAN_MESSAGE_TYPE_PROGRESS_PERCENT,
- SCAN_MESSAGE_TYPE_FREQUENCY, SCAN_MESSAGE_TYPE_SYMBOL_RATE, SCAN_MESSAGE_TYPE_PLP_IDS,
- SCAN_MESSAGE_TYPE_GROUP_IDS, SCAN_MESSAGE_TYPE_INPUT_STREAM_IDS,
- SCAN_MESSAGE_TYPE_STANDARD, SCAN_MESSAGE_TYPE_ATSC3_PLP_INFO})
- public @interface ScanMessageType {}
- public static final int SCAN_MESSAGE_TYPE_LOCKED = Constants.FrontendScanMessageType.LOCKED;
- public static final int SCAN_MESSAGE_TYPE_END = Constants.FrontendScanMessageType.END;
- public static final int SCAN_MESSAGE_TYPE_PROGRESS_PERCENT =
- Constants.FrontendScanMessageType.PROGRESS_PERCENT;
- public static final int SCAN_MESSAGE_TYPE_FREQUENCY =
- Constants.FrontendScanMessageType.FREQUENCY;
- public static final int SCAN_MESSAGE_TYPE_SYMBOL_RATE =
- Constants.FrontendScanMessageType.SYMBOL_RATE;
- public static final int SCAN_MESSAGE_TYPE_PLP_IDS = Constants.FrontendScanMessageType.PLP_IDS;
- public static final int SCAN_MESSAGE_TYPE_GROUP_IDS =
- Constants.FrontendScanMessageType.GROUP_IDS;
- public static final int SCAN_MESSAGE_TYPE_INPUT_STREAM_IDS =
- Constants.FrontendScanMessageType.INPUT_STREAM_IDS;
- public static final int SCAN_MESSAGE_TYPE_STANDARD =
- Constants.FrontendScanMessageType.STANDARD;
- public static final int SCAN_MESSAGE_TYPE_ATSC3_PLP_INFO =
- Constants.FrontendScanMessageType.ATSC3_PLP_INFO;
-
-
- @Retention(RetentionPolicy.SOURCE)
+ /** @hide */
@IntDef({FRONTEND_STATUS_TYPE_DEMOD_LOCK, FRONTEND_STATUS_TYPE_SNR, FRONTEND_STATUS_TYPE_BER,
FRONTEND_STATUS_TYPE_PER, FRONTEND_STATUS_TYPE_PRE_BER,
FRONTEND_STATUS_TYPE_SIGNAL_QUALITY, FRONTEND_STATUS_TYPE_SIGNAL_STRENGTH,
@@ -174,279 +240,342 @@
FRONTEND_STATUS_TYPE_LBER_CN, FRONTEND_STATUS_TYPE_XER_CN, FRONTEND_STATUS_TYPE_MER,
FRONTEND_STATUS_TYPE_FREQ_OFFSET, FRONTEND_STATUS_TYPE_HIERARCHY,
FRONTEND_STATUS_TYPE_RF_LOCK, FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO})
+ @Retention(RetentionPolicy.SOURCE)
public @interface FrontendStatusType {}
/**
* Lock status for Demod.
+ * @hide
*/
public static final int FRONTEND_STATUS_TYPE_DEMOD_LOCK =
Constants.FrontendStatusType.DEMOD_LOCK;
/**
* Signal to Noise Ratio.
+ * @hide
*/
public static final int FRONTEND_STATUS_TYPE_SNR = Constants.FrontendStatusType.SNR;
/**
* Bit Error Ratio.
+ * @hide
*/
public static final int FRONTEND_STATUS_TYPE_BER = Constants.FrontendStatusType.BER;
/**
* Packages Error Ratio.
+ * @hide
*/
public static final int FRONTEND_STATUS_TYPE_PER = Constants.FrontendStatusType.PER;
/**
* Bit Error Ratio before FEC.
+ * @hide
*/
public static final int FRONTEND_STATUS_TYPE_PRE_BER = Constants.FrontendStatusType.PRE_BER;
/**
* Signal Quality (0..100). Good data over total data in percent can be
* used as a way to present Signal Quality.
+ * @hide
*/
public static final int FRONTEND_STATUS_TYPE_SIGNAL_QUALITY =
Constants.FrontendStatusType.SIGNAL_QUALITY;
/**
* Signal Strength.
+ * @hide
*/
public static final int FRONTEND_STATUS_TYPE_SIGNAL_STRENGTH =
Constants.FrontendStatusType.SIGNAL_STRENGTH;
/**
* Symbol Rate.
+ * @hide
*/
public static final int FRONTEND_STATUS_TYPE_SYMBOL_RATE =
Constants.FrontendStatusType.SYMBOL_RATE;
/**
* Forward Error Correction Type.
+ * @hide
*/
public static final int FRONTEND_STATUS_TYPE_FEC = Constants.FrontendStatusType.FEC;
/**
* Modulation Type.
+ * @hide
*/
public static final int FRONTEND_STATUS_TYPE_MODULATION =
Constants.FrontendStatusType.MODULATION;
/**
* Spectral Inversion Type.
+ * @hide
*/
public static final int FRONTEND_STATUS_TYPE_SPECTRAL = Constants.FrontendStatusType.SPECTRAL;
/**
* LNB Voltage.
+ * @hide
*/
public static final int FRONTEND_STATUS_TYPE_LNB_VOLTAGE =
Constants.FrontendStatusType.LNB_VOLTAGE;
/**
* Physical Layer Pipe ID.
+ * @hide
*/
public static final int FRONTEND_STATUS_TYPE_PLP_ID = Constants.FrontendStatusType.PLP_ID;
/**
* Status for Emergency Warning Broadcasting System.
+ * @hide
*/
public static final int FRONTEND_STATUS_TYPE_EWBS = Constants.FrontendStatusType.EWBS;
/**
* Automatic Gain Control.
+ * @hide
*/
public static final int FRONTEND_STATUS_TYPE_AGC = Constants.FrontendStatusType.AGC;
/**
* Low Noise Amplifier.
+ * @hide
*/
public static final int FRONTEND_STATUS_TYPE_LNA = Constants.FrontendStatusType.LNA;
/**
* Error status by layer.
+ * @hide
*/
public static final int FRONTEND_STATUS_TYPE_LAYER_ERROR =
Constants.FrontendStatusType.LAYER_ERROR;
/**
* CN value by VBER.
+ * @hide
*/
public static final int FRONTEND_STATUS_TYPE_VBER_CN = Constants.FrontendStatusType.VBER_CN;
/**
* CN value by LBER.
+ * @hide
*/
public static final int FRONTEND_STATUS_TYPE_LBER_CN = Constants.FrontendStatusType.LBER_CN;
/**
* CN value by XER.
+ * @hide
*/
public static final int FRONTEND_STATUS_TYPE_XER_CN = Constants.FrontendStatusType.XER_CN;
/**
* Moduration Error Ratio.
+ * @hide
*/
public static final int FRONTEND_STATUS_TYPE_MER = Constants.FrontendStatusType.MER;
/**
* Difference between tuning frequency and actual locked frequency.
+ * @hide
*/
public static final int FRONTEND_STATUS_TYPE_FREQ_OFFSET =
Constants.FrontendStatusType.FREQ_OFFSET;
/**
* Hierarchy for DVBT.
+ * @hide
*/
public static final int FRONTEND_STATUS_TYPE_HIERARCHY = Constants.FrontendStatusType.HIERARCHY;
/**
* Lock status for RF.
+ * @hide
*/
public static final int FRONTEND_STATUS_TYPE_RF_LOCK = Constants.FrontendStatusType.RF_LOCK;
/**
* PLP information in a frequency band for ATSC3.0 frontend.
+ * @hide
*/
public static final int FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO =
Constants.FrontendStatusType.ATSC3_PLP_INFO;
-
- @Retention(RetentionPolicy.SOURCE)
+ /** @hide */
@LongDef({FEC_UNDEFINED, FEC_AUTO, FEC_1_2, FEC_1_3, FEC_1_4, FEC_1_5, FEC_2_3, FEC_2_5,
FEC_2_9, FEC_3_4, FEC_3_5, FEC_4_5, FEC_4_15, FEC_5_6, FEC_5_9, FEC_6_7, FEC_7_8,
FEC_7_9, FEC_7_15, FEC_8_9, FEC_8_15, FEC_9_10, FEC_9_20, FEC_11_15, FEC_11_20,
FEC_11_45, FEC_13_18, FEC_13_45, FEC_14_45, FEC_23_36, FEC_25_36, FEC_26_45, FEC_28_45,
FEC_29_45, FEC_31_45, FEC_32_45, FEC_77_90})
+ @Retention(RetentionPolicy.SOURCE)
public @interface FrontendInnerFec {}
/**
* FEC not defined
+ * @hide
*/
public static final long FEC_UNDEFINED = Constants.FrontendInnerFec.FEC_UNDEFINED;
/**
* hardware is able to detect and set FEC automatically
+ * @hide
*/
public static final long FEC_AUTO = Constants.FrontendInnerFec.AUTO;
/**
* 1/2 conv. code rate
+ * @hide
*/
public static final long FEC_1_2 = Constants.FrontendInnerFec.FEC_1_2;
/**
* 1/3 conv. code rate
+ * @hide
*/
public static final long FEC_1_3 = Constants.FrontendInnerFec.FEC_1_3;
/**
* 1/4 conv. code rate
+ * @hide
*/
public static final long FEC_1_4 = Constants.FrontendInnerFec.FEC_1_4;
/**
* 1/5 conv. code rate
+ * @hide
*/
public static final long FEC_1_5 = Constants.FrontendInnerFec.FEC_1_5;
/**
* 2/3 conv. code rate
+ * @hide
*/
public static final long FEC_2_3 = Constants.FrontendInnerFec.FEC_2_3;
/**
* 2/5 conv. code rate
+ * @hide
*/
public static final long FEC_2_5 = Constants.FrontendInnerFec.FEC_2_5;
/**
* 2/9 conv. code rate
+ * @hide
*/
public static final long FEC_2_9 = Constants.FrontendInnerFec.FEC_2_9;
/**
* 3/4 conv. code rate
+ * @hide
*/
public static final long FEC_3_4 = Constants.FrontendInnerFec.FEC_3_4;
/**
* 3/5 conv. code rate
+ * @hide
*/
public static final long FEC_3_5 = Constants.FrontendInnerFec.FEC_3_5;
/**
* 4/5 conv. code rate
+ * @hide
*/
public static final long FEC_4_5 = Constants.FrontendInnerFec.FEC_4_5;
/**
* 4/15 conv. code rate
+ * @hide
*/
public static final long FEC_4_15 = Constants.FrontendInnerFec.FEC_4_15;
/**
* 5/6 conv. code rate
+ * @hide
*/
public static final long FEC_5_6 = Constants.FrontendInnerFec.FEC_5_6;
/**
* 5/9 conv. code rate
+ * @hide
*/
public static final long FEC_5_9 = Constants.FrontendInnerFec.FEC_5_9;
/**
* 6/7 conv. code rate
+ * @hide
*/
public static final long FEC_6_7 = Constants.FrontendInnerFec.FEC_6_7;
/**
* 7/8 conv. code rate
+ * @hide
*/
public static final long FEC_7_8 = Constants.FrontendInnerFec.FEC_7_8;
/**
* 7/9 conv. code rate
+ * @hide
*/
public static final long FEC_7_9 = Constants.FrontendInnerFec.FEC_7_9;
/**
* 7/15 conv. code rate
+ * @hide
*/
public static final long FEC_7_15 = Constants.FrontendInnerFec.FEC_7_15;
/**
* 8/9 conv. code rate
+ * @hide
*/
public static final long FEC_8_9 = Constants.FrontendInnerFec.FEC_8_9;
/**
* 8/15 conv. code rate
+ * @hide
*/
public static final long FEC_8_15 = Constants.FrontendInnerFec.FEC_8_15;
/**
* 9/10 conv. code rate
+ * @hide
*/
public static final long FEC_9_10 = Constants.FrontendInnerFec.FEC_9_10;
/**
* 9/20 conv. code rate
+ * @hide
*/
public static final long FEC_9_20 = Constants.FrontendInnerFec.FEC_9_20;
/**
* 11/15 conv. code rate
+ * @hide
*/
public static final long FEC_11_15 = Constants.FrontendInnerFec.FEC_11_15;
/**
* 11/20 conv. code rate
+ * @hide
*/
public static final long FEC_11_20 = Constants.FrontendInnerFec.FEC_11_20;
/**
* 11/45 conv. code rate
+ * @hide
*/
public static final long FEC_11_45 = Constants.FrontendInnerFec.FEC_11_45;
/**
* 13/18 conv. code rate
+ * @hide
*/
public static final long FEC_13_18 = Constants.FrontendInnerFec.FEC_13_18;
/**
* 13/45 conv. code rate
+ * @hide
*/
public static final long FEC_13_45 = Constants.FrontendInnerFec.FEC_13_45;
/**
* 14/45 conv. code rate
+ * @hide
*/
public static final long FEC_14_45 = Constants.FrontendInnerFec.FEC_14_45;
/**
* 23/36 conv. code rate
+ * @hide
*/
public static final long FEC_23_36 = Constants.FrontendInnerFec.FEC_23_36;
/**
* 25/36 conv. code rate
+ * @hide
*/
public static final long FEC_25_36 = Constants.FrontendInnerFec.FEC_25_36;
/**
* 26/45 conv. code rate
+ * @hide
*/
public static final long FEC_26_45 = Constants.FrontendInnerFec.FEC_26_45;
/**
* 28/45 conv. code rate
+ * @hide
*/
public static final long FEC_28_45 = Constants.FrontendInnerFec.FEC_28_45;
/**
* 29/45 conv. code rate
+ * @hide
*/
public static final long FEC_29_45 = Constants.FrontendInnerFec.FEC_29_45;
/**
* 31/45 conv. code rate
+ * @hide
*/
public static final long FEC_31_45 = Constants.FrontendInnerFec.FEC_31_45;
/**
* 32/45 conv. code rate
+ * @hide
*/
public static final long FEC_32_45 = Constants.FrontendInnerFec.FEC_32_45;
/**
* 77/90 conv. code rate
+ * @hide
*/
public static final long FEC_77_90 = Constants.FrontendInnerFec.FEC_77_90;
- @Retention(RetentionPolicy.SOURCE)
+ /** @hide */
@IntDef({DVBC_MODULATION_UNDEFINED, DVBC_MODULATION_AUTO, DVBC_MODULATION_MOD_16QAM,
DVBC_MODULATION_MOD_32QAM, DVBC_MODULATION_MOD_64QAM, DVBC_MODULATION_MOD_128QAM,
DVBC_MODULATION_MOD_256QAM, DVBS_MODULATION_UNDEFINED, DVBS_MODULATION_AUTO,
@@ -461,166 +590,756 @@
ISDBS3_MODULATION_MOD_32APSK, ISDBT_MODULATION_UNDEFINED, ISDBT_MODULATION_AUTO,
ISDBT_MODULATION_MOD_DQPSK, ISDBT_MODULATION_MOD_QPSK, ISDBT_MODULATION_MOD_16QAM,
ISDBT_MODULATION_MOD_64QAM})
+ @Retention(RetentionPolicy.SOURCE)
public @interface FrontendModulation {}
-
+ /** @hide */
public static final int DVBC_MODULATION_UNDEFINED = Constants.FrontendDvbcModulation.UNDEFINED;
+ /** @hide */
public static final int DVBC_MODULATION_AUTO = Constants.FrontendDvbcModulation.AUTO;
+ /** @hide */
public static final int DVBC_MODULATION_MOD_16QAM = Constants.FrontendDvbcModulation.MOD_16QAM;
+ /** @hide */
public static final int DVBC_MODULATION_MOD_32QAM = Constants.FrontendDvbcModulation.MOD_32QAM;
+ /** @hide */
public static final int DVBC_MODULATION_MOD_64QAM = Constants.FrontendDvbcModulation.MOD_64QAM;
+ /** @hide */
public static final int DVBC_MODULATION_MOD_128QAM =
Constants.FrontendDvbcModulation.MOD_128QAM;
+ /** @hide */
public static final int DVBC_MODULATION_MOD_256QAM =
Constants.FrontendDvbcModulation.MOD_256QAM;
+ /** @hide */
public static final int DVBS_MODULATION_UNDEFINED = Constants.FrontendDvbsModulation.UNDEFINED;
+ /** @hide */
public static final int DVBS_MODULATION_AUTO = Constants.FrontendDvbsModulation.AUTO;
+ /** @hide */
public static final int DVBS_MODULATION_MOD_QPSK = Constants.FrontendDvbsModulation.MOD_QPSK;
+ /** @hide */
public static final int DVBS_MODULATION_MOD_8PSK = Constants.FrontendDvbsModulation.MOD_8PSK;
+ /** @hide */
public static final int DVBS_MODULATION_MOD_16QAM = Constants.FrontendDvbsModulation.MOD_16QAM;
+ /** @hide */
public static final int DVBS_MODULATION_MOD_16PSK = Constants.FrontendDvbsModulation.MOD_16PSK;
+ /** @hide */
public static final int DVBS_MODULATION_MOD_32PSK = Constants.FrontendDvbsModulation.MOD_32PSK;
+ /** @hide */
public static final int DVBS_MODULATION_MOD_ACM = Constants.FrontendDvbsModulation.MOD_ACM;
+ /** @hide */
public static final int DVBS_MODULATION_MOD_8APSK = Constants.FrontendDvbsModulation.MOD_8APSK;
+ /** @hide */
public static final int DVBS_MODULATION_MOD_16APSK =
Constants.FrontendDvbsModulation.MOD_16APSK;
+ /** @hide */
public static final int DVBS_MODULATION_MOD_32APSK =
Constants.FrontendDvbsModulation.MOD_32APSK;
+ /** @hide */
public static final int DVBS_MODULATION_MOD_64APSK =
Constants.FrontendDvbsModulation.MOD_64APSK;
+ /** @hide */
public static final int DVBS_MODULATION_MOD_128APSK =
Constants.FrontendDvbsModulation.MOD_128APSK;
+ /** @hide */
public static final int DVBS_MODULATION_MOD_256APSK =
Constants.FrontendDvbsModulation.MOD_256APSK;
+ /** @hide */
public static final int DVBS_MODULATION_MOD_RESERVED =
Constants.FrontendDvbsModulation.MOD_RESERVED;
+ /** @hide */
public static final int ISDBS_MODULATION_UNDEFINED =
Constants.FrontendIsdbsModulation.UNDEFINED;
+ /** @hide */
public static final int ISDBS_MODULATION_AUTO = Constants.FrontendIsdbsModulation.AUTO;
+ /** @hide */
public static final int ISDBS_MODULATION_MOD_BPSK = Constants.FrontendIsdbsModulation.MOD_BPSK;
+ /** @hide */
public static final int ISDBS_MODULATION_MOD_QPSK = Constants.FrontendIsdbsModulation.MOD_QPSK;
+ /** @hide */
public static final int ISDBS_MODULATION_MOD_TC8PSK =
Constants.FrontendIsdbsModulation.MOD_TC8PSK;
+ /** @hide */
public static final int ISDBS3_MODULATION_UNDEFINED =
Constants.FrontendIsdbs3Modulation.UNDEFINED;
+ /** @hide */
public static final int ISDBS3_MODULATION_AUTO = Constants.FrontendIsdbs3Modulation.AUTO;
+ /** @hide */
public static final int ISDBS3_MODULATION_MOD_BPSK =
Constants.FrontendIsdbs3Modulation.MOD_BPSK;
+ /** @hide */
public static final int ISDBS3_MODULATION_MOD_QPSK =
Constants.FrontendIsdbs3Modulation.MOD_QPSK;
+ /** @hide */
public static final int ISDBS3_MODULATION_MOD_8PSK =
Constants.FrontendIsdbs3Modulation.MOD_8PSK;
+ /** @hide */
public static final int ISDBS3_MODULATION_MOD_16APSK =
Constants.FrontendIsdbs3Modulation.MOD_16APSK;
+ /** @hide */
public static final int ISDBS3_MODULATION_MOD_32APSK =
Constants.FrontendIsdbs3Modulation.MOD_32APSK;
+ /** @hide */
public static final int ISDBT_MODULATION_UNDEFINED =
Constants.FrontendIsdbtModulation.UNDEFINED;
+ /** @hide */
public static final int ISDBT_MODULATION_AUTO = Constants.FrontendIsdbtModulation.AUTO;
+ /** @hide */
public static final int ISDBT_MODULATION_MOD_DQPSK =
Constants.FrontendIsdbtModulation.MOD_DQPSK;
+ /** @hide */
public static final int ISDBT_MODULATION_MOD_QPSK = Constants.FrontendIsdbtModulation.MOD_QPSK;
+ /** @hide */
public static final int ISDBT_MODULATION_MOD_16QAM =
Constants.FrontendIsdbtModulation.MOD_16QAM;
+ /** @hide */
public static final int ISDBT_MODULATION_MOD_64QAM =
Constants.FrontendIsdbtModulation.MOD_64QAM;
- @Retention(RetentionPolicy.SOURCE)
+ /** @hide */
@IntDef({SPECTRAL_INVERSION_UNDEFINED, SPECTRAL_INVERSION_NORMAL, SPECTRAL_INVERSION_INVERTED})
+ @Retention(RetentionPolicy.SOURCE)
public @interface FrontendDvbcSpectralInversion {}
-
+ /** @hide */
public static final int SPECTRAL_INVERSION_UNDEFINED =
Constants.FrontendDvbcSpectralInversion.UNDEFINED;
+ /** @hide */
public static final int SPECTRAL_INVERSION_NORMAL =
Constants.FrontendDvbcSpectralInversion.NORMAL;
+ /** @hide */
public static final int SPECTRAL_INVERSION_INVERTED =
Constants.FrontendDvbcSpectralInversion.INVERTED;
- @Retention(RetentionPolicy.SOURCE)
+ /** @hide */
@IntDef({HIERARCHY_UNDEFINED, HIERARCHY_AUTO, HIERARCHY_NON_NATIVE, HIERARCHY_1_NATIVE,
HIERARCHY_2_NATIVE, HIERARCHY_4_NATIVE, HIERARCHY_NON_INDEPTH, HIERARCHY_1_INDEPTH,
HIERARCHY_2_INDEPTH, HIERARCHY_4_INDEPTH})
+ @Retention(RetentionPolicy.SOURCE)
public @interface FrontendDvbtHierarchy {}
-
+ /** @hide */
public static final int HIERARCHY_UNDEFINED = Constants.FrontendDvbtHierarchy.UNDEFINED;
+ /** @hide */
public static final int HIERARCHY_AUTO = Constants.FrontendDvbtHierarchy.AUTO;
+ /** @hide */
public static final int HIERARCHY_NON_NATIVE =
Constants.FrontendDvbtHierarchy.HIERARCHY_NON_NATIVE;
+ /** @hide */
public static final int HIERARCHY_1_NATIVE = Constants.FrontendDvbtHierarchy.HIERARCHY_1_NATIVE;
+ /** @hide */
public static final int HIERARCHY_2_NATIVE = Constants.FrontendDvbtHierarchy.HIERARCHY_2_NATIVE;
+ /** @hide */
public static final int HIERARCHY_4_NATIVE = Constants.FrontendDvbtHierarchy.HIERARCHY_4_NATIVE;
+ /** @hide */
public static final int HIERARCHY_NON_INDEPTH =
Constants.FrontendDvbtHierarchy.HIERARCHY_NON_INDEPTH;
+ /** @hide */
public static final int HIERARCHY_1_INDEPTH =
Constants.FrontendDvbtHierarchy.HIERARCHY_1_INDEPTH;
+ /** @hide */
public static final int HIERARCHY_2_INDEPTH =
Constants.FrontendDvbtHierarchy.HIERARCHY_2_INDEPTH;
+ /** @hide */
public static final int HIERARCHY_4_INDEPTH =
Constants.FrontendDvbtHierarchy.HIERARCHY_4_INDEPTH;
-
+ /** @hide */
+ @IntDef({FRONTEND_ANALOG_TYPE_UNDEFINED, FRONTEND_ANALOG_TYPE_PAL, FRONTEND_ANALOG_TYPE_SECAM,
+ FRONTEND_ANALOG_TYPE_NTSC})
@Retention(RetentionPolicy.SOURCE)
+ public @interface FrontendAnalogType {}
+ /** @hide */
+ public static final int FRONTEND_ANALOG_TYPE_UNDEFINED = Constants.FrontendAnalogType.UNDEFINED;
+ /** @hide */
+ public static final int FRONTEND_ANALOG_TYPE_PAL = Constants.FrontendAnalogType.PAL;
+ /** @hide */
+ public static final int FRONTEND_ANALOG_TYPE_SECAM = Constants.FrontendAnalogType.SECAM;
+ /** @hide */
+ public static final int FRONTEND_ANALOG_TYPE_NTSC = Constants.FrontendAnalogType.NTSC;
+
+ /** @hide */
+ @IntDef({FRONTEND_ANALOG_SIF_UNDEFINED, FRONTEND_ANALOG_SIF_BG, FRONTEND_ANALOG_SIF_BG_A2,
+ FRONTEND_ANALOG_SIF_BG_NICAM, FRONTEND_ANALOG_SIF_I, FRONTEND_ANALOG_SIF_DK,
+ FRONTEND_ANALOG_SIF_DK1, FRONTEND_ANALOG_SIF_DK2, FRONTEND_ANALOG_SIF_DK3,
+ FRONTEND_ANALOG_SIF_DK_NICAM, FRONTEND_ANALOG_SIF_L, FRONTEND_ANALOG_SIF_M,
+ FRONTEND_ANALOG_SIF_M_BTSC, FRONTEND_ANALOG_SIF_M_A2, FRONTEND_ANALOG_SIF_M_EIA_J,
+ FRONTEND_ANALOG_SIF_I_NICAM, FRONTEND_ANALOG_SIF_L_NICAM, FRONTEND_ANALOG_SIF_L_PRIME})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FrontendAnalogSifStandard {}
+ /** @hide */
+ public static final int FRONTEND_ANALOG_SIF_UNDEFINED =
+ Constants.FrontendAnalogSifStandard.UNDEFINED;
+ /** @hide */
+ public static final int FRONTEND_ANALOG_SIF_BG = Constants.FrontendAnalogSifStandard.BG;
+ /** @hide */
+ public static final int FRONTEND_ANALOG_SIF_BG_A2 = Constants.FrontendAnalogSifStandard.BG_A2;
+ /** @hide */
+ public static final int FRONTEND_ANALOG_SIF_BG_NICAM =
+ Constants.FrontendAnalogSifStandard.BG_NICAM;
+ /** @hide */
+ public static final int FRONTEND_ANALOG_SIF_I = Constants.FrontendAnalogSifStandard.I;
+ /** @hide */
+ public static final int FRONTEND_ANALOG_SIF_DK = Constants.FrontendAnalogSifStandard.DK;
+ /** @hide */
+ public static final int FRONTEND_ANALOG_SIF_DK1 = Constants.FrontendAnalogSifStandard.DK1;
+ /** @hide */
+ public static final int FRONTEND_ANALOG_SIF_DK2 = Constants.FrontendAnalogSifStandard.DK2;
+ /** @hide */
+ public static final int FRONTEND_ANALOG_SIF_DK3 = Constants.FrontendAnalogSifStandard.DK3;
+ /** @hide */
+ public static final int FRONTEND_ANALOG_SIF_DK_NICAM =
+ Constants.FrontendAnalogSifStandard.DK_NICAM;
+ /** @hide */
+ public static final int FRONTEND_ANALOG_SIF_L = Constants.FrontendAnalogSifStandard.L;
+ /** @hide */
+ public static final int FRONTEND_ANALOG_SIF_M = Constants.FrontendAnalogSifStandard.M;
+ /** @hide */
+ public static final int FRONTEND_ANALOG_SIF_M_BTSC = Constants.FrontendAnalogSifStandard.M_BTSC;
+ /** @hide */
+ public static final int FRONTEND_ANALOG_SIF_M_A2 = Constants.FrontendAnalogSifStandard.M_A2;
+ /** @hide */
+ public static final int FRONTEND_ANALOG_SIF_M_EIA_J =
+ Constants.FrontendAnalogSifStandard.M_EIA_J;
+ /** @hide */
+ public static final int FRONTEND_ANALOG_SIF_I_NICAM =
+ Constants.FrontendAnalogSifStandard.I_NICAM;
+ /** @hide */
+ public static final int FRONTEND_ANALOG_SIF_L_NICAM =
+ Constants.FrontendAnalogSifStandard.L_NICAM;
+ /** @hide */
+ public static final int FRONTEND_ANALOG_SIF_L_PRIME =
+ Constants.FrontendAnalogSifStandard.L_PRIME;
+
+ /** @hide */
+ @IntDef({FRONTEND_ATSC_MODULATION_UNDEFINED, FRONTEND_ATSC_MODULATION_AUTO,
+ FRONTEND_ATSC_MODULATION_MOD_8VSB, FRONTEND_ATSC_MODULATION_MOD_16VSB})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FrontendAtscModulation {}
+ /** @hide */
+ public static final int FRONTEND_ATSC_MODULATION_UNDEFINED =
+ Constants.FrontendAtscModulation.UNDEFINED;
+ /** @hide */
+ public static final int FRONTEND_ATSC_MODULATION_AUTO = Constants.FrontendAtscModulation.AUTO;
+ /** @hide */
+ public static final int FRONTEND_ATSC_MODULATION_MOD_8VSB =
+ Constants.FrontendAtscModulation.MOD_8VSB;
+ /** @hide */
+ public static final int FRONTEND_ATSC_MODULATION_MOD_16VSB =
+ Constants.FrontendAtscModulation.MOD_16VSB;
+
+ /** @hide */
+ @IntDef({FRONTEND_ATSC3_BANDWIDTH_UNDEFINED, FRONTEND_ATSC3_BANDWIDTH_AUTO,
+ FRONTEND_ATSC3_BANDWIDTH_BANDWIDTH_6MHZ, FRONTEND_ATSC3_BANDWIDTH_BANDWIDTH_7MHZ,
+ FRONTEND_ATSC3_BANDWIDTH_BANDWIDTH_8MHZ})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FrontendAtsc3Bandwidth {}
+ /** @hide */
+ public static final int FRONTEND_ATSC3_BANDWIDTH_UNDEFINED =
+ Constants.FrontendAtsc3Bandwidth.UNDEFINED;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_BANDWIDTH_AUTO = Constants.FrontendAtsc3Bandwidth.AUTO;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_BANDWIDTH_BANDWIDTH_6MHZ =
+ Constants.FrontendAtsc3Bandwidth.BANDWIDTH_6MHZ;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_BANDWIDTH_BANDWIDTH_7MHZ =
+ Constants.FrontendAtsc3Bandwidth.BANDWIDTH_7MHZ;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_BANDWIDTH_BANDWIDTH_8MHZ =
+ Constants.FrontendAtsc3Bandwidth.BANDWIDTH_8MHZ;
+
+ /** @hide */
+ @IntDef({FRONTEND_ATSC3_MODULATION_UNDEFINED, FRONTEND_ATSC3_MODULATION_AUTO,
+ FRONTEND_ATSC3_MODULATION_MOD_QPSK, FRONTEND_ATSC3_MODULATION_MOD_16QAM,
+ FRONTEND_ATSC3_MODULATION_MOD_64QAM, FRONTEND_ATSC3_MODULATION_MOD_256QAM,
+ FRONTEND_ATSC3_MODULATION_MOD_1024QAM, FRONTEND_ATSC3_MODULATION_MOD_4096QAM})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FrontendAtsc3Modulation {}
+ /** @hide */
+ public static final int FRONTEND_ATSC3_MODULATION_UNDEFINED =
+ Constants.FrontendAtsc3Modulation.UNDEFINED;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_MODULATION_AUTO = Constants.FrontendAtsc3Modulation.AUTO;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_MODULATION_MOD_QPSK =
+ Constants.FrontendAtsc3Modulation.MOD_QPSK;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_MODULATION_MOD_16QAM =
+ Constants.FrontendAtsc3Modulation.MOD_16QAM;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_MODULATION_MOD_64QAM =
+ Constants.FrontendAtsc3Modulation.MOD_64QAM;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_MODULATION_MOD_256QAM =
+ Constants.FrontendAtsc3Modulation.MOD_256QAM;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_MODULATION_MOD_1024QAM =
+ Constants.FrontendAtsc3Modulation.MOD_1024QAM;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_MODULATION_MOD_4096QAM =
+ Constants.FrontendAtsc3Modulation.MOD_4096QAM;
+
+ /** @hide */
+ @IntDef({FRONTEND_ATSC3_TIME_INTERLEAVE_MODE_UNDEFINED,
+ FRONTEND_ATSC3_TIME_INTERLEAVE_MODE_AUTO, FRONTEND_ATSC3_TIME_INTERLEAVE_MODE_CTI,
+ FRONTEND_ATSC3_TIME_INTERLEAVE_MODE_HTI})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FrontendAtsc3TimeInterleaveMode {}
+ /** @hide */
+ public static final int FRONTEND_ATSC3_TIME_INTERLEAVE_MODE_UNDEFINED =
+ Constants.FrontendAtsc3TimeInterleaveMode.UNDEFINED;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_TIME_INTERLEAVE_MODE_AUTO =
+ Constants.FrontendAtsc3TimeInterleaveMode.AUTO;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_TIME_INTERLEAVE_MODE_CTI =
+ Constants.FrontendAtsc3TimeInterleaveMode.CTI;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_TIME_INTERLEAVE_MODE_HTI =
+ Constants.FrontendAtsc3TimeInterleaveMode.HTI;
+
+ /** @hide */
+ @IntDef({FRONTEND_ATSC3_CODERATE_UNDEFINED, FRONTEND_ATSC3_CODERATE_AUTO,
+ FRONTEND_ATSC3_CODERATE_2_15, FRONTEND_ATSC3_CODERATE_3_15,
+ FRONTEND_ATSC3_CODERATE_4_15, FRONTEND_ATSC3_CODERATE_5_15,
+ FRONTEND_ATSC3_CODERATE_6_15, FRONTEND_ATSC3_CODERATE_7_15,
+ FRONTEND_ATSC3_CODERATE_8_15, FRONTEND_ATSC3_CODERATE_9_15,
+ FRONTEND_ATSC3_CODERATE_10_15, FRONTEND_ATSC3_CODERATE_11_15,
+ FRONTEND_ATSC3_CODERATE_12_15, FRONTEND_ATSC3_CODERATE_13_15})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FrontendAtsc3CodeRate {}
+ /** @hide */
+ public static final int FRONTEND_ATSC3_CODERATE_UNDEFINED =
+ Constants.FrontendAtsc3CodeRate.UNDEFINED;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_CODERATE_AUTO = Constants.FrontendAtsc3CodeRate.AUTO;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_CODERATE_2_15 =
+ Constants.FrontendAtsc3CodeRate.CODERATE_2_15;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_CODERATE_3_15 =
+ Constants.FrontendAtsc3CodeRate.CODERATE_3_15;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_CODERATE_4_15 =
+ Constants.FrontendAtsc3CodeRate.CODERATE_4_15;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_CODERATE_5_15 =
+ Constants.FrontendAtsc3CodeRate.CODERATE_5_15;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_CODERATE_6_15 =
+ Constants.FrontendAtsc3CodeRate.CODERATE_6_15;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_CODERATE_7_15 =
+ Constants.FrontendAtsc3CodeRate.CODERATE_7_15;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_CODERATE_8_15 =
+ Constants.FrontendAtsc3CodeRate.CODERATE_8_15;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_CODERATE_9_15 =
+ Constants.FrontendAtsc3CodeRate.CODERATE_9_15;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_CODERATE_10_15 =
+ Constants.FrontendAtsc3CodeRate.CODERATE_10_15;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_CODERATE_11_15 =
+ Constants.FrontendAtsc3CodeRate.CODERATE_11_15;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_CODERATE_12_15 =
+ Constants.FrontendAtsc3CodeRate.CODERATE_12_15;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_CODERATE_13_15 =
+ Constants.FrontendAtsc3CodeRate.CODERATE_13_15;
+
+ /** @hide */
+ @IntDef({FRONTEND_ATSC3_FEC_UNDEFINED, FRONTEND_ATSC3_FEC_AUTO, FRONTEND_ATSC3_FEC_BCH_LDPC_16K,
+ FRONTEND_ATSC3_FEC_BCH_LDPC_64K, FRONTEND_ATSC3_FEC_CRC_LDPC_16K,
+ FRONTEND_ATSC3_FEC_CRC_LDPC_64K, FRONTEND_ATSC3_FEC_LDPC_16K,
+ FRONTEND_ATSC3_FEC_LDPC_64K})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FrontendAtsc3Fec {}
+ /** @hide */
+ public static final int FRONTEND_ATSC3_FEC_UNDEFINED = Constants.FrontendAtsc3Fec.UNDEFINED;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_FEC_AUTO = Constants.FrontendAtsc3Fec.AUTO;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_FEC_BCH_LDPC_16K =
+ Constants.FrontendAtsc3Fec.BCH_LDPC_16K;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_FEC_BCH_LDPC_64K =
+ Constants.FrontendAtsc3Fec.BCH_LDPC_64K;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_FEC_CRC_LDPC_16K =
+ Constants.FrontendAtsc3Fec.CRC_LDPC_16K;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_FEC_CRC_LDPC_64K =
+ Constants.FrontendAtsc3Fec.CRC_LDPC_64K;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_FEC_LDPC_16K = Constants.FrontendAtsc3Fec.LDPC_16K;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_FEC_LDPC_64K = Constants.FrontendAtsc3Fec.LDPC_64K;
+
+ /** @hide */
+ @IntDef({FRONTEND_ATSC3_DEMOD_OUTPUT_FORMAT_UNDEFINED,
+ FRONTEND_ATSC3_DEMOD_OUTPUT_FORMAT_ATSC3_LINKLAYER_PACKET,
+ FRONTEND_ATSC3_DEMOD_OUTPUT_FORMAT_BASEBAND_PACKET})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FrontendAtsc3DemodOutputFormat {}
+ /** @hide */
+ public static final int FRONTEND_ATSC3_DEMOD_OUTPUT_FORMAT_UNDEFINED =
+ Constants.FrontendAtsc3DemodOutputFormat.UNDEFINED;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_DEMOD_OUTPUT_FORMAT_ATSC3_LINKLAYER_PACKET =
+ Constants.FrontendAtsc3DemodOutputFormat.ATSC3_LINKLAYER_PACKET;
+ /** @hide */
+ public static final int FRONTEND_ATSC3_DEMOD_OUTPUT_FORMAT_BASEBAND_PACKET =
+ Constants.FrontendAtsc3DemodOutputFormat.BASEBAND_PACKET;
+
+ /** @hide */
+ @IntDef({FRONTEND_DVBS_STANDARD_AUTO, FRONTEND_DVBS_STANDARD_S, FRONTEND_DVBS_STANDARD_S2,
+ FRONTEND_DVBS_STANDARD_S2X})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FrontendDvbsStandard {}
+ /** @hide */
+ public static final int FRONTEND_DVBS_STANDARD_AUTO = Constants.FrontendDvbsStandard.AUTO;
+ /** @hide */
+ public static final int FRONTEND_DVBS_STANDARD_S = Constants.FrontendDvbsStandard.S;
+ /** @hide */
+ public static final int FRONTEND_DVBS_STANDARD_S2 = Constants.FrontendDvbsStandard.S2;
+ /** @hide */
+ public static final int FRONTEND_DVBS_STANDARD_S2X = Constants.FrontendDvbsStandard.S2X;
+
+ /** @hide */
+ @IntDef({FRONTEND_DVBC_ANNEX_UNDEFINED, FRONTEND_DVBC_ANNEX_A, FRONTEND_DVBC_ANNEX_B,
+ FRONTEND_DVBC_ANNEX_C})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FrontendDvbcAnnex {}
+ /** @hide */
+ public static final int FRONTEND_DVBC_ANNEX_UNDEFINED = Constants.FrontendDvbcAnnex.UNDEFINED;
+ /** @hide */
+ public static final int FRONTEND_DVBC_ANNEX_A = Constants.FrontendDvbcAnnex.A;
+ /** @hide */
+ public static final int FRONTEND_DVBC_ANNEX_B = Constants.FrontendDvbcAnnex.B;
+ /** @hide */
+ public static final int FRONTEND_DVBC_ANNEX_C = Constants.FrontendDvbcAnnex.C;
+
+ /** @hide */
+ @IntDef({FRONTEND_DVBT_TRANSMISSION_MODE_UNDEFINED, FRONTEND_DVBT_TRANSMISSION_MODE_AUTO,
+ FRONTEND_DVBT_TRANSMISSION_MODE_2K, FRONTEND_DVBT_TRANSMISSION_MODE_8K,
+ FRONTEND_DVBT_TRANSMISSION_MODE_4K, FRONTEND_DVBT_TRANSMISSION_MODE_1K,
+ FRONTEND_DVBT_TRANSMISSION_MODE_16K, FRONTEND_DVBT_TRANSMISSION_MODE_32K})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FrontendDvbtTransmissionMode {}
+ /** @hide */
+ public static final int FRONTEND_DVBT_TRANSMISSION_MODE_UNDEFINED =
+ Constants.FrontendDvbtTransmissionMode.UNDEFINED;
+ /** @hide */
+ public static final int FRONTEND_DVBT_TRANSMISSION_MODE_AUTO =
+ Constants.FrontendDvbtTransmissionMode.AUTO;
+ /** @hide */
+ public static final int FRONTEND_DVBT_TRANSMISSION_MODE_2K =
+ Constants.FrontendDvbtTransmissionMode.MODE_2K;
+ /** @hide */
+ public static final int FRONTEND_DVBT_TRANSMISSION_MODE_8K =
+ Constants.FrontendDvbtTransmissionMode.MODE_8K;
+ /** @hide */
+ public static final int FRONTEND_DVBT_TRANSMISSION_MODE_4K =
+ Constants.FrontendDvbtTransmissionMode.MODE_4K;
+ /** @hide */
+ public static final int FRONTEND_DVBT_TRANSMISSION_MODE_1K =
+ Constants.FrontendDvbtTransmissionMode.MODE_1K;
+ /** @hide */
+ public static final int FRONTEND_DVBT_TRANSMISSION_MODE_16K =
+ Constants.FrontendDvbtTransmissionMode.MODE_16K;
+ /** @hide */
+ public static final int FRONTEND_DVBT_TRANSMISSION_MODE_32K =
+ Constants.FrontendDvbtTransmissionMode.MODE_32K;
+
+ /** @hide */
+ @IntDef({FRONTEND_DVBT_BANDWIDTH_UNDEFINED, FRONTEND_DVBT_BANDWIDTH_AUTO,
+ FRONTEND_DVBT_BANDWIDTH_8MHZ, FRONTEND_DVBT_BANDWIDTH_7MHZ,
+ FRONTEND_DVBT_BANDWIDTH_6MHZ, FRONTEND_DVBT_BANDWIDTH_5MHZ,
+ FRONTEND_DVBT_BANDWIDTH_1_7MHZ, FRONTEND_DVBT_BANDWIDTH_10MHZ})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FrontendDvbtBandwidth {}
+ /** @hide */
+ public static final int FRONTEND_DVBT_BANDWIDTH_UNDEFINED =
+ Constants.FrontendDvbtBandwidth.UNDEFINED;
+ /** @hide */
+ public static final int FRONTEND_DVBT_BANDWIDTH_AUTO = Constants.FrontendDvbtBandwidth.AUTO;
+ /** @hide */
+ public static final int FRONTEND_DVBT_BANDWIDTH_8MHZ =
+ Constants.FrontendDvbtBandwidth.BANDWIDTH_8MHZ;
+ /** @hide */
+ public static final int FRONTEND_DVBT_BANDWIDTH_7MHZ =
+ Constants.FrontendDvbtBandwidth.BANDWIDTH_7MHZ;
+ /** @hide */
+ public static final int FRONTEND_DVBT_BANDWIDTH_6MHZ =
+ Constants.FrontendDvbtBandwidth.BANDWIDTH_6MHZ;
+ /** @hide */
+ public static final int FRONTEND_DVBT_BANDWIDTH_5MHZ =
+ Constants.FrontendDvbtBandwidth.BANDWIDTH_5MHZ;
+ /** @hide */
+ public static final int FRONTEND_DVBT_BANDWIDTH_1_7MHZ =
+ Constants.FrontendDvbtBandwidth.BANDWIDTH_1_7MHZ;
+ /** @hide */
+ public static final int FRONTEND_DVBT_BANDWIDTH_10MHZ =
+ Constants.FrontendDvbtBandwidth.BANDWIDTH_10MHZ;
+
+ /** @hide */
+ @IntDef({FRONTEND_DVBT_CONSTELLATION_UNDEFINED, FRONTEND_DVBT_CONSTELLATION_AUTO,
+ FRONTEND_DVBT_CONSTELLATION_CONSTELLATION_QPSK,
+ FRONTEND_DVBT_CONSTELLATION_CONSTELLATION_16QAM,
+ FRONTEND_DVBT_CONSTELLATION_CONSTELLATION_64QAM,
+ FRONTEND_DVBT_CONSTELLATION_CONSTELLATION_256QAM})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FrontendDvbtConstellation {}
+ /** @hide */
+ public static final int FRONTEND_DVBT_CONSTELLATION_UNDEFINED =
+ Constants.FrontendDvbtConstellation.UNDEFINED;
+ /** @hide */
+ public static final int FRONTEND_DVBT_CONSTELLATION_AUTO =
+ Constants.FrontendDvbtConstellation.AUTO;
+ /** @hide */
+ public static final int FRONTEND_DVBT_CONSTELLATION_CONSTELLATION_QPSK =
+ Constants.FrontendDvbtConstellation.CONSTELLATION_QPSK;
+ /** @hide */
+ public static final int FRONTEND_DVBT_CONSTELLATION_CONSTELLATION_16QAM =
+ Constants.FrontendDvbtConstellation.CONSTELLATION_16QAM;
+ /** @hide */
+ public static final int FRONTEND_DVBT_CONSTELLATION_CONSTELLATION_64QAM =
+ Constants.FrontendDvbtConstellation.CONSTELLATION_64QAM;
+ /** @hide */
+ public static final int FRONTEND_DVBT_CONSTELLATION_CONSTELLATION_256QAM =
+ Constants.FrontendDvbtConstellation.CONSTELLATION_256QAM;
+
+ /** @hide */
+ @IntDef({FRONTEND_DVBT_CODERATE_UNDEFINED, FRONTEND_DVBT_CODERATE_AUTO,
+ FRONTEND_DVBT_CODERATE_1_2, FRONTEND_DVBT_CODERATE_2_3, FRONTEND_DVBT_CODERATE_3_4,
+ FRONTEND_DVBT_CODERATE_5_6, FRONTEND_DVBT_CODERATE_7_8, FRONTEND_DVBT_CODERATE_3_5,
+ FRONTEND_DVBT_CODERATE_4_5, FRONTEND_DVBT_CODERATE_6_7, FRONTEND_DVBT_CODERATE_8_9})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FrontendDvbtCoderate {}
+ /** @hide */
+ public static final int FRONTEND_DVBT_CODERATE_UNDEFINED =
+ Constants.FrontendDvbtCoderate.UNDEFINED;
+ /** @hide */
+ public static final int FRONTEND_DVBT_CODERATE_AUTO = Constants.FrontendDvbtCoderate.AUTO;
+ /** @hide */
+ public static final int FRONTEND_DVBT_CODERATE_1_2 =
+ Constants.FrontendDvbtCoderate.CODERATE_1_2;
+ /** @hide */
+ public static final int FRONTEND_DVBT_CODERATE_2_3 =
+ Constants.FrontendDvbtCoderate.CODERATE_2_3;
+ /** @hide */
+ public static final int FRONTEND_DVBT_CODERATE_3_4 =
+ Constants.FrontendDvbtCoderate.CODERATE_3_4;
+ /** @hide */
+ public static final int FRONTEND_DVBT_CODERATE_5_6 =
+ Constants.FrontendDvbtCoderate.CODERATE_5_6;
+ /** @hide */
+ public static final int FRONTEND_DVBT_CODERATE_7_8 =
+ Constants.FrontendDvbtCoderate.CODERATE_7_8;
+ /** @hide */
+ public static final int FRONTEND_DVBT_CODERATE_3_5 =
+ Constants.FrontendDvbtCoderate.CODERATE_3_5;
+ /** @hide */
+ public static final int FRONTEND_DVBT_CODERATE_4_5 =
+ Constants.FrontendDvbtCoderate.CODERATE_4_5;
+ /** @hide */
+ public static final int FRONTEND_DVBT_CODERATE_6_7 =
+ Constants.FrontendDvbtCoderate.CODERATE_6_7;
+ /** @hide */
+ public static final int FRONTEND_DVBT_CODERATE_8_9 =
+ Constants.FrontendDvbtCoderate.CODERATE_8_9;
+
+ /** @hide */
+ @IntDef({FRONTEND_DVBT_GUARD_INTERVAL_UNDEFINED, FRONTEND_DVBT_GUARD_INTERVAL_AUTO,
+ FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_1_32, FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_1_16,
+ FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_1_8, FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_1_4,
+ FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_1_128,
+ FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_19_128,
+ FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_19_256})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FrontendDvbtGuardInterval {}
+ /** @hide */
+ public static final int FRONTEND_DVBT_GUARD_INTERVAL_UNDEFINED =
+ Constants.FrontendDvbtGuardInterval.UNDEFINED;
+ /** @hide */
+ public static final int FRONTEND_DVBT_GUARD_INTERVAL_AUTO =
+ Constants.FrontendDvbtGuardInterval.AUTO;
+ /** @hide */
+ public static final int FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_1_32 =
+ Constants.FrontendDvbtGuardInterval.INTERVAL_1_32;
+ /** @hide */
+ public static final int FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_1_16 =
+ Constants.FrontendDvbtGuardInterval.INTERVAL_1_16;
+ /** @hide */
+ public static final int FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_1_8 =
+ Constants.FrontendDvbtGuardInterval.INTERVAL_1_8;
+ /** @hide */
+ public static final int FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_1_4 =
+ Constants.FrontendDvbtGuardInterval.INTERVAL_1_4;
+ /** @hide */
+ public static final int FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_1_128 =
+ Constants.FrontendDvbtGuardInterval.INTERVAL_1_128;
+ /** @hide */
+ public static final int FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_19_128 =
+ Constants.FrontendDvbtGuardInterval.INTERVAL_19_128;
+ /** @hide */
+ public static final int FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_19_256 =
+ Constants.FrontendDvbtGuardInterval.INTERVAL_19_256;
+
+ /** @hide */
+ @IntDef({FRONTEND_ISDBS_CODERATE_UNDEFINED, FRONTEND_ISDBS_CODERATE_AUTO,
+ FRONTEND_ISDBS_CODERATE_1_2, FRONTEND_ISDBS_CODERATE_2_3, FRONTEND_ISDBS_CODERATE_3_4,
+ FRONTEND_ISDBS_CODERATE_5_6, FRONTEND_ISDBS_CODERATE_7_8})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FrontendIsdbsCoderate {}
+ /** @hide */
+ public static final int FRONTEND_ISDBS_CODERATE_UNDEFINED =
+ Constants.FrontendIsdbsCoderate.UNDEFINED;
+ /** @hide */
+ public static final int FRONTEND_ISDBS_CODERATE_AUTO = Constants.FrontendIsdbsCoderate.AUTO;
+ /** @hide */
+ public static final int FRONTEND_ISDBS_CODERATE_1_2 =
+ Constants.FrontendIsdbsCoderate.CODERATE_1_2;
+ /** @hide */
+ public static final int FRONTEND_ISDBS_CODERATE_2_3 =
+ Constants.FrontendIsdbsCoderate.CODERATE_2_3;
+ /** @hide */
+ public static final int FRONTEND_ISDBS_CODERATE_3_4 =
+ Constants.FrontendIsdbsCoderate.CODERATE_3_4;
+ /** @hide */
+ public static final int FRONTEND_ISDBS_CODERATE_5_6 =
+ Constants.FrontendIsdbsCoderate.CODERATE_5_6;
+ /** @hide */
+ public static final int FRONTEND_ISDBS_CODERATE_7_8 =
+ Constants.FrontendIsdbsCoderate.CODERATE_7_8;
+
+ /** @hide */
+ @IntDef({FRONTEND_ISDBT_MODE_UNDEFINED, FRONTEND_ISDBT_MODE_AUTO, FRONTEND_ISDBT_MODE_1,
+ FRONTEND_ISDBT_MODE_2, FRONTEND_ISDBT_MODE_3})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FrontendIsdbtMode {}
+ /** @hide */
+ public static final int FRONTEND_ISDBT_MODE_UNDEFINED = Constants.FrontendIsdbtMode.UNDEFINED;
+ /** @hide */
+ public static final int FRONTEND_ISDBT_MODE_AUTO = Constants.FrontendIsdbtMode.AUTO;
+ /** @hide */
+ public static final int FRONTEND_ISDBT_MODE_1 = Constants.FrontendIsdbtMode.MODE_1;
+ /** @hide */
+ public static final int FRONTEND_ISDBT_MODE_2 = Constants.FrontendIsdbtMode.MODE_2;
+ /** @hide */
+ public static final int FRONTEND_ISDBT_MODE_3 = Constants.FrontendIsdbtMode.MODE_3;
+
+ /** @hide */
+ @IntDef({FRONTEND_ISDBT_BANDWIDTH_UNDEFINED, FRONTEND_ISDBT_BANDWIDTH_AUTO,
+ FRONTEND_ISDBT_BANDWIDTH_8MHZ, FRONTEND_ISDBT_BANDWIDTH_7MHZ,
+ FRONTEND_ISDBT_BANDWIDTH_6MHZ})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FrontendIsdbtBandwidth {}
+ /** @hide */
+ public static final int FRONTEND_ISDBT_BANDWIDTH_UNDEFINED =
+ Constants.FrontendIsdbtBandwidth.UNDEFINED;
+ /** @hide */
+ public static final int FRONTEND_ISDBT_BANDWIDTH_AUTO = Constants.FrontendIsdbtBandwidth.AUTO;
+ /** @hide */
+ public static final int FRONTEND_ISDBT_BANDWIDTH_8MHZ =
+ Constants.FrontendIsdbtBandwidth.BANDWIDTH_8MHZ;
+ /** @hide */
+ public static final int FRONTEND_ISDBT_BANDWIDTH_7MHZ =
+ Constants.FrontendIsdbtBandwidth.BANDWIDTH_7MHZ;
+ /** @hide */
+ public static final int FRONTEND_ISDBT_BANDWIDTH_6MHZ =
+ Constants.FrontendIsdbtBandwidth.BANDWIDTH_6MHZ;
+
+ /** @hide */
@IntDef({FILTER_SETTINGS_TS, FILTER_SETTINGS_MMTP, FILTER_SETTINGS_IP, FILTER_SETTINGS_TLV,
FILTER_SETTINGS_ALP})
+ @Retention(RetentionPolicy.SOURCE)
public @interface FilterSettingsType {}
-
+ /** @hide */
public static final int FILTER_SETTINGS_TS = Constants.DemuxFilterMainType.TS;
+ /** @hide */
public static final int FILTER_SETTINGS_MMTP = Constants.DemuxFilterMainType.MMTP;
+ /** @hide */
public static final int FILTER_SETTINGS_IP = Constants.DemuxFilterMainType.IP;
+ /** @hide */
public static final int FILTER_SETTINGS_TLV = Constants.DemuxFilterMainType.TLV;
+ /** @hide */
public static final int FILTER_SETTINGS_ALP = Constants.DemuxFilterMainType.ALP;
- @Retention(RetentionPolicy.SOURCE)
+ /** @hide */
@IntDef({DVR_SETTINGS_RECORD, DVR_SETTINGS_PLAYBACK})
+ @Retention(RetentionPolicy.SOURCE)
public @interface DvrSettingsType {}
-
+ /** @hide */
public static final int DVR_SETTINGS_RECORD = Constants.DvrType.RECORD;
+ /** @hide */
public static final int DVR_SETTINGS_PLAYBACK = Constants.DvrType.PLAYBACK;
- @Retention(RetentionPolicy.SOURCE)
+ /** @hide */
@IntDef({LNB_VOLTAGE_NONE, LNB_VOLTAGE_5V, LNB_VOLTAGE_11V, LNB_VOLTAGE_12V, LNB_VOLTAGE_13V,
LNB_VOLTAGE_14V, LNB_VOLTAGE_15V, LNB_VOLTAGE_18V, LNB_VOLTAGE_19V})
+ @Retention(RetentionPolicy.SOURCE)
public @interface LnbVoltage {}
-
+ /** @hide */
public static final int LNB_VOLTAGE_NONE = Constants.LnbVoltage.NONE;
+ /** @hide */
public static final int LNB_VOLTAGE_5V = Constants.LnbVoltage.VOLTAGE_5V;
+ /** @hide */
public static final int LNB_VOLTAGE_11V = Constants.LnbVoltage.VOLTAGE_11V;
+ /** @hide */
public static final int LNB_VOLTAGE_12V = Constants.LnbVoltage.VOLTAGE_12V;
+ /** @hide */
public static final int LNB_VOLTAGE_13V = Constants.LnbVoltage.VOLTAGE_13V;
+ /** @hide */
public static final int LNB_VOLTAGE_14V = Constants.LnbVoltage.VOLTAGE_14V;
+ /** @hide */
public static final int LNB_VOLTAGE_15V = Constants.LnbVoltage.VOLTAGE_15V;
+ /** @hide */
public static final int LNB_VOLTAGE_18V = Constants.LnbVoltage.VOLTAGE_18V;
+ /** @hide */
public static final int LNB_VOLTAGE_19V = Constants.LnbVoltage.VOLTAGE_19V;
- @Retention(RetentionPolicy.SOURCE)
+ /** @hide */
@IntDef({LNB_TONE_NONE, LNB_TONE_CONTINUOUS})
+ @Retention(RetentionPolicy.SOURCE)
public @interface LnbTone {}
-
+ /** @hide */
public static final int LNB_TONE_NONE = Constants.LnbTone.NONE;
+ /** @hide */
public static final int LNB_TONE_CONTINUOUS = Constants.LnbTone.CONTINUOUS;
- @Retention(RetentionPolicy.SOURCE)
+ /** @hide */
@IntDef({LNB_POSITION_UNDEFINED, LNB_POSITION_A, LNB_POSITION_B})
+ @Retention(RetentionPolicy.SOURCE)
public @interface LnbPosition {}
-
+ /** @hide */
public static final int LNB_POSITION_UNDEFINED = Constants.LnbPosition.UNDEFINED;
+ /** @hide */
public static final int LNB_POSITION_A = Constants.LnbPosition.POSITION_A;
+ /** @hide */
public static final int LNB_POSITION_B = Constants.LnbPosition.POSITION_B;
- @Retention(RetentionPolicy.SOURCE)
+ /** @hide */
@IntDef({RESULT_SUCCESS, RESULT_UNAVAILABLE, RESULT_NOT_INITIALIZED, RESULT_INVALID_STATE,
RESULT_INVALID_ARGUMENT, RESULT_OUT_OF_MEMORY, RESULT_UNKNOWN_ERROR})
+ @Retention(RetentionPolicy.SOURCE)
public @interface Result {}
-
+ /** @hide */
public static final int RESULT_SUCCESS = Constants.Result.SUCCESS;
+ /** @hide */
public static final int RESULT_UNAVAILABLE = Constants.Result.UNAVAILABLE;
+ /** @hide */
public static final int RESULT_NOT_INITIALIZED = Constants.Result.NOT_INITIALIZED;
+ /** @hide */
public static final int RESULT_INVALID_STATE = Constants.Result.INVALID_STATE;
+ /** @hide */
public static final int RESULT_INVALID_ARGUMENT = Constants.Result.INVALID_ARGUMENT;
+ /** @hide */
public static final int RESULT_OUT_OF_MEMORY = Constants.Result.OUT_OF_MEMORY;
+ /** @hide */
public static final int RESULT_UNKNOWN_ERROR = Constants.Result.UNKNOWN_ERROR;
private TunerConstants() {
diff --git a/media/java/android/media/tv/tuner/TunerUtils.java b/media/java/android/media/tv/tuner/TunerUtils.java
index b3bd780..a7ccb02 100644
--- a/media/java/android/media/tv/tuner/TunerUtils.java
+++ b/media/java/android/media/tv/tuner/TunerUtils.java
@@ -30,14 +30,26 @@
public final class TunerUtils {
private static final String PERMISSION = android.Manifest.permission.ACCESS_TV_TUNER;
- static void checkTunerPermission(Context context) {
+ /**
+ * Checks whether the caller has permission to access tuner.
+ *
+ * @param context context of the caller.
+ * @throws SecurityException if the caller doesn't have the permission.
+ */
+ public static void checkTunerPermission(Context context) {
if (context.checkCallingOrSelfPermission(PERMISSION)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Caller must have " + PERMISSION + " permission.");
}
}
- static int getFilterSubtype(@FilterType int mainType, @FilterSubtype int subtype) {
+ /**
+ * Gets the corresponding filter subtype constant defined in tuner HAL.
+ *
+ * @param mainType filter main type.
+ * @param subtype filter subtype.
+ */
+ public static int getFilterSubtype(@FilterType int mainType, @FilterSubtype int subtype) {
if (mainType == TunerConstants.FILTER_TYPE_TS) {
switch (subtype) {
case TunerConstants.FILTER_SUBTYPE_UNDEFINED:
diff --git a/media/java/android/media/tv/tuner/filter/AudioExtraMetaData.java b/media/java/android/media/tv/tuner/filter/AudioExtraMetaData.java
new file mode 100644
index 0000000..306de84
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/AudioExtraMetaData.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.filter;
+
+/**
+ * Extra Meta Data from AD (Audio Descriptor) according to
+ * ETSI TS 101 154 V2.1.1.
+ * @hide
+ */
+public class AudioExtraMetaData {
+ private byte mAdFade;
+ private byte mAdPan;
+ private byte mVersionTextTag;
+ private byte mAdGainCenter;
+ private byte mAdGainFront;
+ private byte mAdGainSurround;
+}
diff --git a/media/java/android/media/tv/tuner/filter/DownloadEvent.java b/media/java/android/media/tv/tuner/filter/DownloadEvent.java
new file mode 100644
index 0000000..548fa77
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/DownloadEvent.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.filter;
+
+/**
+ * Download event.
+ * @hide
+ */
+public class DownloadEvent extends FilterEvent {
+ private int mItemId;
+ private int mMpuSequenceNumber;
+ private int mItemFragmentIndex;
+ private int mLastItemFragmentIndex;
+ private int mDataLength;
+}
+
diff --git a/media/java/android/media/tv/tuner/filter/FilterEvent.java b/media/java/android/media/tv/tuner/filter/FilterEvent.java
new file mode 100644
index 0000000..56a77d4
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/FilterEvent.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.filter;
+
+import android.annotation.SystemApi;
+
+/**
+ * An entity class that is passed to the filter callbacks.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class FilterEvent {
+}
diff --git a/media/java/android/media/tv/tuner/filter/IpPayloadEvent.java b/media/java/android/media/tv/tuner/filter/IpPayloadEvent.java
new file mode 100644
index 0000000..4da1d21
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/IpPayloadEvent.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.filter;
+
+/**
+ * IP payload event.
+ * @hide
+ */
+public class IpPayloadEvent extends FilterEvent {
+ private int mDataLength;
+}
diff --git a/media/java/android/media/tv/tuner/filter/MediaEvent.java b/media/java/android/media/tv/tuner/filter/MediaEvent.java
new file mode 100644
index 0000000..7703248
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/MediaEvent.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.filter;
+
+import android.os.NativeHandle;
+
+/**
+ * Media event.
+ * @hide
+ */
+public class MediaEvent extends FilterEvent {
+ private int mStreamId;
+ private boolean mIsPtsPresent;
+ private long mPts;
+ private int mDataLength;
+ private NativeHandle mHandle;
+ private boolean mIsSecureMemory;
+ private int mMpuSequenceNumber;
+ private boolean mIsPrivateData;
+ private AudioExtraMetaData mExtraMetaData;
+}
diff --git a/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java b/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
new file mode 100644
index 0000000..dbd8c77
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.filter;
+
+/**
+ * MMPT record event.
+ * @hide
+ */
+public class MmtpRecordEvent extends FilterEvent {
+ private int mScHevcIndexMask;
+ private long mByteNumber;
+}
diff --git a/media/java/android/media/tv/tuner/filter/PesEvent.java b/media/java/android/media/tv/tuner/filter/PesEvent.java
new file mode 100644
index 0000000..16536e2
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/PesEvent.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.filter;
+
+/**
+ * PES event.
+ * @hide
+ */
+public class PesEvent extends FilterEvent {
+ private int mStreamId;
+ private int mDataLength;
+ private int mMpuSequenceNumber;
+}
diff --git a/media/java/android/media/tv/tuner/filter/SectionEvent.java b/media/java/android/media/tv/tuner/filter/SectionEvent.java
new file mode 100644
index 0000000..e211dda
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/SectionEvent.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.filter;
+
+import android.annotation.SystemApi;
+import android.media.tv.tuner.Tuner.Filter;
+
+/**
+ * Filter event sent from {@link Filter} objects with section type.
+ *
+ * @hide
+ */
+@SystemApi
+public class SectionEvent extends FilterEvent {
+ private final int mTableId;
+ private final int mVersion;
+ private final int mSectionNum;
+ private final int mDataLength;
+
+ // This constructor is used by JNI code only
+ private SectionEvent(int tableId, int version, int sectionNum, int dataLength) {
+ mTableId = tableId;
+ mVersion = version;
+ mSectionNum = sectionNum;
+ mDataLength = dataLength;
+ }
+
+ /**
+ * Gets table ID of filtered data.
+ */
+ public int getTableId() {
+ return mTableId;
+ }
+
+ /**
+ * Gets version number of filtered data.
+ */
+ public int getVersion() {
+ return mVersion;
+ }
+
+ /**
+ * Gets section number of filtered data.
+ */
+ public int getSectionNumber() {
+ return mSectionNum;
+ }
+
+ /**
+ * Gets data size in bytes of filtered data.
+ */
+ public int getDataLength() {
+ return mDataLength;
+ }
+}
diff --git a/media/java/android/media/tv/tuner/filter/TemiEvent.java b/media/java/android/media/tv/tuner/filter/TemiEvent.java
new file mode 100644
index 0000000..3841604
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/TemiEvent.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.filter;
+
+/**
+ * TEMI event.
+ * @hide
+ */
+public class TemiEvent extends FilterEvent {
+ private long mPts;
+ private byte mDescrTag;
+ private byte[] mDescrData;
+}
diff --git a/media/java/android/media/tv/tuner/filter/TsRecordEvent.java b/media/java/android/media/tv/tuner/filter/TsRecordEvent.java
new file mode 100644
index 0000000..875b5bd
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/TsRecordEvent.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.filter;
+
+/**
+ * TS record event.
+ * @hide
+ */
+public class TsRecordEvent extends FilterEvent {
+ private int mTpid;
+ private int mIndexMask;
+ private long mByteNumber;
+}
diff --git a/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java
new file mode 100644
index 0000000..16308ce
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.frontend;
+
+import android.media.tv.tuner.FrontendSettings;
+import android.media.tv.tuner.TunerConstants;
+
+/**
+ * Frontend settings for analog.
+ * @hide
+ */
+public class AnalogFrontendSettings extends FrontendSettings {
+ private int mAnalogType;
+ private int mSifStandard;
+
+ @Override
+ public int getType() {
+ return TunerConstants.FRONTEND_TYPE_ANALOG;
+ }
+
+ public int getAnalogType() {
+ return mAnalogType;
+ }
+
+ public int getSifStandard() {
+ return mSifStandard;
+ }
+
+ /**
+ * Creates a new builder object.
+ */
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ private AnalogFrontendSettings(int frequency, int analogType, int sifStandard) {
+ super(frequency);
+ mAnalogType = analogType;
+ mSifStandard = sifStandard;
+ }
+
+ /**
+ * Builder for FrontendAnalogSettings.
+ */
+ public static class Builder {
+ private int mFrequency;
+ private int mAnalogType;
+ private int mSifStandard;
+
+ private Builder() {}
+
+ /**
+ * Sets frequency.
+ */
+ public Builder setFrequency(int frequency) {
+ mFrequency = frequency;
+ return this;
+ }
+
+ /**
+ * Sets analog type.
+ */
+ public Builder setAnalogType(int analogType) {
+ mAnalogType = analogType;
+ return this;
+ }
+
+ /**
+ * Sets sif standard.
+ */
+ public Builder setSifStandard(int sifStandard) {
+ mSifStandard = sifStandard;
+ return this;
+ }
+
+ /**
+ * Builds a FrontendAnalogSettings instance.
+ */
+ public AnalogFrontendSettings build() {
+ return new AnalogFrontendSettings(mFrequency, mAnalogType, mSifStandard);
+ }
+ }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
new file mode 100644
index 0000000..bce8a64
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.frontend;
+
+
+import android.media.tv.tuner.FrontendSettings;
+import android.media.tv.tuner.TunerConstants;
+
+import java.util.List;
+
+/**
+ * Frontend settings for ATSC-3.
+ * @hide
+ */
+public class Atsc3FrontendSettings extends FrontendSettings {
+ public int bandwidth;
+ public byte demodOutputFormat;
+ public List<Atsc3PlpSettings> plpSettings;
+
+ Atsc3FrontendSettings(int frequency) {
+ super(frequency);
+ }
+
+ @Override
+ public int getType() {
+ return TunerConstants.FRONTEND_TYPE_ATSC3;
+ }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/Atsc3PlpSettings.java b/media/java/android/media/tv/tuner/frontend/Atsc3PlpSettings.java
new file mode 100644
index 0000000..61c6fec
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/Atsc3PlpSettings.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.frontend;
+
+/**
+ * PLP settings for ATSC-3.
+ * @hide
+ */
+public class Atsc3PlpSettings {
+ public byte plpId;
+ public int modulation;
+ public int interleaveMode;
+ public int codeRate;
+ public int fec;
+}
diff --git a/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java
new file mode 100644
index 0000000..14c5cdd
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.frontend;
+
+import android.media.tv.tuner.FrontendSettings;
+import android.media.tv.tuner.TunerConstants;
+
+/**
+ * Frontend settings for ATSC.
+ * @hide
+ */
+public class AtscFrontendSettings extends FrontendSettings {
+ public int modulation;
+
+ AtscFrontendSettings(int frequency) {
+ super(frequency);
+ }
+
+ @Override
+ public int getType() {
+ return TunerConstants.FRONTEND_TYPE_ATSC;
+ }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
new file mode 100644
index 0000000..07e49ff
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.frontend;
+
+import android.media.tv.tuner.FrontendSettings;
+import android.media.tv.tuner.TunerConstants;
+
+/**
+ * Frontend settings for DVBC.
+ * @hide
+ */
+public class DvbcFrontendSettings extends FrontendSettings {
+ public int modulation;
+ public long fec;
+ public int symbolRate;
+ public int outerFec;
+ public byte annex;
+ public int spectralInversion;
+
+ DvbcFrontendSettings(int frequency) {
+ super(frequency);
+ }
+
+ @Override
+ public int getType() {
+ return TunerConstants.FRONTEND_TYPE_DVBC;
+ }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/DvbsCodeRate.java b/media/java/android/media/tv/tuner/frontend/DvbsCodeRate.java
new file mode 100644
index 0000000..bfa4391
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/DvbsCodeRate.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.frontend;
+
+/**
+ * Code rate for DVBS.
+ * @hide
+ */
+public class DvbsCodeRate {
+ public long fec;
+ public boolean isLinear;
+ public boolean isShortFrames;
+ public int bitsPer1000Symbol;
+}
diff --git a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
new file mode 100644
index 0000000..23c0a7b1
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.frontend;
+
+import android.media.tv.tuner.FrontendSettings;
+import android.media.tv.tuner.TunerConstants;
+
+/**
+ * Frontend settings for DVBS.
+ * @hide
+ */
+public class DvbsFrontendSettings extends FrontendSettings {
+ public int modulation;
+ public DvbsCodeRate coderate;
+ public int symbolRate;
+ public int rolloff;
+ public int pilot;
+ public int inputStreamId;
+ public byte standard;
+
+ DvbsFrontendSettings(int frequency) {
+ super(frequency);
+ }
+
+ @Override
+ public int getType() {
+ return TunerConstants.FRONTEND_TYPE_DVBS;
+ }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
new file mode 100644
index 0000000..eec00f3
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.frontend;
+
+
+import android.media.tv.tuner.FrontendSettings;
+import android.media.tv.tuner.TunerConstants;
+
+/**
+ * Frontend settings for DVBT.
+ * @hide
+ */
+public class DvbtFrontendSettings extends FrontendSettings {
+ public int transmissionMode;
+ public int bandwidth;
+ public int constellation;
+ public int hierarchy;
+ public int hpCoderate;
+ public int lpCoderate;
+ public int guardInterval;
+ public boolean isHighPriority;
+ public byte standard;
+ public boolean isMiso;
+ public int plpMode;
+ public byte plpId;
+ public byte plpGroupId;
+
+ DvbtFrontendSettings(int frequency) {
+ super(frequency);
+ }
+
+ @Override
+ public int getType() {
+ return TunerConstants.FRONTEND_TYPE_DVBT;
+ }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendCallback.java b/media/java/android/media/tv/tuner/frontend/FrontendCallback.java
new file mode 100644
index 0000000..0992eb6
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/FrontendCallback.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.frontend;
+
+/**
+ * Frontend Callback.
+ *
+ * @hide
+ */
+public interface FrontendCallback {
+
+ /**
+ * Invoked when there is a frontend event.
+ */
+ void onEvent(int frontendEventType);
+
+ /**
+ * Invoked when there is a scan message.
+ * @param msg
+ */
+ void onScanMessage(ScanMessage msg);
+}
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendInfo.java b/media/java/android/media/tv/tuner/frontend/FrontendInfo.java
new file mode 100644
index 0000000..ef6c029
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/FrontendInfo.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.frontend;
+
+import android.media.tv.tuner.FrontendCapabilities;
+import android.media.tv.tuner.TunerConstants.FrontendType;
+
+/**
+ * Frontend info.
+ * @hide
+ */
+public class FrontendInfo {
+ private final int mId;
+ private final int mType;
+ private final int mMinFrequency;
+ private final int mMaxFrequency;
+ private final int mMinSymbolRate;
+ private final int mMaxSymbolRate;
+ private final int mAcquireRange;
+ private final int mExclusiveGroupId;
+ private final int[] mStatusCaps;
+ private final FrontendCapabilities mFrontendCap;
+
+ FrontendInfo(int id, int type, int minFrequency, int maxFrequency, int minSymbolRate,
+ int maxSymbolRate, int acquireRange, int exclusiveGroupId, int[] statusCaps,
+ FrontendCapabilities frontendCap) {
+ mId = id;
+ mType = type;
+ mMinFrequency = minFrequency;
+ mMaxFrequency = maxFrequency;
+ mMinSymbolRate = minSymbolRate;
+ mMaxSymbolRate = maxSymbolRate;
+ mAcquireRange = acquireRange;
+ mExclusiveGroupId = exclusiveGroupId;
+ mStatusCaps = statusCaps;
+ mFrontendCap = frontendCap;
+ }
+
+ /** Gets frontend ID. */
+ public int getId() {
+ return mId;
+ }
+ /** Gets frontend type. */
+ @FrontendType
+ public int getType() {
+ return mType;
+ }
+ /** Gets min frequency. */
+ public int getMinFrequency() {
+ return mMinFrequency;
+ }
+ /** Gets max frequency. */
+ public int getMaxFrequency() {
+ return mMaxFrequency;
+ }
+ /** Gets min symbol rate. */
+ public int getMinSymbolRate() {
+ return mMinSymbolRate;
+ }
+ /** Gets max symbol rate. */
+ public int getMaxSymbolRate() {
+ return mMaxSymbolRate;
+ }
+ /** Gets acquire range. */
+ public int getAcquireRange() {
+ return mAcquireRange;
+ }
+ /** Gets exclusive group ID. */
+ public int getExclusiveGroupId() {
+ return mExclusiveGroupId;
+ }
+ /** Gets status capabilities. */
+ public int[] getStatusCapabilities() {
+ return mStatusCaps;
+ }
+ /** Gets frontend capability. */
+ public FrontendCapabilities getFrontendCapability() {
+ return mFrontendCap;
+ }
+}
diff --git a/media/java/android/media/tv/tuner/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
similarity index 98%
rename from media/java/android/media/tv/tuner/FrontendStatus.java
rename to media/java/android/media/tv/tuner/frontend/FrontendStatus.java
index f8b2d12..89ec536 100644
--- a/media/java/android/media/tv/tuner/FrontendStatus.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package android.media.tv.tuner;
+package android.media.tv.tuner.frontend;
+import android.media.tv.tuner.TunerConstants;
import android.media.tv.tuner.TunerConstants.FrontendDvbcSpectralInversion;
import android.media.tv.tuner.TunerConstants.FrontendDvbtHierarchy;
import android.media.tv.tuner.TunerConstants.FrontendInnerFec;
diff --git a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
new file mode 100644
index 0000000..736d0b1
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.frontend;
+
+import android.media.tv.tuner.FrontendSettings;
+import android.media.tv.tuner.TunerConstants;
+
+/**
+ * Frontend settings for ISDBS-3.
+ * @hide
+ */
+public class Isdbs3FrontendSettings extends FrontendSettings {
+ public int streamId;
+ public int streamIdType;
+ public int modulation;
+ public int coderate;
+ public int symbolRate;
+ public int rolloff;
+
+ Isdbs3FrontendSettings(int frequency) {
+ super(frequency);
+ }
+
+ @Override
+ public int getType() {
+ return TunerConstants.FRONTEND_TYPE_ISDBS3;
+ }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
new file mode 100644
index 0000000..7fd5da7
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.frontend;
+
+import android.media.tv.tuner.FrontendSettings;
+import android.media.tv.tuner.TunerConstants;
+
+/**
+ * Frontend settings for ISDBS.
+ * @hide
+ */
+public class IsdbsFrontendSettings extends FrontendSettings {
+ public int streamId;
+ public int streamIdType;
+ public int modulation;
+ public int coderate;
+ public int symbolRate;
+ public int rolloff;
+
+ IsdbsFrontendSettings(int frequency) {
+ super(frequency);
+ }
+
+ @Override
+ public int getType() {
+ return TunerConstants.FRONTEND_TYPE_ISDBS;
+ }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java
new file mode 100644
index 0000000..3f83267
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.frontend;
+
+
+import android.media.tv.tuner.FrontendSettings;
+import android.media.tv.tuner.TunerConstants;
+
+/**
+ * Frontend settings for ISDBT.
+ * @hide
+ */
+public class IsdbtFrontendSettings extends FrontendSettings {
+ public int modulation;
+ public int bandwidth;
+ public int coderate;
+ public int guardInterval;
+ public int serviceAreaId;
+
+ IsdbtFrontendSettings(int frequency) {
+ super(frequency);
+ }
+
+ @Override
+ public int getType() {
+ return TunerConstants.FRONTEND_TYPE_ISDBT;
+ }
+}
diff --git a/media/java/android/media/tv/tuner/ScanMessage.java b/media/java/android/media/tv/tuner/frontend/ScanMessage.java
similarity index 61%
rename from media/java/android/media/tv/tuner/ScanMessage.java
rename to media/java/android/media/tv/tuner/frontend/ScanMessage.java
index 35f54f8..dd687dd 100644
--- a/media/java/android/media/tv/tuner/ScanMessage.java
+++ b/media/java/android/media/tv/tuner/frontend/ScanMessage.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,9 +14,13 @@
* limitations under the License.
*/
-package android.media.tv.tuner;
+package android.media.tv.tuner.frontend;
-import android.media.tv.tuner.TunerConstants.ScanMessageType;
+import android.annotation.IntDef;
+import android.hardware.tv.tuner.V1_0.Constants;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
* Message from frontend during scan operations.
@@ -24,6 +28,43 @@
* @hide
*/
public class ScanMessage {
+
+ /** @hide */
+ @IntDef({
+ LOCKED,
+ END,
+ PROGRESS_PERCENT,
+ FREQUENCY,
+ SYMBOL_RATE,
+ PLP_IDS,
+ GROUP_IDS,
+ INPUT_STREAM_IDS,
+ STANDARD,
+ ATSC3_PLP_INFO
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {}
+ /** @hide */
+ public static final int LOCKED = Constants.FrontendScanMessageType.LOCKED;
+ /** @hide */
+ public static final int END = Constants.FrontendScanMessageType.END;
+ /** @hide */
+ public static final int PROGRESS_PERCENT = Constants.FrontendScanMessageType.PROGRESS_PERCENT;
+ /** @hide */
+ public static final int FREQUENCY = Constants.FrontendScanMessageType.FREQUENCY;
+ /** @hide */
+ public static final int SYMBOL_RATE = Constants.FrontendScanMessageType.SYMBOL_RATE;
+ /** @hide */
+ public static final int PLP_IDS = Constants.FrontendScanMessageType.PLP_IDS;
+ /** @hide */
+ public static final int GROUP_IDS = Constants.FrontendScanMessageType.GROUP_IDS;
+ /** @hide */
+ public static final int INPUT_STREAM_IDS = Constants.FrontendScanMessageType.INPUT_STREAM_IDS;
+ /** @hide */
+ public static final int STANDARD = Constants.FrontendScanMessageType.STANDARD;
+ /** @hide */
+ public static final int ATSC3_PLP_INFO = Constants.FrontendScanMessageType.ATSC3_PLP_INFO;
+
private final int mType;
private final Object mValue;
@@ -33,69 +74,69 @@
}
/** Gets scan message type. */
- @ScanMessageType
+ @Type
public int getMessageType() {
return mType;
}
/** Message indicates whether frontend is locked or not. */
public boolean getIsLocked() {
- if (mType != TunerConstants.SCAN_MESSAGE_TYPE_LOCKED) {
+ if (mType != LOCKED) {
throw new IllegalStateException();
}
return (Boolean) mValue;
}
/** Message indicates whether the scan has reached the end or not. */
public boolean getIsEnd() {
- if (mType != TunerConstants.SCAN_MESSAGE_TYPE_END) {
+ if (mType != END) {
throw new IllegalStateException();
}
return (Boolean) mValue;
}
/** Progress message in percent. */
public int getProgressPercent() {
- if (mType != TunerConstants.SCAN_MESSAGE_TYPE_PROGRESS_PERCENT) {
+ if (mType != PROGRESS_PERCENT) {
throw new IllegalStateException();
}
return (Integer) mValue;
}
/** Gets frequency. */
public int getFrequency() {
- if (mType != TunerConstants.SCAN_MESSAGE_TYPE_FREQUENCY) {
+ if (mType != FREQUENCY) {
throw new IllegalStateException();
}
return (Integer) mValue;
}
/** Gets symbol rate. */
public int getSymbolRate() {
- if (mType != TunerConstants.SCAN_MESSAGE_TYPE_SYMBOL_RATE) {
+ if (mType != SYMBOL_RATE) {
throw new IllegalStateException();
}
return (Integer) mValue;
}
/** Gets PLP IDs. */
public int[] getPlpIds() {
- if (mType != TunerConstants.SCAN_MESSAGE_TYPE_PLP_IDS) {
+ if (mType != PLP_IDS) {
throw new IllegalStateException();
}
return (int[]) mValue;
}
/** Gets group IDs. */
public int[] getGroupIds() {
- if (mType != TunerConstants.SCAN_MESSAGE_TYPE_GROUP_IDS) {
+ if (mType != GROUP_IDS) {
throw new IllegalStateException();
}
return (int[]) mValue;
}
/** Gets Input stream IDs. */
public int[] getInputStreamIds() {
- if (mType != TunerConstants.SCAN_MESSAGE_TYPE_INPUT_STREAM_IDS) {
+ if (mType != INPUT_STREAM_IDS) {
throw new IllegalStateException();
}
return (int[]) mValue;
}
/** Gets the DVB-T or DVB-S standard. */
public int getStandard() {
- if (mType != TunerConstants.SCAN_MESSAGE_TYPE_STANDARD) {
+ if (mType != STANDARD) {
throw new IllegalStateException();
}
return (int) mValue;
@@ -103,7 +144,7 @@
/** Gets PLP information for ATSC3. */
public Atsc3PlpInfo[] getAtsc3PlpInfos() {
- if (mType != TunerConstants.SCAN_MESSAGE_TYPE_ATSC3_PLP_INFO) {
+ if (mType != ATSC3_PLP_INFO) {
throw new IllegalStateException();
}
return (Atsc3PlpInfo[]) mValue;
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 4ca23a1..ee67613 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -139,6 +139,7 @@
"libfmq",
"libhidlbase",
"liblog",
+ "libnativehelper",
"libutils",
],
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index da52696..f0f3688 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -22,6 +22,7 @@
#include <android/hardware/tv/tuner/1.0/ITuner.h>
#include <media/stagefright/foundation/ADebug.h>
+#include <nativehelper/JNIHelp.h>
#pragma GCC diagnostic ignored "-Wunused-function"
@@ -92,19 +93,35 @@
}
void DvrCallback::setDvr(const jobject dvr) {
- ALOGD("FilterCallback::setDvr");
+ ALOGD("DvrCallback::setDvr");
JNIEnv *env = AndroidRuntime::getJNIEnv();
mDvr = env->NewWeakGlobalRef(dvr);
}
/////////////// Dvr ///////////////////////
-Dvr::Dvr(sp<IDvr> sp, jweak obj) : mDvrSp(sp), mDvrObj(obj) {}
+Dvr::Dvr(sp<IDvr> sp, jweak obj) : mDvrSp(sp), mDvrObj(obj), mDvrMQEventFlag(nullptr) {}
+
+Dvr::~Dvr() {
+ EventFlag::deleteEventFlag(&mDvrMQEventFlag);
+}
+
+int Dvr::close() {
+ Result r = mDvrSp->close();
+ if (r == Result::SUCCESS) {
+ EventFlag::deleteEventFlag(&mDvrMQEventFlag);
+ }
+ return (int)r;
+}
sp<IDvr> Dvr::getIDvr() {
return mDvrSp;
}
+DvrMQ& Dvr::getDvrMQ() {
+ return *mDvrMQ;
+}
+
/////////////// FilterCallback ///////////////////////
//TODO: implement filter callback
Return<void> FilterCallback::onFilterEvent(const DemuxFilterEvent& /*filterEvent*/) {
@@ -636,6 +653,26 @@
return NULL;
}
+static int android_media_tv_Tuner_gat_av_sync_hw_id(JNIEnv*, jobject, jobject) {
+ return 0;
+}
+
+static jlong android_media_tv_Tuner_gat_av_sync_time(JNIEnv*, jobject, jint) {
+ return 0;
+}
+
+static int android_media_tv_Tuner_connect_cicam(JNIEnv*, jobject, jint) {
+ return 0;
+}
+
+static int android_media_tv_Tuner_disconnect_cicam(JNIEnv*, jobject) {
+ return 0;
+}
+
+static jobject android_media_tv_Tuner_get_frontend_info(JNIEnv*, jobject, jint) {
+ return NULL;
+}
+
static jobject android_media_tv_Tuner_get_lnb_ids(JNIEnv *env, jobject thiz) {
sp<JTuner> tuner = getTuner(env, thiz);
return tuner->getLnbIds();
@@ -843,6 +880,10 @@
return tuner->openDvr(static_cast<DvrType>(type), bufferSize);
}
+static jobject android_media_tv_Tuner_get_demux_caps(JNIEnv*, jobject) {
+ return NULL;
+}
+
static int android_media_tv_Tuner_attach_filter(JNIEnv *env, jobject dvr, jobject filter) {
sp<IDvr> dvrSp = getDvr(env, dvr)->getIDvr();
sp<IFilter> filterSp = getFilter(env, filter)->getIFilter();
@@ -864,12 +905,28 @@
}
static int android_media_tv_Tuner_configure_dvr(JNIEnv *env, jobject dvr, jobject settings) {
- sp<IDvr> dvrSp = getDvr(env, dvr)->getIDvr();
+ sp<Dvr> dvrSp = getDvr(env, dvr);
+ sp<IDvr> iDvrSp = dvrSp->getIDvr();
if (dvrSp == NULL) {
ALOGD("Failed to configure dvr: dvr not found");
return (int)Result::INVALID_STATE;
}
- Result result = dvrSp->configure(getDvrSettings(env, settings));
+ Result result = iDvrSp->configure(getDvrSettings(env, settings));
+ MQDescriptorSync<uint8_t> dvrMQDesc;
+ if (result == Result::SUCCESS) {
+ Result getQueueDescResult = Result::UNKNOWN_ERROR;
+ iDvrSp->getQueueDesc(
+ [&](Result r, const MQDescriptorSync<uint8_t>& desc) {
+ dvrMQDesc = desc;
+ getQueueDescResult = r;
+ ALOGD("getDvrQueueDesc");
+ });
+ if (getQueueDescResult == Result::SUCCESS) {
+ dvrSp->mDvrMQ = std::make_unique<DvrMQ>(dvrMQDesc, true);
+ EventFlag::createEventFlag(
+ dvrSp->mDvrMQ->getEventFlagWord(), &(dvrSp->mDvrMQEventFlag));
+ }
+ }
return (int)result;
}
@@ -927,6 +984,115 @@
return 0;
}
+static void android_media_tv_Tuner_dvr_set_fd(JNIEnv *env, jobject dvr, jobject jfd) {
+ sp<Dvr> dvrSp = getDvr(env, dvr);
+ if (dvrSp == NULL) {
+ ALOGD("Failed to set FD for dvr: dvr not found");
+ }
+ dvrSp->mFd = jniGetFDFromFileDescriptor(env, jfd);
+ ALOGD("set fd = %d", dvrSp->mFd);
+}
+
+static int android_media_tv_Tuner_read_dvr(JNIEnv *env, jobject dvr, jint size) {
+ sp<Dvr> dvrSp = getDvr(env, dvr);
+ if (dvrSp == NULL) {
+ ALOGD("Failed to read dvr: dvr not found");
+ }
+
+ int available = dvrSp->mDvrMQ->availableToWrite();
+ int write = std::min(size, available);
+
+ DvrMQ::MemTransaction tx;
+ int ret = 0;
+ if (dvrSp->mDvrMQ->beginWrite(write, &tx)) {
+ auto first = tx.getFirstRegion();
+ auto data = first.getAddress();
+ int length = first.getLength();
+ int firstToWrite = std::min(length, write);
+ ret = read(dvrSp->mFd, data, firstToWrite);
+ if (ret < firstToWrite) {
+ ALOGW("[DVR] file to MQ, first region: %d bytes to write, but %d bytes written",
+ firstToWrite, ret);
+ } else if (firstToWrite < write) {
+ ALOGD("[DVR] write second region: %d bytes written, %d bytes in total", ret, write);
+ auto second = tx.getSecondRegion();
+ data = second.getAddress();
+ length = second.getLength();
+ int secondToWrite = std::min(length, write - firstToWrite);
+ ret += read(dvrSp->mFd, data, secondToWrite);
+ }
+ ALOGD("[DVR] file to MQ: %d bytes need to be written, %d bytes written", write, ret);
+ if (!dvrSp->mDvrMQ->commitWrite(ret)) {
+ ALOGE("[DVR] Error: failed to commit write!");
+ }
+
+ } else {
+ ALOGE("dvrMq.beginWrite failed");
+ }
+ return ret;
+}
+
+static int android_media_tv_Tuner_read_dvr_from_array(
+ JNIEnv /* *env */, jobject /* dvr */, jbyteArray /* bytes */, jint /* offset */,
+ jint /* size */) {
+ //TODO: impl
+ return 0;
+}
+
+static int android_media_tv_Tuner_write_dvr(JNIEnv *env, jobject dvr, jint size) {
+ sp<Dvr> dvrSp = getDvr(env, dvr);
+ if (dvrSp == NULL) {
+ ALOGW("Failed to write dvr: dvr not found");
+ return 0;
+ }
+
+ if (dvrSp->mDvrMQ == NULL) {
+ ALOGW("Failed to write dvr: dvr not configured");
+ return 0;
+ }
+
+ DvrMQ& dvrMq = dvrSp->getDvrMQ();
+
+ int available = dvrMq.availableToRead();
+ int toRead = std::min(size, available);
+
+ int ret = 0;
+ DvrMQ::MemTransaction tx;
+ if (dvrMq.beginRead(toRead, &tx)) {
+ auto first = tx.getFirstRegion();
+ auto data = first.getAddress();
+ int length = first.getLength();
+ int firstToRead = std::min(length, toRead);
+ ret = write(dvrSp->mFd, data, firstToRead);
+ if (ret < firstToRead) {
+ ALOGW("[DVR] MQ to file: %d bytes read, but %d bytes written", firstToRead, ret);
+ } else if (firstToRead < toRead) {
+ ALOGD("[DVR] read second region: %d bytes read, %d bytes in total", ret, toRead);
+ auto second = tx.getSecondRegion();
+ data = second.getAddress();
+ length = second.getLength();
+ int secondToRead = toRead - firstToRead;
+ ret += write(dvrSp->mFd, data, secondToRead);
+ }
+ ALOGD("[DVR] MQ to file: %d bytes to be read, %d bytes written", toRead, ret);
+ if (!dvrMq.commitRead(ret)) {
+ ALOGE("[DVR] Error: failed to commit read!");
+ }
+
+ } else {
+ ALOGE("dvrMq.beginRead failed");
+ }
+
+ return ret;
+}
+
+static int android_media_tv_Tuner_write_dvr_to_array(
+ JNIEnv /* *env */, jobject /* dvr */, jbyteArray /* bytes */, jint /* offset */,
+ jint /* size */) {
+ //TODO: impl
+ return 0;
+}
+
static const JNINativeMethod gTunerMethods[] = {
{ "nativeInit", "()V", (void *)android_media_tv_Tuner_native_init },
{ "nativeSetup", "()V", (void *)android_media_tv_Tuner_native_setup },
@@ -944,6 +1110,13 @@
{ "nativeSetLna", "(Z)I", (void *)android_media_tv_Tuner_set_lna },
{ "nativeGetFrontendStatus", "([I)[Landroid/media/tv/tuner/FrontendStatus;",
(void *)android_media_tv_Tuner_get_frontend_status },
+ { "nativeGetAvSyncHwId", "(Landroid/media/tv/tuner/Tuner$Filter;)I",
+ (void *)android_media_tv_Tuner_gat_av_sync_hw_id },
+ { "nativeGetAvSyncTime", "(I)J", (void *)android_media_tv_Tuner_gat_av_sync_time },
+ { "nativeConnectCiCam", "(I)I", (void *)android_media_tv_Tuner_connect_cicam },
+ { "nativeDisconnectCiCam", "()I", (void *)android_media_tv_Tuner_disconnect_cicam },
+ { "nativeGetFrontendInfo", "(I)[Landroid/media/tv/tuner/FrontendInfo;",
+ (void *)android_media_tv_Tuner_get_frontend_info },
{ "nativeOpenFilter", "(III)Landroid/media/tv/tuner/Tuner$Filter;",
(void *)android_media_tv_Tuner_open_filter },
{ "nativeGetLnbIds", "()Ljava/util/List;",
@@ -954,6 +1127,8 @@
(void *)android_media_tv_Tuner_open_descrambler },
{ "nativeOpenDvr", "(II)Landroid/media/tv/tuner/Tuner$Dvr;",
(void *)android_media_tv_Tuner_open_dvr },
+ { "nativeGetDemuxCapabilities", "()Landroid/media/tv/tuner/DemuxCapabilities;",
+ (void *)android_media_tv_Tuner_get_demux_caps },
};
static const JNINativeMethod gFilterMethods[] = {
@@ -989,6 +1164,12 @@
{ "nativeStopDvr", "()I", (void *)android_media_tv_Tuner_stop_dvr },
{ "nativeFlushDvr", "()I", (void *)android_media_tv_Tuner_flush_dvr },
{ "nativeClose", "()I", (void *)android_media_tv_Tuner_close_dvr },
+ { "nativeSetFileDescriptor", "(Ljava/io/FileDescriptor;)V",
+ (void *)android_media_tv_Tuner_dvr_set_fd },
+ { "nativeRead", "(I)I", (void *)android_media_tv_Tuner_read_dvr },
+ { "nativeRead", "([BII)I", (void *)android_media_tv_Tuner_read_dvr_from_array },
+ { "nativeWrite", "(I)I", (void *)android_media_tv_Tuner_write_dvr },
+ { "nativeWrite", "([BII)I", (void *)android_media_tv_Tuner_write_dvr_to_array },
};
static const JNINativeMethod gLnbMethods[] = {
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index d37a2d9..5c012bb 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -19,6 +19,8 @@
#include <android/hardware/tv/tuner/1.0/ITuner.h>
#include <fmq/MessageQueue.h>
+#include <fstream>
+#include <string>
#include <unordered_map>
#include <utils/RefBase.h>
@@ -58,6 +60,7 @@
using ::android::hardware::tv::tuner::V1_0::RecordStatus;
using FilterMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+using DvrMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
namespace android {
@@ -80,9 +83,16 @@
struct Dvr : public RefBase {
Dvr(sp<IDvr> sp, jweak obj);
+ ~Dvr();
+ int close();
+ DvrMQ& getDvrMQ();
sp<IDvr> getIDvr();
sp<IDvr> mDvrSp;
jweak mDvrObj;
+ std::unique_ptr<DvrMQ> mDvrMQ;
+ EventFlag* mDvrMQEventFlag;
+ std::string mFilePath;
+ int mFd;
};
struct FilterCallback : public IFilterCallback {
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index 747d4c01..007dd10 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -268,8 +268,9 @@
static jint
android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
- jstring type, jstring uuid, jint priority, jint sessionId, jintArray jId,
- jobjectArray javadesc, jstring opPackageName)
+ jstring type, jstring uuid, jint priority, jint sessionId,
+ jint deviceType, jstring deviceAddress,
+ jintArray jId, jobjectArray javadesc, jstring opPackageName)
{
ALOGV("android_media_AudioEffect_native_setup");
AudioEffectJniStorage* lpJniStorage = NULL;
@@ -280,6 +281,7 @@
const char *uuidStr = NULL;
effect_descriptor_t desc;
jobject jdesc;
+ AudioDeviceTypeAddr device;
ScopedUtfChars opPackageNameStr(env, opPackageName);
@@ -328,6 +330,12 @@
goto setup_failure;
}
+ if (deviceType != AUDIO_DEVICE_NONE) {
+ device.mType = deviceType;
+ ScopedUtfChars address(env, deviceAddress);
+ device.mAddress = address.c_str();
+ }
+
// create the native AudioEffect object
lpAudioEffect = new AudioEffect(typeStr,
String16(opPackageNameStr.c_str()),
@@ -336,7 +344,8 @@
effectCallback,
&lpJniStorage->mCallbackData,
(audio_session_t) sessionId,
- AUDIO_IO_HANDLE_NONE);
+ AUDIO_IO_HANDLE_NONE,
+ device);
if (lpAudioEffect == 0) {
ALOGE("Error creating AudioEffect");
goto setup_failure;
@@ -757,7 +766,7 @@
// Dalvik VM type signatures
static const JNINativeMethod gMethods[] = {
{"native_init", "()V", (void *)android_media_AudioEffect_native_init},
- {"native_setup", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;II[I[Ljava/lang/Object;Ljava/lang/String;)I",
+ {"native_setup", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;IIILjava/lang/String;[I[Ljava/lang/Object;Ljava/lang/String;)I",
(void *)android_media_AudioEffect_native_setup},
{"native_finalize", "()V", (void *)android_media_AudioEffect_native_finalize},
{"native_release", "()V", (void *)android_media_AudioEffect_native_release},
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
index 10c17dc..86b9706 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
@@ -258,6 +258,7 @@
final CountDownLatch successLatch = new CountDownLatch(1);
final CountDownLatch failureLatch = new CountDownLatch(1);
+ final List<RouteSessionController> controllers = new ArrayList<>();
// Create session with this route
SessionCallback sessionCallback = new SessionCallback() {
@@ -266,6 +267,7 @@
assertNotNull(controller);
assertTrue(createRouteMap(controller.getSelectedRoutes()).containsKey(ROUTE_ID1));
assertTrue(TextUtils.equals(CATEGORY_SAMPLE, controller.getControlCategory()));
+ controllers.add(controller);
successLatch.countDown();
}
@@ -288,7 +290,7 @@
// onSessionCreationFailed should not be called.
assertFalse(failureLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} finally {
- // TODO: Release controllers
+ releaseControllers(controllers);
mRouter2.unregisterRouteCallback(routeCallback);
mRouter2.unregisterSessionCallback(sessionCallback);
}
@@ -305,11 +307,13 @@
final CountDownLatch successLatch = new CountDownLatch(1);
final CountDownLatch failureLatch = new CountDownLatch(1);
+ final List<RouteSessionController> controllers = new ArrayList<>();
// Create session with this route
SessionCallback sessionCallback = new SessionCallback() {
@Override
public void onSessionCreated(RouteSessionController controller) {
+ controllers.add(controller);
successLatch.countDown();
}
@@ -334,7 +338,7 @@
// onSessionCreated should not be called.
assertFalse(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} finally {
- // TODO: Release controllers
+ releaseControllers(controllers);
mRouter2.unregisterRouteCallback(routeCallback);
mRouter2.unregisterSessionCallback(sessionCallback);
}
@@ -347,7 +351,6 @@
final CountDownLatch successLatch = new CountDownLatch(2);
final CountDownLatch failureLatch = new CountDownLatch(1);
-
final List<RouteSessionController> createdControllers = new ArrayList<>();
// Create session with this route
@@ -395,7 +398,7 @@
assertTrue(TextUtils.equals(CATEGORY_SAMPLE, controller1.getControlCategory()));
assertTrue(TextUtils.equals(CATEGORY_SAMPLE, controller2.getControlCategory()));
} finally {
- // TODO: Release controllers
+ releaseControllers(createdControllers);
mRouter2.unregisterRouteCallback(routeCallback);
mRouter2.unregisterSessionCallback(sessionCallback);
}
@@ -412,11 +415,13 @@
final CountDownLatch successLatch = new CountDownLatch(1);
final CountDownLatch failureLatch = new CountDownLatch(1);
+ final List<RouteSessionController> controllers = new ArrayList<>();
// Create session with this route
SessionCallback sessionCallback = new SessionCallback() {
@Override
public void onSessionCreated(RouteSessionController controller) {
+ controllers.add(controller);
successLatch.countDown();
}
@@ -442,7 +447,7 @@
assertFalse(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertFalse(failureLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} finally {
- // TODO: Release controllers
+ releaseControllers(controllers);
mRouter2.unregisterRouteCallback(routeCallback);
mRouter2.unregisterSessionCallback(sessionCallback);
}
@@ -541,8 +546,7 @@
assertTrue(onSessionInfoChangedLatchForDeselect.await(
TIMEOUT_MS, TimeUnit.MILLISECONDS));
} finally {
- // TODO: Release controllers
- controllers.clear();
+ releaseControllers(controllers);
mRouter2.unregisterRouteCallback(routeCallback);
mRouter2.unregisterSessionCallback(sessionCallback);
}
@@ -618,19 +622,85 @@
controller.transferToRoute(routeToTransferTo);
assertTrue(onSessionInfoChangedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} finally {
- // TODO: Release controllers
- controllers.clear();
+ releaseControllers(controllers);
mRouter2.unregisterRouteCallback(routeCallback);
mRouter2.unregisterSessionCallback(sessionCallback);
}
+ }
+ // TODO: Add tests for onSessionReleased() call.
+
+ @Test
+ public void testRouteSessionControllerReleaseShouldIgnoreTransferTo() throws Exception {
+ final List<String> sampleControlCategory = new ArrayList<>();
+ sampleControlCategory.add(CATEGORY_SAMPLE);
+
+ Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleControlCategory);
+ MediaRoute2Info routeToCreateSessionWith = routes.get(ROUTE_ID1);
+ assertNotNull(routeToCreateSessionWith);
+
+ final CountDownLatch onSessionCreatedLatch = new CountDownLatch(1);
+ final CountDownLatch onSessionInfoChangedLatch = new CountDownLatch(1);
+ final List<RouteSessionController> controllers = new ArrayList<>();
+
+ // Create session with ROUTE_ID1
+ SessionCallback sessionCallback = new SessionCallback() {
+ @Override
+ public void onSessionCreated(RouteSessionController controller) {
+ assertNotNull(controller);
+ assertTrue(getRouteIds(controller.getSelectedRoutes()).contains(ROUTE_ID1));
+ assertTrue(TextUtils.equals(CATEGORY_SAMPLE, controller.getControlCategory()));
+ controllers.add(controller);
+ onSessionCreatedLatch.countDown();
+ }
+
+ @Override
+ public void onSessionInfoChanged(RouteSessionController controller,
+ RouteSessionInfo oldInfo, RouteSessionInfo newInfo) {
+ if (onSessionCreatedLatch.getCount() != 0
+ || controllers.get(0).getSessionId() != controller.getSessionId()) {
+ return;
+ }
+ onSessionInfoChangedLatch.countDown();
+ }
+ };
+
+ // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
+ RouteCallback routeCallback = new RouteCallback();
+ mRouter2.registerRouteCallback(mExecutor, routeCallback);
+
+ try {
+ mRouter2.registerSessionCallback(mExecutor, sessionCallback);
+ mRouter2.requestCreateSession(routeToCreateSessionWith, CATEGORY_SAMPLE);
+ assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+ assertEquals(1, controllers.size());
+ RouteSessionController controller = controllers.get(0);
+ assertTrue(getRouteIds(controller.getTransferrableRoutes())
+ .contains(ROUTE_ID5_TO_TRANSFER_TO));
+
+ // Release controller. Future calls should be ignored.
+ controller.release();
+
+ // Transfer to ROUTE_ID5_TO_TRANSFER_TO
+ MediaRoute2Info routeToTransferTo = routes.get(ROUTE_ID5_TO_TRANSFER_TO);
+ assertNotNull(routeToTransferTo);
+
+ // This call should be ignored.
+ // The onSessionInfoChanged() shouldn't be called.
+ controller.transferToRoute(routeToTransferTo);
+ assertFalse(onSessionInfoChangedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ } finally {
+ releaseControllers(controllers);
+ mRouter2.unregisterRouteCallback(routeCallback);
+ mRouter2.unregisterSessionCallback(sessionCallback);
+ }
}
// Helper for getting routes easily
static Map<String, MediaRoute2Info> createRouteMap(List<MediaRoute2Info> routes) {
Map<String, MediaRoute2Info> routeMap = new HashMap<>();
for (MediaRoute2Info route : routes) {
- // intentionally not using route.getUniqueId() for convenience.
routeMap.put(route.getId(), route);
}
return routeMap;
@@ -663,8 +733,14 @@
}
}
+ static void releaseControllers(@NonNull List<RouteSessionController> controllers) {
+ for (RouteSessionController controller : controllers) {
+ controller.release();
+ }
+ }
+
/**
- * Returns a list of IDs (not uniqueId) of the given route list.
+ * Returns a list of IDs of the given route list.
*/
List<String> getRouteIds(@NonNull List<MediaRoute2Info> routes) {
List<String> result = new ArrayList<>();
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
index 83c7c17..1fd0141 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
@@ -57,30 +57,37 @@
public class MediaRouterManagerTest {
private static final String TAG = "MediaRouterManagerTest";
- // Must be the same as SampleMediaRoute2ProviderService
- public static final String ROUTE_ID1 = "route_id1";
+ public static final String SAMPLE_PROVIDER_ROUTES_ID_PREFIX =
+ "com.android.mediarouteprovider.example/.SampleMediaRoute2ProviderService:";
+
+ // Must be the same as SampleMediaRoute2ProviderService except the prefix of IDs.
+ public static final String ROUTE_ID1 = SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_id1";
public static final String ROUTE_NAME1 = "Sample Route 1";
- public static final String ROUTE_ID2 = "route_id2";
+ public static final String ROUTE_ID2 = SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_id2";
public static final String ROUTE_NAME2 = "Sample Route 2";
public static final String ROUTE_ID3_SESSION_CREATION_FAILED =
- "route_id3_session_creation_failed";
+ SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_id3_session_creation_failed";
public static final String ROUTE_NAME3 = "Sample Route 3 - Session creation failed";
public static final String ROUTE_ID4_TO_SELECT_AND_DESELECT =
- "route_id4_to_select_and_deselect";
+ SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_id4_to_select_and_deselect";
public static final String ROUTE_NAME4 = "Sample Route 4 - Route to select and deselect";
- public static final String ROUTE_ID5_TO_TRANSFER_TO = "route_id5_to_transfer_to";
+ public static final String ROUTE_ID5_TO_TRANSFER_TO =
+ SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_id5_to_transfer_to";
public static final String ROUTE_NAME5 = "Sample Route 5 - Route to transfer to";
- public static final String ROUTE_ID_SPECIAL_CATEGORY = "route_special_category";
+ public static final String ROUTE_ID_SPECIAL_CATEGORY =
+ SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_special_category";
public static final String ROUTE_NAME_SPECIAL_CATEGORY = "Special Category Route";
public static final String SYSTEM_PROVIDER_ID =
"com.android.server.media/.SystemMediaRoute2Provider";
public static final int VOLUME_MAX = 100;
- public static final String ROUTE_ID_FIXED_VOLUME = "route_fixed_volume";
+ public static final String ROUTE_ID_FIXED_VOLUME =
+ SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_fixed_volume";
public static final String ROUTE_NAME_FIXED_VOLUME = "Fixed Volume Route";
- public static final String ROUTE_ID_VARIABLE_VOLUME = "route_variable_volume";
+ public static final String ROUTE_ID_VARIABLE_VOLUME =
+ SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_variable_volume";
public static final String ROUTE_NAME_VARIABLE_VOLUME = "Variable Volume Route";
public static final String ACTION_REMOVE_ROUTE =
@@ -430,7 +437,6 @@
static Map<String, MediaRoute2Info> createRouteMap(List<MediaRoute2Info> routes) {
Map<String, MediaRoute2Info> routeMap = new HashMap<>();
for (MediaRoute2Info route : routes) {
- // intentionally not using route.getUniqueId() for convenience.
routeMap.put(route.getId(), route);
}
return routeMap;
diff --git a/mms/java/android/telephony/MmsManager.java b/mms/java/android/telephony/MmsManager.java
index 4bcf046..6554267 100644
--- a/mms/java/android/telephony/MmsManager.java
+++ b/mms/java/android/telephony/MmsManager.java
@@ -97,22 +97,4 @@
// Ignore it
}
}
-
- /**
- * Get carrier-dependent configuration values.
- *
- * @param subId the subscription id
- * @return bundle key/values pairs of configuration values
- */
- public Bundle getCarrierConfigValues(int subId) {
- try {
- IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
- if (iMms != null) {
- return iMms.getCarrierConfigValues(subId);
- }
- } catch (RemoteException ex) {
- // ignore it
- }
- return null;
- }
}
diff --git a/mms/java/com/android/internal/telephony/IMms.aidl b/mms/java/com/android/internal/telephony/IMms.aidl
index fa5073e..8be5111 100644
--- a/mms/java/com/android/internal/telephony/IMms.aidl
+++ b/mms/java/com/android/internal/telephony/IMms.aidl
@@ -60,13 +60,6 @@
in PendingIntent downloadedIntent);
/**
- * Get carrier-dependent configuration values.
- *
- * @param subId the SIM id
- */
- Bundle getCarrierConfigValues(int subId);
-
- /**
* Import a text message into system's SMS store
*
* @param callingPkg the calling app's package name
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/CryptoSettings.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/CryptoSettings.java
index 033f1b1..bb1336f 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/CryptoSettings.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/CryptoSettings.java
@@ -16,7 +16,6 @@
package com.android.server.backup.encryption;
-import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.internal.util.Preconditions.checkState;
import android.content.Context;
@@ -29,6 +28,7 @@
import com.android.internal.annotations.VisibleForTesting;
import java.security.KeyStoreException;
+import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
@@ -88,8 +88,8 @@
}
private CryptoSettings(SharedPreferences sharedPreferences, Context context) {
- mSharedPreferences = checkNotNull(sharedPreferences);
- mContext = checkNotNull(context);
+ mSharedPreferences = Objects.requireNonNull(sharedPreferences);
+ mContext = Objects.requireNonNull(context);
}
/**
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/BackupFileBuilder.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/BackupFileBuilder.java
index 3d3fb55..4010bfd 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/BackupFileBuilder.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/BackupFileBuilder.java
@@ -17,7 +17,6 @@
package com.android.server.backup.encryption.chunking;
import static com.android.internal.util.Preconditions.checkArgument;
-import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.internal.util.Preconditions.checkState;
import android.annotation.Nullable;
@@ -35,6 +34,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
/**
* Writes batches of {@link EncryptedChunk} to a diff script, and generates the associated {@link
@@ -174,7 +174,7 @@
* IllegalStateException}.
*/
public void finish(ChunksMetadataProto.ChunksMetadata metadata) throws IOException {
- checkNotNull(metadata, "Metadata cannot be null");
+ Objects.requireNonNull(metadata, "Metadata cannot be null");
long startOfMetadata = mBackupWriter.getBytesWritten();
mBackupWriter.writeBytes(ChunksMetadataProto.ChunksMetadata.toByteArray(metadata));
@@ -190,7 +190,7 @@
*/
private void writeChunkToFileAndListing(
ChunkHash chunkHash, Map<ChunkHash, EncryptedChunk> newChunks) throws IOException {
- checkNotNull(chunkHash, "Hash cannot be null");
+ Objects.requireNonNull(chunkHash, "Hash cannot be null");
if (mOldChunkListing.hasChunk(chunkHash)) {
ChunkListingMap.Entry oldChunk = mOldChunkListing.getChunkEntry(chunkHash);
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/ProtoStore.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/ProtoStore.java
index 3ba5f2b..b0a562c 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/ProtoStore.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/ProtoStore.java
@@ -16,8 +16,6 @@
package com.android.server.backup.encryption.chunking;
-import static com.android.internal.util.Preconditions.checkNotNull;
-
import android.content.Context;
import android.text.TextUtils;
import android.util.AtomicFile;
@@ -34,6 +32,7 @@
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
+import java.util.Objects;
import java.util.Optional;
/**
@@ -75,7 +74,7 @@
*/
@VisibleForTesting
ProtoStore(Class<T> clazz, File storeFolder) throws IOException {
- mClazz = checkNotNull(clazz);
+ mClazz = Objects.requireNonNull(clazz);
mStoreFolder = ensureDirectoryExistsOrThrow(storeFolder);
}
@@ -118,7 +117,7 @@
/** Saves a proto to disk, associating it with the given package. */
public void saveProto(String packageName, T proto) throws IOException {
- checkNotNull(proto);
+ Objects.requireNonNull(proto);
File file = getFileForPackage(packageName);
try (FileOutputStream os = new FileOutputStream(file)) {
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/Hkdf.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/Hkdf.java
index 6f4f549..c7af8c8 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/Hkdf.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/Hkdf.java
@@ -16,10 +16,9 @@
package com.android.server.backup.encryption.chunking.cdc;
-import static com.android.internal.util.Preconditions.checkNotNull;
-
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
+import java.util.Objects;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
@@ -49,9 +48,9 @@
* @throws InvalidKeyException If the salt can not be used as a valid key.
*/
static byte[] hkdf(byte[] masterKey, byte[] salt, byte[] data) throws InvalidKeyException {
- checkNotNull(masterKey, "HKDF requires master key to be set.");
- checkNotNull(salt, "HKDF requires a salt.");
- checkNotNull(data, "No data provided to HKDF.");
+ Objects.requireNonNull(masterKey, "HKDF requires master key to be set.");
+ Objects.requireNonNull(salt, "HKDF requires a salt.");
+ Objects.requireNonNull(data, "No data provided to HKDF.");
return hkdfSha256Expand(hkdfSha256Extract(masterKey, salt), data);
}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKey.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKey.java
index f356b4f..436c6de8 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKey.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKey.java
@@ -16,14 +16,14 @@
package com.android.server.backup.encryption.keys;
-import static com.android.internal.util.Preconditions.checkNotNull;
-
import android.annotation.IntDef;
import android.content.Context;
import android.security.keystore.recovery.InternalRecoveryServiceException;
import android.security.keystore.recovery.RecoveryController;
import android.util.Slog;
+import java.util.Objects;
+
import javax.crypto.SecretKey;
/**
@@ -46,8 +46,8 @@
* @param secretKey The key.
*/
public RecoverableKeyStoreSecondaryKey(String alias, SecretKey secretKey) {
- mAlias = checkNotNull(alias);
- mSecretKey = checkNotNull(secretKey);
+ mAlias = Objects.requireNonNull(alias);
+ mSecretKey = Objects.requireNonNull(secretKey);
}
/**
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/KeyValueListingBuilder.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/KeyValueListingBuilder.java
index b3518e1..217304c 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/KeyValueListingBuilder.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/KeyValueListingBuilder.java
@@ -17,7 +17,6 @@
package com.android.server.backup.encryption.kv;
import static com.android.internal.util.Preconditions.checkArgument;
-import static com.android.internal.util.Preconditions.checkNotNull;
import com.android.server.backup.encryption.chunk.ChunkHash;
import com.android.server.backup.encryption.protos.nano.KeyValueListingProto;
@@ -26,6 +25,7 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Objects;
/**
* Builds a {@link KeyValueListingProto.KeyValueListing}, which is a nano proto and so has no
@@ -37,7 +37,7 @@
/** Adds a new pair entry to the listing. */
public KeyValueListingBuilder addPair(String key, ChunkHash hash) {
checkArgument(key.length() != 0, "Key must have non-zero length");
- checkNotNull(hash, "Hash must not be null");
+ Objects.requireNonNull(hash, "Hash must not be null");
KeyValueListingProto.KeyValueEntry entry = new KeyValueListingProto.KeyValueEntry();
entry.key = key;
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupDataProcessor.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupDataProcessor.java
index 0baec8b..71588f6 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupDataProcessor.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupDataProcessor.java
@@ -16,7 +16,6 @@
package com.android.server.backup.encryption.tasks;
-import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.internal.util.Preconditions.checkState;
import android.annotation.Nullable;
@@ -34,6 +33,7 @@
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.security.SecureRandom;
+import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -67,12 +67,12 @@
SecureRandom secureRandom,
RecoverableKeyStoreSecondaryKey secondaryKey,
String packageName) {
- mContext = checkNotNull(context);
- mExecutorService = checkNotNull(executorService);
- mCryptoBackupServer = checkNotNull(cryptoBackupServer);
- mSecureRandom = checkNotNull(secureRandom);
- mSecondaryKey = checkNotNull(secondaryKey);
- mPackageName = checkNotNull(packageName);
+ mContext = Objects.requireNonNull(context);
+ mExecutorService = Objects.requireNonNull(executorService);
+ mCryptoBackupServer = Objects.requireNonNull(cryptoBackupServer);
+ mSecureRandom = Objects.requireNonNull(secureRandom);
+ mSecondaryKey = Objects.requireNonNull(secondaryKey);
+ mPackageName = Objects.requireNonNull(packageName);
}
@Override
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/RotateSecondaryKeyTask.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/RotateSecondaryKeyTask.java
index d58cb66..e5e2c1c 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/RotateSecondaryKeyTask.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/RotateSecondaryKeyTask.java
@@ -18,8 +18,6 @@
import static android.os.Build.VERSION_CODES.P;
-import static com.android.internal.util.Preconditions.checkNotNull;
-
import android.content.Context;
import android.security.keystore.recovery.InternalRecoveryServiceException;
import android.security.keystore.recovery.RecoveryController;
@@ -42,6 +40,7 @@
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import javax.crypto.IllegalBlockSizeException;
@@ -77,10 +76,10 @@
CryptoSettings cryptoSettings,
RecoveryController recoveryController) {
mContext = context;
- mSecondaryKeyManager = checkNotNull(secondaryKeyManager);
- mCryptoSettings = checkNotNull(cryptoSettings);
- mBackupServer = checkNotNull(backupServer);
- mRecoveryController = checkNotNull(recoveryController);
+ mSecondaryKeyManager = Objects.requireNonNull(secondaryKeyManager);
+ mCryptoSettings = Objects.requireNonNull(cryptoSettings);
+ mBackupServer = Objects.requireNonNull(backupServer);
+ mRecoveryController = Objects.requireNonNull(recoveryController);
}
/** Runs the task. */
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTask.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTask.java
index 77cfded..81169e2 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTask.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTask.java
@@ -26,6 +26,7 @@
import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKeyManager;
import java.security.UnrecoverableKeyException;
+import java.util.Objects;
import java.util.Optional;
/**
@@ -41,8 +42,8 @@
public StartSecondaryKeyRotationTask(
CryptoSettings cryptoSettings,
RecoverableKeyStoreSecondaryKeyManager secondaryKeyManager) {
- mCryptoSettings = Preconditions.checkNotNull(cryptoSettings);
- mSecondaryKeyManager = Preconditions.checkNotNull(secondaryKeyManager);
+ mCryptoSettings = Objects.requireNonNull(cryptoSettings);
+ mSecondaryKeyManager = Objects.requireNonNull(secondaryKeyManager);
}
/** Begin the key rotation */
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/testing/DiffScriptProcessor.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/testing/DiffScriptProcessor.java
index faddb6c..7e97924 100644
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/testing/DiffScriptProcessor.java
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/testing/DiffScriptProcessor.java
@@ -17,7 +17,6 @@
package com.android.server.backup.encryption.testing;
import static com.android.internal.util.Preconditions.checkArgument;
-import static com.android.internal.util.Preconditions.checkNotNull;
import static java.nio.charset.StandardCharsets.UTF_8;
@@ -29,6 +28,7 @@
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.Locale;
+import java.util.Objects;
import java.util.Optional;
import java.util.Scanner;
import java.util.regex.Pattern;
@@ -69,7 +69,7 @@
checkArgument(input.exists(), "input file did not exist.");
mInput = input;
mInputLength = input.length();
- mOutput = checkNotNull(output);
+ mOutput = Objects.requireNonNull(output);
}
public void process(InputStream diffScript) throws IOException, MalformedDiffScriptException {
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/DataEntity.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/DataEntity.java
index 6d3b5e9..06f4859 100644
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/DataEntity.java
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/DataEntity.java
@@ -16,10 +16,9 @@
package com.android.server.testing.shadows;
-import static com.google.common.base.Preconditions.checkNotNull;
-
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
+import java.util.Objects;
/**
* Represents a key value pair in {@link ShadowBackupDataInput} and {@link ShadowBackupDataOutput}.
@@ -34,7 +33,7 @@
* StandardCharsets#UTF_8}.
*/
public DataEntity(String key, String value) {
- this.mKey = checkNotNull(key);
+ this.mKey = Objects.requireNonNull(key);
this.mValue = value.getBytes(StandardCharsets.UTF_8);
mSize = this.mValue.length;
}
@@ -44,7 +43,7 @@
* pair.
*/
public DataEntity(String key) {
- this.mKey = checkNotNull(key);
+ this.mKey = Objects.requireNonNull(key);
mSize = -1;
mValue = null;
}
@@ -62,7 +61,7 @@
* @param size the length of the value in bytes
*/
public DataEntity(String key, byte[] data, int size) {
- this.mKey = checkNotNull(key);
+ this.mKey = Objects.requireNonNull(key);
this.mSize = size;
mValue = new byte[size];
for (int i = 0; i < size; i++) {
diff --git a/packages/CarSystemUI/res/drawable/car_ic_notification_selected.xml b/packages/CarSystemUI/res/drawable/car_ic_notification_selected.xml
deleted file mode 100644
index dd22545..0000000
--- a/packages/CarSystemUI/res/drawable/car_ic_notification_selected.xml
+++ /dev/null
@@ -1,25 +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
- -->
-<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 39.125C23.925 39.125 25.5 37.55 25.5 35.625L18.5 35.625C18.5 37.55 20.0575 39.125 22 39.125ZM32.5 28.625L32.5 19.875C32.5 14.5025 29.63 10.005 24.625 8.815L24.625 7.625C24.625 6.1725 23.4525 5 22 5C20.5475 5 19.375 6.1725 19.375 7.625L19.375 8.815C14.3525 10.005 11.5 14.485 11.5 19.875L11.5 28.625L8 32.125L8 33.875L36 33.875L36 32.125L32.5 28.625Z"
- android:fillColor="@color/car_nav_icon_fill_color_selected" />
-</vector>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res/drawable/car_ic_notification_selected_unseen.xml b/packages/CarSystemUI/res/drawable/car_ic_notification_selected_unseen.xml
deleted file mode 100644
index c5d7728..0000000
--- a/packages/CarSystemUI/res/drawable/car_ic_notification_selected_unseen.xml
+++ /dev/null
@@ -1,34 +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
- -->
-<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 39.125C23.925 39.125 25.5 37.55 25.5 35.625L18.5 35.625C18.5 37.55 20.0575 39.125 22 39.125ZM32.5 28.625L32.5 19.875C32.5 14.5025 29.63 10.005 24.625 8.815L24.625 7.625C24.625 6.1725 23.4525 5 22 5C20.5475 5 19.375 6.1725 19.375 7.625L19.375 8.815C14.3525 10.005 11.5 14.485 11.5 19.875L11.5 28.625L8 32.125L8 33.875L36 33.875L36 32.125L32.5 28.625Z"
- android:fillColor="@color/car_nav_icon_fill_color_selected" />
- <group
- android:translateX="30"
- android:translateY="2">
- <path
- android:fillColor="@color/car_nav_notification_unseen_indicator_color"
- android:strokeWidth="1"
- android:pathData="M 6 0 C 9.31370849898 0 12 2.68629150102 12 6 C 12 9.31370849898 9.31370849898 12 6 12 C 2.68629150102 12 0 9.31370849898 0 6 C 0 2.68629150102 2.68629150102 0 6 0 Z" />
- </group>
-</vector>
-
diff --git a/packages/CarSystemUI/res/drawable/car_ic_notification_unseen.xml b/packages/CarSystemUI/res/drawable/car_ic_unseen_indicator.xml
similarity index 69%
rename from packages/CarSystemUI/res/drawable/car_ic_notification_unseen.xml
rename to packages/CarSystemUI/res/drawable/car_ic_unseen_indicator.xml
index 25d1af3..025fc9c 100644
--- a/packages/CarSystemUI/res/drawable/car_ic_notification_unseen.xml
+++ b/packages/CarSystemUI/res/drawable/car_ic_unseen_indicator.xml
@@ -19,14 +19,11 @@
android:viewportHeight="44"
android:width="44dp"
android:height="44dp">
- <path
- android:pathData="M22 39.125C23.925 39.125 25.5 37.55 25.5 35.625L18.5 35.625C18.5 37.55 20.0575 39.125 22 39.125ZM32.5 28.625L32.5 19.875C32.5 14.5025 29.63 10.005 24.625 8.815L24.625 7.625C24.625 6.1725 23.4525 5 22 5C20.5475 5 19.375 6.1725 19.375 7.625L19.375 8.815C14.3525 10.005 11.5 14.485 11.5 19.875L11.5 28.625L8 32.125L8 33.875L36 33.875L36 32.125L32.5 28.625Z"
- android:fillColor="@color/car_nav_icon_fill_color" />
<group
android:translateX="30"
android:translateY="2">
<path
- android:fillColor="@color/car_nav_notification_unseen_indicator_color"
+ android:fillColor="@color/car_nav_unseen_indicator_color"
android:strokeWidth="1"
android:pathData="M 6 0 C 9.31370849898 0 12 2.68629150102 12 6 C 12 9.31370849898 9.31370849898 12 6 12 C 2.68629150102 12 0 9.31370849898 0 6 C 0 2.68629150102 2.68629150102 0 6 0 Z" />
</group>
diff --git a/packages/CarSystemUI/res/layout/car_facet_button.xml b/packages/CarSystemUI/res/layout/car_facet_button.xml
deleted file mode 100644
index 8e7ebad..0000000
--- a/packages/CarSystemUI/res/layout/car_facet_button.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2018, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
- <LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/car_facet_button"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:gravity="center"
- android:animateLayoutChanges="true"
- android:orientation="vertical">
-
- <com.android.keyguard.AlphaOptimizedImageButton
- android:id="@+id/car_nav_button_icon"
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:animateLayoutChanges="true"
- android:background="@android:color/transparent"
- android:scaleType="fitCenter">
- </com.android.keyguard.AlphaOptimizedImageButton>
-
- <com.android.keyguard.AlphaOptimizedImageButton
- android:id="@+id/car_nav_button_more_icon"
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:animateLayoutChanges="true"
- android:src="@drawable/car_ic_arrow"
- android:background="@android:color/transparent"
- android:scaleType="fitCenter">
- </com.android.keyguard.AlphaOptimizedImageButton>
-
- </LinearLayout>
-</merge>
diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
index 6c7a04f..e2e9a33 100644
--- a/packages/CarSystemUI/res/layout/car_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
@@ -33,14 +33,14 @@
android:paddingEnd="20dp"
android:gravity="center">
- <com.android.systemui.navigationbar.car.CarFacetButton
+ <com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/home"
style="@style/NavigationBarButton"
systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
systemui:icon="@drawable/car_ic_overview"
systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
systemui:selectedIcon="@drawable/car_ic_overview_selected"
- systemui:useMoreIcon="false"
+ systemui:highlightWhenSelected="true"
/>
<Space
@@ -48,14 +48,14 @@
android:layout_height="match_parent"
android:layout_weight="1"/>
- <com.android.systemui.navigationbar.car.CarFacetButton
+ <com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/maps_nav"
style="@style/NavigationBarButton"
systemui:categories="android.intent.category.APP_MAPS"
systemui:icon="@drawable/car_ic_navigation"
systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MAPS;launchFlags=0x14000000;end"
systemui:selectedIcon="@drawable/car_ic_navigation_selected"
- systemui:useMoreIcon="false"
+ systemui:highlightWhenSelected="true"
/>
<Space
@@ -63,7 +63,7 @@
android:layout_height="match_parent"
android:layout_weight="1"/>
- <com.android.systemui.navigationbar.car.CarFacetButton
+ <com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/music_nav"
style="@style/NavigationBarButton"
systemui:categories="android.intent.category.APP_MUSIC"
@@ -71,7 +71,7 @@
systemui:intent="intent:#Intent;action=android.car.intent.action.MEDIA_TEMPLATE;launchFlags=0x10000000;end"
systemui:packages="com.android.car.media"
systemui:selectedIcon="@drawable/car_ic_music_selected"
- systemui:useMoreIcon="false"
+ systemui:highlightWhenSelected="true"
/>
<Space
@@ -79,14 +79,14 @@
android:layout_height="match_parent"
android:layout_weight="1"/>
- <com.android.systemui.navigationbar.car.CarFacetButton
+ <com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/phone_nav"
style="@style/NavigationBarButton"
systemui:icon="@drawable/car_ic_phone"
systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;package=com.android.car.dialer;launchFlags=0x10000000;end"
systemui:packages="com.android.car.dialer"
systemui:selectedIcon="@drawable/car_ic_phone_selected"
- systemui:useMoreIcon="false"
+ systemui:highlightWhenSelected="true"
/>
<Space
@@ -94,14 +94,14 @@
android:layout_height="match_parent"
android:layout_weight="1"/>
- <com.android.systemui.navigationbar.car.CarFacetButton
+ <com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/grid_nav"
style="@style/NavigationBarButton"
systemui:componentNames="com.android.car.carlauncher/.AppGridActivity"
systemui:icon="@drawable/car_ic_apps"
systemui:intent="intent:#Intent;component=com.android.car.carlauncher/.AppGridActivity;launchFlags=0x24000000;end"
systemui:selectedIcon="@drawable/car_ic_apps_selected"
- systemui:useMoreIcon="false"
+ systemui:highlightWhenSelected="true"
/>
<Space
@@ -114,8 +114,6 @@
style="@style/NavigationBarButton"
systemui:icon="@drawable/car_ic_notification"
systemui:longIntent="intent:#Intent;component=com.android.car.bugreport/.BugReportActivity;end"
- systemui:selectedIcon="@drawable/car_ic_notification_selected"
- systemui:useMoreIcon="false"
/>
<Space
@@ -127,7 +125,6 @@
android:id="@+id/assist"
style="@style/NavigationBarButton"
systemui:icon="@drawable/ic_mic_white"
- systemui:useMoreIcon="false"
/>
</LinearLayout>
@@ -140,8 +137,7 @@
android:paddingStart="@dimen/car_keyline_1"
android:paddingEnd="@dimen/car_keyline_1"
android:gravity="center"
- android:visibility="gone">
-
- </LinearLayout>
+ android:visibility="gone"
+ />
</com.android.systemui.navigationbar.car.CarNavigationBarView>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml
index 0f964fd..1c5d37f 100644
--- a/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml
@@ -31,7 +31,7 @@
android:paddingStart="@*android:dimen/car_padding_5"
android:paddingEnd="@*android:dimen/car_padding_5">
- <com.android.systemui.navigationbar.car.CarFacetButton
+ <com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/home"
android:layout_width="@*android:dimen/car_touch_target_size"
android:layout_height="match_parent"
@@ -39,7 +39,8 @@
systemui:icon="@drawable/car_ic_overview"
systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
systemui:selectedIcon="@drawable/car_ic_overview_selected"
- systemui:useMoreIcon="false"/>
+ systemui:highlightWhenSelected="true"
+ />
</LinearLayout>
</com.android.systemui.navigationbar.car.CarNavigationBarView>
diff --git a/packages/CarSystemUI/res/layout/car_navigation_button.xml b/packages/CarSystemUI/res/layout/car_navigation_button.xml
index 6d8cca9..837252b 100644
--- a/packages/CarSystemUI/res/layout/car_navigation_button.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_button.xml
@@ -18,12 +18,48 @@
-->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
- <com.android.keyguard.AlphaOptimizedImageButton
+ <FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/car_nav_button_icon"
- android:layout_height="wrap_content"
- android:layout_width="@dimen/car_navigation_button_width"
- android:layout_centerInParent="true"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
android:animateLayoutChanges="true"
- android:scaleType="fitCenter">
- </com.android.keyguard.AlphaOptimizedImageButton>
+ android:orientation="vertical">
+
+ <com.android.keyguard.AlphaOptimizedImageButton
+ android:id="@+id/car_nav_button_icon_image"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_gravity="center"
+ android:animateLayoutChanges="true"
+ android:background="@android:color/transparent"
+ android:scaleType="fitCenter"
+ android:clickable="false"
+ />
+
+ <com.android.keyguard.AlphaOptimizedImageButton
+ android:id="@+id/car_nav_button_more_icon"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_gravity="center"
+ android:animateLayoutChanges="true"
+ android:src="@drawable/car_ic_arrow"
+ android:background="@android:color/transparent"
+ android:scaleType="fitCenter"
+ android:clickable="false"
+ />
+
+ <ImageView
+ android:id="@+id/car_nav_button_unseen_icon"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_gravity="center"
+ android:src="@drawable/car_ic_unseen_indicator"
+ android:background="@android:color/transparent"
+ android:scaleType="fitCenter"
+ android:clickable="false"
+ />
+
+ </FrameLayout>
</merge>
diff --git a/packages/CarSystemUI/res/layout/super_status_bar.xml b/packages/CarSystemUI/res/layout/super_status_bar.xml
index 37cd1d4..0b34626 100644
--- a/packages/CarSystemUI/res/layout/super_status_bar.xml
+++ b/packages/CarSystemUI/res/layout/super_status_bar.xml
@@ -93,7 +93,7 @@
android:layout_height="match_parent"
android:visibility="invisible"/>
- <ViewStub android:id="@+id/status_bar_expanded"
+ <include layout="@layout/status_bar_expanded"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible"/>
diff --git a/packages/CarSystemUI/res/values/attrs.xml b/packages/CarSystemUI/res/values/attrs.xml
index 6178738..54026af 100644
--- a/packages/CarSystemUI/res/values/attrs.xml
+++ b/packages/CarSystemUI/res/values/attrs.xml
@@ -16,6 +16,12 @@
-->
<resources>
+ <attr name="icon" format="reference"/>
+ <attr name="selectedIcon" format="reference"/>
+ <attr name="intent" format="string"/>
+ <attr name="longIntent" format="string"/>
+ <attr name="selectedAlpha" format="float" />
+ <attr name="unselectedAlpha" format="float" />
<!-- Custom attributes to configure hvac values -->
<declare-styleable name="AnimatedTemperatureView">
@@ -32,4 +38,67 @@
<attr name="android:minEms"/>
<attr name="android:textAppearance"/>
</declare-styleable>
+
+ <!-- Allow for custom attribs to be added to a nav button -->
+ <declare-styleable name="CarNavigationButton">
+ <!-- intent to start when button is click -->
+ <attr name="intent" />
+ <!-- intent to start when a long press has happened -->
+ <attr name="longIntent" />
+ <!-- start the intent as a broad cast instead of an activity if true-->
+ <attr name="broadcast" format="boolean"/>
+ <!-- Alpha value to used when in selected state. Defaults 1f -->
+ <attr name="selectedAlpha" />
+ <!-- Alpha value to used when in un-selected state. Defaults 0.7f -->
+ <attr name="unselectedAlpha" />
+ <!-- icon to be rendered when in selected state -->
+ <attr name="selectedIcon" />
+ <!-- icon to be rendered (drawable) -->
+ <attr name="icon"/>
+ <!-- categories that will be added as extras to the fired intents -->
+ <attr name="categories" format="string"/>
+ <!-- package names that will be added as extras to the fired intents -->
+ <attr name="packages" format="string" />
+ <!-- componentName names that will be used for detecting selected state -->
+ <attr name="componentNames" format="string" />
+ <!-- whether to highlight the button when selected. Defaults false -->
+ <attr name="showMoreWhenSelected" format="boolean" />
+ <!-- whether to highlight the button when selected. Defaults false -->
+ <attr name="highlightWhenSelected" format="boolean" />
+ </declare-styleable>
+
+ <!-- Custom attributes to configure hvac values -->
+ <declare-styleable name="TemperatureView">
+ <attr name="hvacAreaId" format="integer"/>
+ <attr name="hvacPropertyId" format="integer"/>
+ <attr name="hvacTempFormat" format="string"/>
+ </declare-styleable>
+
+ <declare-styleable name="carVolumeItems"/>
+ <declare-styleable name="carVolumeItems_item">
+ <!-- Align with AudioAttributes.USAGE_* -->
+ <attr name="usage">
+ <enum name="unknown" value="0"/>
+ <enum name="media" value="1"/>
+ <enum name="voice_communication" value="2"/>
+ <enum name="voice_communication_signalling" value="3"/>
+ <enum name="alarm" value="4"/>
+ <enum name="notification" value="5"/>
+ <enum name="notification_ringtone" value="6"/>
+ <enum name="notification_communication_request" value="7"/>
+ <enum name="notification_communication_instant" value="8"/>
+ <enum name="notification_communication_delayed" value="9"/>
+ <enum name="notification_event" value="10"/>
+ <enum name="assistance_accessibility" value="11"/>
+ <enum name="assistance_navigation_guidance" value="12"/>
+ <enum name="assistance_sonification" value="13"/>
+ <enum name="game" value="14"/>
+ <!-- hidden, do not use -->
+ <!-- enum name="virtual_source" value="15"/ -->
+ <enum name="assistant" value="16"/>
+ </attr>
+
+ <!-- Icon resource ids to render on UI -->
+ <attr name="icon" />
+ </declare-styleable>
</resources>
diff --git a/packages/CarSystemUI/res/values/colors.xml b/packages/CarSystemUI/res/values/colors.xml
index 5fcf38fc..7972e09 100644
--- a/packages/CarSystemUI/res/values/colors.xml
+++ b/packages/CarSystemUI/res/values/colors.xml
@@ -43,8 +43,8 @@
<!-- The color of the dividing line between grouped notifications. -->
<color name="notification_divider_color">@*android:color/notification_action_list</color>
- <!-- The color for the unseen notification indicator. -->
- <color name="car_nav_notification_unseen_indicator_color">#e25142</color>
+ <!-- The color for the unseen indicator. -->
+ <color name="car_nav_unseen_indicator_color">#e25142</color>
<!-- The color of the ripples on the untinted notifications -->
<color name="notification_ripple_untinted_color">@color/ripple_material_light</color>
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
index ed945e7..f7802d2 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
@@ -18,6 +18,7 @@
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
import com.android.systemui.statusbar.notification.logging.NotifLog;
@@ -40,8 +41,9 @@
NotifLog notifLog,
NotificationGroupManager groupManager,
NotificationRankingManager rankingManager,
- KeyguardEnvironment keyguardEnvironment) {
- super(notifLog, groupManager, rankingManager, keyguardEnvironment);
+ KeyguardEnvironment keyguardEnvironment,
+ FeatureFlags featureFlags) {
+ super(notifLog, groupManager, rankingManager, keyguardEnvironment, featureFlags);
}
@Override
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/AssitantButton.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/AssitantButton.java
index c50de22..98cc00e 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/AssitantButton.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/AssitantButton.java
@@ -30,9 +30,9 @@
/**
* AssitantButton is a ui component that will trigger the Voice Interaction Service.
*/
-public class AssitantButton extends CarFacetButton {
+public class AssitantButton extends CarNavigationButton {
- private static final String TAG = "CarFacetButton";
+ private static final String TAG = "AssistantButton";
private final AssistUtils mAssistUtils;
private IVoiceInteractionSessionShowCallback mShowCallback =
new IVoiceInteractionSessionShowCallback.Stub() {
@@ -62,7 +62,7 @@
}
@Override
- protected void setupIntents(TypedArray typedArray) {
+ protected void setUpIntents(TypedArray typedArray) {
// left blank because for the assistant button Intent will not be passed from the layout.
}
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/ButtonSelectionStateController.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/ButtonSelectionStateController.java
new file mode 100644
index 0000000..c36aaa0
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/ButtonSelectionStateController.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.navigationbar.car;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.view.Display;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * CarNavigationButtons can optionally have selection state that toggles certain visual indications
+ * based on whether the active application on screen is associated with it. This is basically a
+ * similar concept to a radio button group.
+ *
+ * This class controls the selection state of CarNavigationButtons that have opted in to have such
+ * selection state-dependent visual indications.
+ */
+@Singleton
+public class ButtonSelectionStateController {
+
+ private final Set<CarNavigationButton> mRegisteredViews = new HashSet<>();
+
+ protected ButtonMap mButtonsByCategory = new ButtonMap();
+ protected ButtonMap mButtonsByPackage = new ButtonMap();
+ protected ButtonMap mButtonsByComponentName = new ButtonMap();
+ protected HashSet<CarNavigationButton> mSelectedButtons;
+ protected Context mContext;
+
+ @Inject
+ public ButtonSelectionStateController(Context context) {
+ mContext = context;
+ mSelectedButtons = new HashSet<>();
+ }
+
+ /**
+ * Iterate through a view looking for CarNavigationButton and add it to the controller if it
+ * opted in to be highlighted when the active application is associated with it.
+ *
+ * @param v the View that may contain CarFacetButtons
+ */
+ protected void addAllButtonsWithSelectionState(View v) {
+ if (v instanceof CarNavigationButton) {
+ if (((CarNavigationButton) v).hasSelectionState()) {
+ addButtonWithSelectionState((CarNavigationButton) v);
+ }
+ } else if (v instanceof ViewGroup) {
+ ViewGroup viewGroup = (ViewGroup) v;
+ for (int i = 0; i < viewGroup.getChildCount(); i++) {
+ addAllButtonsWithSelectionState(viewGroup.getChildAt(i));
+ }
+ }
+ }
+
+ /** Removes all buttons from the button maps. */
+ protected void removeAll() {
+ mButtonsByCategory.clear();
+ mButtonsByPackage.clear();
+ mButtonsByComponentName.clear();
+ mSelectedButtons.clear();
+ mRegisteredViews.clear();
+ }
+
+ /**
+ * This will unselect the currently selected CarNavigationButton and determine which one should
+ * be selected next. It does this by reading the properties on the CarNavigationButton and
+ * seeing if they are a match with the supplied StackInfo list.
+ * The order of selection detection is ComponentName, PackageName then Category
+ * They will then be compared with the supplied StackInfo list.
+ * The StackInfo is expected to be supplied in order of recency and StackInfo will only be used
+ * for consideration if it has the same displayId as the CarNavigationButton.
+ *
+ * @param stackInfoList of the currently running application
+ * @param validDisplay index of the valid display
+ */
+
+ protected void taskChanged(List<ActivityManager.StackInfo> stackInfoList, int validDisplay) {
+ ActivityManager.StackInfo validStackInfo = null;
+ for (ActivityManager.StackInfo stackInfo : stackInfoList) {
+ // Find the first stack info with a topActivity in the primary display.
+ // TODO: We assume that CarFacetButton will launch an app only in the primary display.
+ // We need to extend the functionality to handle the multiple display properly.
+ if (stackInfo.topActivity != null && stackInfo.displayId == validDisplay) {
+ validStackInfo = stackInfo;
+ break;
+ }
+ }
+
+ if (validStackInfo == null) {
+ // No stack was found that was on the same display as the buttons thus return
+ return;
+ }
+ int displayId = validStackInfo.displayId;
+
+ mSelectedButtons.forEach(carNavigationButton -> {
+ if (carNavigationButton.getDisplayId() == displayId) {
+ carNavigationButton.setSelected(false);
+ }
+ });
+ mSelectedButtons.clear();
+
+ HashSet<CarNavigationButton> selectedButtons = findSelectedButtons(validStackInfo);
+
+ if (selectedButtons != null) {
+ selectedButtons.forEach(carNavigationButton -> {
+ if (carNavigationButton.getDisplayId() == displayId) {
+ carNavigationButton.setSelected(true);
+ mSelectedButtons.add(carNavigationButton);
+ }
+ });
+ }
+ }
+
+ /**
+ * Defaults to Display.DEFAULT_DISPLAY when no parameter is provided for the validDisplay.
+ *
+ * @param stackInfoList
+ */
+ protected void taskChanged(List<ActivityManager.StackInfo> stackInfoList) {
+ taskChanged(stackInfoList, Display.DEFAULT_DISPLAY);
+ }
+
+ /**
+ * Add navigation button to this controller if it uses selection state.
+ */
+ private void addButtonWithSelectionState(CarNavigationButton carNavigationButton) {
+ if (mRegisteredViews.contains(carNavigationButton)) {
+ return;
+ }
+ String[] categories = carNavigationButton.getCategories();
+ for (int i = 0; i < categories.length; i++) {
+ mButtonsByCategory.add(categories[i], carNavigationButton);
+ }
+
+ String[] packages = carNavigationButton.getPackages();
+ for (int i = 0; i < packages.length; i++) {
+ mButtonsByPackage.add(packages[i], carNavigationButton);
+ }
+ String[] componentNames = carNavigationButton.getComponentName();
+ for (int i = 0; i < componentNames.length; i++) {
+ mButtonsByComponentName.add(componentNames[i], carNavigationButton);
+ }
+
+ mRegisteredViews.add(carNavigationButton);
+ }
+
+ private HashSet<CarNavigationButton> findSelectedButtons(
+ ActivityManager.StackInfo validStackInfo) {
+ String packageName = validStackInfo.topActivity.getPackageName();
+
+ HashSet<CarNavigationButton> selectedButtons =
+ findButtonsByComponentName(validStackInfo.topActivity);
+ if (selectedButtons == null) {
+ selectedButtons = mButtonsByPackage.get(packageName);
+ }
+ if (selectedButtons == null) {
+ String category = getPackageCategory(packageName);
+ if (category != null) {
+ selectedButtons = mButtonsByCategory.get(category);
+ }
+ }
+
+ return selectedButtons;
+ }
+
+ private HashSet<CarNavigationButton> findButtonsByComponentName(
+ ComponentName componentName) {
+ HashSet<CarNavigationButton> buttons =
+ mButtonsByComponentName.get(componentName.flattenToShortString());
+ return (buttons != null) ? buttons :
+ mButtonsByComponentName.get(componentName.flattenToString());
+ }
+
+ private String getPackageCategory(String packageName) {
+ PackageManager pm = mContext.getPackageManager();
+ Set<String> supportedCategories = mButtonsByCategory.keySet();
+ for (String category : supportedCategories) {
+ Intent intent = new Intent();
+ intent.setPackage(packageName);
+ intent.setAction(Intent.ACTION_MAIN);
+ intent.addCategory(category);
+ List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
+ if (list.size() > 0) {
+ // Cache this package name into ButtonsByPackage map, so we won't have to query
+ // all categories next time this package name shows up.
+ mButtonsByPackage.put(packageName, mButtonsByCategory.get(category));
+ return category;
+ }
+ }
+ return null;
+ }
+
+ // simple multi-map
+ private static class ButtonMap extends HashMap<String, HashSet<CarNavigationButton>> {
+
+ public boolean add(String key, CarNavigationButton value) {
+ if (containsKey(key)) {
+ return get(key).add(value);
+ }
+ HashSet<CarNavigationButton> set = new HashSet<>();
+ set.add(value);
+ put(key, set);
+ return true;
+ }
+ }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/FacetButtonTaskStackListener.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/ButtonSelectionStateListener.java
similarity index 75%
rename from packages/CarSystemUI/src/com/android/systemui/navigationbar/car/FacetButtonTaskStackListener.java
rename to packages/CarSystemUI/src/com/android/systemui/navigationbar/car/ButtonSelectionStateListener.java
index 4925220..9da4121 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/FacetButtonTaskStackListener.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/ButtonSelectionStateListener.java
@@ -24,28 +24,25 @@
import javax.inject.Inject;
import javax.inject.Singleton;
-import dagger.Lazy;
-
/**
* An implementation of TaskStackChangeListener, that listens for changes in the system
* task stack and notifies the navigation bar.
*/
@Singleton
-class FacetButtonTaskStackListener extends TaskStackChangeListener {
- private static final String TAG = FacetButtonTaskStackListener.class.getSimpleName();
+class ButtonSelectionStateListener extends TaskStackChangeListener {
+ private static final String TAG = ButtonSelectionStateListener.class.getSimpleName();
- private final Lazy<CarFacetButtonController> mFacetButtonControllerLazy;
+ private final ButtonSelectionStateController mButtonSelectionStateController;
@Inject
- FacetButtonTaskStackListener(
- Lazy<CarFacetButtonController> carFacetButtonControllerLazy) {
- mFacetButtonControllerLazy = carFacetButtonControllerLazy;
+ ButtonSelectionStateListener(ButtonSelectionStateController carNavigationButtonController) {
+ mButtonSelectionStateController = carNavigationButtonController;
}
@Override
public void onTaskStackChanged() {
try {
- mFacetButtonControllerLazy.get().taskChanged(
+ mButtonSelectionStateController.taskChanged(
ActivityTaskManager.getService().getAllStackInfos());
} catch (Exception e) {
Log.e(TAG, "Getting StackInfo from activity manager failed", e);
@@ -55,7 +52,7 @@
@Override
public void onTaskDisplayChanged(int taskId, int newDisplayId) {
try {
- mFacetButtonControllerLazy.get().taskChanged(
+ mButtonSelectionStateController.taskChanged(
ActivityTaskManager.getService().getAllStackInfos());
} catch (Exception e) {
Log.e(TAG, "Getting StackInfo from activity manager failed", e);
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarFacetButton.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarFacetButton.java
deleted file mode 100644
index 0b89992..0000000
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarFacetButton.java
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.navigationbar.car;
-
-import android.app.ActivityOptions;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.TypedArray;
-import android.os.Build;
-import android.os.UserHandle;
-import android.util.AttributeSet;
-import android.view.Display;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-
-import com.android.keyguard.AlphaOptimizedImageButton;
-import com.android.systemui.R;
-
-/**
- * CarFacetButton is a ui component designed to be used as a shortcut for an app of a defined
- * category. It can also render a indicator implying that there are more options of apps to launch
- * using this component. This is done with a "More icon" currently an arrow as defined in the layout
- * file. The class is to serve as an example.
- *
- * New activity will be launched on the same display as the button is on.
- * Usage example: A button that allows a user to select a music app and indicate that there are
- * other music apps installed.
- */
-public class CarFacetButton extends LinearLayout {
- private static final String FACET_FILTER_DELIMITER = ";";
- /**
- * Extra information to be sent to a helper to make the decision of what app to launch when
- * clicked.
- */
- private static final String EXTRA_FACET_CATEGORIES = "categories";
- private static final String EXTRA_FACET_PACKAGES = "packages";
- private static final String EXTRA_FACET_ID = "filter_id";
- private static final String EXTRA_FACET_LAUNCH_PICKER = "launch_picker";
- private static final String TAG = "CarFacetButton";
-
- private Context mContext;
- private AlphaOptimizedImageButton mIcon;
- private AlphaOptimizedImageButton mMoreIcon;
- private boolean mSelected = false;
- private String[] mComponentNames;
- /** App categories that are to be used with this widget */
- private String[] mFacetCategories;
- /** App packages that are allowed to be used with this widget */
- private String[] mFacetPackages;
- private int mIconResourceId;
- /**
- * If defined in the xml this will be the icon that's rendered when the button is marked as
- * selected
- */
- private int mSelectedIconResourceId;
- private boolean mUseMoreIcon = true;
- private float mSelectedAlpha = 1f;
- private float mUnselectedAlpha = 1f;
-
- public CarFacetButton(Context context, AttributeSet attrs) {
- super(context, attrs);
- mContext = context;
- View.inflate(context, R.layout.car_facet_button, this);
- // extract custom attributes
- TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarFacetButton);
- setupIntents(typedArray);
- setupIcons(typedArray);
- }
-
- /**
- * Reads the custom attributes to setup click handlers for this component.
- */
- protected void setupIntents(TypedArray typedArray) {
- String intentString = typedArray.getString(R.styleable.CarFacetButton_intent);
- String longPressIntentString = typedArray.getString(R.styleable.CarFacetButton_longIntent);
- String categoryString = typedArray.getString(R.styleable.CarFacetButton_categories);
- String packageString = typedArray.getString(R.styleable.CarFacetButton_packages);
- String componentNameString =
- typedArray.getString(R.styleable.CarFacetButton_componentNames);
- try {
- final Intent intent = Intent.parseUri(intentString, Intent.URI_INTENT_SCHEME);
- intent.putExtra(EXTRA_FACET_ID, Integer.toString(getId()));
-
- if (packageString != null) {
- mFacetPackages = packageString.split(FACET_FILTER_DELIMITER);
- intent.putExtra(EXTRA_FACET_PACKAGES, mFacetPackages);
- }
- if (categoryString != null) {
- mFacetCategories = categoryString.split(FACET_FILTER_DELIMITER);
- intent.putExtra(EXTRA_FACET_CATEGORIES, mFacetCategories);
- }
- if (componentNameString != null) {
- mComponentNames = componentNameString.split(FACET_FILTER_DELIMITER);
- }
-
- setOnClickListener(v -> {
- ActivityOptions options = ActivityOptions.makeBasic();
- options.setLaunchDisplayId(mContext.getDisplayId());
- intent.putExtra(EXTRA_FACET_LAUNCH_PICKER, mSelected);
- mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);
- mContext.sendBroadcastAsUser(
- new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), UserHandle.CURRENT);
- });
-
- if (longPressIntentString != null && (Build.IS_ENG || Build.IS_USERDEBUG)) {
- final Intent longPressIntent = Intent.parseUri(longPressIntentString,
- Intent.URI_INTENT_SCHEME);
- setOnLongClickListener(v -> {
- ActivityOptions options = ActivityOptions.makeBasic();
- options.setLaunchDisplayId(mContext.getDisplayId());
- mContext.startActivityAsUser(longPressIntent, options.toBundle(),
- UserHandle.CURRENT);
- mContext.sendBroadcastAsUser(
- new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), UserHandle.CURRENT);
- return true;
- });
- }
- } catch (Exception e) {
- throw new RuntimeException("Failed to attach intent", e);
- }
- }
-
- private void setupIcons(TypedArray styledAttributes) {
- mSelectedAlpha = styledAttributes.getFloat(
- R.styleable.CarFacetButton_selectedAlpha, mSelectedAlpha);
- mUnselectedAlpha = styledAttributes.getFloat(
- R.styleable.CarFacetButton_unselectedAlpha, mUnselectedAlpha);
- mIcon = findViewById(R.id.car_nav_button_icon);
- mIcon.setScaleType(ImageView.ScaleType.CENTER);
- mIcon.setClickable(false);
- mIcon.setAlpha(mUnselectedAlpha);
- mIconResourceId = styledAttributes.getResourceId(R.styleable.CarFacetButton_icon, 0);
- mIcon.setImageResource(mIconResourceId);
- mSelectedIconResourceId = styledAttributes.getResourceId(
- R.styleable.CarFacetButton_selectedIcon, mIconResourceId);
-
- mMoreIcon = findViewById(R.id.car_nav_button_more_icon);
- mMoreIcon.setClickable(false);
- mMoreIcon.setAlpha(mSelectedAlpha);
- mMoreIcon.setVisibility(GONE);
- mUseMoreIcon = styledAttributes.getBoolean(R.styleable.CarFacetButton_useMoreIcon, true);
- }
-
- /**
- * @return The app categories the component represents
- */
- public String[] getCategories() {
- if (mFacetCategories == null) {
- return new String[0];
- }
- return mFacetCategories;
- }
-
- /**
- * @return The valid packages that should be considered.
- */
- public String[] getFacetPackages() {
- if (mFacetPackages == null) {
- return new String[0];
- }
- return mFacetPackages;
- }
-
- /**
- * @return The list of component names.
- */
- public String[] getComponentName() {
- if (mComponentNames == null) {
- return new String[0];
- }
- return mComponentNames;
- }
-
- /**
- * Updates the alpha of the icons to "selected" and shows the "More icon"
- *
- * @param selected true if the view must be selected, false otherwise
- */
- public void setSelected(boolean selected) {
- super.setSelected(selected);
- setSelected(selected, selected);
- }
-
- /**
- * Updates the visual state to let the user know if it's been selected.
- *
- * @param selected true if should update the alpha of the icon to selected, false otherwise
- * @param showMoreIcon true if the "more icon" should be shown, false otherwise. Note this
- * is ignored if the attribute useMoreIcon is set to false
- */
- public void setSelected(boolean selected, boolean showMoreIcon) {
- mSelected = selected;
- mIcon.setAlpha(mSelected ? mSelectedAlpha : mUnselectedAlpha);
- mIcon.setImageResource(mSelected ? mSelectedIconResourceId : mIconResourceId);
- if (mUseMoreIcon) {
- mMoreIcon.setVisibility(showMoreIcon ? VISIBLE : GONE);
- }
- }
-
- /**
- * @return The id of the display the button is on or Display.INVALID_DISPLAY if it's not yet on
- * a display.
- */
- public int getDisplayId() {
- Display display = getDisplay();
- if (display == null) {
- return Display.INVALID_DISPLAY;
- }
- return display.getDisplayId();
- }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarFacetButtonController.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarFacetButtonController.java
deleted file mode 100644
index f66e828..0000000
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarFacetButtonController.java
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.navigationbar.car;
-
-import android.app.ActivityManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.view.Display;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-/**
- * CarFacetButtons placed on the nav bar are designed to have visual indication that the active
- * application on screen is associated with it. This is basically a similar concept to a radio
- * button group.
- */
-@Singleton
-public class CarFacetButtonController {
-
- private final Set<CarFacetButton> mRegisteredViews = new HashSet<>();
-
- protected ButtonMap mButtonsByCategory = new ButtonMap();
- protected ButtonMap mButtonsByPackage = new ButtonMap();
- protected ButtonMap mButtonsByComponentName = new ButtonMap();
- protected HashSet<CarFacetButton> mSelectedFacetButtons;
- protected Context mContext;
-
- @Inject
- public CarFacetButtonController(Context context) {
- mContext = context;
- mSelectedFacetButtons = new HashSet<>();
- }
-
- /**
- * Add facet button to this controller. The expected use is for the facet button
- * to get a reference to this controller via {@link com.android.systemui.Dependency}
- * and self add.
- */
- private void addFacetButton(CarFacetButton facetButton) {
- if (mRegisteredViews.contains(facetButton)) {
- return;
- }
-
- String[] categories = facetButton.getCategories();
- for (int i = 0; i < categories.length; i++) {
- mButtonsByCategory.add(categories[i], facetButton);
- }
-
- String[] facetPackages = facetButton.getFacetPackages();
- for (int i = 0; i < facetPackages.length; i++) {
- mButtonsByPackage.add(facetPackages[i], facetButton);
- }
- String[] componentNames = facetButton.getComponentName();
- for (int i = 0; i < componentNames.length; i++) {
- mButtonsByComponentName.add(componentNames[i], facetButton);
- }
-
- mRegisteredViews.add(facetButton);
- }
-
- /** Removes all buttons from the button maps. */
- public void removeAll() {
- mButtonsByCategory.clear();
- mButtonsByPackage.clear();
- mButtonsByComponentName.clear();
- mSelectedFacetButtons.clear();
- mRegisteredViews.clear();
- }
-
- /**
- * Iterate through a view looking for CarFacetButtons and adding them to the controller if found
- *
- * @param v the View that may contain CarFacetButtons
- */
- public void addAllFacetButtons(View v) {
- if (v instanceof CarFacetButton) {
- addFacetButton((CarFacetButton) v);
- } else if (v instanceof ViewGroup) {
- ViewGroup viewGroup = (ViewGroup) v;
- for (int i = 0; i < viewGroup.getChildCount(); i++) {
- addAllFacetButtons(viewGroup.getChildAt(i));
- }
- }
- }
-
- /**
- * This will unselect the currently selected CarFacetButton and determine which one should be
- * selected next. It does this by reading the properties on the CarFacetButton and seeing if
- * they are a match with the supplied StackInfo list.
- * The order of selection detection is ComponentName, PackageName then Category
- * They will then be compared with the supplied StackInfo list.
- * The StackInfo is expected to be supplied in order of recency and StackInfo will only be used
- * for consideration if it has the same displayId as the CarFacetButtons.
- *
- * @param stackInfoList of the currently running application
- */
- public void taskChanged(List<ActivityManager.StackInfo> stackInfoList) {
- ActivityManager.StackInfo validStackInfo = null;
- for (ActivityManager.StackInfo stackInfo : stackInfoList) {
- // Find the first stack info with a topActivity in the primary display.
- // TODO: We assume that CarFacetButton will launch an app only in the primary display.
- // We need to extend the functionality to handle the mutliple display properly.
- if (stackInfo.topActivity != null && stackInfo.displayId == Display.DEFAULT_DISPLAY) {
- validStackInfo = stackInfo;
- break;
- }
- }
-
- if (validStackInfo == null) {
- // No stack was found that was on the same display as the facet buttons thus return
- return;
- }
-
- if (mSelectedFacetButtons != null) {
- Iterator<CarFacetButton> iterator = mSelectedFacetButtons.iterator();
- while (iterator.hasNext()) {
- CarFacetButton carFacetButton = iterator.next();
- if (carFacetButton.getDisplayId() == validStackInfo.displayId) {
- carFacetButton.setSelected(false);
- iterator.remove();
- }
- }
- }
-
- String packageName = validStackInfo.topActivity.getPackageName();
- HashSet<CarFacetButton> facetButton =
- findFacetButtonByComponentName(validStackInfo.topActivity);
- if (facetButton == null) {
- facetButton = mButtonsByPackage.get(packageName);
- }
-
- if (facetButton == null) {
- String category = getPackageCategory(packageName);
- if (category != null) {
- facetButton = mButtonsByCategory.get(category);
- }
- }
-
- if (facetButton != null) {
- for (CarFacetButton carFacetButton : facetButton) {
- if (carFacetButton.getDisplayId() == validStackInfo.displayId) {
- carFacetButton.setSelected(true);
- mSelectedFacetButtons.add(carFacetButton);
- }
- }
- }
-
- }
-
- private HashSet<CarFacetButton> findFacetButtonByComponentName(ComponentName componentName) {
- HashSet<CarFacetButton> buttons =
- mButtonsByComponentName.get(componentName.flattenToShortString());
- return (buttons != null) ? buttons :
- mButtonsByComponentName.get(componentName.flattenToString());
- }
-
- protected String getPackageCategory(String packageName) {
- PackageManager pm = mContext.getPackageManager();
- Set<String> supportedCategories = mButtonsByCategory.keySet();
- for (String category : supportedCategories) {
- Intent intent = new Intent();
- intent.setPackage(packageName);
- intent.setAction(Intent.ACTION_MAIN);
- intent.addCategory(category);
- List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
- if (list.size() > 0) {
- // Cache this package name into facetPackageMap, so we won't have to query
- // all categories next time this package name shows up.
- mButtonsByPackage.put(packageName, mButtonsByCategory.get(category));
- return category;
- }
- }
- return null;
- }
-
- // simple multi-map
- private static class ButtonMap extends HashMap<String, HashSet<CarFacetButton>> {
-
- public boolean add(String key, CarFacetButton value) {
- if (containsKey(key)) {
- return get(key).add(value);
- }
- HashSet<CarFacetButton> set = new HashSet<>();
- set.add(value);
- put(key, set);
- return true;
- }
- }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
index 59a084e..d8c9d17 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
@@ -57,12 +57,12 @@
private final WindowManager mWindowManager;
private final CarDeviceProvisionedController mCarDeviceProvisionedController;
private final CommandQueue mCommandQueue;
- private final Lazy<FacetButtonTaskStackListener> mFacetButtonTaskStackListenerLazy;
+ private final ButtonSelectionStateListener mButtonSelectionStateListener;
private final Handler mMainHandler;
private final Lazy<KeyguardStateController> mKeyguardStateControllerLazy;
private final Lazy<NavigationBarController> mNavigationBarControllerLazy;
private final SuperStatusBarViewFactory mSuperStatusBarViewFactory;
- private final Lazy<CarFacetButtonController> mCarFacetButtonControllerLazy;
+ private final ButtonSelectionStateController mButtonSelectionStateController;
private IStatusBarService mBarService;
private ActivityManagerWrapper mActivityManagerWrapper;
@@ -92,24 +92,24 @@
WindowManager windowManager,
DeviceProvisionedController deviceProvisionedController,
CommandQueue commandQueue,
- Lazy<FacetButtonTaskStackListener> facetButtonTaskStackListenerLazy,
+ ButtonSelectionStateListener buttonSelectionStateListener,
@Main Handler mainHandler,
Lazy<KeyguardStateController> keyguardStateControllerLazy,
Lazy<NavigationBarController> navigationBarControllerLazy,
SuperStatusBarViewFactory superStatusBarViewFactory,
- Lazy<CarFacetButtonController> carFacetButtonControllerLazy) {
+ ButtonSelectionStateController buttonSelectionStateController) {
super(context);
mCarNavigationBarController = carNavigationBarController;
mWindowManager = windowManager;
mCarDeviceProvisionedController = (CarDeviceProvisionedController)
deviceProvisionedController;
mCommandQueue = commandQueue;
- mFacetButtonTaskStackListenerLazy = facetButtonTaskStackListenerLazy;
+ mButtonSelectionStateListener = buttonSelectionStateListener;
mMainHandler = mainHandler;
mKeyguardStateControllerLazy = keyguardStateControllerLazy;
mNavigationBarControllerLazy = navigationBarControllerLazy;
mSuperStatusBarViewFactory = superStatusBarViewFactory;
- mCarFacetButtonControllerLazy = carFacetButtonControllerLazy;
+ mButtonSelectionStateController = buttonSelectionStateController;
}
@Override
@@ -156,7 +156,7 @@
createNavigationBar(result);
mActivityManagerWrapper = ActivityManagerWrapper.getInstance();
- mActivityManagerWrapper.registerTaskStackListener(mFacetButtonTaskStackListenerLazy.get());
+ mActivityManagerWrapper.registerTaskStackListener(mButtonSelectionStateListener);
mCarNavigationBarController.connectToHvac();
}
@@ -181,7 +181,7 @@
// remove and reattach all hvac components such that we don't keep a reference to unused
// ui elements
mCarNavigationBarController.removeAllFromHvac();
- mCarFacetButtonControllerLazy.get().removeAll();
+ mButtonSelectionStateController.removeAll();
if (mTopNavigationBarWindow != null) {
mTopNavigationBarWindow.removeAllViews();
@@ -343,7 +343,7 @@
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.print(" mTaskStackListener=");
- pw.println(mFacetButtonTaskStackListenerLazy.get());
+ pw.println(mButtonSelectionStateListener);
pw.print(" mBottomNavigationBarView=");
pw.println(mBottomNavigationBarView);
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java
index 6f28843..a56c4ed 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java
@@ -37,7 +37,7 @@
private final Context mContext;
private final NavigationBarViewFactory mNavigationBarViewFactory;
- private final Lazy<CarFacetButtonController> mCarFacetButtonControllerLazy;
+ private final ButtonSelectionStateController mButtonSelectionStateController;
private final Lazy<HvacController> mHvacControllerLazy;
private boolean mShowBottom;
@@ -58,11 +58,11 @@
@Inject
public CarNavigationBarController(Context context,
NavigationBarViewFactory navigationBarViewFactory,
- Lazy<CarFacetButtonController> carFacetButtonControllerLazy,
+ ButtonSelectionStateController buttonSelectionStateController,
Lazy<HvacController> hvacControllerLazy) {
mContext = context;
mNavigationBarViewFactory = navigationBarViewFactory;
- mCarFacetButtonControllerLazy = carFacetButtonControllerLazy;
+ mButtonSelectionStateController = buttonSelectionStateController;
mHvacControllerLazy = hvacControllerLazy;
// Read configuration.
@@ -175,7 +175,7 @@
NotificationsShadeController notifShadeController) {
view.setStatusBarWindowTouchListener(statusBarTouchListener);
view.setNotificationsPanelController(notifShadeController);
- mCarFacetButtonControllerLazy.get().addAllFacetButtons(view);
+ mButtonSelectionStateController.addAllButtonsWithSelectionState(view);
mHvacControllerLazy.get().addTemperatureViewToController(view);
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationButton.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationButton.java
index 922bfff..b4d4785 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationButton.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationButton.java
@@ -24,8 +24,12 @@
import android.os.UserHandle;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.Display;
+import android.view.View;
import android.widget.ImageView;
+import android.widget.LinearLayout;
+import com.android.keyguard.AlphaOptimizedImageButton;
import com.android.systemui.R;
import java.net.URISyntaxException;
@@ -35,110 +39,64 @@
* xml file level. This allows for more control via overlays instead of having to update
* code.
*/
-public class CarNavigationButton extends com.android.keyguard.AlphaOptimizedImageButton {
- private static final String TAG = "CarNavigationButton";
+public class CarNavigationButton extends LinearLayout {
- private static final int UNSEEN_ICON_RESOURCE_ID = R.drawable.car_ic_notification_unseen;
- private static final int UNSEEN_SELECTED_ICON_RESOURCE_ID =
- R.drawable.car_ic_notification_selected_unseen;
+ protected static final float DEFAULT_SELECTED_ALPHA = 1f;
+ protected static final float DEFAULT_UNSELECTED_ALPHA = 0.75f;
+
+ private static final String TAG = "CarNavigationButton";
+ private static final String BUTTON_FILTER_DELIMITER = ";";
+ private static final String EXTRA_BUTTON_CATEGORIES = "categories";
+ private static final String EXTRA_BUTTON_PACKAGES = "packages";
private Context mContext;
+ private AlphaOptimizedImageButton mIcon;
+ private AlphaOptimizedImageButton mMoreIcon;
+ private ImageView mUnseenIcon;
private String mIntent;
private String mLongIntent;
private boolean mBroadcastIntent;
private boolean mHasUnseen = false;
private boolean mSelected = false;
- private float mSelectedAlpha = 1f;
- private float mUnselectedAlpha = 1f;
+ private float mSelectedAlpha;
+ private float mUnselectedAlpha;
private int mSelectedIconResourceId;
private int mIconResourceId;
-
+ private String[] mComponentNames;
+ /** App categories that are to be used with this widget */
+ private String[] mButtonCategories;
+ /** App packages that are allowed to be used with this widget */
+ private String[] mButtonPackages;
+ /** Whether to display more icon beneath the primary icon when the button is selected */
+ private boolean mShowMoreWhenSelected = false;
+ /** Whether to highlight the button if the active application is associated with it */
+ private boolean mHighlightWhenSelected = false;
public CarNavigationButton(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
-
+ View.inflate(mContext, R.layout.car_navigation_button, /* root= */ this);
// CarNavigationButton attrs
- TypedArray typedArray = context.obtainStyledAttributes(
- attrs, R.styleable.CarNavigationButton);
- mIntent = typedArray.getString(R.styleable.CarNavigationButton_intent);
- mLongIntent = typedArray.getString(R.styleable.CarNavigationButton_longIntent);
- mBroadcastIntent = typedArray.getBoolean(R.styleable.CarNavigationButton_broadcast, false);
- mSelectedAlpha = typedArray.getFloat(
- R.styleable.CarNavigationButton_selectedAlpha, mSelectedAlpha);
- mUnselectedAlpha = typedArray.getFloat(
- R.styleable.CarNavigationButton_unselectedAlpha, mUnselectedAlpha);
- mSelectedIconResourceId = typedArray.getResourceId(
- R.styleable.CarNavigationButton_selectedIcon, mIconResourceId);
- mIconResourceId = typedArray.getResourceId(
- R.styleable.CarNavigationButton_icon, 0);
+ TypedArray typedArray = context.obtainStyledAttributes(attrs,
+ R.styleable.CarNavigationButton);
+
+ setUpIntents(typedArray);
+ setUpIcons(typedArray);
typedArray.recycle();
}
-
- /**
- * After the standard inflate this then adds the xml defined intents to click and long click
- * actions if defined.
- */
- @Override
- public void onFinishInflate() {
- super.onFinishInflate();
- setScaleType(ImageView.ScaleType.CENTER);
- setAlpha(mUnselectedAlpha);
- setImageResource(mIconResourceId);
- try {
- if (mIntent != null) {
- final Intent intent = Intent.parseUri(mIntent, Intent.URI_INTENT_SCHEME);
- setOnClickListener(v -> {
- try {
- if (mBroadcastIntent) {
- mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
- mContext.sendBroadcastAsUser(
- new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS),
- UserHandle.CURRENT);
- return;
- }
- ActivityOptions options = ActivityOptions.makeBasic();
- options.setLaunchDisplayId(mContext.getDisplayId());
- mContext.startActivityAsUser(intent, options.toBundle(),
- UserHandle.CURRENT);
- } catch (Exception e) {
- Log.e(TAG, "Failed to launch intent", e);
- }
- });
- }
- } catch (URISyntaxException e) {
- throw new RuntimeException("Failed to attach intent", e);
- }
-
- try {
- if (mLongIntent != null && (Build.IS_ENG || Build.IS_USERDEBUG)) {
- final Intent intent = Intent.parseUri(mLongIntent, Intent.URI_INTENT_SCHEME);
- setOnLongClickListener(v -> {
- try {
- ActivityOptions options = ActivityOptions.makeBasic();
- options.setLaunchDisplayId(mContext.getDisplayId());
- mContext.startActivityAsUser(intent, options.toBundle(),
- UserHandle.CURRENT);
- } catch (Exception e) {
- Log.e(TAG, "Failed to launch intent", e);
- }
- // consume event either way
- return true;
- });
- }
- } catch (URISyntaxException e) {
- throw new RuntimeException("Failed to attach long press intent", e);
- }
- }
-
/**
* @param selected true if should indicate if this is a selected state, false otherwise
*/
public void setSelected(boolean selected) {
super.setSelected(selected);
mSelected = selected;
- setAlpha(mSelected ? mSelectedAlpha : mUnselectedAlpha);
+ if (mHighlightWhenSelected) {
+ setAlpha(mSelected ? mSelectedAlpha : mUnselectedAlpha);
+ }
+ if (mShowMoreWhenSelected && mMoreIcon != null) {
+ mMoreIcon.setVisibility(selected ? VISIBLE : GONE);
+ }
updateImage();
}
@@ -155,12 +113,172 @@
return mHasUnseen;
}
- private void updateImage() {
- if (mHasUnseen) {
- setImageResource(mSelected ? UNSEEN_SELECTED_ICON_RESOURCE_ID
- : UNSEEN_ICON_RESOURCE_ID);
- } else {
- setImageResource(mSelected ? mSelectedIconResourceId : mIconResourceId);
+ /**
+ * @return The app categories the component represents
+ */
+ public String[] getCategories() {
+ if (mButtonCategories == null) {
+ return new String[0];
+ }
+ return mButtonCategories;
+ }
+
+ /**
+ * @return The valid packages that should be considered.
+ */
+ public String[] getPackages() {
+ if (mButtonPackages == null) {
+ return new String[0];
+ }
+ return mButtonPackages;
+ }
+
+ /**
+ * @return The list of component names.
+ */
+ public String[] getComponentName() {
+ if (mComponentNames == null) {
+ return new String[0];
+ }
+ return mComponentNames;
+ }
+
+ /**
+ * @return The id of the display the button is on or Display.INVALID_DISPLAY if it's not yet on
+ * a display.
+ */
+ protected int getDisplayId() {
+ Display display = getDisplay();
+ if (display == null) {
+ return Display.INVALID_DISPLAY;
+ }
+ return display.getDisplayId();
+ }
+
+ protected boolean hasSelectionState() {
+ return mHighlightWhenSelected || mShowMoreWhenSelected;
+ }
+
+ /**
+ * Sets up intents for click, long touch, and broadcast.
+ */
+ protected void setUpIntents(TypedArray typedArray) {
+ mIntent = typedArray.getString(R.styleable.CarNavigationButton_intent);
+ mLongIntent = typedArray.getString(R.styleable.CarNavigationButton_longIntent);
+ mBroadcastIntent = typedArray.getBoolean(R.styleable.CarNavigationButton_broadcast, false);
+
+ String categoryString = typedArray.getString(R.styleable.CarNavigationButton_categories);
+ String packageString = typedArray.getString(R.styleable.CarNavigationButton_packages);
+ String componentNameString =
+ typedArray.getString(R.styleable.CarNavigationButton_componentNames);
+
+ try {
+ if (mIntent != null) {
+ final Intent intent = Intent.parseUri(mIntent, Intent.URI_INTENT_SCHEME);
+ setOnClickListener(getButtonClickListener(intent));
+ if (packageString != null) {
+ mButtonPackages = packageString.split(BUTTON_FILTER_DELIMITER);
+ intent.putExtra(EXTRA_BUTTON_PACKAGES, mButtonPackages);
+ }
+ if (categoryString != null) {
+ mButtonCategories = categoryString.split(BUTTON_FILTER_DELIMITER);
+ intent.putExtra(EXTRA_BUTTON_CATEGORIES, mButtonCategories);
+ }
+ if (componentNameString != null) {
+ mComponentNames = componentNameString.split(BUTTON_FILTER_DELIMITER);
+ }
+ }
+ } catch (URISyntaxException e) {
+ throw new RuntimeException("Failed to attach intent", e);
+ }
+
+ try {
+ if (mLongIntent != null && (Build.IS_ENG || Build.IS_USERDEBUG)) {
+ final Intent intent = Intent.parseUri(mLongIntent, Intent.URI_INTENT_SCHEME);
+ setOnLongClickListener(getButtonLongClickListener(intent));
+ }
+ } catch (URISyntaxException e) {
+ throw new RuntimeException("Failed to attach long press intent", e);
}
}
+
+ /** Defines the behavior of a button click. */
+ protected OnClickListener getButtonClickListener(Intent toSend) {
+ return v -> {
+ mContext.sendBroadcastAsUser(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS),
+ UserHandle.CURRENT);
+ try {
+ if (mBroadcastIntent) {
+ mContext.sendBroadcastAsUser(toSend, UserHandle.CURRENT);
+ return;
+ }
+ ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchDisplayId(mContext.getDisplayId());
+ mContext.startActivityAsUser(toSend, options.toBundle(),
+ UserHandle.CURRENT);
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to launch intent", e);
+ }
+ };
+ }
+
+ /** Defines the behavior of a long click. */
+ protected OnLongClickListener getButtonLongClickListener(Intent toSend) {
+ return v -> {
+ mContext.sendBroadcastAsUser(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS),
+ UserHandle.CURRENT);
+ try {
+ ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchDisplayId(mContext.getDisplayId());
+ mContext.startActivityAsUser(toSend, options.toBundle(),
+ UserHandle.CURRENT);
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to launch intent", e);
+ }
+ // consume event either way
+ return true;
+ };
+ }
+
+
+ /**
+ * Initializes view-related aspects of the button.
+ */
+ private void setUpIcons(TypedArray typedArray) {
+ mSelectedAlpha = typedArray.getFloat(
+ R.styleable.CarNavigationButton_selectedAlpha, DEFAULT_SELECTED_ALPHA);
+ mUnselectedAlpha = typedArray.getFloat(
+ R.styleable.CarNavigationButton_unselectedAlpha, DEFAULT_UNSELECTED_ALPHA);
+ mHighlightWhenSelected = typedArray.getBoolean(
+ R.styleable.CarNavigationButton_highlightWhenSelected,
+ mHighlightWhenSelected);
+ mShowMoreWhenSelected = typedArray.getBoolean(
+ R.styleable.CarNavigationButton_showMoreWhenSelected,
+ mShowMoreWhenSelected);
+
+ mSelectedIconResourceId = typedArray.getResourceId(
+ R.styleable.CarNavigationButton_selectedIcon, mIconResourceId);
+ mIconResourceId = typedArray.getResourceId(
+ R.styleable.CarNavigationButton_icon, 0);
+
+ mIcon = findViewById(R.id.car_nav_button_icon_image);
+ mIcon.setScaleType(ImageView.ScaleType.CENTER);
+ // Always apply selected alpha if the button does not toggle alpha based on selection state.
+ mIcon.setAlpha(mHighlightWhenSelected ? mUnselectedAlpha : mSelectedAlpha);
+ mIcon.setImageResource(mIconResourceId);
+
+ mMoreIcon = findViewById(R.id.car_nav_button_more_icon);
+ mMoreIcon.setAlpha(mSelectedAlpha);
+ mMoreIcon.setVisibility(GONE);
+
+ mUnseenIcon = findViewById(R.id.car_nav_button_unseen_icon);
+
+ mUnseenIcon.setVisibility(mHasUnseen ? VISIBLE : GONE);
+ }
+
+ private void updateImage() {
+ mIcon.setImageResource(mSelected ? mSelectedIconResourceId : mIconResourceId);
+ mUnseenIcon.setVisibility(mHasUnseen ? VISIBLE : GONE);
+ }
+
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 77db54c..1ee8518 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -110,6 +110,7 @@
import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
@@ -128,11 +129,11 @@
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.phone.StatusBarComponent;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
+import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -276,6 +277,7 @@
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
NotificationEntryManager notificationEntryManager,
+ NotificationRowContentBinder notificationRowContentBinder,
NotificationInterruptionStateProvider notificationInterruptionStateProvider,
NotificationViewHierarchyManager notificationViewHierarchyManager,
KeyguardViewMediator keyguardViewMediator,
@@ -363,6 +365,7 @@
notificationGutsManager,
notificationLogger,
notificationEntryManager,
+ notificationRowContentBinder,
notificationInterruptionStateProvider,
notificationViewHierarchyManager,
keyguardViewMediator,
@@ -465,7 +468,7 @@
super.start();
- mNotificationPanel.setScrollingEnabled(true);
+ mNotificationPanelViewController.setScrollingEnabled(true);
mSettleOpenPercentage = mContext.getResources().getInteger(
R.integer.notification_settle_open_percentage);
mSettleClosePercentage = mContext.getResources().getInteger(
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
index 1ebaef7..7108e65 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
@@ -70,6 +70,7 @@
import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.DozeParameters;
@@ -86,11 +87,11 @@
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBarComponent;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
+import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -146,6 +147,7 @@
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
NotificationEntryManager notificationEntryManager,
+ NotificationRowContentBinder notificationRowContentBinder,
NotificationInterruptionStateProvider notificationInterruptionStateProvider,
NotificationViewHierarchyManager notificationViewHierarchyManager,
KeyguardViewMediator keyguardViewMediator,
@@ -232,6 +234,7 @@
notificationGutsManager,
notificationLogger,
notificationEntryManager,
+ notificationRowContentBinder,
notificationInterruptionStateProvider,
notificationViewHierarchyManager,
keyguardViewMediator,
diff --git a/packages/CarSystemUI/tests/res/layout/car_button_selection_state_controller_test.xml b/packages/CarSystemUI/tests/res/layout/car_button_selection_state_controller_test.xml
new file mode 100644
index 0000000..f0e0216
--- /dev/null
+++ b/packages/CarSystemUI/tests/res/layout/car_button_selection_state_controller_test.xml
@@ -0,0 +1,55 @@
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:systemui="http://schemas.android.com/apk/res-auto"
+ android:id="@id/nav_buttons"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingStart="20dp"
+ android:paddingEnd="20dp"
+ android:gravity="center">
+
+ <com.android.systemui.navigationbar.car.CarNavigationButton
+ android:id="@+id/detectable_by_component_name"
+ style="@style/NavigationBarButton"
+ systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
+ systemui:icon="@drawable/car_ic_overview"
+ systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
+ systemui:highlightWhenSelected="true"
+ />
+
+ <com.android.systemui.navigationbar.car.CarNavigationButton
+ android:id="@+id/detectable_by_category"
+ style="@style/NavigationBarButton"
+ systemui:categories="android.intent.category.APP_MAPS"
+ systemui:icon="@drawable/car_ic_navigation"
+ systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MAPS;launchFlags=0x14000000;end"
+ systemui:highlightWhenSelected="true"
+ />
+
+ <com.android.systemui.navigationbar.car.CarNavigationButton
+ android:id="@+id/detectable_by_package"
+ style="@style/NavigationBarButton"
+ systemui:icon="@drawable/car_ic_phone"
+ systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;package=com.android.car.dialer;launchFlags=0x10000000;end"
+ systemui:packages="com.android.car.dialer"
+ systemui:highlightWhenSelected="true"
+ />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/CarSystemUI/tests/res/layout/car_navigation_button_test.xml b/packages/CarSystemUI/tests/res/layout/car_navigation_button_test.xml
new file mode 100644
index 0000000..576928c
--- /dev/null
+++ b/packages/CarSystemUI/tests/res/layout/car_navigation_button_test.xml
@@ -0,0 +1,115 @@
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:systemui="http://schemas.android.com/apk/res-auto"
+ android:id="@id/nav_buttons"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingStart="20dp"
+ android:paddingEnd="20dp"
+ android:gravity="center">
+
+ <com.android.systemui.navigationbar.car.CarNavigationButton
+ android:id="@+id/default_no_selection_state"
+ style="@style/NavigationBarButton"
+ systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
+ systemui:icon="@drawable/car_ic_overview"
+ systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
+ systemui:selectedIcon="@drawable/car_ic_overview_selected"
+ />
+
+ <com.android.systemui.navigationbar.car.CarNavigationButton
+ android:id="@+id/app_grid_activity"
+ style="@style/NavigationBarButton"
+ systemui:componentNames="com.android.car.carlauncher/.AppGridActivity"
+ systemui:icon="@drawable/car_ic_apps"
+ systemui:intent="intent:#Intent;component=com.android.car.carlauncher/.AppGridActivity;launchFlags=0x24000000;end"
+ systemui:selectedIcon="@drawable/car_ic_apps_selected"
+ systemui:highlightWhenSelected="true"
+ />
+
+ <com.android.systemui.navigationbar.car.CarNavigationButton
+ android:id="@+id/long_click_app_grid_activity"
+ style="@style/NavigationBarButton"
+ systemui:componentNames="com.android.car.carlauncher/.AppGridActivity"
+ systemui:icon="@drawable/car_ic_apps"
+ systemui:longIntent="intent:#Intent;component=com.android.car.carlauncher/.AppGridActivity;launchFlags=0x24000000;end"
+ systemui:selectedIcon="@drawable/car_ic_apps_selected"
+ systemui:highlightWhenSelected="true"
+ />
+
+ <com.android.systemui.navigationbar.car.CarNavigationButton
+ android:id="@+id/broadcast"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@null"
+ systemui:broadcast="true"
+ systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
+ />
+
+ <com.android.systemui.navigationbar.car.CarNavigationButton
+ android:id="@+id/selected_icon_undefined"
+ style="@style/NavigationBarButton"
+ systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
+ systemui:icon="@drawable/car_ic_overview"
+ systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
+ />
+
+ <com.android.systemui.navigationbar.car.CarNavigationButton
+ android:id="@+id/highlightable_no_more_button"
+ style="@style/NavigationBarButton"
+ systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
+ systemui:icon="@drawable/car_ic_overview"
+ systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
+ systemui:selectedIcon="@drawable/car_ic_overview_selected"
+ systemui:highlightWhenSelected="true"
+ />
+
+ <com.android.systemui.navigationbar.car.CarNavigationButton
+ android:id="@+id/not_highlightable_more_button"
+ style="@style/NavigationBarButton"
+ systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
+ systemui:icon="@drawable/car_ic_overview"
+ systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
+ systemui:selectedIcon="@drawable/car_ic_overview_selected"
+ systemui:showMoreWhenSelected="true"
+ />
+
+ <com.android.systemui.navigationbar.car.CarNavigationButton
+ android:id="@+id/highlightable_more_button"
+ style="@style/NavigationBarButton"
+ systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
+ systemui:icon="@drawable/car_ic_overview"
+ systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
+ systemui:selectedIcon="@drawable/car_ic_overview_selected"
+ systemui:highlightWhenSelected="true"
+ systemui:showMoreWhenSelected="true"
+ />
+
+ <com.android.systemui.navigationbar.car.CarNavigationButton
+ android:id="@+id/broadcast"
+ style="@style/NavigationBarButton"
+ systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
+ systemui:icon="@drawable/car_ic_overview"
+ systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
+ systemui:selectedIcon="@drawable/car_ic_overview_selected"
+ systemui:broadcast="true"
+ />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/ButtonSelectionStateControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/ButtonSelectionStateControllerTest.java
new file mode 100644
index 0000000..f94dd82
--- /dev/null
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/ButtonSelectionStateControllerTest.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.navigationbar.car;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.LayoutInflater;
+import android.widget.LinearLayout;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.tests.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class ButtonSelectionStateControllerTest extends SysuiTestCase {
+
+ private static final String TEST_COMPONENT_NAME_PACKAGE = "com.android.car.carlauncher";
+ private static final String TEST_COMPONENT_NAME_CLASS = ".CarLauncher";
+ private static final String TEST_CATEGORY = "com.google.android.apps.maps";
+ private static final String TEST_CATEGORY_CLASS = ".APP_MAPS";
+ private static final String TEST_PACKAGE = "com.android.car.dialer";
+ private static final String TEST_PACKAGE_CLASS = ".Dialer";
+
+ // LinearLayout with CarNavigationButtons with different configurations.
+ private LinearLayout mTestView;
+ private ButtonSelectionStateController mButtonSelectionStateController;
+ private ComponentName mComponentName;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mTestView = (LinearLayout) LayoutInflater.from(mContext).inflate(
+ R.layout.car_button_selection_state_controller_test, /* root= */ null);
+ mButtonSelectionStateController = new ButtonSelectionStateController(mContext);
+ mButtonSelectionStateController.addAllButtonsWithSelectionState(mTestView);
+ }
+
+ @Test
+ public void onTaskChanged_buttonDetectableByComponentName_selectsAssociatedButton() {
+ CarNavigationButton testButton = mTestView.findViewById(R.id.detectable_by_component_name);
+ mComponentName = new ComponentName(TEST_COMPONENT_NAME_PACKAGE, TEST_COMPONENT_NAME_CLASS);
+ List<ActivityManager.StackInfo> testStack = createTestStack(mComponentName);
+ testButton.setSelected(false);
+ mButtonSelectionStateController.taskChanged(testStack, /* validDisplay= */ -1);
+
+ assertbuttonSelected(testButton);
+ }
+
+ @Test
+ public void onTaskChanged_buttonDetectableByCategory_selectsAssociatedButton() {
+ CarNavigationButton testButton = mTestView.findViewById(R.id.detectable_by_category);
+ mComponentName = new ComponentName(TEST_CATEGORY, TEST_CATEGORY_CLASS);
+ List<ActivityManager.StackInfo> testStack = createTestStack(mComponentName);
+ testButton.setSelected(false);
+ mButtonSelectionStateController.taskChanged(testStack, /* validDisplay= */ -1);
+
+ assertbuttonSelected(testButton);
+ }
+
+ @Test
+ public void onTaskChanged_buttonDetectableByPackage_selectsAssociatedButton() {
+ CarNavigationButton testButton = mTestView.findViewById(R.id.detectable_by_package);
+ mComponentName = new ComponentName(TEST_PACKAGE, TEST_PACKAGE_CLASS);
+ List<ActivityManager.StackInfo> testStack = createTestStack(mComponentName);
+ testButton.setSelected(false);
+ mButtonSelectionStateController.taskChanged(testStack, /* validDisplay= */ -1);
+
+ assertbuttonSelected(testButton);
+ }
+
+ @Test
+ public void onTaskChanged_deselectsPreviouslySelectedButton() {
+ CarNavigationButton oldButton = mTestView.findViewById(R.id.detectable_by_component_name);
+ mComponentName = new ComponentName(TEST_COMPONENT_NAME_PACKAGE, TEST_COMPONENT_NAME_CLASS);
+ List<ActivityManager.StackInfo> oldStack = createTestStack(mComponentName);
+ oldButton.setSelected(false);
+ mButtonSelectionStateController.taskChanged(oldStack, /* validDisplay= */ -1);
+
+ mComponentName = new ComponentName(TEST_PACKAGE, TEST_PACKAGE_CLASS);
+ List<ActivityManager.StackInfo> newStack = createTestStack(mComponentName);
+ mButtonSelectionStateController.taskChanged(newStack, /* validDisplay= */ -1);
+
+ assertButtonUnselected(oldButton);
+ }
+
+ // Comparing alpha is a valid way to verify button selection state because all test buttons use
+ // highlightWhenSelected = true.
+ private void assertbuttonSelected(CarNavigationButton button) {
+ assertThat(button.getAlpha()).isEqualTo(CarNavigationButton.DEFAULT_SELECTED_ALPHA);
+ }
+
+ private void assertButtonUnselected(CarNavigationButton button) {
+ assertThat(button.getAlpha()).isEqualTo(CarNavigationButton.DEFAULT_UNSELECTED_ALPHA);
+ }
+
+ private List<ActivityManager.StackInfo> createTestStack(ComponentName componentName) {
+ ActivityManager.StackInfo validStackInfo = new ActivityManager.StackInfo();
+ validStackInfo.displayId = -1; // No display is assigned to this test view
+ validStackInfo.topActivity = componentName;
+
+ List<ActivityManager.StackInfo> testStack = new ArrayList<>();
+ testStack.add(validStackInfo);
+
+ return testStack;
+ }
+}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarControllerTest.java
index 642b114..e0c13ed 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarControllerTest.java
@@ -51,7 +51,7 @@
private TestableResources mTestableResources;
@Mock
- private CarFacetButtonController mCarFacetButtonController;
+ private ButtonSelectionStateController mButtonSelectionStateController;
@Mock
private HvacController mHvacController;
@@ -69,7 +69,7 @@
@Test
public void testConnectToHvac_callsConnect() {
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
- () -> mCarFacetButtonController, () -> mHvacController);
+ mButtonSelectionStateController, () -> mHvacController);
mCarNavigationBar.connectToHvac();
@@ -79,7 +79,7 @@
@Test
public void testRemoveAllFromHvac_callsRemoveAll() {
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
- () -> mCarFacetButtonController, () -> mHvacController);
+ mButtonSelectionStateController, () -> mHvacController);
mCarNavigationBar.removeAllFromHvac();
@@ -90,7 +90,7 @@
public void testGetBottomWindow_bottomDisabled_returnsNull() {
mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, false);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
- () -> mCarFacetButtonController, () -> mHvacController);
+ mButtonSelectionStateController, () -> mHvacController);
ViewGroup window = mCarNavigationBar.getBottomWindow();
@@ -101,7 +101,7 @@
public void testGetBottomWindow_bottomEnabled_returnsWindow() {
mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
- () -> mCarFacetButtonController, () -> mHvacController);
+ mButtonSelectionStateController, () -> mHvacController);
ViewGroup window = mCarNavigationBar.getBottomWindow();
@@ -112,7 +112,7 @@
public void testGetBottomWindow_bottomEnabled_calledTwice_returnsSameWindow() {
mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
- () -> mCarFacetButtonController, () -> mHvacController);
+ mButtonSelectionStateController, () -> mHvacController);
ViewGroup window1 = mCarNavigationBar.getBottomWindow();
ViewGroup window2 = mCarNavigationBar.getBottomWindow();
@@ -124,7 +124,7 @@
public void testGetLeftWindow_leftDisabled_returnsNull() {
mTestableResources.addOverride(R.bool.config_enableLeftNavigationBar, false);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
- () -> mCarFacetButtonController, () -> mHvacController);
+ mButtonSelectionStateController, () -> mHvacController);
ViewGroup window = mCarNavigationBar.getLeftWindow();
assertThat(window).isNull();
}
@@ -133,7 +133,7 @@
public void testGetLeftWindow_leftEnabled_returnsWindow() {
mTestableResources.addOverride(R.bool.config_enableLeftNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
- () -> mCarFacetButtonController, () -> mHvacController);
+ mButtonSelectionStateController, () -> mHvacController);
ViewGroup window = mCarNavigationBar.getLeftWindow();
@@ -144,7 +144,7 @@
public void testGetLeftWindow_leftEnabled_calledTwice_returnsSameWindow() {
mTestableResources.addOverride(R.bool.config_enableLeftNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
- () -> mCarFacetButtonController, () -> mHvacController);
+ mButtonSelectionStateController, () -> mHvacController);
ViewGroup window1 = mCarNavigationBar.getLeftWindow();
ViewGroup window2 = mCarNavigationBar.getLeftWindow();
@@ -156,7 +156,7 @@
public void testGetRightWindow_rightDisabled_returnsNull() {
mTestableResources.addOverride(R.bool.config_enableRightNavigationBar, false);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
- () -> mCarFacetButtonController, () -> mHvacController);
+ mButtonSelectionStateController, () -> mHvacController);
ViewGroup window = mCarNavigationBar.getRightWindow();
@@ -167,7 +167,7 @@
public void testGetRightWindow_rightEnabled_returnsWindow() {
mTestableResources.addOverride(R.bool.config_enableRightNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
- () -> mCarFacetButtonController, () -> mHvacController);
+ mButtonSelectionStateController, () -> mHvacController);
ViewGroup window = mCarNavigationBar.getRightWindow();
@@ -178,7 +178,7 @@
public void testGetRightWindow_rightEnabled_calledTwice_returnsSameWindow() {
mTestableResources.addOverride(R.bool.config_enableRightNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
- () -> mCarFacetButtonController, () -> mHvacController);
+ mButtonSelectionStateController, () -> mHvacController);
ViewGroup window1 = mCarNavigationBar.getRightWindow();
ViewGroup window2 = mCarNavigationBar.getRightWindow();
@@ -190,7 +190,7 @@
public void testSetBottomWindowVisibility_setTrue_isVisible() {
mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
- () -> mCarFacetButtonController, () -> mHvacController);
+ mButtonSelectionStateController, () -> mHvacController);
ViewGroup window = mCarNavigationBar.getBottomWindow();
mCarNavigationBar.setBottomWindowVisibility(View.VISIBLE);
@@ -202,7 +202,7 @@
public void testSetBottomWindowVisibility_setFalse_isGone() {
mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
- () -> mCarFacetButtonController, () -> mHvacController);
+ mButtonSelectionStateController, () -> mHvacController);
ViewGroup window = mCarNavigationBar.getBottomWindow();
mCarNavigationBar.setBottomWindowVisibility(View.GONE);
@@ -214,7 +214,7 @@
public void testSetLeftWindowVisibility_setTrue_isVisible() {
mTestableResources.addOverride(R.bool.config_enableLeftNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
- () -> mCarFacetButtonController, () -> mHvacController);
+ mButtonSelectionStateController, () -> mHvacController);
ViewGroup window = mCarNavigationBar.getLeftWindow();
mCarNavigationBar.setLeftWindowVisibility(View.VISIBLE);
@@ -226,7 +226,7 @@
public void testSetLeftWindowVisibility_setFalse_isGone() {
mTestableResources.addOverride(R.bool.config_enableLeftNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
- () -> mCarFacetButtonController, () -> mHvacController);
+ mButtonSelectionStateController, () -> mHvacController);
ViewGroup window = mCarNavigationBar.getLeftWindow();
mCarNavigationBar.setLeftWindowVisibility(View.GONE);
@@ -238,7 +238,7 @@
public void testSetRightWindowVisibility_setTrue_isVisible() {
mTestableResources.addOverride(R.bool.config_enableRightNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
- () -> mCarFacetButtonController, () -> mHvacController);
+ mButtonSelectionStateController, () -> mHvacController);
ViewGroup window = mCarNavigationBar.getRightWindow();
mCarNavigationBar.setRightWindowVisibility(View.VISIBLE);
@@ -250,7 +250,7 @@
public void testSetRightWindowVisibility_setFalse_isGone() {
mTestableResources.addOverride(R.bool.config_enableRightNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
- () -> mCarFacetButtonController, () -> mHvacController);
+ mButtonSelectionStateController, () -> mHvacController);
ViewGroup window = mCarNavigationBar.getRightWindow();
mCarNavigationBar.setRightWindowVisibility(View.GONE);
@@ -262,7 +262,7 @@
public void testRegisterBottomBarTouchListener_createViewFirst_registrationSuccessful() {
mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
- () -> mCarFacetButtonController, () -> mHvacController);
+ mButtonSelectionStateController, () -> mHvacController);
CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
View.OnTouchListener controller = bottomBar.getStatusBarWindowTouchListener();
@@ -277,7 +277,7 @@
public void testRegisterBottomBarTouchListener_registerFirst_registrationSuccessful() {
mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
- () -> mCarFacetButtonController, () -> mHvacController);
+ mButtonSelectionStateController, () -> mHvacController);
mCarNavigationBar.registerBottomBarTouchListener(mock(View.OnTouchListener.class));
CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
@@ -290,7 +290,7 @@
public void testRegisterNotificationController_createViewFirst_registrationSuccessful() {
mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
- () -> mCarFacetButtonController, () -> mHvacController);
+ mButtonSelectionStateController, () -> mHvacController);
CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
CarNavigationBarController.NotificationsShadeController controller =
@@ -307,7 +307,7 @@
public void testRegisterNotificationController_registerFirst_registrationSuccessful() {
mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
- () -> mCarFacetButtonController, () -> mHvacController);
+ mButtonSelectionStateController, () -> mHvacController);
mCarNavigationBar.registerNotificationController(
mock(CarNavigationBarController.NotificationsShadeController.class));
@@ -322,7 +322,7 @@
public void testShowAllKeyguardButtons_bottomEnabled_bottomKeyguardButtonsVisible() {
mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
- () -> mCarFacetButtonController, () -> mHvacController);
+ mButtonSelectionStateController, () -> mHvacController);
CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
View bottomKeyguardButtons = bottomBar.findViewById(R.id.lock_screen_nav_buttons);
@@ -335,7 +335,7 @@
public void testShowAllKeyguardButtons_bottomEnabled_bottomNavButtonsGone() {
mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
- () -> mCarFacetButtonController, () -> mHvacController);
+ mButtonSelectionStateController, () -> mHvacController);
CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
View bottomButtons = bottomBar.findViewById(R.id.nav_buttons);
@@ -348,7 +348,7 @@
public void testHideAllKeyguardButtons_bottomEnabled_bottomKeyguardButtonsGone() {
mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
- () -> mCarFacetButtonController, () -> mHvacController);
+ mButtonSelectionStateController, () -> mHvacController);
CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
View bottomKeyguardButtons = bottomBar.findViewById(R.id.lock_screen_nav_buttons);
@@ -363,7 +363,7 @@
public void testHideAllKeyguardButtons_bottomEnabled_bottomNavButtonsVisible() {
mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
- () -> mCarFacetButtonController, () -> mHvacController);
+ mButtonSelectionStateController, () -> mHvacController);
CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
View bottomButtons = bottomBar.findViewById(R.id.nav_buttons);
@@ -378,7 +378,7 @@
public void testToggleAllNotificationsUnseenIndicator_bottomEnabled_hasUnseen_setCorrectly() {
mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
- () -> mCarFacetButtonController, () -> mHvacController);
+ mButtonSelectionStateController, () -> mHvacController);
CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
CarNavigationButton notifications = bottomBar.findViewById(R.id.notifications);
@@ -393,7 +393,7 @@
public void testToggleAllNotificationsUnseenIndicator_bottomEnabled_noUnseen_setCorrectly() {
mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
- () -> mCarFacetButtonController, () -> mHvacController);
+ mButtonSelectionStateController, () -> mHvacController);
CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
CarNavigationButton notifications = bottomBar.findViewById(R.id.notifications);
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationButtonTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationButtonTest.java
new file mode 100644
index 0000000..96d567d
--- /dev/null
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationButtonTest.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.navigationbar.car;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.keyguard.AlphaOptimizedImageButton;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.tests.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class CarNavigationButtonTest extends SysuiTestCase {
+
+ private static final String DEFAULT_BUTTON_ACTIVITY_NAME =
+ "com.android.car.carlauncher/.CarLauncher";
+ private static final String APP_GRID_BUTTON_ACTIVITY_NAME =
+ "com.android.car.carlauncher/.AppGridActivity";
+ private static final String BROADCAST_ACTION_NAME =
+ "android.car.intent.action.TOGGLE_HVAC_CONTROLS";
+
+ private ActivityManager mActivityManager;
+ // LinearLayout with CarNavigationButtons with different configurations.
+ private LinearLayout mTestView;
+ // Does not have any selection state which is the default configuration.
+ private CarNavigationButton mDefaultButton;
+
+ @Before
+ public void setUp() {
+ mContext = spy(mContext);
+ mTestView = (LinearLayout) LayoutInflater.from(mContext).inflate(
+ R.layout.car_navigation_button_test, /* root= */ null);
+ mDefaultButton = mTestView.findViewById(R.id.default_no_selection_state);
+ mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
+ }
+
+ @Test
+ public void onCreate_iconIsVisible() {
+ AlphaOptimizedImageButton icon = mDefaultButton.findViewById(
+ R.id.car_nav_button_icon_image);
+
+ assertThat(icon.getDrawable()).isNotNull();
+ }
+
+ @Test
+ public void onSelected_selectedIconDefined_togglesIcon() {
+ mDefaultButton.setSelected(true);
+ Drawable selectedIconDrawable = ((AlphaOptimizedImageButton) mDefaultButton.findViewById(
+ R.id.car_nav_button_icon_image)).getDrawable();
+
+
+ mDefaultButton.setSelected(false);
+ Drawable unselectedIconDrawable = ((AlphaOptimizedImageButton) mDefaultButton.findViewById(
+ R.id.car_nav_button_icon_image)).getDrawable();
+
+ assertThat(selectedIconDrawable).isNotEqualTo(unselectedIconDrawable);
+ }
+
+ @Test
+ public void onSelected_selectedIconUndefined_displaysSameIcon() {
+ CarNavigationButton selectedIconUndefinedButton = mTestView.findViewById(
+ R.id.selected_icon_undefined);
+
+ selectedIconUndefinedButton.setSelected(true);
+ Drawable selectedIconDrawable = ((AlphaOptimizedImageButton) mDefaultButton.findViewById(
+ R.id.car_nav_button_icon_image)).getDrawable();
+
+
+ selectedIconUndefinedButton.setSelected(false);
+ Drawable unselectedIconDrawable = ((AlphaOptimizedImageButton) mDefaultButton.findViewById(
+ R.id.car_nav_button_icon_image)).getDrawable();
+
+ assertThat(selectedIconDrawable).isEqualTo(unselectedIconDrawable);
+ }
+
+ @Test
+ public void onUnselected_doesNotHighlightWhenSelected_applySelectedAlpha() {
+ mDefaultButton.setSelected(false);
+
+ assertThat(mDefaultButton.getAlpha()).isEqualTo(
+ CarNavigationButton.DEFAULT_SELECTED_ALPHA);
+ }
+
+ @Test
+ public void onSelected_doesNotHighlightWhenSelected_applySelectedAlpha() {
+ mDefaultButton.setSelected(true);
+
+ assertThat(mDefaultButton.getAlpha()).isEqualTo(
+ CarNavigationButton.DEFAULT_SELECTED_ALPHA);
+ }
+
+ @Test
+ public void onUnselected_highlightWhenSelected_applyDefaultUnselectedAlpha() {
+ CarNavigationButton highlightWhenSelectedButton = mTestView.findViewById(
+ R.id.highlightable_no_more_button);
+ highlightWhenSelectedButton.setSelected(false);
+
+ assertThat(highlightWhenSelectedButton.getAlpha()).isEqualTo(
+ CarNavigationButton.DEFAULT_UNSELECTED_ALPHA);
+ }
+
+ @Test
+ public void onSelected_highlightWhenSelected_applyDefaultSelectedAlpha() {
+ CarNavigationButton highlightWhenSelectedButton = mTestView.findViewById(
+ R.id.highlightable_no_more_button);
+ highlightWhenSelectedButton.setSelected(true);
+
+ assertThat(highlightWhenSelectedButton.getAlpha()).isEqualTo(
+ CarNavigationButton.DEFAULT_SELECTED_ALPHA);
+ }
+
+ @Test
+ public void onSelected_doesNotShowMoreWhenSelected_doesNotShowMoreIcon() {
+ mDefaultButton.setSelected(true);
+ AlphaOptimizedImageButton moreIcon = mDefaultButton.findViewById(
+ R.id.car_nav_button_more_icon);
+
+ assertThat(moreIcon.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void onSelected_showMoreWhenSelected_showsMoreIcon() {
+ CarNavigationButton showMoreWhenSelected = mTestView.findViewById(
+ R.id.not_highlightable_more_button);
+ showMoreWhenSelected.setSelected(true);
+ AlphaOptimizedImageButton moreIcon = showMoreWhenSelected.findViewById(
+ R.id.car_nav_button_more_icon);
+
+ assertThat(moreIcon.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void onUnselected_showMoreWhenSelected_doesNotShowMoreIcon() {
+ CarNavigationButton showMoreWhenSelected = mTestView.findViewById(
+ R.id.highlightable_no_more_button);
+ showMoreWhenSelected.setSelected(true);
+ showMoreWhenSelected.setSelected(false);
+ AlphaOptimizedImageButton moreIcon = showMoreWhenSelected.findViewById(
+ R.id.car_nav_button_more_icon);
+
+ assertThat(moreIcon.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void onClick_launchesIntentActivity() {
+ mDefaultButton.performClick();
+
+ assertThat(getCurrentActivityName()).isEqualTo(DEFAULT_BUTTON_ACTIVITY_NAME);
+
+ CarNavigationButton appGridButton = mTestView.findViewById(R.id.app_grid_activity);
+ appGridButton.performClick();
+
+ assertThat(getCurrentActivityName()).isEqualTo(APP_GRID_BUTTON_ACTIVITY_NAME);
+ }
+
+ @Test
+ public void onLongClick_longIntentDefined_launchesLongIntentActivity() {
+ mDefaultButton.performClick();
+
+ assertThat(getCurrentActivityName()).isEqualTo(DEFAULT_BUTTON_ACTIVITY_NAME);
+
+ CarNavigationButton appGridButton = mTestView.findViewById(
+ R.id.long_click_app_grid_activity);
+ appGridButton.performLongClick();
+
+ assertThat(getCurrentActivityName()).isEqualTo(APP_GRID_BUTTON_ACTIVITY_NAME);
+ }
+
+ @Test
+ public void onClick_useBroadcast_broadcastsIntent() {
+ CarNavigationButton appGridButton = mTestView.findViewById(R.id.broadcast);
+ appGridButton.performClick();
+
+ verify(mContext).sendBroadcastAsUser(argThat(new ArgumentMatcher<Intent>() {
+ @Override
+ public boolean matches(Intent argument) {
+ return argument.getAction().equals(BROADCAST_ACTION_NAME);
+ }
+ }), any());
+ }
+
+ @Test
+ public void onSetUnseen_hasUnseen_showsUnseenIndicator() {
+ mDefaultButton.setUnseen(true);
+ ImageView hasUnseenIndicator = mDefaultButton.findViewById(R.id.car_nav_button_unseen_icon);
+
+ assertThat(hasUnseenIndicator.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void onSetUnseen_doesNotHaveUnseen_hidesUnseenIndicator() {
+ mDefaultButton.setUnseen(false);
+ ImageView hasUnseenIndicator = mDefaultButton.findViewById(R.id.car_nav_button_unseen_icon);
+
+ assertThat(hasUnseenIndicator.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ private String getCurrentActivityName() {
+ return mActivityManager.getRunningTasks(1).get(0).topActivity.flattenToShortString();
+ }
+}
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index 9e49826..c2ce840 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -262,10 +262,11 @@
return;
}
+ stopForeground(true);
mJustCancelledByUser = true;
if (mInstallTask.cancel(false)) {
- // Will cleanup and post status in onResult()
+ // Will stopSelf() in onResult()
Log.d(TAG, "Cancel request filed successfully");
} else {
Log.e(TAG, "Trying to cancel installation while it's already completed.");
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java
index 8a2948b..3b3933b 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java
@@ -16,9 +16,6 @@
package com.android.dynsystem;
-import static android.os.image.DynamicSystemClient.KEY_SYSTEM_SIZE;
-import static android.os.image.DynamicSystemClient.KEY_USERDATA_SIZE;
-
import android.app.Activity;
import android.app.KeyguardManager;
import android.content.Context;
@@ -88,12 +85,8 @@
private void startInstallationService() {
// retrieve data from calling intent
Intent callingIntent = getIntent();
-
Uri url = callingIntent.getData();
- long systemSize = callingIntent.getLongExtra(KEY_SYSTEM_SIZE, 0);
- long userdataSize = callingIntent.getLongExtra(KEY_USERDATA_SIZE, 0);
- boolean enableWhenCompleted = callingIntent.getBooleanExtra(
- DynamicSystemInstallationService.KEY_ENABLE_WHEN_COMPLETED, false);
+ Bundle extras = callingIntent.getExtras();
sVerifiedUrl = url.toString();
@@ -101,10 +94,7 @@
Intent intent = new Intent(this, DynamicSystemInstallationService.class);
intent.setData(url);
intent.setAction(DynamicSystemClient.ACTION_START_INSTALL);
- intent.putExtra(KEY_SYSTEM_SIZE, systemSize);
- intent.putExtra(KEY_USERDATA_SIZE, userdataSize);
- intent.putExtra(
- DynamicSystemInstallationService.KEY_ENABLE_WHEN_COMPLETED, enableWhenCompleted);
+ intent.putExtras(extras);
Log.d(TAG, "Starting Installation Service");
startServiceAsUser(intent, UserHandle.SYSTEM);
diff --git a/packages/InputDevices/res/values-el/strings.xml b/packages/InputDevices/res/values-el/strings.xml
index 05de3b7..b9d65c5 100644
--- a/packages/InputDevices/res/values-el/strings.xml
+++ b/packages/InputDevices/res/values-el/strings.xml
@@ -8,7 +8,7 @@
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Αγγλικά (ΗΠΑ), τύπου International"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Αγγλικά (ΗΠΑ), τύπου Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Αγγλικά (ΗΠΑ), τύπου Dvorak"</string>
- <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Αγγλικά (ΗΠΑ), στυλ Workman"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Αγγλικά (ΗΠΑ), στιλ Workman"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Γερμανικά"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Γαλλικά"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Γαλλικά (Καναδά)"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 3f99d10..5b84b10 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -203,9 +203,9 @@
<string name="vpn_settings_not_available" msgid="2894137119965668920">"Erabiltzaile honek ezin ditu VPN ezarpenak atzitu"</string>
<string name="tethering_settings_not_available" msgid="266821736434699780">"Erabiltzaile honek ezin ditu konexioa partekatzeko ezarpenak atzitu"</string>
<string name="apn_settings_not_available" msgid="1147111671403342300">"Erabiltzaile honek ezin ditu APN ezarpenak atzitu"</string>
- <string name="enable_adb" msgid="8072776357237289039">"USB arazketa"</string>
+ <string name="enable_adb" msgid="8072776357237289039">"USB bidezko arazketa"</string>
<string name="enable_adb_summary" msgid="3711526030096574316">"Gaitu arazketa modua USBa konektatzean"</string>
- <string name="clear_adb_keys" msgid="3010148733140369917">"Baliogabetu USB arazketarako baimenak"</string>
+ <string name="clear_adb_keys" msgid="3010148733140369917">"Baliogabetu USB bidezko arazketarako baimenak"</string>
<string name="bugreport_in_power" msgid="8664089072534638709">"Akatsen txostenerako lasterbidea"</string>
<string name="bugreport_in_power_summary" msgid="1885529649381831775">"Bateriaren menuan, erakutsi akatsen txostena sortzeko botoia"</string>
<string name="keep_screen_on" msgid="1187161672348797558">"Mantendu aktibo"</string>
@@ -267,9 +267,9 @@
<string name="debug_view_attributes" msgid="3539609843984208216">"Gaitu ikuspegiaren atributuak ikuskatzeko aukera"</string>
<string name="mobile_data_always_on_summary" msgid="1112156365594371019">"Mantendu datu-konexioa beti aktibo, baita wifi-konexioa aktibo dagoenean ere (sare batetik bestera bizkor aldatu ahal izateko)."</string>
<string name="tethering_hardware_offload_summary" msgid="7801345335142803029">"Erabilgarri badago, erabili konexioa partekatzeko hardwarearen azelerazioa"</string>
- <string name="adb_warning_title" msgid="7708653449506485728">"USB arazketa onartu?"</string>
- <string name="adb_warning_message" msgid="8145270656419669221">"USB arazketa garapen-xedeetarako soilik dago diseinatuta. Erabil ezazu ordenagailuaren eta gailuaren artean datuak kopiatzeko, aplikazioak gailuan jakinarazi gabe instalatzeko eta erregistro-datuak irakurtzeko."</string>
- <string name="adb_keys_warning_message" msgid="2968555274488101220">"Aurretik baimendutako ordenagailu guztiei USB arazketarako sarbidea baliogabetu nahi diezu?"</string>
+ <string name="adb_warning_title" msgid="7708653449506485728">"USB bidezko arazketa onartu?"</string>
+ <string name="adb_warning_message" msgid="8145270656419669221">"USB bidezko arazketa garapen-xedeetarako soilik dago diseinatuta. Erabil ezazu ordenagailuaren eta gailuaren artean datuak kopiatzeko, aplikazioak gailuan jakinarazi gabe instalatzeko eta erregistro-datuak irakurtzeko."</string>
+ <string name="adb_keys_warning_message" msgid="2968555274488101220">"Aurretik baimendutako ordenagailu guztiei USB bidezko arazketarako sarbidea baliogabetu nahi diezu?"</string>
<string name="dev_settings_warning_title" msgid="8251234890169074553">"Baimendu garapenerako ezarpenak?"</string>
<string name="dev_settings_warning_message" msgid="37741686486073668">"Ezarpen hauek garapen-xedeetarako pentsatu dira soilik. Baliteke ezarpenen eraginez gailua matxuratzea edo funtzionamendu okerra izatea."</string>
<string name="verify_apps_over_usb_title" msgid="6031809675604442636">"Egiaztatu USBko aplikazioak"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index f9ac3a5..d0307ec 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -276,7 +276,7 @@
<string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"नुकसानदेह व्यवहार के लिए ADB/ADT से इंस्टॉल किए गए ऐप्लिकेशन जाँचें."</string>
<string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"बिना नाम वाले ब्लूटूथ डिवाइस (केवल MAC पते वाले) दिखाए जाएंगे"</string>
<string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"दूर के डिवाइस पर आवाज़ बहुत बढ़ जाने या उससे नियंत्रण हटने जैसी समस्याएं होने पर, यह ब्लूटूथ के ज़रिए आवाज़ के नियंत्रण की सुविधा रोक देता है."</string>
- <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Bluetooth Gabeldorsche सुविधा का स्टैक चालू करें."</string>
+ <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"ब्लूटूथ Gabeldorsche सुविधा का स्टैक चालू करें."</string>
<string name="enable_terminal_title" msgid="3834790541986303654">"स्थानीय टर्मिनल"</string>
<string name="enable_terminal_summary" msgid="2481074834856064500">"लोकल शेल तक पहुंचने की सुविधा देने वाले टर्मिनल ऐप को चालू करें"</string>
<string name="hdcp_checking_title" msgid="3155692785074095986">"एचडीसीपी जाँच"</string>
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index 9b4a6d6..7b589370 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -132,6 +132,20 @@
<item>avrcp16</item>
</string-array>
+ <!-- Titles for Bluetooth MAP Versions -->
+ <string-array name="bluetooth_map_versions">
+ <item>MAP 1.2 (Default)</item>
+ <item>MAP 1.3</item>
+ <item>MAP 1.4</item>
+ </string-array>
+
+ <!-- Values for Bluetooth MAP Versions -->
+ <string-array name="bluetooth_map_version_values">
+ <item>map12</item>
+ <item>map13</item>
+ <item>map14</item>
+ </string-array>
+
<!-- Titles for Bluetooth Audio Codec selection preference. [CHAR LIMIT=50] -->
<string-array name="bluetooth_a2dp_codec_titles">
<item>Use System Selection (Default)</item>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index f40105d..a7df6db 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -606,6 +606,11 @@
<!-- UI debug setting: Select Bluetooth AVRCP Version -->
<string name="bluetooth_select_avrcp_version_dialog_title">Select Bluetooth AVRCP Version</string>
+ <!-- UI debug setting: Select Bluetooth MAP Version [CHAR LIMIT=NONE] -->
+ <string name="bluetooth_select_map_version_string">Bluetooth MAP Version</string>
+ <!-- UI debug setting: Select Bluetooth MAP Version [CHAR LIMIT=NONE] -->
+ <string name="bluetooth_select_map_version_dialog_title">Select Bluetooth MAP Version</string>
+
<!-- UI debug setting: Trigger Bluetooth Audio Codec Selection -->
<string name="bluetooth_select_a2dp_codec_type">Bluetooth Audio Codec</string>
<!-- UI debug setting: Trigger Bluetooth Audio Codec Selection -->
@@ -699,7 +704,7 @@
<!-- Summary of checkbox for disabling Bluetooth absolute volume -->
<string name="bluetooth_disable_absolute_volume_summary">Disables the Bluetooth absolute volume feature in case of volume issues with remote devices such as unacceptably loud volume or lack of control.</string>
<!-- Summary of checkbox for enabling Bluetooth Gabeldorche features [CHAR LIMIT=none] -->
- <string name="bluetooth_enable_gabeldorsche_summary">Enables the Bluetooth Gabeldorche feature stack.</string>
+ <string name="bluetooth_enable_gabeldorsche_summary">Enables the Bluetooth Gabeldorsche feature stack.</string>
<!-- Title of checkbox setting that enables the terminal app. [CHAR LIMIT=32] -->
<string name="enable_terminal_title">Local terminal</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index b13c483..81739e0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -1766,7 +1766,10 @@
if (config.allowedKeyManagement.get(KeyMgmt.OWE)) {
return SECURITY_OWE;
}
- return (config.wepKeys[0] != null) ? SECURITY_WEP : SECURITY_NONE;
+ return (config.wepTxKeyIndex >= 0
+ && config.wepTxKeyIndex < config.wepKeys.length
+ && config.wepKeys[config.wepTxKeyIndex] != null)
+ ? SECURITY_WEP : SECURITY_NONE;
}
public static String securityToString(int security, int pskType) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 0e3f81b..aa36dca 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -266,9 +266,6 @@
dumpSetting(s, p,
Settings.Global.BACKUP_AGENT_TIMEOUT_PARAMETERS,
GlobalSettingsProto.Backup.BACKUP_AGENT_TIMEOUT_PARAMETERS);
- dumpSetting(s, p,
- Settings.Global.BACKUP_MULTI_USER_ENABLED,
- GlobalSettingsProto.Backup.BACKUP_MULTI_USER_ENABLED);
p.end(backupToken);
final long batteryToken = p.start(GlobalSettingsProto.BATTERY);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 4309c80..1e0c1d8 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -387,8 +387,10 @@
case Settings.CALL_METHOD_SET_ALL_CONFIG: {
String prefix = getSettingPrefix(args);
Map<String, String> flags = getSettingFlags(args);
- setAllConfigSettings(prefix, flags);
- break;
+ Bundle result = new Bundle();
+ result.putBoolean(Settings.KEY_CONFIG_SET_RETURN,
+ setAllConfigSettings(prefix, flags));
+ return result;
}
case Settings.CALL_METHOD_RESET_CONFIG: {
@@ -2666,20 +2668,28 @@
return success;
}
+ /**
+ * Set Settings using consumed keyValues, returns true if the keyValues can be set, false
+ * otherwise.
+ */
public boolean setSettingsLocked(int type, int userId, String prefix,
Map<String, String> keyValues, String packageName) {
final int key = makeKey(type, userId);
SettingsState settingsState = peekSettingsStateLocked(key);
if (settingsState != null) {
+ if (SETTINGS_TYPE_CONFIG == type && settingsState.isNewConfigBannedLocked(prefix,
+ keyValues)) {
+ return false;
+ }
List<String> changedSettings =
settingsState.setSettingsLocked(prefix, keyValues, packageName);
if (!changedSettings.isEmpty()) {
notifyForConfigSettingsChangeLocked(key, prefix, changedSettings);
}
}
-
- return settingsState != null;
+ // keyValues aren't banned and can be set
+ return true;
}
public boolean deleteSettingLocked(int type, int userId, String name, boolean forceNotify,
@@ -2739,7 +2749,8 @@
public void resetSettingsLocked(int type, int userId, String packageName, int mode,
String tag) {
- resetSettingsLocked(type, userId, packageName, mode, tag, null);
+ resetSettingsLocked(type, userId, packageName, mode, tag, /*prefix=*/
+ null);
}
public void resetSettingsLocked(int type, int userId, String packageName, int mode,
@@ -2750,6 +2761,7 @@
return;
}
+ banConfigurationIfNecessary(type, prefix, settingsState);
switch (mode) {
case Settings.RESET_MODE_PACKAGE_DEFAULTS: {
for (String name : settingsState.getSettingNamesLocked()) {
@@ -3173,6 +3185,34 @@
return getTypeFromKey(key) == SETTINGS_TYPE_SSAID;
}
+ private boolean shouldBan(int type) {
+ if (SETTINGS_TYPE_CONFIG != type) {
+ return false;
+ }
+ final int callingUid = Binder.getCallingUid();
+ final int appId = UserHandle.getAppId(callingUid);
+
+ // Only non-shell resets should result in namespace banning
+ return appId != SHELL_UID;
+ }
+
+ private void banConfigurationIfNecessary(int type, @Nullable String prefix,
+ SettingsState settingsState) {
+ // Banning should be performed only for Settings.Config and for non-shell reset calls
+ if (!shouldBan(type)) {
+ return;
+ }
+ if (prefix != null) {
+ settingsState.banConfigurationLocked(prefix, getAllConfigFlags(prefix));
+ } else {
+ Set<String> configPrefixes = settingsState.getAllConfigPrefixesLocked();
+ for (String configPrefix : configPrefixes) {
+ settingsState.banConfigurationLocked(configPrefix,
+ getAllConfigFlags(configPrefix));
+ }
+ }
+ }
+
private File getSettingsFile(int key) {
if (isConfigSettingsKey(key)) {
final int userId = getUserIdFromKey(key);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 086b20f..5b1b530 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -65,9 +65,12 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
/**
* This class contains the state for one type of settings. It is responsible
@@ -109,6 +112,11 @@
private static final String ATTR_ID = "id";
private static final String ATTR_NAME = "name";
+ private static final String TAG_NAMESPACE_HASHES = "namespaceHashes";
+ private static final String TAG_NAMESPACE_HASH = "namespaceHash";
+ private static final String ATTR_NAMESPACE = "namespace";
+ private static final String ATTR_BANNED_HASH = "bannedHash";
+
/**
* Non-binary value will be written in this attributes.
*/
@@ -159,6 +167,9 @@
private final ArrayMap<String, Setting> mSettings = new ArrayMap<>();
@GuardedBy("mLock")
+ private final ArrayMap<String, String> mNamespaceBannedHashes = new ArrayMap<>();
+
+ @GuardedBy("mLock")
private final ArrayMap<String, Integer> mPackageToMemoryUsage;
@GuardedBy("mLock")
@@ -418,6 +429,41 @@
return true;
}
+ @GuardedBy("mLock")
+ public boolean isNewConfigBannedLocked(String prefix, Map<String, String> keyValues) {
+ // Replaces old style "null" String values with actual null's. This is done to simulate
+ // what will happen to String "null" values when they are written to Settings. This needs to
+ // be done here make sure that config hash computed during is banned check matches the
+ // one computed during banning when values are already stored.
+ keyValues = removeNullValueOldStyle(keyValues);
+ String bannedHash = mNamespaceBannedHashes.get(prefix);
+ if (bannedHash == null) {
+ return false;
+ }
+ return bannedHash.equals(hashCode(keyValues));
+ }
+
+ @GuardedBy("mLock")
+ public void banConfigurationLocked(String prefix, Map<String, String> keyValues) {
+ if (prefix == null || keyValues.isEmpty()) {
+ return;
+ }
+ // The write is intentionally not scheduled here, banned hashes should and will be written
+ // when the related setting changes are written
+ mNamespaceBannedHashes.put(prefix, hashCode(keyValues));
+ }
+
+ @GuardedBy("mLock")
+ public Set<String> getAllConfigPrefixesLocked() {
+ Set<String> prefixSet = new HashSet<>();
+ final int settingsCount = mSettings.size();
+ for (int i = 0; i < settingsCount; i++) {
+ String name = mSettings.keyAt(i);
+ prefixSet.add(name.split("/")[0] + "/");
+ }
+ return prefixSet;
+ }
+
// The settings provider must hold its lock when calling here.
// Returns the list of keys which changed (added, updated, or deleted).
@GuardedBy("mLock")
@@ -710,10 +756,12 @@
boolean wroteState = false;
final int version;
final ArrayMap<String, Setting> settings;
+ final ArrayMap<String, String> namespaceBannedHashes;
synchronized (mLock) {
version = mVersion;
settings = new ArrayMap<>(mSettings);
+ namespaceBannedHashes = new ArrayMap<>(mNamespaceBannedHashes);
mDirty = false;
mWriteScheduled = false;
}
@@ -756,8 +804,19 @@
+ setting.getValue());
}
}
-
serializer.endTag(null, TAG_SETTINGS);
+
+ serializer.startTag(null, TAG_NAMESPACE_HASHES);
+ for (int i = 0; i < namespaceBannedHashes.size(); i++) {
+ String namespace = namespaceBannedHashes.keyAt(i);
+ String bannedHash = namespaceBannedHashes.get(namespace);
+ writeSingleNamespaceHash(serializer, namespace, bannedHash);
+ if (DEBUG_PERSISTENCE) {
+ Slog.i(LOG_TAG, "[PERSISTED] namespace=" + namespace
+ + ", bannedHash=" + bannedHash);
+ }
+ }
+ serializer.endTag(null, TAG_NAMESPACE_HASHES);
serializer.endDocument();
destination.finishWrite(out);
@@ -869,6 +928,21 @@
}
}
+ private static void writeSingleNamespaceHash(XmlSerializer serializer, String namespace,
+ String bannedHashCode) throws IOException {
+ if (namespace == null || bannedHashCode == null) {
+ return;
+ }
+ serializer.startTag(null, TAG_NAMESPACE_HASH);
+ serializer.attribute(null, ATTR_NAMESPACE, namespace);
+ serializer.attribute(null, ATTR_BANNED_HASH, bannedHashCode);
+ serializer.endTag(null, TAG_NAMESPACE_HASH);
+ }
+
+ private static String hashCode(Map<String, String> keyValues) {
+ return Integer.toString(keyValues.hashCode());
+ }
+
private String getValueAttribute(XmlPullParser parser, String attr, String base64Attr) {
if (mVersion >= SETTINGS_VERSION_NEW_ENCODING) {
final String value = parser.getAttributeValue(null, attr);
@@ -939,6 +1013,8 @@
String tagName = parser.getName();
if (tagName.equals(TAG_SETTINGS)) {
parseSettingsLocked(parser);
+ } else if (tagName.equals(TAG_NAMESPACE_HASHES)) {
+ parseNamespaceHash(parser);
}
}
}
@@ -982,6 +1058,37 @@
}
}
+ @GuardedBy("mLock")
+ private void parseNamespaceHash(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ if (parser.getName().equals(TAG_NAMESPACE_HASH)) {
+ String namespace = parser.getAttributeValue(null, ATTR_NAMESPACE);
+ String bannedHashCode = parser.getAttributeValue(null, ATTR_BANNED_HASH);
+ mNamespaceBannedHashes.put(namespace, bannedHashCode);
+ }
+ }
+ }
+
+ private static Map<String, String> removeNullValueOldStyle(Map<String, String> keyValues) {
+ Iterator<Map.Entry<String, String>> it = keyValues.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry<String, String> keyValueEntry = it.next();
+ if (NULL_VALUE_OLD_STYLE.equals(keyValueEntry.getValue())) {
+ keyValueEntry.setValue(null);
+ }
+ }
+ return keyValues;
+ }
+
private final class MyHandler extends Handler {
public static final int MSG_PERSIST_SETTINGS = 1;
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 443811f..7278225 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -571,7 +571,6 @@
Settings.Global.CHAINED_BATTERY_ATTRIBUTION_ENABLED,
Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS,
Settings.Global.BACKUP_AGENT_TIMEOUT_PARAMETERS,
- Settings.Global.BACKUP_MULTI_USER_ENABLED,
Settings.Global.ISOLATED_STORAGE_LOCAL,
Settings.Global.ISOLATED_STORAGE_REMOTE,
Settings.Global.APPOP_HISTORY_PARAMETERS,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index aefdce4..347d6c2 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -230,6 +230,9 @@
<!-- Permission required for CTS test - CtsOsTestCases -->
<uses-permission android:name="android.permission.MANAGE_CRATES"/>
+ <!-- Allows setting brightness from the shell -->
+ <uses-permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS"/>
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/res-keyguard/values-ar/strings.xml b/packages/SystemUI/res-keyguard/values-ar/strings.xml
index efe4088..393da27 100644
--- a/packages/SystemUI/res-keyguard/values-ar/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ar/strings.xml
@@ -74,7 +74,7 @@
<string name="kg_pattern_instructions" msgid="5376036737065051736">"ارسم نقشك"</string>
<string name="kg_sim_pin_instructions" msgid="1942424305184242951">"أدخل رقم التعريف الشخصي لشريحة SIM."</string>
<string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"أدخل رقم التعريف الشخصي لشريحة SIM التابعة للمشغّل \"<xliff:g id="CARRIER">%1$s</xliff:g>\"."</string>
- <string name="kg_sim_lock_esim_instructions" msgid="5577169988158738030">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> يجب إيقاف eSIM لاستخدام الجهاز دون خدمة جوّال."</string>
+ <string name="kg_sim_lock_esim_instructions" msgid="5577169988158738030">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> يجب إيقاف eSIM لاستخدام الجهاز بدون خدمة الأجهزة الجوّالة."</string>
<string name="kg_pin_instructions" msgid="822353548385014361">"أدخل رقم التعريف الشخصي"</string>
<string name="kg_password_instructions" msgid="324455062831719903">"أدخل كلمة المرور"</string>
<string name="kg_puk_enter_puk_hint" msgid="3005288372875367017">"شريحة SIM غير مفعّلة الآن. أدخل رمز PUK للمتابعة. اتصل بمشغل شبكة الجوّال للاطلاع على التفاصيل."</string>
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index f7e9fed..4d184d5 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -240,6 +240,15 @@
<!-- Description of airplane mode -->
<string name="airplane_mode">Airplane mode</string>
+ <!-- An explanation text that the PIN needs to be entered to prepare for an operating system update. [CHAR LIMIT=80] -->
+ <string name="kg_prompt_reason_prepare_for_update_pin">PIN required to prepare for update</string>
+
+ <!-- An explanation text that the pattern needs to be entered to prepare for an operating system update. [CHAR LIMIT=80] -->
+ <string name="kg_prompt_reason_prepare_for_update_pattern">Pattern required to prepare for update</string>
+
+ <!-- An explanation text that the password needs to be entered to prepare for an operating system update. [CHAR LIMIT=80] -->
+ <string name="kg_prompt_reason_prepare_for_update_password">Password required to prepare for update</string>
+
<!-- An explanation text that the pattern needs to be solved since the device has just been restarted. [CHAR LIMIT=80] -->
<string name="kg_prompt_reason_restart_pattern">Pattern required after device restarts</string>
diff --git a/packages/SystemUI/res/layout/people_strip.xml b/packages/SystemUI/res/layout/people_strip.xml
index f0ac08b..982aa8e 100644
--- a/packages/SystemUI/res/layout/people_strip.xml
+++ b/packages/SystemUI/res/layout/people_strip.xml
@@ -18,7 +18,10 @@
<com.android.systemui.statusbar.notification.stack.PeopleHubView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="105dp">
+ android:layout_height="@dimen/notification_section_header_height"
+ android:focusable="true"
+ android:clickable="true"
+>
<com.android.systemui.statusbar.notification.row.NotificationBackgroundView
android:id="@+id/backgroundNormal"
@@ -34,199 +37,56 @@
android:id="@+id/people_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:paddingTop="12dp"
- android:paddingBottom="12dp"
android:gravity="center"
android:orientation="horizontal">
- <View
- android:layout_width="8dp"
- android:layout_height="match_parent"
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:layout_weight="1"
+ android:layout_marginStart="@dimen/notification_section_header_padding_left"
+ android:gravity="start"
+ android:textAlignment="gravity"
+ android:text="@string/notification_section_header_conversations"
+ android:textSize="12sp"
+ android:textColor="@color/notification_section_header_label_color"
+ android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
/>
- <LinearLayout
- android:layout_width="70dp"
- android:layout_height="match_parent"
- android:gravity="center_horizontal"
- android:orientation="vertical"
- android:visibility="invisible">
-
- <ImageView
- android:id="@+id/person_icon"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:scaleType="fitCenter"
- />
-
- <TextView
- android:id="@+id/person_name"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="8dp"
- android:ellipsize="end"
- android:maxLines="2"
- android:textAlignment="center"
- />
-
- </LinearLayout>
-
- <View
- android:layout_width="8dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
+ <ImageView
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:padding="8dp"
+ android:scaleType="fitCenter"
/>
- <View
- android:layout_width="8dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
+ <ImageView
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:padding="8dp"
+ android:scaleType="fitCenter"
/>
- <LinearLayout
- android:layout_width="70dp"
- android:layout_height="match_parent"
- android:gravity="center_horizontal"
- android:orientation="vertical"
- android:visibility="invisible">
-
- <ImageView
- android:id="@+id/person_icon"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:scaleType="fitCenter"
- />
-
- <TextView
- android:id="@+id/person_name"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="8dp"
- android:ellipsize="end"
- android:maxLines="2"
- android:textAlignment="center"
- />
-
- </LinearLayout>
-
- <View
- android:layout_width="8dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
+ <ImageView
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:padding="8dp"
+ android:scaleType="fitCenter"
/>
- <View
- android:layout_width="8dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
+ <ImageView
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:padding="8dp"
+ android:scaleType="fitCenter"
/>
- <LinearLayout
- android:layout_width="70dp"
- android:layout_height="match_parent"
- android:gravity="center_horizontal"
- android:orientation="vertical"
- android:visibility="invisible">
-
- <ImageView
- android:id="@+id/person_icon"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:scaleType="fitCenter"
- />
-
- <TextView
- android:id="@+id/person_name"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="8dp"
- android:ellipsize="end"
- android:maxLines="2"
- android:textAlignment="center"
- />
-
- </LinearLayout>
-
- <View
- android:layout_width="8dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- />
-
- <View
- android:layout_width="8dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- />
-
- <LinearLayout
- android:layout_width="70dp"
- android:layout_height="match_parent"
- android:gravity="center_horizontal"
- android:orientation="vertical"
- android:visibility="invisible">
-
- <ImageView
- android:id="@+id/person_icon"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:scaleType="fitCenter"
- />
-
- <TextView
- android:id="@+id/person_name"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="8dp"
- android:ellipsize="end"
- android:maxLines="2"
- android:textAlignment="center"
- />
-
- </LinearLayout>
-
- <View
- android:layout_width="8dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- />
-
- <View
- android:layout_width="8dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- />
-
- <LinearLayout
- android:layout_width="70dp"
- android:layout_height="match_parent"
- android:gravity="center_horizontal"
- android:orientation="vertical"
- android:visibility="invisible">
-
- <ImageView
- android:id="@+id/person_icon"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:scaleType="fitCenter"
- />
-
- <TextView
- android:id="@+id/person_name"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="8dp"
- android:ellipsize="end"
- android:maxLines="2"
- android:textAlignment="center"
- />
-
- </LinearLayout>
-
- <View
- android:layout_width="8dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
+ <ImageView
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_marginEnd="8dp"
+ android:padding="8dp"
+ android:scaleType="fitCenter"
/>
</LinearLayout>
@@ -236,4 +96,4 @@
android:layout_width="match_parent"
android:layout_height="match_parent" />
-</com.android.systemui.statusbar.notification.stack.PeopleHubView>
\ No newline at end of file
+</com.android.systemui.statusbar.notification.stack.PeopleHubView>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 4869be1..479f255 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -17,9 +17,14 @@
*/
-->
-<merge
+
+<com.android.systemui.statusbar.phone.NotificationPanelView
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:systemui="http://schemas.android.com/apk/res-auto">
+ xmlns:systemui="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/notification_panel"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/transparent">
<FrameLayout
android:id="@+id/big_clock_container"
android:layout_width="match_parent"
@@ -97,4 +102,4 @@
android:background="@drawable/qs_navbar_scrim" />
<include layout="@layout/status_bar_expanded_plugin_frame"/>
-</merge>
+</com.android.systemui.statusbar.phone.NotificationPanelView>
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index 57834da..9716a00 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -64,7 +64,7 @@
sysui:ignoreRightInset="true"
/>
- <ViewStub android:id="@+id/status_bar_expanded"
+ <include layout="@layout/status_bar_expanded"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible" />
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 08be42a..ea7b9bb 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Skakel Batterybespaarder af"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> sal toegang hê tot al die inligting wat op jou skerm sigbaar is of wat op jou toestel gespeel word terwyl dit opneem of uitsaai. Dit sluit in inligting soos wagwoorde, betalingbesonderhede, foto\'s, boodskappe en oudio wat jy speel."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"Die diens wat hierdie funksie verskaf, sal toegang hê tot al die inligting wat op jou skerm sigbaar is of wat op jou toestel gespeel word terwyl dit opneem of uitsaai. Dit sluit in inligting soos wagwoorde, betalingbesonderhede, foto\'s, boodskappe en oudio wat jy speel."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Begin opneem of uitsaai?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Begin opneem of uitsaai met <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Moenie weer wys nie"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Vee alles uit"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 18a4a7a..9412f0f 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"ባትሪ ቆጣቢን አጥፋ"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> በእርስዎ ማያ ገጽ ላይ ያለን ወይም በእርስዎ መሣሪያ ላይ በመጫወት ላይ ያለን ሁሉንም መረጃ በቀረጻ ወይም casting ላይ እያለ መዳረሻ ይኖረዋል። ይህ እንደ የይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ ፎቶዎች፣ መልዕክቶች እና እርስዎ የሚጫውቱት ኦዲዮን የመሳሰለ መረጃን ያካትታል።"</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"ይህን ተግባር የሚያቀርበው አገልግሎት በእርስዎ ማያ ገጽ ላይ ያለን ወይም በእርስዎ መሣሪያ ላይ በመጫወት ላይ ያለን ሁሉንም መረጃ በቀረጻ ወይም casting ላይ እያለ መዳረሻ ይኖረዋል። ይህ እንደ የይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ ፎቶዎች፣ መልዕክቶች እና እርስዎ የሚጫውቱት ኦዲዮን የመሳሰለ መረጃን ያካትታል።"</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"ቀረጻ ወይም cast ማድረግ ይጀምር?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"ከ<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ጋር ቀረጻ ወይም casting ይጀምር?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"ዳግመኛ አታሳይ"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"ሁሉንም አጽዳ"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 07be692..f675265 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -111,7 +111,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"المساعد الصوتي"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"إلغاء القفل"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"في انتظار بصمة الإصبع"</string>
- <string name="accessibility_unlock_without_fingerprint" msgid="1811563723195375298">"إلغاء القفل دون استخدام بصمة إصبعك"</string>
+ <string name="accessibility_unlock_without_fingerprint" msgid="1811563723195375298">"فتح القفل بدون استخدام بصمة إصبعك"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"مسح الوجه"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"إرسال"</string>
<string name="accessibility_manage_notification" msgid="582215815790143983">"إدارة الإشعارات"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 14f5e7f..2b94d67 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -468,8 +468,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Isključi Uštedu baterije"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> će imati pristup svim informacijama koje se prikazuju na ekranu ili reprodukuju sa uređaja tokom snimanja ili prebacivanja. To obuhvata informacije poput lozinki, informacija o plaćanju, slika, poruka i zvuka koji puštate."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"Usluga koja pruža ovu funkciju će imati pristup svim informacijama koje se prikazuju na ekranu ili reprodukuju sa uređaja tokom snimanja ili prebacivanja. To obuhvata informacije poput lozinki, informacija o plaćanju, slika, poruka i zvuka koji puštate."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Želite da počnete snimanje ili prebacivanje?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Želite da počnete snimanje ili prebacivanje pomoću aplikacije <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Ne prikazuj ponovo"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Obriši sve"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 9b7b62a..26102fb 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Изключване на режима за запазване на батерията"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ще има достъп до цялата информация, която е видима на екрана или възпроизвеждана от устройството ви по време на записване или предаване. Това включва различна информация, като например пароли, данни за плащане, снимки, съобщения и възпроизвеждано аудио."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"Услугата, предоставяща тази функция, ще има достъп до цялата информация, която е видима на екрана или възпроизвеждана от устройството ви по време на записване или предаване. Това включва различна информация, като например пароли, данни за плащане, снимки, съобщения и възпроизвеждано аудио."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Да се стартира ли записване или предаване?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Да се стартира ли записване или предаване чрез <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Да не се показва отново"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Изчистване на всички"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 6e2285e..5851011 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -468,8 +468,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Isključi Uštedu baterije"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"Aplikacija <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> će imati pristup svim informacijama koje se prikazuju na ekranu ili koje se reproduciraju s vašeg uređaja za vrijeme snimanja ili emitiranja. To obuhvata informacije kao što su lozinke, detalji o plaćanju, fotografije, poruke i zvuk koji reproducirate."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"Usluga koja pruža ovu funkciju će imati pristup svim informacijama koje se prikazuju na ekranu ili koje se reproduciraju s vašeg uređaja za vrijeme snimanja ili emitiranja. To obuhvata informacije kao što su lozinke, detalji o plaćanju, fotografije, poruke i zvuk koji reproducirate."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Započeti snimanje ili emitiranje?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Započeti snimanje ili emitiranje s aplikacijom <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Ne prikazuj opet"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Očisti sve"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 4909e10..9fb7230 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Desactiva l\'estalvi de bateria"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tindrà accés a tota la informació que es veu en pantalla o que es reprodueix al dispositiu mentre graves o emets contingut, com ara contrasenyes, detalls dels pagaments, fotos, missatges i àudio."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"El servei que ofereix aquesta funció tindrà accés a tota la informació que es veu en pantalla o que es reprodueix al dispositiu mentre graves o emets contingut, com ara contrasenyes, detalls dels pagaments, fotos, missatges i àudio."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Vols començar a gravar o emetre contingut?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Vols començar a gravar o emetre contingut amb <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"No ho tornis a mostrar"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Esborra-ho tot"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index b5b8aa3..30724f8 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -471,8 +471,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Vypnout spořič baterie"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"Aplikace <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> bude mít přístup ke všem informacím, které jsou viditelné na obrazovce nebo které jsou přehrávány ze za řízení při nahrávání nebo odesílání. Týká se to i hesel, údajů o platbě, fotek, zpráv a přehrávaných zvuků."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"Služba, která poskytuje tuto funkci, bude mít přístup ke všem informacím, které jsou viditelné na obrazovce nebo které jsou přehrávány ze zařízení při nahrávání nebo odesílání. Týká se to i hesel, údajů o platbě, fotek, zpráv a přehrávaných zvuků."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Začít nahrávat nebo odesílat?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Začít nahrávat nebo odesílat s aplikací <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Tuto zprávu příště nezobrazovat"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Smazat vše"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 80c8d28..e20a276 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Deaktiver batterisparefunktion"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> får adgang til alle de oplysninger, der er synlige på din skærm, eller som afspilles på din enhed, når du optager eller caster. Dette omfatter oplysninger som f.eks. adgangskoder, betalingsoplysninger, billeder, beskeder og afspillet lyd."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"Tjenesten, der tilbyder denne funktion, får adgang til alle de oplysninger, der er synlige på din skærm, eller som afspilles på din enhed, når du optager eller caster. Dette omfatter oplysninger som f.eks. adgangskoder, betalingsoplysninger, billeder, beskeder og afspillet lyd."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Vil du begynde at optage eller caste?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Vil du begynde at optage eller caste via <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Vis ikke igen"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Ryd alle"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 8a71fb2..14da101 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Απενεργοποίηση Εξοικονόμησης μπαταρίας"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"Η εφαρμογή <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> θα έχει πρόσβαση σε όλες τις πληροφορίες που εμφανίζονται στην οθόνη σας ή που αναπαράγονται από τη συσκευή σας κατά την εγγραφή ή τη μετάδοση. Αυτό περιλαμβάνει πληροφορίες όπως κωδικούς πρόσβασης, στοιχεία πληρωμής, φωτογραφίες, μηνύματα και ήχο που αναπαράγετε."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"Η υπηρεσία που παρέχει αυτήν τη λειτουργία θα έχει πρόσβαση σε όλες τις πληροφορίες που εμφανίζονται στην οθόνη σας ή που αναπαράγονται από τη συσκευή σας κατά την εγγραφή ή τη μετάδοση. Αυτό περιλαμβάνει πληροφορίες όπως κωδικούς πρόσβασης, στοιχεία πληρωμής, φωτογραφίες, μηνύματα και ήχο που αναπαράγετε."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Έναρξη εγγραφής ή μετάδοσης;"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Έναρξη εγγραφής ή μετάδοσης με <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>;"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Να μην εμφανιστεί ξανά"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Διαγραφή όλων"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 1ffd1b1..5f38830 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Turn off Battery Saver"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information, such as passwords, payment details, photos, messages and audio that you play."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"The service providing this function will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information, such as passwords, payment details, photos, messages and audio that you play."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Start recording or casting?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Start recording or casting with <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Don\'t show again"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Clear all"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 652353c..1da1df3 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Turn off Battery Saver"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information, such as passwords, payment details, photos, messages and audio that you play."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"The service providing this function will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information, such as passwords, payment details, photos, messages and audio that you play."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Start recording or casting?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Start recording or casting with <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Don\'t show again"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Clear all"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 1ffd1b1..5f38830 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Turn off Battery Saver"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information, such as passwords, payment details, photos, messages and audio that you play."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"The service providing this function will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information, such as passwords, payment details, photos, messages and audio that you play."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Start recording or casting?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Start recording or casting with <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Don\'t show again"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Clear all"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 1ffd1b1..5f38830 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Turn off Battery Saver"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information, such as passwords, payment details, photos, messages and audio that you play."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"The service providing this function will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information, such as passwords, payment details, photos, messages and audio that you play."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Start recording or casting?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Start recording or casting with <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Don\'t show again"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Clear all"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index cc5ae64..fd3ff45 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Turn off Battery Saver"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages, and audio that you play."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"The service providing this function will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages, and audio that you play."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Start recording or casting?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Start recording or casting with <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Don\'t show again"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Clear all"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 713a996..eb27998f 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Desactivar el Ahorro de batería"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tendrá acceso a toda la información que sea visible en la pantalla o que reproduzcas en tu dispositivo durante una grabación o transmisión. Se incluyen las contraseñas, los detalles del pago, las fotos, los mensajes y el audio que reproduzcas."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"El servicio que brinda esta función tendrá acceso a toda la información que sea visible en la pantalla o que reproduzcas en tu dispositivo durante una grabación o transmisión. Se incluyen las contraseñas, los detalles del pago, las fotos, los mensajes y el audio que reproduzcas."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"¿Deseas comenzar a grabar o transmitir contenido?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"¿Deseas iniciar una grabación o transmisión con <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"No volver a mostrar"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Borrar todo"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index ffcafae..731d727 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Desactivar Ahorro de batería"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tendrá acceso a toda la información que se muestra en pantalla o se reproduce en el dispositivo mientras grabas o envías contenido, incluyendo contraseñas, detalles de pagos, fotos, mensajes y audios que reproduzcas."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"El servicio que ofrece esta función tendrá acceso a toda la información que se muestra en pantalla o se reproduce en el dispositivo mientras grabas o envías contenido, incluyendo contraseñas, detalles de pagos, fotos, mensajes y audios que reproduzcas."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"¿Quieres empezar a grabar o enviar contenido?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"¿Quieres iniciar la grabación o el envío de contenido con <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"No volver a mostrar"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Borrar todo"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index db87961..ab86e03 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Akusäästja väljalülitamine"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"Rakendus <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> saab juurdepääsu kogu teabele, mis on teie ekraanikuval nähtav või mida seadmes salvestamise või ülekande ajal esitatakse. See hõlmab teavet, nagu paroolid, maksete üksikasjad, fotod, sõnumid ja esitatav heli."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"Seda funktsiooni pakkuv teenus saab juurdepääsu kogu teabele, mis on teie ekraanikuval nähtav või mida seadmes salvestamise või ülekande ajal esitatakse. See hõlmab teavet, nagu paroolid, maksete üksikasjad, fotod, sõnumid ja esitatav heli."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Kas alustada salvestamist või ülekannet?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Kas alustada rakendusega <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> salvestamist või ülekannet?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Ära kuva uuesti"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Tühjenda kõik"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 51e3d2b..18ac031 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Desaktibatu bateria-aurrezlea"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"Zerbait grabatzen edo igortzen duzunean, pantailan ikus daitekeen edo gailuak erreproduzitzen duen informazio guztia atzitu ahalko du <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aplikazioak. Horrek barne hartzen ditu pasahitzak, ordainketen xehetasunak, argazkiak, mezuak eta erreproduzitzen dituzun audioak."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"Zerbait grabatzen edo igortzen duzunean, pantailan ikus daitekeen edo gailuak erreproduzitzen duen informazio guztia atzitu ahalko du funtzio hori eskaintzen duen zerbitzuak. Horrek barne hartzen ditu pasahitzak, ordainketen xehetasunak, argazkiak, mezuak eta erreproduzitzen dituzun audioak."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Grabatzen edo igortzen hasi nahi duzu?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aplikazioarekin grabatzen edo igortzen hasi nahi duzu?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Ez erakutsi berriro"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Garbitu guztiak"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 20b5565..77164b7 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"«بهینهسازی باتری» را خاموش کنید"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> به همه اطلاعاتی که روی صفحهنمایش قابلمشاهد است و هنگام ضبط کردن یا ارسال محتوا از دستگاهتان پخش میشود دسترسی خواهد داشت. این شامل اطلاعاتی مانند گذرواژهها، جزئیات پرداخت، عکسها، پیامها، و صداهایی که پخش میکنید میشود."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"سرویس ارائهدهنده این عملکرد به همه اطلاعاتی که روی صفحهنمایش قابلمشاهد است و هنگام ضبط کردن یا ارسال محتوا از دستگاهتان پخش میشود دسترسی خواهد داشت. این شامل اطلاعاتی مانند گذرواژهها، جزئیات پرداخت، عکسها، پیامها، و صداهایی که پخش میکنید میشود."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"ضبط یا ارسال محتوا شروع شود؟"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"ضبط یا ارسال محتوا با <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> شروع شود؟"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"دوباره نشان داده نشود"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"پاک کردن همه موارد"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 945e63d..e70b4d5 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Poista virransäästö käytöstä"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> saa pääsyn kaikkiin näytölläsi näkyviin tietoihin ja tietoihin laitteesi toistamasta sisällöstä tallennuksen tai striimauksen aikana. Näitä tietoja ovat esimerkiksi salasanat, maksutiedot, kuvat, viestit ja toistettava audiosisältö."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"Ominaisuuden tarjoavalla palvelulla on pääsy kaikkiin näytölläsi näkyviin tietoihin ja tietoihin laitteesi toistamasta sisällöstä tallennuksen tai striimauksen aikana. Näitä tietoja ovat esimerkiksi salasanat, maksutiedot, kuvat, viestit ja toistettava audiosisältö."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Aloitetaanko tallentaminen tai striimaus?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Haluatko, että <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aloittaa tallennuksen tai striimauksen?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Älä näytä uudelleen"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Poista kaikki"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index ac27ea8..ead29c0 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Désactiver la fonction Économie d\'énergie"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aura accès à toute l\'information visible sur votre écran ou qui joue sur votre appareil durant l\'enregistrement ou la diffusion. Cela comprend des renseignements comme les mots de passe, les détails du paiement, les photos, les messages et l\'audio que vous faites jouer."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"Le service offrant cette fonction aura accès à toute l\'information visible sur votre écran ou qui joue sur votre appareil durant l\'enregistrement ou la diffusion. Cela comprend des renseignements comme les mots de passe, les détails du paiement, les photos, les messages et l\'audio que vous faites jouer."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Commencer à enregistrer ou à diffuser?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Commencer à enregistrer ou à diffuser avec <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Ne plus afficher"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Tout effacer"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index d88201d00..b701276 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Désactiver l\'économiseur de batterie"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aura accès à toutes les informations visibles sur votre écran ou lues depuis votre appareil lors d\'un enregistrement ou d\'une diffusion de contenu. Par exemple, vos mots de passe, vos données de paiement, vos photos, vos messages ou encore vos contenus audio lus."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"Le service fournissant cette fonctionnalité aura accès à toutes les informations visibles sur votre écran ou lues depuis votre appareil lors d\'un enregistrement ou d\'une diffusion de contenu. Par exemple, vos mots de passe, vos données de paiement, vos photos, vos messages ou encore vos contenus audio lus."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Démarrer l\'enregistrement ou la diffusion ?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Démarrer l\'enregistrement ou la diffusion avec <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Ne plus afficher"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Tout effacer"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 0cbb0bc..de6b3793 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"બૅટરી સેવર બંધ કરો"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"રેકૉર્ડ અથવા કાસ્ટ કરતી વખતે, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ને તમારી સ્ક્રીન પર દેખાતી હોય અથવા તમારા ડિવાઇસ પર ચલાવવામાં આવતી હોય તેવી બધી માહિતીનો ઍક્સેસ હશે. આમાં પાસવર્ડ, ચુકવણીની વિગતો, ફોટા, સંદેશા અને તમે ચલાવો છો તે ઑડિયો જેવી માહિતીનો સમાવેશ થાય છે."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"રેકૉર્ડ અથવા કાસ્ટ કરતી વખતે, આ સુવિધા આપતી સેવાને તમારી સ્ક્રીન પર દેખાતી હોય અથવા તમારા ડિવાઇસ પર ચલાવવામાં આવતી હોય તેવી બધી માહિતીનો ઍક્સેસ હશે. આમાં પાસવર્ડ, ચુકવણીની વિગતો, ફોટા, સંદેશા અને તમે ચલાવો છો તે ઑડિયો જેવી માહિતીનો સમાવેશ થાય છે."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"શું રેકૉર્ડ અથવા કાસ્ટ કરવાનું શરૂ કરીએ?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> વડે રેકૉર્ડ અથવા કાસ્ટ કરવાનું શરૂ કરીએ?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"ફરીથી બતાવશો નહીં"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"બધુ સાફ કરો"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 23a5126..706f8b3 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -946,10 +946,7 @@
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"सिस्टम नेविगेशन अपडेट हो गया. बदलाव करने के लिए \'सेटिंग\' पर जाएं."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"सिस्टम नेविगेशन अपडेट करने के लिए \'सेटिंग\' में जाएं"</string>
<string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"स्टैंडबाई"</string>
- <!-- no translation found for magnification_overlay_title (6584179429612427958) -->
- <skip />
- <!-- no translation found for magnification_window_title (4863914360847258333) -->
- <skip />
- <!-- no translation found for magnification_controls_title (8421106606708891519) -->
- <skip />
+ <string name="magnification_overlay_title" msgid="6584179429612427958">"Magnification Overlay Window"</string>
+ <string name="magnification_window_title" msgid="4863914360847258333">"स्क्रीन को बड़ा करके दिखाने वाली विंडो"</string>
+ <string name="magnification_controls_title" msgid="8421106606708891519">"स्क्रीन को बड़ा करके दिखाने वाली विंडो के नियंत्रण"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 42f0df5..bf027fb 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -468,8 +468,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Isključite Štednju baterije"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> imat će pristup svim podacima koji su vidljivi na vašem zaslonu ili koji se reproduciraju s vašeg uređaja tijekom snimanja ili emitiranja. To uključuje podatke kao što su zaporke, podaci o plaćanju, fotografije, poruke i audiozapisi koje reproducirate."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"Usluga koja pruža ovu funkcionalnost imat će pristup svim podacima koji su vidljivi na vašem zaslonu ili koji se reproduciraju s vašeg uređaja tijekom snimanja ili emitiranja. To uključuje podatke kao što su zaporke, podaci o plaćanju, fotografije, poruke i audiozapisi koje reproducirate."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Započeti snimanje ili emitiranje?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Započeti snimanje ili emitiranja pomoću aplikacije <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Ne prikazuj ponovo"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Izbriši sve"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 7a5eadf..90c8a1a 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Akkumulátorkímélő mód kikapcsolása"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"A(z) <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> hozzáfér majd minden olyan információhoz, amely látható az Ön képernyőjén, vagy amelyet az Ön eszközéről játszanak le rögzítés vagy átküldés során. Ez olyan információkat is tartalmaz, mint a jelszavak, a fizetési részletek, fotók, üzenetek és lejátszott audiotartalmak."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"A funkciót biztosító szolgáltatás hozzáfér majd minden olyan információhoz, amely látható az Ön képernyőjén, illetve amelyet az Ön eszközéről játszanak le rögzítés vagy átküldés közben. Ez olyan információkat is tartalmaz, mint a jelszavak, a fizetési részletek, fotók, üzenetek és lejátszott audiotartalmak."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Biztosan elkezdi a rögzítést vagy az átküldést?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Elkezdi a rögzítést vagy átküldést a következővel: <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Ne jelenjen meg többé"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Az összes törlése"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 9f24d74..95e4010 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Slökkva á rafhlöðusparnaði"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> mun hafa aðgang að öllum upplýsingum sem sjást á skjánum eða eru spilaðar í tækinu á meðan upptaka eða útsending er í gangi. Þar á meðal eru upplýsingar á borð við aðgangsorð, greiðsluupplýsingar, myndir, skilaboð og hljóð sem þú spilar."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"Þjónustan sem býður upp á þennan eiginleika mun hafa aðgang að öllum upplýsingum sem sjást á skjánum eða eru spilaðar í tækinu á meðan upptaka eða útsending er í gangi. Þar á meðal eru upplýsingar á borð við aðgangsorð, greiðsluupplýsingar, myndir, skilaboð og hljóð sem þú spilar."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Viltu hefja upptöku eða útsendingu?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Viltu hefja upptöku eða útsendingu með <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Ekki sýna þetta aftur"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Hreinsa allt"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 9b2faa5..4cec980 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Disattiva Risparmio energetico"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> avrà accesso a tutte le informazioni visibili sul tuo schermo o riprodotte dal tuo dispositivo durante la registrazione o la trasmissione. Sono incluse informazioni quali password, dettagli sui pagamenti, foto, messaggi e audio riprodotto."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"Il servizio che offre questa funzione avrà accesso a tutte le informazioni visibili sul tuo schermo o riprodotte dal tuo dispositivo durante la registrazione o la trasmissione. Sono incluse informazioni quali password, dettagli sui pagamenti, foto, messaggi e audio riprodotto."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Vuoi avviare la registrazione o la trasmissione?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Vuoi avviare la registrazione o la trasmissione con <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Non mostrare più"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Cancella tutto"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 474327d..949c8db 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -471,8 +471,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"כיבוי תכונת החיסכון בסוללה"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"לאפליקציה <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> תהיה גישה לכל הפרטים שגלויים במסך שלך או מופעלים מהמכשיר שלך בזמן הקלטה או העברה (cast). זה כולל פרטים כמו סיסמאות, פרטי תשלום, תמונות, הודעות ואודיו שמושמע מהמכשיר."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"לשירות שמספק את הפונקציה הזו תהיה גישה לכל הפרטים שגלויים במסך שלך או מופעלים מהמכשיר שלך בזמן הקלטה או העברה (cast). זה כולל פרטים כמו סיסמאות, פרטי תשלום, תמונות, הודעות ואודיו שמושמע מהמכשיר."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"להתחיל להקליט או להעביר (cast)?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"להתחיל להקליט או להעביר (cast) באמצעות <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"אל תציג שוב"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"ניקוי הכל"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 60b4fe4..49d678b 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -471,8 +471,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Išjungti Akumuliatoriaus tausojimo priemonę"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"„<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>“ galės pasiekti visą informaciją, matomą ekrane ir leidžiamą iš įrenginio įrašant ar perduodant turinį. Tai apima įvairią informaciją, pvz., slaptažodžius, išsamią mokėjimo informaciją, nuotraukas, pranešimus ir leidžiamus garso įrašus."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"Šią funkcija teikianti paslauga galės pasiekti visą informaciją, matomą ekrane ir leidžiamą iš įrenginio įrašant ar perduodant turinį. Tai apima įvairią informaciją, pvz., slaptažodžius, išsamią mokėjimo informaciją, nuotraukas, pranešimus ir leidžiamus garso įrašus."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Pradėti įrašyti ar perduoti turinį?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Pradėti įrašyti ar perduoti turinį naudojant „<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>“?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Daugiau neberodyti"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Viską išvalyti"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 5233b16..0294a1f 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Исклучете го штедачот на батерија"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ќе има пристап до сите информации што се видливи на екранот или пуштени од вашиот уред додека се снима или емитува. Ова вклучува информации како, на пример, лозинки, детали на исплатата, фотографии, пораки и аудио што го пуштате."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"Услугата што ја обезбедува функцијава ќе има пристап до сите информации што се видливи на екранот или пуштени од вашиот уред додека се снима или емитува. Ова вклучува информации како, на пример, лозинки, детали на исплатата, фотографии, пораки и аудио што го пуштате."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Да почне снимање или емитување?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Да почне снимање или емитување со <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Не покажувај повторно"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Избриши сѐ"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 862f63e..e5497ea 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Тэжээл хэмнэгчийг унтраах"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> нь бичлэг хийх эсвэл дамжуулах үед таны дэлгэц дээр харагдах эсвэл таны төхөөрөмжөөс тоглуулах бүх мэдээлэлд хандах боломжтой байна. Үүнд нууц үг, төлбөрийн дэлгэрэнгүй, зураг болон таны тоглуулдаг аудио зэрэг мэдээлэл багтана."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"Энэ функцийг ажиллуулж байгаа үйлчилгээ нь бичлэг хийх эсвэл дамжуулах үед таны дэлгэц дээр харагдах эсвэл таны төхөөрөмжөөс тоглуулах бүх мэдээлэлд хандах боломжтой байна. Үүнд нууц үг, төлбөрийн дэлгэрэнгүй, зураг болон таны тоглуулдаг аудио зэрэг мэдээлэл багтана."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Бичлэг хийх эсвэл дамжуулахыг эхлүүлэх үү?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>-тай бичлэг хийж эсвэл дамжуулж эхлэх үү?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Дахиж үл харуулах"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Бүгдийг арилгах"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 9a8b50fc..b632bcd 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Matikan Penjimat Bateri"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> akan mempunyai akses kepada semua maklumat yang kelihatan pada skrin anda atau yang dimainkan daripada peranti anda semasa merakam atau membuat penghantaran. Ini termasuklah maklumat seperti kata laluan, butiran pembayaran, foto, mesej dan audio yang anda mainkan."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"Perkhidmatan yang menyediakan fungsi ini akan mempunyai akses kepada semua maklumat yang kelihatan pada skrin anda atau dimainkan daripada peranti anda semasa merakam atau membuat penghantaran. Ini termasuklah maklumat seperti kata laluan, butiran pembayaran, foto, mesej dan audio yang anda mainkan."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Mulakan rakaman atau penghantaran?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Mulakan rakaman atau penghantaran dengan <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Jangan tunjukkan lagi"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Kosongkan semua"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index ef6bc47..ba6b0ed 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"ब्याट्री सेभर निष्क्रिय पार्नुहोस्"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ले तपाईंको स्क्रिनमा देख्न सकिने सबै जानकारी अथवा रेकर्ड वा cast गर्दा तपाईंको यन्त्रबाट प्ले गरिएका कुरामाथि पहुँच राख्न सक्ने छ। यसअन्तर्गत पासवर्ड, भुक्तानीका विवरण, तस्बिर, सन्देश र तपाईंले प्ले गर्ने अडियो जस्ता जानकारी समावेश हुन्छन्।"</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"यो कार्य प्रदान गर्ने सेवाले तपाईंको स्क्रिनमा देख्न सकिने सबै जानकारी अथवा रेकर्ड वा cast गर्दा तपाईंको यन्त्रबाट प्ले गरिएका कुरामाथि पहुँच राख्न सक्ने छ। यसअन्तर्गत पासवर्ड, भुक्तानीका विवरण, तस्बिर, सन्देश र तपाईंले प्ले गर्ने अडियो जस्ता जानकारी समावेश हुन्छन्।"</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"रेकर्ड गर्न वा cast गर्न थाल्ने हो?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> मार्फत रेकर्ड गर्न वा cast गर्न थाल्ने हो?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"फेरि नदेखाउनुहोस्"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"सबै हटाउनुहोस्"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 5d57927..638fcad 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Batterijbesparing uitschakelen"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> krijgt toegang tot alle informatie die zichtbaar is op je scherm of die wordt afgespeeld vanaf je apparaat tijdens het opnemen of casten. Dit omvat informatie zoals wachtwoorden, betalingsgegevens, foto\'s, berichten en audio die je afspeelt."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"De service die deze functie levert, krijgt toegang tot alle informatie die zichtbaar is op je scherm of die wordt afgespeeld vanaf je apparaat tijdens het opnemen of casten. Dit omvat informatie zoals wachtwoorden, betalingsgegevens, foto\'s, berichten en audio die je afspeelt."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Beginnen met opnemen of casten?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Beginnen met opnemen of casten met <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Niet opnieuw weergeven"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Alles wissen"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index d3f5ae2..307d417 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -471,8 +471,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Wyłącz Oszczędzanie baterii"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"Podczas nagrywania i przesyłania aplikacja <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> będzie mieć dostęp do wszystkich informacji widocznych na ekranie lub odtwarzanych na urządzeniu. Dotyczy to m.in. haseł, szczegółów płatności, zdjęć, wiadomości i odtwarzanych dźwięków."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"Podczas nagrywania i przesyłania usługa udostępniająca tę funkcję będzie miała dostęp do wszystkich informacji widocznych na ekranie lub odtwarzanych na urządzeniu. Dotyczy to m.in. haseł, szczegółów płatności, zdjęć, wiadomości i odtwarzanych dźwięków."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Rozpocząć nagrywanie lub przesyłanie?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Rozpocząć nagrywanie lub przesyłanie za pomocą aplikacji <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Nie pokazuj ponownie"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Usuń wszystkie"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 4d4a607..c1dd243 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Desativar a Economia de bateria"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"O app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> terá acesso a todas as informações visíveis na tela ou tocadas no dispositivo, como gravação ou transmissão Isso inclui informações como senhas, detalhes de pagamento, fotos, mensagens e áudio que você toca."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"O serviço que oferece essa função terá acesso a todas as informações visíveis na tela ou tocadas no dispositivo durante uma gravação ou transmissão. Isso inclui informações como senhas, detalhes de pagamento, fotos, mensagens e áudio que você toca."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Iniciar gravação ou transmissão?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Iniciar gravação ou transmissão com o app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Não mostrar novamente"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Limpar tudo"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 1f23159..9a5a4b3 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Desativar a Poupança de bateria"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"A aplicação <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> terá acesso a todas as informações que estiverem visíveis no ecrã ou que forem reproduzidas a partir do dispositivo durante a gravação ou transmissão. Isto inclui informações como palavras-passe, detalhes de pagamentos, fotos, mensagens e áudio reproduzido."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"O serviço que fornece esta função terá acesso a todas as informações que estiverem visíveis no ecrã ou que forem reproduzidas a partir do dispositivo durante a gravação ou transmissão. Isto inclui informações como palavras-passe, detalhes de pagamentos, fotos, mensagens e áudio reproduzido."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Pretende começar a gravar ou a transmitir?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Pretende começar a gravar ou a transmitir com a aplicação <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Não mostrar de novo"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Limpar tudo"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 4d4a607..c1dd243 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Desativar a Economia de bateria"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"O app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> terá acesso a todas as informações visíveis na tela ou tocadas no dispositivo, como gravação ou transmissão Isso inclui informações como senhas, detalhes de pagamento, fotos, mensagens e áudio que você toca."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"O serviço que oferece essa função terá acesso a todas as informações visíveis na tela ou tocadas no dispositivo durante uma gravação ou transmissão. Isso inclui informações como senhas, detalhes de pagamento, fotos, mensagens e áudio que você toca."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Iniciar gravação ou transmissão?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Iniciar gravação ou transmissão com o app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Não mostrar novamente"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Limpar tudo"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 6fc30de..c38c855 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -471,8 +471,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Vypnúť šetrič batérie"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"Povolenie <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> bude mať prístup k všetkým informáciám zobrazovaným na obrazovke alebo prehrávaným v zariadení počas nahrávania či prenosu. Patria medzi ne informácie, akými sú napríklad heslá, platobné podrobnosti, fotky, správy a prehrávaný zvuk."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"Služba poskytujúca túto funkciu bude mať prístup k všetkým informáciám zobrazovaným na obrazovke alebo prehrávaným v zariadení počas nahrávania či prenosu. Patria medzi ne informácie, akými sú napríklad heslá, platobné podrobnosti, fotky, správy a prehrávaný zvuk."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Chcete začať nahrávanie alebo prenos?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Chcete spustiť nahrávanie alebo prenos s aktivovaným povolením <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Nabudúce nezobrazovať"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Vymazať všetko"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 730f4b0..445f8ca4 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Çaktivizo \"Kursyesin e baterisë\""</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> do të ketë qasje te të gjitha informacionet që janë të dukshme në ekran ose që luhen nga pajisja jote gjatë regjistrimit ose transmetimit. Kjo përfshin informacione si p.sh. fjalëkalimet, detajet e pagesave, fotografitë, mesazhet dhe audion që luan ti."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"Shërbimi që e ofron këtë funksion do të ketë qasje te të gjitha informacionet që janë të dukshme në ekran ose që luhen nga pajisja jote gjatë regjistrimit ose transmetimit. Kjo përfshin informacione si p.sh. fjalëkalimet, detajet e pagesave, fotografitë, mesazhet dhe audion që luan ti."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Do të fillosh regjistrimin ose transmetimin?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Fillo regjistrimin ose transmetimin me <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Mos e shfaq sërish"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Pastroji të gjitha"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 3660b06..acff269 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -468,8 +468,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Искључи Уштеду батерије"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ће имати приступ свим информацијама које се приказују на екрану или репродукују са уређаја током снимања или пребацивања. То обухвата информације попут лозинки, информација о плаћању, слика, порука и звука који пуштате."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"Услуга која пружа ову функцију ће имати приступ свим информацијама које се приказују на екрану или репродукују са уређаја током снимања или пребацивања. То обухвата информације попут лозинки, информација о плаћању, слика, порука и звука који пуштате."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Желите да почнете снимање или пребацивање?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Желите да почнете снимање или пребацивање помоћу апликације <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Не приказуј поново"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Обриши све"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 67e2e04..d63206a3 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Inaktivera batterisparläget"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> får åtkomst till all information som visas på skärmen eller spelas upp från enheten när du spelar in eller castar. Detta omfattar uppgifter som lösenord, betalningsinformation, foton, meddelanden och ljud som du spelar upp."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"Den tjänst som tillhandahåller funktionen får åtkomst till all information som visas på skärmen eller spelas upp från enheten när du spelar in eller castar. Detta omfattar uppgifter som lösenord, betalningsinformation, foton, meddelanden och ljud som du spelar upp."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Vill du börja spela in eller casta?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Vill du börja spela in eller casta med <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Visa inte igen"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Rensa alla"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 94377d7..3640c80 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -471,8 +471,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Вимкнути режим економії заряду акумулятора"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"Додаток <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> матиме доступ до всієї інформації, яка з\'являється на екрані або відтворюється на пристрої під час запису чи трансляції, зокрема до паролів, інформації про платежі, фотографій, повідомлень і аудіофайлів."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"Сервіс, що надає цю функцію, матиме доступ до всієї інформації, яка з\'являється на екрані або відтворюється на пристрої під час запису чи трансляції, зокрема до паролів, інформації про платежі, фотографій, повідомлень і аудіофайлів."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Почати запис або трансляцію?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Почати запис або трансляцію за допомогою додатка <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Більше не показувати"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Очистити все"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index ec11d1f..fdc618b 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"بیٹری سیور آف کریں"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> کو ان تمام معلومات تک رسائی حاصل ہوگی جو آپ کی اسکرین پر مرئی ہے یا ریکارڈنگ یا کاسٹنگ کے دوران آپ کے آلے سے چلائے گئے ہوں۔ اس میں پاس ورڈز، ادائیگی کی تفصیلات، تصاویر، پیغامات، اور آپ کے ذریعے چلائی جانے والی آڈیو جیسی معلومات شامل ہے۔"</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"یہ فنکشن فراہم کرنے والی سروس کو ان تمام معلومات تک رسائی حاصل ہوگی جو آپ کی اسکرین پر مرئی ہے یا ریکارڈنگ یا کاسٹنگ کے دوران آپ کے آلے سے چلائے گئے ہوں۔ اس میں پاس ورڈز، ادائیگی کی تفصیلات، تصاویر، پیغامات، اور آپ کے ذریعے چلائی جانے والی آڈیو جیسی معلومات شامل ہے۔"</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"ریکارڈنگ یا کاسٹنگ شروع کریں؟"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> کے ذریعے ریکارڈنگ یا کاسٹنگ شروع کریں؟"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"دوبارہ نہ دکھائیں"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"سبھی کو صاف کریں"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 10348fb..dae6eb1 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"關閉省電模式"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"在錄影或投放時,「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」可以存取螢幕顯示或裝置播放的任何資料,當中包括密碼、付款詳情、相片、訊息和播放的語音等。"</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"在錄影或投放時,此功能的服務供應商可以存取螢幕顯示或裝置播放的任何資料,當中包括密碼、付款詳情、相片、訊息和播放的語音等。"</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"要開始錄影或投放嗎?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"要使用「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」開始錄影或投放嗎?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"不用再顯示"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"全部清除"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 7ae14cc..5353018 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -465,8 +465,7 @@
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Vala isilondolozi sebhethri"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> izithola ukufinyelela kulo lonke ulwazi olubonakalayo esikrinini sakho noma idlalwe kusuka kudivayisi yakho ngenkathi urekhoda noma usakaza. Lokhu kubandakanya ulwazi olufana namaphasiwedi, imininingwane yenkokhelo, izithombe, imilayezo, nomsindo owudlalayo."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"Isevisi enikezela ngalo msebenzi izothola ukufinyelela kulo lonke ulwazi olubonakalayo esikrinini sakho noma oludlalwa kusuka kudivayisi yakho ngenkathi urekhoda noma usakaza. Lokhu kubandakanya ulwazi olufana namaphasiwedi, imininingwane yenkokhelo, izithombe, imilayezo, nomsindo owudlalayo."</string>
- <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
- <skip />
+ <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Qala ukurekhoda noma ukusakaza?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Qala ukurekhoda noma ukusakaza nge-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"Ungabonisi futhi"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Sula konke"</string>
diff --git a/packages/SystemUI/res/values/attrs_car.xml b/packages/SystemUI/res/values/attrs_car.xml
deleted file mode 100644
index 49b87f3..0000000
--- a/packages/SystemUI/res/values/attrs_car.xml
+++ /dev/null
@@ -1,103 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<resources>
- <attr name="icon" format="reference"/>
- <attr name="selectedIcon" format="reference"/>
- <attr name="intent" format="string"/>
- <attr name="longIntent" format="string"/>
- <attr name="selectedAlpha" format="float" />
- <attr name="unselectedAlpha" format="float" />
-
- <!-- Allow for custom attribs to be added to a facet button -->
- <declare-styleable name="CarFacetButton">
- <!-- icon to be rendered (drawable) -->
- <attr name="icon"/>
- <!-- icon to be rendered when in selected state -->
- <attr name="selectedIcon"/>
- <!-- intent to start when button is click -->
- <attr name="intent"/>
- <!-- intent to start when a long press has happened -->
- <attr name="longIntent"/>
- <!-- categories that will be added as extras to the fired intents -->
- <attr name="categories" format="string"/>
- <!-- package names that will be added as extras to the fired intents -->
- <attr name="packages" format="string" />
- <!-- componentName names that will be used for detecting selected state -->
- <attr name="componentNames" format="string" />
- <!-- Alpha value to used when in selected state. Defaults 1f -->
- <attr name="selectedAlpha" />
- <!-- Alpha value to used when in un-selected state. Defaults 0.7f -->
- <attr name="unselectedAlpha" />
- <!-- Render a "more" icon. Defaults true -->
- <attr name="useMoreIcon" format="boolean" />
-
- </declare-styleable>
-
-
- <!-- Allow for custom attribs to be added to a nav button -->
- <declare-styleable name="CarNavigationButton">
- <!-- intent to start when button is click -->
- <attr name="intent" />
- <!-- intent to start when a long press has happened -->
- <attr name="longIntent" />
- <!-- start the intent as a broad cast instead of an activity if true-->
- <attr name="broadcast" format="boolean"/>
- <!-- Alpha value to used when in selected state. Defaults 1f -->
- <attr name="selectedAlpha" />
- <!-- Alpha value to used when in un-selected state. Defaults 0.7f -->
- <attr name="unselectedAlpha" />
- <!-- icon to be rendered when in selected state -->
- <attr name="selectedIcon" />
- <!-- icon to be rendered (drawable) -->
- <attr name="icon"/>
- </declare-styleable>
-
- <!-- Custom attributes to configure hvac values -->
- <declare-styleable name="TemperatureView">
- <attr name="hvacAreaId" format="integer"/>
- <attr name="hvacPropertyId" format="integer"/>
- <attr name="hvacTempFormat" format="string"/>
- </declare-styleable>
-
- <declare-styleable name="carVolumeItems"/>
- <declare-styleable name="carVolumeItems_item">
- <!-- Align with AudioAttributes.USAGE_* -->
- <attr name="usage">
- <enum name="unknown" value="0"/>
- <enum name="media" value="1"/>
- <enum name="voice_communication" value="2"/>
- <enum name="voice_communication_signalling" value="3"/>
- <enum name="alarm" value="4"/>
- <enum name="notification" value="5"/>
- <enum name="notification_ringtone" value="6"/>
- <enum name="notification_communication_request" value="7"/>
- <enum name="notification_communication_instant" value="8"/>
- <enum name="notification_communication_delayed" value="9"/>
- <enum name="notification_event" value="10"/>
- <enum name="assistance_accessibility" value="11"/>
- <enum name="assistance_navigation_guidance" value="12"/>
- <enum name="assistance_sonification" value="13"/>
- <enum name="game" value="14"/>
- <!-- hidden, do not use -->
- <!-- enum name="virtual_source" value="15"/ -->
- <enum name="assistant" value="16"/>
- </attr>
-
- <!-- Icon resource ids to render on UI -->
- <attr name="icon" />
- </declare-styleable>
-</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 52c0a26..4f532b7 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1163,6 +1163,9 @@
<!-- Section title for notifications that do not vibrate or make noise. [CHAR LIMIT=40] -->
<string name="notification_section_header_gentle">Silent notifications</string>
+ <!-- Section title for conversational notifications. [CHAR LIMIT=40] -->
+ <string name="notification_section_header_conversations">Conversations</string>
+
<!-- Content description for accessibility: Tapping this button will dismiss all gentle notifications [CHAR LIMIT=NONE] -->
<string name="accessibility_notification_section_header_gentle_clear_all">Clear all silent notifications</string>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
index 2c8f238..ed1cd81 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
@@ -168,8 +168,9 @@
*
* @param reason a flag indicating which string should be shown, see
* {@link KeyguardSecurityView#PROMPT_REASON_NONE},
- * {@link KeyguardSecurityView#PROMPT_REASON_RESTART} and
- * {@link KeyguardSecurityView#PROMPT_REASON_TIMEOUT}.
+ * {@link KeyguardSecurityView#PROMPT_REASON_RESTART},
+ * {@link KeyguardSecurityView#PROMPT_REASON_TIMEOUT}, and
+ * {@link KeyguardSecurityView#PROMPT_REASON_PREPARE_FOR_UPDATE}.
*/
public void showPromptReason(int reason) {
mSecurityContainer.showPromptReason(reason);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index f8f3dc8..718bcf1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -137,6 +137,8 @@
return R.string.kg_prompt_reason_device_admin;
case PROMPT_REASON_USER_REQUEST:
return R.string.kg_prompt_reason_user_request;
+ case PROMPT_REASON_PREPARE_FOR_UPDATE:
+ return R.string.kg_prompt_reason_prepare_for_update_password;
case PROMPT_REASON_NONE:
return 0;
default:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index 9eb168a..48c6bd1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -438,6 +438,10 @@
case PROMPT_REASON_USER_REQUEST:
mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_user_request);
break;
+ case PROMPT_REASON_PREPARE_FOR_UPDATE:
+ mSecurityMessageDisplay.setMessage(
+ R.string.kg_prompt_reason_prepare_for_update_pattern);
+ break;
case PROMPT_REASON_NONE:
break;
default:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index c67decc..6d865ab 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -116,6 +116,8 @@
return R.string.kg_prompt_reason_device_admin;
case PROMPT_REASON_USER_REQUEST:
return R.string.kg_prompt_reason_user_request;
+ case PROMPT_REASON_PREPARE_FOR_UPDATE:
+ return R.string.kg_prompt_reason_prepare_for_update_pin;
case PROMPT_REASON_NONE:
return 0;
default:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
index e108194..09d4d5f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
@@ -51,6 +51,11 @@
*/
int PROMPT_REASON_AFTER_LOCKOUT = 5;
+ /***
+ * Strong auth is require to prepare for an unattended update.
+ */
+ int PROMPT_REASON_PREPARE_FOR_UPDATE = 6;
+
/**
* Interface back to keyguard to tell it when security
* @param callback
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 694c623..431862f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1072,7 +1072,7 @@
mHandler.sendMessage(mHandler.obtainMessage(MSG_PHONE_STATE_CHANGED, state));
} else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
mHandler.sendEmptyMessage(MSG_AIRPLANE_MODE_CHANGED);
- } else if (TelephonyIntents.ACTION_SERVICE_STATE_CHANGED.equals(action)) {
+ } else if (Intent.ACTION_SERVICE_STATE.equals(action)) {
ServiceState serviceState = ServiceState.newFromBundle(intent.getExtras());
int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
@@ -1633,8 +1633,8 @@
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
- filter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
- filter.addAction(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
+ filter.addAction(Intent.ACTION_SERVICE_STATE);
+ filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index eecc54c..bbe972d 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -121,6 +121,7 @@
import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.util.leak.LeakReporter;
import com.android.systemui.util.sensors.AsyncSensorManager;
+import com.android.systemui.wm.DisplayImeController;
import com.android.systemui.wm.DisplayWindowController;
import com.android.systemui.wm.SystemWindows;
@@ -321,6 +322,7 @@
@Inject Lazy<StatusBar> mStatusBar;
@Inject Lazy<DisplayWindowController> mDisplayWindowController;
@Inject Lazy<SystemWindows> mSystemWindows;
+ @Inject Lazy<DisplayImeController> mDisplayImeController;
@Inject
public Dependency() {
@@ -509,6 +511,7 @@
mProviders.put(StatusBar.class, mStatusBar::get);
mProviders.put(DisplayWindowController.class, mDisplayWindowController::get);
mProviders.put(SystemWindows.class, mSystemWindows::get);
+ mProviders.put(DisplayImeController.class, mDisplayImeController::get);
// TODO(b/118592525): to support multi-display , we start to add something which is
// per-display, while others may be global. I think it's time to add
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 7cd29ea..c7492a2 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -344,11 +344,14 @@
mSavedBubbleKeysPerUser = new SparseSetArray<>();
mCurrentUserId = mNotifUserManager.getCurrentUserId();
mNotifUserManager.addUserChangedListener(
- newUserId -> {
- saveBubbles(mCurrentUserId);
- mBubbleData.dismissAll(DISMISS_USER_CHANGED);
- restoreBubbles(newUserId);
- mCurrentUserId = newUserId;
+ new NotificationLockscreenUserManager.UserChangedListener() {
+ @Override
+ public void onUserChanged(int newUserId) {
+ BubbleController.this.saveBubbles(mCurrentUserId);
+ mBubbleData.dismissAll(DISMISS_USER_CHANGED);
+ BubbleController.this.restoreBubbles(newUserId);
+ mCurrentUserId = newUserId;
+ }
});
mUserCreatedBubbles = new HashSet<>();
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index 8d10552..53a23b8 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -20,7 +20,6 @@
import android.app.INotificationManager;
import android.content.Context;
-import android.content.pm.IPackageManager;
import android.hardware.display.AmbientDisplayConfiguration;
import android.hardware.display.NightDisplayListener;
import android.os.Handler;
@@ -115,13 +114,6 @@
/** */
@Singleton
@Provides
- public IPackageManager provideIPackageManager() {
- return IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
- }
-
- /** */
- @Singleton
- @Provides
public LayoutInflater providerLayoutInflater(Context context) {
return LayoutInflater.from(context);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
index 0b73ab6..26337b1 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
@@ -27,6 +27,7 @@
import android.app.WallpaperManager;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
+import android.content.pm.IPackageManager;
import android.content.res.Resources;
import android.hardware.SensorPrivacyManager;
import android.os.Handler;
@@ -45,6 +46,7 @@
import com.android.internal.util.LatencyTracker;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.shared.system.PackageManagerWrapper;
@@ -72,6 +74,19 @@
@Provides
@Singleton
+ static ActivityManager provideActivityManager(Context context) {
+ return context.getSystemService(ActivityManager.class);
+ }
+
+
+ @Provides
+ @DisplayId
+ static int provideDisplayId(Context context) {
+ return context.getDisplayId();
+ }
+
+ @Provides
+ @Singleton
static DevicePolicyManager provideDevicePolicyManager(Context context) {
return context.getSystemService(DevicePolicyManager.class);
}
@@ -109,6 +124,13 @@
return WindowManagerGlobal.getWindowManagerService();
}
+ /** */
+ @Singleton
+ @Provides
+ public IPackageManager provideIPackageManager() {
+ return IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
+ }
+
@Singleton
@Provides
static KeyguardManager provideKeyguardManager(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 442313d..58ddda9 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -35,7 +35,7 @@
import com.android.systemui.statusbar.notification.people.PeopleHubModule;
import com.android.systemui.statusbar.phone.KeyguardLiftController;
import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.phone.StatusBarComponent;
+import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.concurrency.ConcurrencyModule;
import com.android.systemui.util.sensors.AsyncSensorManager;
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/DisplayId.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/DisplayId.java
new file mode 100644
index 0000000..155a6d2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/DisplayId.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dagger.qualifiers;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface DisplayId {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
index 4b28540..657a308 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
@@ -206,7 +206,7 @@
Log.d(TAG, "createEglSurface start");
}
- if (hasEglDisplay()) {
+ if (hasEglDisplay() && surfaceHolder.getSurface().isValid()) {
int[] attrs = null;
int wcgCapability = getWcgCapability();
if (wcg && checkExtensionCapability(KHR_GL_COLOR_SPACE) && wcgCapability > 0) {
@@ -214,7 +214,8 @@
}
mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, surfaceHolder, attrs, 0);
} else {
- Log.w(TAG, "mEglDisplay is null");
+ Log.w(TAG, "Create EglSurface failed: hasEglDisplay=" + hasEglDisplay()
+ + ", has valid surface=" + surfaceHolder.getSurface().isValid());
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 9fcf022..e13c3e0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -22,6 +22,7 @@
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
import static com.android.systemui.DejankUtils.whitelistIpcs;
import android.app.ActivityManager;
@@ -86,7 +87,7 @@
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.NotificationPanelView;
+import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
@@ -670,6 +671,8 @@
return KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
} else if (any && (strongAuth & STRONG_AUTH_REQUIRED_AFTER_LOCKOUT) != 0) {
return KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT;
+ } else if (any && (strongAuth & STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE) != 0) {
+ return KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE;
}
return KeyguardSecurityView.PROMPT_REASON_NONE;
}
@@ -2102,7 +2105,7 @@
}
public StatusBarKeyguardViewManager registerStatusBar(StatusBar statusBar,
- ViewGroup container, NotificationPanelView panelView,
+ ViewGroup container, NotificationPanelViewController panelView,
BiometricUnlockController biometricUnlockController, ViewGroup lockIconContainer,
View notificationContainer, KeyguardBypassController bypassController) {
mStatusBarKeyguardViewManagerLazy.get().registerStatusBar(statusBar, container, panelView,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
index 2005d79..ac05c53 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -40,7 +40,7 @@
* You will probably need to restart systemui for the changes to be picked up:
*
* {@code
- * $ adb shell am crash com.android.systemui
+ * $ adb shell am restart com.android.systemui
* }
*/
@Singleton
@@ -59,6 +59,11 @@
return getDeviceConfigFlag("notification.newpipeline.enabled", false);
}
+ public boolean isNewNotifPipelineRenderingEnabled() {
+ return isNewNotifPipelineEnabled()
+ && getDeviceConfigFlag("notification.newpipeline.rendering", false);
+ }
+
private void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
synchronized (mCachedDeviceConfigFlags) {
for (String key : properties.getKeyset()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
index 525b5b7..12749fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
@@ -368,13 +368,14 @@
public static class Builder {
private final DisplayMetrics mDisplayMetrics;
float mMaxLengthSeconds;
- float mSpeedUpFactor = 0.0f;
- float mX2 = -1.0f;
- float mY2 = 1.0f;
+ float mSpeedUpFactor;
+ float mX2;
+ float mY2;
@Inject
public Builder(DisplayMetrics displayMetrics) {
mDisplayMetrics = displayMetrics;
+ reset();
}
public Builder setMaxLengthSeconds(float maxLengthSeconds) {
@@ -397,6 +398,15 @@
return this;
}
+ public Builder reset() {
+ mMaxLengthSeconds = 0;
+ mSpeedUpFactor = 0.0f;
+ mX2 = -1.0f;
+ mY2 = 1.0f;
+
+ return this;
+ }
+
public FlingAnimationUtils build() {
return new FlingAnimationUtils(mDisplayMetrics, mMaxLengthSeconds, mSpeedUpFactor,
mX2, mY2);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 5abca6b..7ad07c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -168,7 +168,8 @@
mStatusBarStateController = statusBarStateController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mDockManager = dockManager;
- mDockManager.addAlignmentStateListener(this::handleAlignStateChanged);
+ mDockManager.addAlignmentStateListener(
+ alignState -> mHandler.post(() -> handleAlignStateChanged(alignState)));
// lock icon is not used on all form factors.
if (mLockIcon != null) {
mLockIcon.setOnLongClickListener(this::handleLockLongClick);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index ff4ce94..ebf7c2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -49,6 +49,12 @@
/** Adds a listener to be notified when the current user changes. */
void addUserChangedListener(UserChangedListener listener);
+ /**
+ * Removes a listener previously registered with
+ * {@link #addUserChangedListener(UserChangedListener)}
+ */
+ void removeUserChangedListener(UserChangedListener listener);
+
SparseArray<UserInfo> getCurrentProfiles();
void setLockscreenPublicMode(boolean isProfilePublic, int userId);
@@ -79,6 +85,7 @@
/** Notified when the current user changes. */
interface UserChangedListener {
- void onUserChanged(int userId);
+ default void onUserChanged(int userId) {}
+ default void onCurrentProfilesChanged(SparseArray<UserInfo> currentProfiles) {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 2e369b3..976531d8b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -117,51 +117,63 @@
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
- if (Intent.ACTION_USER_SWITCHED.equals(action)) {
- mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
- updateCurrentProfilesCache();
- Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
+ switch (action) {
+ case Intent.ACTION_USER_SWITCHED:
+ mCurrentUserId = intent.getIntExtra(
+ Intent.EXTRA_USER_HANDLE, UserHandle.USER_ALL);
+ updateCurrentProfilesCache();
- updateLockscreenNotificationSetting();
- updatePublicMode();
- // The filtering needs to happen before the update call below in order to make sure
- // the presenter has the updated notifications from the new user
- getEntryManager().reapplyFilterAndSort("user switched");
- mPresenter.onUserSwitched(mCurrentUserId);
+ Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
- for (UserChangedListener listener : mListeners) {
- listener.onUserChanged(mCurrentUserId);
- }
- } else if (Intent.ACTION_USER_ADDED.equals(action)) {
- updateCurrentProfilesCache();
- } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
- // Start the overview connection to the launcher service
- Dependency.get(OverviewProxyService.class).startConnectionToCurrentUser();
- } else if (NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION.equals(action)) {
- final IntentSender intentSender = intent.getParcelableExtra(Intent.EXTRA_INTENT);
- final String notificationKey = intent.getStringExtra(Intent.EXTRA_INDEX);
- if (intentSender != null) {
- try {
- mContext.startIntentSender(intentSender, null, 0, 0, 0);
- } catch (IntentSender.SendIntentException e) {
- /* ignore */
+ updateLockscreenNotificationSetting();
+ updatePublicMode();
+ // The filtering needs to happen before the update call below in order to
+ // make sure
+ // the presenter has the updated notifications from the new user
+ getEntryManager().reapplyFilterAndSort("user switched");
+ mPresenter.onUserSwitched(mCurrentUserId);
+
+ for (UserChangedListener listener : mListeners) {
+ listener.onUserChanged(mCurrentUserId);
}
- }
- if (notificationKey != null) {
- NotificationEntry entry =
- getEntryManager().getActiveNotificationUnfiltered(notificationKey);
- final int count = getEntryManager().getActiveNotificationsCount();
- final int rank = entry != null ? entry.getRanking().getRank() : 0;
- NotificationVisibility.NotificationLocation location =
- NotificationLogger.getNotificationLocation(entry);
- final NotificationVisibility nv = NotificationVisibility.obtain(notificationKey,
- rank, count, true, location);
- try {
- mBarService.onNotificationClick(notificationKey, nv);
- } catch (RemoteException exception) {
- /* ignore */
+ break;
+ case Intent.ACTION_USER_ADDED:
+ case Intent.ACTION_MANAGED_PROFILE_AVAILABLE:
+ case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE:
+ updateCurrentProfilesCache();
+ break;
+ case Intent.ACTION_USER_UNLOCKED:
+ // Start the overview connection to the launcher service
+ Dependency.get(OverviewProxyService.class).startConnectionToCurrentUser();
+ break;
+ case NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION:
+ final IntentSender intentSender = intent.getParcelableExtra(
+ Intent.EXTRA_INTENT);
+ final String notificationKey = intent.getStringExtra(Intent.EXTRA_INDEX);
+ if (intentSender != null) {
+ try {
+ mContext.startIntentSender(intentSender, null, 0, 0, 0);
+ } catch (IntentSender.SendIntentException e) {
+ /* ignore */
+ }
}
- }
+ if (notificationKey != null) {
+ NotificationEntry entry =
+ getEntryManager().getActiveNotificationUnfiltered(notificationKey);
+ final int count = getEntryManager().getActiveNotificationsCount();
+ final int rank = entry != null ? entry.getRanking().getRank() : 0;
+ NotificationVisibility.NotificationLocation location =
+ NotificationLogger.getNotificationLocation(entry);
+ final NotificationVisibility nv = NotificationVisibility.obtain(
+ notificationKey,
+ rank, count, true, location);
+ try {
+ mBarService.onNotificationClick(notificationKey, nv);
+ } catch (RemoteException exception) {
+ /* ignore */
+ }
+ }
+ break;
}
}
};
@@ -266,6 +278,8 @@
filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_USER_ADDED);
filter.addAction(Intent.ACTION_USER_UNLOCKED);
+ filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
+ filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
mBroadcastDispatcher.registerReceiver(mBaseBroadcastReceiver, filter);
IntentFilter internalFilter = new IntentFilter();
@@ -489,6 +503,11 @@
}
}
}
+ mMainHandler.post(() -> {
+ for (UserChangedListener listener : mListeners) {
+ listener.onCurrentProfilesChanged(mCurrentProfiles);
+ }
+ });
}
public boolean isAnyProfilePublicMode() {
@@ -555,6 +574,11 @@
mListeners.add(listener);
}
+ @Override
+ public void removeUserChangedListener(UserChangedListener listener) {
+ mListeners.remove(listener);
+ }
+
// public void updatePublicMode() {
// //TODO: I think there may be a race condition where mKeyguardViewManager.isShowing() returns
// // false when it should be true. Therefore, if we are not on the SHADE, don't even bother
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 43d0399..667e721 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -584,7 +584,15 @@
public void bindRow(ExpandableNotificationRow row) {
row.setRemoteInputController(mRemoteInputController);
- row.setRemoteViewClickHandler(mOnClickHandler);
+ }
+
+ /**
+ * Return on-click handler for notification remote views
+ *
+ * @return on-click handler
+ */
+ public RemoteViews.OnClickHandler getRemoteViewsOnClickHandler() {
+ return mOnClickHandler;
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarDependenciesModule.java
index d1f6ebf..ec8dbea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarDependenciesModule.java
@@ -18,6 +18,8 @@
import android.content.Context;
+import com.android.systemui.statusbar.notification.row.NotificationRowModule;
+
import javax.inject.Singleton;
import dagger.Module;
@@ -26,7 +28,7 @@
/**
* Dagger Module providing common dependencies of StatusBar.
*/
-@Module
+@Module(includes = {NotificationRowModule.class})
public class StatusBarDependenciesModule {
/**
* Provides our instance of CommandQueue which is considered optional.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index 9b31234..6660569 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -37,7 +37,7 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
-import com.android.systemui.statusbar.phone.NotificationPanelView;
+import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.StatusBarWindowViewController;
/**
@@ -53,7 +53,7 @@
CollapsedStatusBarFragment.FADE_IN_DURATION - CollapsedStatusBarFragment.FADE_IN_DELAY
- 16;
private static final long LAUNCH_TIMEOUT = 500;
- private final NotificationPanelView mNotificationPanel;
+ private final NotificationPanelViewController mNotificationPanel;
private final NotificationListContainer mNotificationContainer;
private final float mWindowCornerRadius;
private final StatusBarWindowViewController mStatusBarWindowViewController;
@@ -69,7 +69,7 @@
public ActivityLaunchAnimator(
StatusBarWindowViewController statusBarWindowViewController,
Callback callback,
- NotificationPanelView notificationPanel,
+ NotificationPanelViewController notificationPanel,
NotificationListContainer container) {
mNotificationPanel = notificationPanel;
mNotificationContainer = container;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 33d97a1..a2578ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -34,6 +34,7 @@
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLifetimeExtender;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
@@ -131,6 +132,7 @@
private final KeyguardEnvironment mKeyguardEnvironment;
private final NotificationGroupManager mGroupManager;
private final NotificationRankingManager mRankingManager;
+ private final FeatureFlags mFeatureFlags;
private NotificationPresenter mPresenter;
private RankingMap mLatestRankingMap;
@@ -170,11 +172,13 @@
NotifLog notifLog,
NotificationGroupManager groupManager,
NotificationRankingManager rankingManager,
- KeyguardEnvironment keyguardEnvironment) {
+ KeyguardEnvironment keyguardEnvironment,
+ FeatureFlags featureFlags) {
mNotifLog = notifLog;
mGroupManager = groupManager;
mRankingManager = rankingManager;
mKeyguardEnvironment = keyguardEnvironment;
+ mFeatureFlags = featureFlags;
}
/** Once called, the NEM will start processing notification events from system server. */
@@ -290,6 +294,10 @@
* WARNING: this will call back into us. Don't hold any locks.
*/
@Override
+ public void handleInflationException(NotificationEntry n, Exception e) {
+ handleInflationException(n.getSbn(), e);
+ }
+
public void handleInflationException(StatusBarNotification n, Exception e) {
removeNotificationInternal(
n.getKey(), null, null, true /* forceRemove */, false /* removedByUser */,
@@ -529,9 +537,12 @@
NotificationEntry entry = new NotificationEntry(notification, ranking);
Dependency.get(LeakDetector.class).trackInstance(entry);
+
// Construct the expanded view.
- requireBinder().inflateViews(entry, () -> performRemoveNotification(notification,
- REASON_CANCEL));
+ if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
+ requireBinder().inflateViews(entry, () -> performRemoveNotification(notification,
+ REASON_CANCEL));
+ }
abortExistingInflation(key, "addNotification");
mPendingNotifications.put(key, entry);
@@ -574,8 +585,11 @@
listener.onPreEntryUpdated(entry);
}
- requireBinder().inflateViews(entry, () -> performRemoveNotification(notification,
- REASON_CANCEL));
+ if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
+ requireBinder().inflateViews(entry, () -> performRemoveNotification(notification,
+ REASON_CANCEL));
+ }
+
updateNotifications("updateNotificationInternal");
if (DEBUG) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 873cdbc..856b75b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -299,6 +299,14 @@
if (!isLifetimeExtended(entry)) {
Ranking ranking = requireRanking(rankingMap, entry.getKey());
entry.setRanking(ranking);
+
+ // TODO: (b/145659174) update the sbn's overrideGroupKey in
+ // NotificationEntry.setRanking instead of here once we fully migrate to the
+ // NewNotifPipeline
+ final String newOverrideGroupKey = ranking.getOverrideGroupKey();
+ if (!Objects.equals(entry.getSbn().getOverrideGroupKey(), newOverrideGroupKey)) {
+ entry.getSbn().setOverrideGroupKey(newOverrideGroupKey);
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflater.java
new file mode 100644
index 0000000..fc04827
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflater.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection;
+import com.android.systemui.statusbar.notification.collection.coordinator.PreparationCoordinator;
+
+/**
+ * Used by the {@link PreparationCoordinator}. When notifications are added or updated, the
+ * NotifInflater is asked to (re)inflated and prepare their views. This inflation occurs off the
+ * main thread. When the inflation is finished, NotifInflater will trigger its InflationCallback.
+ */
+public interface NotifInflater {
+
+ /**
+ * Callback used when inflation is finished.
+ */
+ void setInflationCallback(InflationCallback callback);
+
+ /**
+ * Called to rebind the entry's views.
+ */
+ void rebindViews(NotificationEntry entry);
+
+ /**
+ * Called to inflate the views of an entry. Views are not considered inflated until all of its
+ * views are bound. Once all views are inflated, the InflationCallback is triggered.
+ */
+ void inflateViews(NotificationEntry entry);
+
+ /**
+ * Request to stop the inflation of an entry. For example, called when a notification is
+ * removed and no longer needs to be inflated.
+ */
+ void abortInflation(NotificationEntry entry);
+
+ /**
+ * Callback once all the views are inflated and bound for a given NotificationEntry.
+ */
+ interface InflationCallback {
+ void onInflationFinished(NotificationEntry entry);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
new file mode 100644
index 0000000..0d17557
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection;
+
+import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
+
+import android.os.RemoteException;
+import android.service.notification.NotificationStats;
+import android.service.notification.StatusBarNotification;
+
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.statusbar.notification.InflationException;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Handles notification inflating, rebinding, and inflation aborting.
+ *
+ * Currently a wrapper for NotificationRowBinderImpl.
+ */
+@Singleton
+public class NotifInflaterImpl implements NotifInflater {
+
+ private final IStatusBarService mStatusBarService;
+ private final NotifCollection mNotifCollection;
+
+ private NotificationRowBinderImpl mNotificationRowBinder;
+ private InflationCallback mExternalInflationCallback;
+
+ @Inject
+ public NotifInflaterImpl(
+ IStatusBarService statusBarService,
+ NotifCollection notifCollection) {
+ mStatusBarService = statusBarService;
+ mNotifCollection = notifCollection;
+ }
+
+ /**
+ * Attaches the row binder for inflation.
+ */
+ public void setRowBinder(NotificationRowBinderImpl rowBinder) {
+ mNotificationRowBinder = rowBinder;
+ mNotificationRowBinder.setInflationCallback(mInflationCallback);
+ }
+
+ @Override
+ public void setInflationCallback(InflationCallback callback) {
+ mExternalInflationCallback = callback;
+ }
+
+ @Override
+ public void rebindViews(NotificationEntry entry) {
+ inflateViews(entry);
+ }
+
+ /**
+ * Called to inflate the views of an entry. Views are not considered inflated until all of its
+ * views are bound.
+ */
+ @Override
+ public void inflateViews(NotificationEntry entry) {
+ try {
+ entry.setHasInflationError(false);
+ requireBinder().inflateViews(entry, getDismissCallback(entry));
+ } catch (InflationException e) {
+ // logged in mInflationCallback.handleInflationException
+ }
+ }
+
+ @Override
+ public void abortInflation(NotificationEntry entry) {
+ entry.abortTask();
+ }
+
+ private Runnable getDismissCallback(NotificationEntry entry) {
+ return new Runnable() {
+ @Override
+ public void run() {
+ int dismissalSurface = NotificationStats.DISMISSAL_SHADE;
+ /**
+ * TODO: determine dismissal surface (ie: shade / headsup / aod)
+ * see {@link NotificationLogger#logNotificationClear}
+ */
+ mNotifCollection.dismissNotification(
+ entry,
+ 0,
+ new DismissedByUserStats(
+ dismissalSurface,
+ DISMISS_SENTIMENT_NEUTRAL,
+ NotificationVisibility.obtain(entry.getKey(),
+ entry.getRanking().getRank(),
+ mNotifCollection.getNotifs().size(),
+ true,
+ NotificationLogger.getNotificationLocation(entry))
+ ));
+ }
+ };
+ }
+
+ private NotificationRowBinderImpl requireBinder() {
+ if (mNotificationRowBinder == null) {
+ throw new RuntimeException("NotificationRowBinder must be attached before using "
+ + "NotifInflaterImpl.");
+ }
+ return mNotificationRowBinder;
+ }
+
+ private final NotificationContentInflater.InflationCallback mInflationCallback =
+ new NotificationContentInflater.InflationCallback() {
+ @Override
+ public void handleInflationException(
+ NotificationEntry entry,
+ Exception e) {
+ entry.setHasInflationError(true);
+ try {
+ final StatusBarNotification sbn = entry.getSbn();
+ // report notification inflation errors back up
+ // to notification delegates
+ mStatusBarService.onNotificationError(
+ sbn.getPackageName(),
+ sbn.getTag(),
+ sbn.getId(),
+ sbn.getUid(),
+ sbn.getInitialPid(),
+ e.getMessage(),
+ sbn.getUserId());
+ } catch (RemoteException ex) {
+ }
+ }
+
+ @Override
+ public void onAsyncInflationFinished(
+ NotificationEntry entry,
+ int inflatedFlags) {
+ if (mExternalInflationCallback != null) {
+ mExternalInflationCallback.onInflationFinished(entry);
+ }
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 4f4fb24..28e486d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -102,6 +102,9 @@
/** If this was a group child that was promoted to the top level, then who did the promoting. */
@Nullable NotifPromoter mNotifPromoter;
+ /** If this notification had an issue with inflating. Only used with the NewNotifPipeline **/
+ private boolean mHasInflationError;
+
/*
* Old members
@@ -576,6 +579,18 @@
remoteInputTextWhenReset = null;
}
+ void setHasInflationError(boolean hasError) {
+ mHasInflationError = hasError;
+ }
+
+ /**
+ * Whether this notification had an error when attempting to inflate. This is only used in
+ * the NewNotifPipeline
+ */
+ public boolean hasInflationError() {
+ return mHasInflationError;
+ }
+
public void setHasSentReply() {
hasSentReply = true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
index 6c93618..20f206b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
@@ -41,8 +41,8 @@
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder;
import com.android.systemui.statusbar.notification.row.RowInflaterTask;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -65,6 +65,7 @@
Dependency.get(NotificationInterruptionStateProvider.class);
private final Context mContext;
+ private final NotificationRowContentBinder mRowContentBinder;
private final NotificationMessagingUtil mMessagingUtil;
private final ExpandableNotificationRow.ExpansionLogger mExpansionLogger =
this::logNotificationExpansion;
@@ -76,7 +77,7 @@
private NotificationPresenter mPresenter;
private NotificationListContainer mListContainer;
private HeadsUpManager mHeadsUpManager;
- private NotificationContentInflater.InflationCallback mInflationCallback;
+ private NotificationRowContentBinder.InflationCallback mInflationCallback;
private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
private BindRowCallback mBindRowCallback;
private NotificationClicker mNotificationClicker;
@@ -84,11 +85,13 @@
public NotificationRowBinderImpl(
Context context,
+ NotificationRowContentBinder rowContentBinder,
boolean allowLongPress,
KeyguardBypassController keyguardBypassController,
StatusBarStateController statusBarStateController,
NotificationLogger logger) {
mContext = context;
+ mRowContentBinder = rowContentBinder;
mMessagingUtil = new NotificationMessagingUtil(context);
mAllowLongPress = allowLongPress;
mKeyguardBypassController = keyguardBypassController;
@@ -109,16 +112,18 @@
public void setUpWithPresenter(NotificationPresenter presenter,
NotificationListContainer listContainer,
HeadsUpManager headsUpManager,
- NotificationContentInflater.InflationCallback inflationCallback,
BindRowCallback bindRowCallback) {
mPresenter = presenter;
mListContainer = listContainer;
mHeadsUpManager = headsUpManager;
- mInflationCallback = inflationCallback;
mBindRowCallback = bindRowCallback;
mOnAppOpsClickListener = mGutsManager::openGuts;
}
+ public void setInflationCallback(NotificationRowContentBinder.InflationCallback callback) {
+ mInflationCallback = callback;
+ }
+
public void setNotificationClicker(NotificationClicker clicker) {
mNotificationClicker = clicker;
}
@@ -154,19 +159,6 @@
private void bindRow(NotificationEntry entry, PackageManager pmUser,
StatusBarNotification sbn, ExpandableNotificationRow row,
Runnable onDismissRunnable) {
- row.setExpansionLogger(mExpansionLogger, entry.getSbn().getKey());
- row.setBypassController(mKeyguardBypassController);
- row.setStatusBarStateController(mStatusBarStateController);
- row.setGroupManager(mGroupManager);
- row.setHeadsUpManager(mHeadsUpManager);
- row.setOnExpandClickListener(mPresenter);
- row.setInflationCallback(mInflationCallback);
- if (mAllowLongPress) {
- row.setLongPressListener(mGutsManager::openGuts);
- }
- mListContainer.bindRow(row);
- getRemoteInputManager().bindRow(row);
-
// Get the app name.
// Note that Notification.Builder#bindHeaderAppName has similar logic
// but since this field is used in the guts, it must be accurate.
@@ -184,15 +176,33 @@
} catch (PackageManager.NameNotFoundException e) {
// Do nothing
}
- row.setAppName(appname);
+
+ row.initialize(
+ appname,
+ sbn.getKey(),
+ mExpansionLogger,
+ mKeyguardBypassController,
+ mGroupManager,
+ mHeadsUpManager,
+ mRowContentBinder,
+ mPresenter);
+
+ // TODO: Either move these into ExpandableNotificationRow#initialize or out of row entirely
+ row.setStatusBarStateController(mStatusBarStateController);
+ row.setInflationCallback(mInflationCallback);
+ row.setAppOpsOnClickListener(mOnAppOpsClickListener);
+ if (mAllowLongPress) {
+ row.setLongPressListener(mGutsManager::openGuts);
+ }
+ mListContainer.bindRow(row);
+ getRemoteInputManager().bindRow(row);
+
row.setOnDismissRunnable(onDismissRunnable);
row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
if (ENABLE_REMOTE_INPUT) {
row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
}
- row.setAppOpsOnClickListener(mOnAppOpsClickListener);
-
mBindRowCallback.onBindRow(entry, pmUser, sbn, row);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
index 13247193..eeb54ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.collection.coordinator;
import com.android.systemui.Dumpable;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.NotifLifetimeExtender;
@@ -45,14 +46,19 @@
*/
@Inject
public NotifCoordinators(
+ FeatureFlags featureFlags,
KeyguardCoordinator keyguardCoordinator,
RankingCoordinator rankingCoordinator,
ForegroundCoordinator foregroundCoordinator,
- DeviceProvisionedCoordinator deviceProvisionedCoordinator) {
+ DeviceProvisionedCoordinator deviceProvisionedCoordinator,
+ PreparationCoordinator preparationCoordinator) {
mCoordinators.add(keyguardCoordinator);
mCoordinators.add(rankingCoordinator);
mCoordinators.add(foregroundCoordinator);
mCoordinators.add(deviceProvisionedCoordinator);
+ if (featureFlags.isNewNotifPipelineRenderingEnabled()) {
+ mCoordinators.add(preparationCoordinator);
+ }
// TODO: add new Coordinators here! (b/145134683, b/112656837)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
new file mode 100644
index 0000000..a14f0e1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator;
+
+import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.collection.NotifInflater;
+import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.notification.logging.NotifEvent;
+import com.android.systemui.statusbar.notification.logging.NotifLog;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Kicks off notification inflation and view rebinding when a notification is added or updated.
+ * Aborts inflation when a notification is removed.
+ *
+ * If a notification is not done inflating, this coordinator will filter the notification out
+ * from the NotifListBuilder.
+ */
+@Singleton
+public class PreparationCoordinator implements Coordinator {
+ private static final String TAG = "PreparationCoordinator";
+
+ private final NotifLog mNotifLog;
+ private final NotifInflater mNotifInflater;
+ private final List<NotificationEntry> mPendingNotifications = new ArrayList<>();
+
+ @Inject
+ public PreparationCoordinator(NotifLog notifLog, NotifInflaterImpl notifInflater) {
+ mNotifLog = notifLog;
+ mNotifInflater = notifInflater;
+ mNotifInflater.setInflationCallback(mInflationCallback);
+ }
+
+ @Override
+ public void attach(NotifCollection notifCollection, NotifListBuilder notifListBuilder) {
+ notifCollection.addCollectionListener(mNotifCollectionListener);
+ notifListBuilder.addPreRenderFilter(mNotifInflationErrorFilter);
+ notifListBuilder.addPreRenderFilter(mNotifInflatingFilter);
+ }
+
+ private final NotifCollectionListener mNotifCollectionListener = new NotifCollectionListener() {
+ @Override
+ public void onEntryAdded(NotificationEntry entry) {
+ inflateEntry(entry, "entryAdded");
+ }
+
+ @Override
+ public void onEntryUpdated(NotificationEntry entry) {
+ rebind(entry, "entryUpdated");
+ }
+
+ @Override
+ public void onEntryRemoved(NotificationEntry entry, int reason, boolean removedByUser) {
+ abortInflation(entry, "entryRemoved reason=" + reason);
+ }
+ };
+
+ private final NotifFilter mNotifInflationErrorFilter = new NotifFilter(
+ TAG + "InflationError") {
+ /**
+ * Filters out notifications that threw an error when attempting to inflate.
+ */
+ @Override
+ public boolean shouldFilterOut(NotificationEntry entry, long now) {
+ if (entry.hasInflationError()) {
+ mPendingNotifications.remove(entry);
+ return true;
+ }
+ return false;
+ }
+ };
+
+ private final NotifFilter mNotifInflatingFilter = new NotifFilter(TAG + "Inflating") {
+ /**
+ * Filters out notifications that haven't been inflated yet
+ */
+ @Override
+ public boolean shouldFilterOut(NotificationEntry entry, long now) {
+ return mPendingNotifications.contains(entry);
+ }
+ };
+
+ private final NotifInflater.InflationCallback mInflationCallback =
+ new NotifInflater.InflationCallback() {
+ @Override
+ public void onInflationFinished(NotificationEntry entry) {
+ mNotifLog.log(NotifEvent.INFLATED, entry);
+ mPendingNotifications.remove(entry);
+ mNotifInflatingFilter.invalidateList();
+ }
+ };
+
+ private void inflateEntry(NotificationEntry entry, String reason) {
+ abortInflation(entry, reason);
+ mPendingNotifications.add(entry);
+ mNotifInflater.inflateViews(entry);
+ }
+
+ private void rebind(NotificationEntry entry, String reason) {
+ mNotifInflater.rebindViews(entry);
+ }
+
+ private void abortInflation(NotificationEntry entry, String reason) {
+ mNotifLog.log(NotifEvent.INFLATION_ABORTED, reason);
+ entry.abortTask();
+ mPendingNotifications.remove(entry);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NewNotifPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NewNotifPipeline.java
index 5e0bd4f..8d3d0ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NewNotifPipeline.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NewNotifPipeline.java
@@ -20,9 +20,12 @@
import com.android.systemui.DumpController;
import com.android.systemui.Dumpable;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
import com.android.systemui.statusbar.notification.collection.NotifListBuilderImpl;
+import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.collection.coordinator.NotifCoordinators;
import com.android.systemui.statusbar.notification.collection.notifcollection.GroupCoalescer;
@@ -41,7 +44,9 @@
private final NotifCollection mNotifCollection;
private final NotifListBuilderImpl mNotifPipeline;
private final NotifCoordinators mNotifPluggableCoordinators;
+ private final NotifInflaterImpl mNotifInflater;
private final DumpController mDumpController;
+ private final FeatureFlags mFeatureFlags;
private final FakePipelineConsumer mFakePipelineConsumer = new FakePipelineConsumer();
@@ -51,20 +56,30 @@
NotifCollection notifCollection,
NotifListBuilderImpl notifPipeline,
NotifCoordinators notifCoordinators,
- DumpController dumpController) {
+ NotifInflaterImpl notifInflater,
+ DumpController dumpController,
+ FeatureFlags featureFlags) {
mGroupCoalescer = groupCoalescer;
mNotifCollection = notifCollection;
mNotifPipeline = notifPipeline;
mNotifPluggableCoordinators = notifCoordinators;
mDumpController = dumpController;
+ mNotifInflater = notifInflater;
+ mFeatureFlags = featureFlags;
}
/** Hooks the new pipeline up to NotificationManager */
public void initialize(
- NotificationListener notificationService) {
+ NotificationListener notificationService,
+ NotificationRowBinderImpl rowBinder) {
mDumpController.registerDumpable("NotifPipeline", this);
+ // Setup inflation
+ if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
+ mNotifInflater.setRowBinder(rowBinder);
+ }
+
// Wire up coordinators
mFakePipelineConsumer.attach(mNotifPipeline);
mNotifPluggableCoordinators.attach(mNotifCollection, mNotifPipeline);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java
index e4a57d7..c6c36ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java
@@ -112,7 +112,12 @@
LIFETIME_EXTENDED,
REMOVE_INTERCEPTED,
INFLATION_ABORTED,
- INFLATED
+ INFLATED,
+
+ // GroupCoalescer
+ COALESCED_EVENT,
+ EARLY_BATCH_EMIT,
+ EMIT_EVENT_BATCH
})
@Retention(RetentionPolicy.SOURCE)
public @interface EventType {}
@@ -147,8 +152,10 @@
"InflationAborted",
"Inflated",
+ // GroupCoalescer labels:
"CoalescedEvent",
- "EarlyBatchEmit"
+ "EarlyBatchEmit",
+ "EmitEventBatch"
};
private static final int TOTAL_EVENT_LABELS = EVENT_LABELS.length;
@@ -174,25 +181,28 @@
/**
* Events related to {@link NotificationEntryManager}
*/
- public static final int NOTIF_ADDED = TOTAL_LIST_BUILDER_EVENT_TYPES;
- public static final int NOTIF_REMOVED = TOTAL_LIST_BUILDER_EVENT_TYPES + 1;
- public static final int NOTIF_UPDATED = TOTAL_LIST_BUILDER_EVENT_TYPES + 2;
- public static final int FILTER = TOTAL_LIST_BUILDER_EVENT_TYPES + 3;
- public static final int SORT = TOTAL_LIST_BUILDER_EVENT_TYPES + 4;
- public static final int FILTER_AND_SORT = TOTAL_LIST_BUILDER_EVENT_TYPES + 5;
- public static final int NOTIF_VISIBILITY_CHANGED = TOTAL_LIST_BUILDER_EVENT_TYPES + 6;
- public static final int LIFETIME_EXTENDED = TOTAL_LIST_BUILDER_EVENT_TYPES + 7;
+ private static final int NEM_EVENT_START_INDEX = TOTAL_LIST_BUILDER_EVENT_TYPES;
+ public static final int NOTIF_ADDED = NEM_EVENT_START_INDEX;
+ public static final int NOTIF_REMOVED = NEM_EVENT_START_INDEX + 1;
+ public static final int NOTIF_UPDATED = NEM_EVENT_START_INDEX + 2;
+ public static final int FILTER = NEM_EVENT_START_INDEX + 3;
+ public static final int SORT = NEM_EVENT_START_INDEX + 4;
+ public static final int FILTER_AND_SORT = NEM_EVENT_START_INDEX + 5;
+ public static final int NOTIF_VISIBILITY_CHANGED = NEM_EVENT_START_INDEX + 6;
+ public static final int LIFETIME_EXTENDED = NEM_EVENT_START_INDEX + 7;
// unable to remove notif - removal intercepted by {@link NotificationRemoveInterceptor}
- public static final int REMOVE_INTERCEPTED = TOTAL_LIST_BUILDER_EVENT_TYPES + 8;
- public static final int INFLATION_ABORTED = TOTAL_LIST_BUILDER_EVENT_TYPES + 9;
- public static final int INFLATED = TOTAL_LIST_BUILDER_EVENT_TYPES + 10;
+ public static final int REMOVE_INTERCEPTED = NEM_EVENT_START_INDEX + 8;
+ public static final int INFLATION_ABORTED = NEM_EVENT_START_INDEX + 9;
+ public static final int INFLATED = NEM_EVENT_START_INDEX + 10;
private static final int TOTAL_NEM_EVENT_TYPES = 11;
/**
* Events related to {@link GroupCoalescer}
*/
- public static final int COALESCED_EVENT = TOTAL_NEM_EVENT_TYPES;
- public static final int EARLY_BATCH_EMIT = TOTAL_NEM_EVENT_TYPES + 1;
- public static final int EMIT_EVENT_BATCH = TOTAL_NEM_EVENT_TYPES + 2;
- private static final int TOTAL_COALESCER_EVENT_TYPES = 2;
+ private static final int COALESCER_EVENT_START_INDEX = NEM_EVENT_START_INDEX
+ + TOTAL_NEM_EVENT_TYPES;
+ public static final int COALESCED_EVENT = COALESCER_EVENT_START_INDEX;
+ public static final int EARLY_BATCH_EMIT = COALESCER_EVENT_START_INDEX + 1;
+ public static final int EMIT_EVENT_BATCH = COALESCER_EVENT_START_INDEX + 2;
+ private static final int TOTAL_COALESCER_EVENT_TYPES = 3;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 3e1b5bd..89e5f55 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -294,6 +294,9 @@
}
}
+ /**
+ * Logs Notification inflation error
+ */
private void logNotificationError(
StatusBarNotification notification,
Exception exception) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHub.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHub.kt
index 2c0c942..e81d361 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHub.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHub.kt
@@ -37,7 +37,8 @@
val key: PersonKey,
val name: CharSequence,
val avatar: Drawable,
- val clickIntent: PendingIntent
+ val clickIntent: PendingIntent,
+ val userId: Int
)
/** Unique identifier for a Person in PeopleHub. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt
index 784673e..88b4147 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt
@@ -17,28 +17,27 @@
package com.android.systemui.statusbar.notification.people
import android.app.Notification
-import android.content.Context
-import android.graphics.Canvas
-import android.graphics.ColorFilter
-import android.graphics.PixelFormat
-import android.graphics.drawable.BitmapDrawable
+import android.content.pm.UserInfo
import android.graphics.drawable.Drawable
-import android.os.UserHandle
+import android.os.UserManager
import android.service.notification.StatusBarNotification
-import android.util.TypedValue
+import android.util.SparseArray
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import com.android.internal.statusbar.NotificationVisibility
import com.android.internal.widget.MessagingGroup
-import com.android.launcher3.icons.BaseIconFactory
import com.android.systemui.R
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.NotificationPersonExtractorPlugin
+import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.notification.NotificationEntryListener
import com.android.systemui.statusbar.notification.NotificationEntryManager
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.policy.ExtensionController
import java.util.ArrayDeque
+import java.util.concurrent.Executor
import javax.inject.Inject
import javax.inject.Singleton
@@ -52,8 +51,7 @@
@Singleton
class NotificationPersonExtractorPluginBoundary @Inject constructor(
- extensionController: ExtensionController,
- private val context: Context
+ extensionController: ExtensionController
) : NotificationPersonExtractor {
private var plugin: NotificationPersonExtractorPlugin? = null
@@ -70,9 +68,8 @@
}
override fun extractPerson(sbn: StatusBarNotification) =
- plugin?.extractPerson(sbn)?.let { data ->
- val badged = addBadgeToDrawable(data.avatar, context, sbn.packageName, sbn.user)
- PersonModel(data.key, data.name, badged, data.clickIntent)
+ plugin?.extractPerson(sbn)?.run {
+ PersonModel(key, name, avatar, clickIntent, sbn.user.identifier)
}
override fun extractPersonKey(sbn: StatusBarNotification) = plugin?.extractPersonKey(sbn)
@@ -84,11 +81,16 @@
@Singleton
class PeopleHubDataSourceImpl @Inject constructor(
private val notificationEntryManager: NotificationEntryManager,
- private val peopleHubManager: PeopleHubManager,
- private val extractor: NotificationPersonExtractor
+ private val extractor: NotificationPersonExtractor,
+ private val userManager: UserManager,
+ @Background private val bgExecutor: Executor,
+ @Main private val mainExecutor: Executor,
+ private val notifLockscreenUserMgr: NotificationLockscreenUserManager
) : DataSource<PeopleHubModel> {
+ private var userChangeSubscription: Subscription? = null
private val dataListeners = mutableListOf<DataListener<PeopleHubModel>>()
+ private val peopleHubManagerForUser = SparseArray<PeopleHubManager>()
private val notificationEntryListener = object : NotificationEntryListener {
override fun onEntryInflated(entry: NotificationEntry, inflatedFlags: Int) =
@@ -106,31 +108,56 @@
}
private fun removeVisibleEntry(entry: NotificationEntry) {
- val key = extractor.extractPersonKey(entry.sbn) ?: entry.extractPersonKey()
- if (key?.let(peopleHubManager::removeActivePerson) == true) {
- updateUi()
+ (extractor.extractPersonKey(entry.sbn) ?: entry.extractPersonKey())?.let { key ->
+ val userId = entry.sbn.user.identifier
+ bgExecutor.execute {
+ val parentId = userManager.getProfileParent(userId)?.id ?: userId
+ mainExecutor.execute {
+ if (peopleHubManagerForUser[parentId]?.removeActivePerson(key) == true) {
+ updateUi()
+ }
+ }
+ }
}
}
private fun addVisibleEntry(entry: NotificationEntry) {
- val personModel = extractor.extractPerson(entry.sbn) ?: entry.extractPerson()
- if (personModel?.let(peopleHubManager::addActivePerson) == true) {
- updateUi()
+ (extractor.extractPerson(entry.sbn) ?: entry.extractPerson())?.let { personModel ->
+ val userId = entry.sbn.user.identifier
+ bgExecutor.execute {
+ val parentId = userManager.getProfileParent(userId)?.id ?: userId
+ mainExecutor.execute {
+ val manager = peopleHubManagerForUser[parentId]
+ ?: PeopleHubManager().also { peopleHubManagerForUser.put(parentId, it) }
+ if (manager.addActivePerson(personModel)) {
+ updateUi()
+ }
+ }
+ }
}
}
override fun registerListener(listener: DataListener<PeopleHubModel>): Subscription {
- val registerWithNotificationEntryManager = dataListeners.isEmpty()
+ val register = dataListeners.isEmpty()
dataListeners.add(listener)
- if (registerWithNotificationEntryManager) {
+ if (register) {
+ userChangeSubscription = notifLockscreenUserMgr.registerListener(
+ object : NotificationLockscreenUserManager.UserChangedListener {
+ override fun onUserChanged(userId: Int) = updateUi()
+ override fun onCurrentProfilesChanged(
+ currentProfiles: SparseArray<UserInfo>?
+ ) = updateUi()
+ })
notificationEntryManager.addNotificationEntryListener(notificationEntryListener)
} else {
- listener.onDataChanged(peopleHubManager.getPeopleHubModel())
+ getPeopleHubModelForCurrentUser()?.let(listener::onDataChanged)
}
return object : Subscription {
override fun unsubscribe() {
dataListeners.remove(listener)
if (dataListeners.isEmpty()) {
+ userChangeSubscription?.unsubscribe()
+ userChangeSubscription = null
notificationEntryManager
.removeNotificationEntryListener(notificationEntryListener)
}
@@ -138,16 +165,36 @@
}
}
+ private fun getPeopleHubModelForCurrentUser(): PeopleHubModel? {
+ val currentUserId = notifLockscreenUserMgr.currentUserId
+ val model = peopleHubManagerForUser[currentUserId]?.getPeopleHubModel()
+ ?: return null
+ val currentProfiles = notifLockscreenUserMgr.currentProfiles
+ return model.copy(people = model.people.filter { person ->
+ currentProfiles[person.userId]?.isQuietModeEnabled == false
+ })
+ }
+
private fun updateUi() {
- val model = peopleHubManager.getPeopleHubModel()
+ val model = getPeopleHubModelForCurrentUser() ?: return
for (listener in dataListeners) {
listener.onDataChanged(model)
}
}
}
-@Singleton
-class PeopleHubManager @Inject constructor() {
+private fun NotificationLockscreenUserManager.registerListener(
+ listener: NotificationLockscreenUserManager.UserChangedListener
+): Subscription {
+ addUserChangedListener(listener)
+ return object : Subscription {
+ override fun unsubscribe() {
+ removeUserChangedListener(listener)
+ }
+ }
+}
+
+class PeopleHubManager {
private val activePeople = mutableMapOf<PersonKey, PersonModel>()
private val inactivePeople = ArrayDeque<PersonModel>(MAX_STORED_INACTIVE_PEOPLE)
@@ -157,7 +204,7 @@
if (inactivePeople.size >= MAX_STORED_INACTIVE_PEOPLE) {
inactivePeople.removeLast()
}
- inactivePeople.push(data)
+ inactivePeople.add(data)
return true
}
return false
@@ -190,63 +237,7 @@
?: extras.getString(Notification.EXTRA_TITLE)
?: return null
val drawable = extractAvatarFromRow(this) ?: return null
- val badgedAvatar = addBadgeToDrawable(drawable, row.context, sbn.packageName, sbn.user)
- return PersonModel(key, name, badgedAvatar, clickIntent)
-}
-
-private fun addBadgeToDrawable(
- drawable: Drawable,
- context: Context,
- packageName: String,
- user: UserHandle
-): Drawable {
- val pm = context.packageManager
- val appInfo = pm.getApplicationInfoAsUser(packageName, 0, user)
- return object : Drawable() {
- override fun draw(canvas: Canvas) {
- val iconBounds = getBounds()
- val factory = object : BaseIconFactory(
- context,
- 0 /* unused */,
- iconBounds.width(),
- true) {}
- val badge = factory.createBadgedIconBitmap(
- appInfo.loadIcon(pm),
- user,
- true,
- appInfo.isInstantApp,
- null)
- val badgeDrawable = BitmapDrawable(context.resources, badge.icon)
- .apply {
- alpha = drawable.alpha
- colorFilter = drawable.colorFilter
- val badgeWidth = TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_DIP,
- 15f,
- context.resources.displayMetrics
- ).toInt()
- setBounds(
- iconBounds.left + (iconBounds.width() - badgeWidth),
- iconBounds.top + (iconBounds.height() - badgeWidth),
- iconBounds.right,
- iconBounds.bottom)
- }
- drawable.bounds = iconBounds
- drawable.draw(canvas)
- badgeDrawable.draw(canvas)
- }
-
- override fun setAlpha(alpha: Int) {
- drawable.alpha = alpha
- }
-
- override fun setColorFilter(colorFilter: ColorFilter?) {
- drawable.colorFilter = colorFilter
- }
-
- @PixelFormat.Opacity
- override fun getOpacity(): Int = PixelFormat.OPAQUE
- }
+ return PersonModel(key, name, drawable, clickIntent, sbn.user.identifier)
}
fun extractAvatarFromRow(entry: NotificationEntry): Drawable? =
@@ -272,4 +263,4 @@
if (isMessagingNotification()) key else null
private fun NotificationEntry.isMessagingNotification() =
- sbn.notification.notificationStyle == Notification.MessagingStyle::class.java
\ No newline at end of file
+ sbn.notification.notificationStyle == Notification.MessagingStyle::class.java
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 3c247df..a8a35d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -65,7 +65,6 @@
import android.widget.Chronometer;
import android.widget.FrameLayout;
import android.widget.ImageView;
-import android.widget.RemoteViews;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
@@ -150,7 +149,7 @@
private StatusBarStateController mStatusbarStateController;
private KeyguardBypassController mBypassController;
private LayoutListener mLayoutListener;
- private final NotificationContentInflater mNotificationInflater;
+ private NotificationRowContentBinder mNotificationContentBinder;
private int mIconTransformContentShift;
private int mIconTransformContentShiftNoIcon;
private int mMaxHeadsUpHeightBeforeN;
@@ -464,7 +463,7 @@
* Inflate views based off the inflation flags set. Inflation happens asynchronously.
*/
public void inflateViews() {
- mNotificationInflater.bindContent(mEntry, this, mInflationFlags, mBindParams,
+ mNotificationContentBinder.bindContent(mEntry, this, mInflationFlags, mBindParams,
false /* forceInflate */, mInflationCallback);
}
@@ -478,7 +477,7 @@
// View should not be reinflated in the future
clearInflationFlags(inflationFlag);
Runnable freeViewRunnable =
- () -> mNotificationInflater.unbindContent(mEntry, this, inflationFlag);
+ () -> mNotificationContentBinder.unbindContent(mEntry, this, inflationFlag);
switch (inflationFlag) {
case FLAG_CONTENT_VIEW_HEADS_UP:
getPrivateLayout().performWhenContentInactive(VISIBLE_TYPE_HEADSUP,
@@ -742,23 +741,10 @@
return mIsHeadsUp || mHeadsupDisappearRunning;
}
-
- public void setGroupManager(NotificationGroupManager groupManager) {
- mGroupManager = groupManager;
- mPrivateLayout.setGroupManager(groupManager);
- }
-
public void setRemoteInputController(RemoteInputController r) {
mPrivateLayout.setRemoteInputController(r);
}
- public void setAppName(String appName) {
- mAppName = appName;
- if (mMenuRow != null && mMenuRow.getMenuView() != null) {
- mMenuRow.setAppName(mAppName);
- }
- }
-
public void addChildNotification(ExpandableNotificationRow row) {
addChildNotification(row, -1);
}
@@ -852,7 +838,7 @@
mIsChildInGroup = isChildInGroup;
if (mIsLowPriority) {
int flags = FLAG_CONTENT_VIEW_CONTRACTED | FLAG_CONTENT_VIEW_EXPANDED;
- mNotificationInflater.bindContent(mEntry, this, flags, mBindParams,
+ mNotificationContentBinder.bindContent(mEntry, this, flags, mBindParams,
false /* forceInflate */, mInflationCallback);
}
}
@@ -1105,10 +1091,6 @@
return mPrivateLayout.getContractedNotificationHeader();
}
- public void setOnExpandClickListener(OnExpandClickListener onExpandClickListener) {
- mOnExpandClickListener = onExpandClickListener;
- }
-
public void setLongPressListener(LongPressListener longPressListener) {
mLongPressListener = longPressListener;
}
@@ -1131,10 +1113,6 @@
}
}
- public void setHeadsUpManager(HeadsUpManager headsUpManager) {
- mHeadsUpManager = headsUpManager;
- }
-
public HeadsUpManager getHeadsUpManager() {
return mHeadsUpManager;
}
@@ -1259,7 +1237,7 @@
l.reInflateViews();
}
mEntry.getSbn().clearPackageContext();
- mNotificationInflater.bindContent(mEntry, this, mInflationFlags, mBindParams,
+ mNotificationContentBinder.bindContent(mEntry, this, mInflationFlags, mBindParams,
true /* forceInflate */, mInflationCallback);
}
@@ -1634,10 +1612,6 @@
mBindParams.usesIncreasedHeadsUpHeight = use;
}
- public void setRemoteViewClickHandler(RemoteViews.OnClickHandler remoteViewClickHandler) {
- mNotificationInflater.setRemoteViewClickHandler(remoteViewClickHandler);
- }
-
/**
* Set callback for notification content inflation
*
@@ -1652,7 +1626,7 @@
mNeedsRedaction = needsRedaction;
if (needsRedaction) {
setInflationFlags(FLAG_CONTENT_VIEW_PUBLIC);
- mNotificationInflater.bindContent(mEntry, this, FLAG_CONTENT_VIEW_PUBLIC,
+ mNotificationContentBinder.bindContent(mEntry, this, FLAG_CONTENT_VIEW_PUBLIC,
mBindParams, false /* forceInflate */, mInflationCallback);
} else {
clearInflationFlags(FLAG_CONTENT_VIEW_PUBLIC);
@@ -1661,18 +1635,12 @@
}
}
- @VisibleForTesting
- public NotificationContentInflater getNotificationInflater() {
- return mNotificationInflater;
- }
-
public interface ExpansionLogger {
void logNotificationExpansion(String key, boolean userAction, boolean expanded);
}
public ExpandableNotificationRow(Context context, AttributeSet attrs) {
super(context, attrs);
- mNotificationInflater = new NotificationContentInflater();
mMenuRow = new NotificationMenuRow(mContext);
mImageResolver = new NotificationInlineImageResolver(context,
new NotificationInlineImageCache());
@@ -1680,8 +1648,30 @@
initDimens();
}
- public void setBypassController(KeyguardBypassController bypassController) {
+ /**
+ * Initialize row.
+ */
+ public void initialize(
+ String appName,
+ String notificationKey,
+ ExpansionLogger logger,
+ KeyguardBypassController bypassController,
+ NotificationGroupManager groupManager,
+ HeadsUpManager headsUpManager,
+ NotificationRowContentBinder rowContentBinder,
+ OnExpandClickListener onExpandClickListener) {
+ mAppName = appName;
+ if (mMenuRow != null && mMenuRow.getMenuView() != null) {
+ mMenuRow.setAppName(mAppName);
+ }
+ mLogger = logger;
+ mLoggingKey = notificationKey;
mBypassController = bypassController;
+ mGroupManager = groupManager;
+ mPrivateLayout.setGroupManager(groupManager);
+ mHeadsUpManager = headsUpManager;
+ mNotificationContentBinder = rowContentBinder;
+ mOnExpandClickListener = onExpandClickListener;
}
public void setStatusBarStateController(StatusBarStateController statusBarStateController) {
@@ -2920,11 +2910,6 @@
return 0;
}
- public void setExpansionLogger(ExpansionLogger logger, String key) {
- mLogger = logger;
- mLoggingKey = key;
- }
-
public void onExpandedByGesture(boolean userExpanded) {
int event = MetricsEvent.ACTION_NOTIFICATION_GESTURE_EXPANDER;
if (mGroupManager.isSummaryOfGroup(mEntry.getSbn())) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCache.java
new file mode 100644
index 0000000..c11c60f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCache.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import android.widget.RemoteViews;
+
+import androidx.annotation.Nullable;
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
+
+/**
+ * Caches {@link RemoteViews} for a notification's content views.
+ */
+public interface NotifRemoteViewCache {
+
+ /**
+ * Whether the notification has the remote view cached
+ *
+ * @param entry notification
+ * @param flag inflation flag for content view
+ * @return true if the remote view is cached
+ */
+ boolean hasCachedView(NotificationEntry entry, @InflationFlag int flag);
+
+ /**
+ * Get the remote view for the content flag specified.
+ *
+ * @param entry notification
+ * @param flag inflation flag for the content view
+ * @return the remote view if it is cached, null otherwise
+ */
+ @Nullable RemoteViews getCachedView(NotificationEntry entry, @InflationFlag int flag);
+
+ /**
+ * Cache a remote view for a given content flag on a notification.
+ *
+ * @param entry notification
+ * @param flag inflation flag for the content view
+ * @param remoteView remote view to store
+ */
+ void putCachedView(
+ NotificationEntry entry,
+ @InflationFlag int flag,
+ RemoteViews remoteView);
+
+ /**
+ * Remove a cached remote view for a given content flag on a notification.
+ *
+ * @param entry notification
+ * @param flag inflation flag for the content view
+ */
+ void removeCachedView(NotificationEntry entry, @InflationFlag int flag);
+
+ /**
+ * Clear a notification's remote view cache.
+ *
+ * @param entry notification
+ */
+ void clearCache(NotificationEntry entry);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImpl.java
new file mode 100644
index 0000000..a19099a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImpl.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import android.util.ArrayMap;
+import android.util.SparseArray;
+import android.widget.RemoteViews;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
+
+import java.util.Map;
+
+import javax.inject.Inject;
+
+/**
+ * Implementation of remote view cache that keeps remote views cached for all active notifications.
+ */
+public class NotifRemoteViewCacheImpl implements NotifRemoteViewCache {
+ private final Map<NotificationEntry, SparseArray<RemoteViews>> mNotifCachedContentViews =
+ new ArrayMap<>();
+
+ @Inject
+ NotifRemoteViewCacheImpl(NotificationEntryManager entryManager) {
+ entryManager.addNotificationEntryListener(mEntryListener);
+ }
+
+ @Override
+ public boolean hasCachedView(NotificationEntry entry, @InflationFlag int flag) {
+ return getCachedView(entry, flag) != null;
+ }
+
+ @Override
+ public @Nullable RemoteViews getCachedView(NotificationEntry entry, @InflationFlag int flag) {
+ return getContentViews(entry).get(flag);
+ }
+
+ @Override
+ public void putCachedView(
+ NotificationEntry entry,
+ @InflationFlag int flag,
+ RemoteViews remoteView) {
+ getContentViews(entry).put(flag, remoteView);
+ }
+
+ @Override
+ public void removeCachedView(NotificationEntry entry, @InflationFlag int flag) {
+ getContentViews(entry).remove(flag);
+ }
+
+ @Override
+ public void clearCache(NotificationEntry entry) {
+ getContentViews(entry).clear();
+ }
+
+ private @NonNull SparseArray<RemoteViews> getContentViews(NotificationEntry entry) {
+ SparseArray<RemoteViews> contentViews = mNotifCachedContentViews.get(entry);
+ if (contentViews == null) {
+ throw new IllegalStateException(
+ String.format("Remote view cache was never created for notification %s",
+ entry.getKey()));
+ }
+ return contentViews;
+ }
+
+ private final NotificationEntryListener mEntryListener = new NotificationEntryListener() {
+ @Override
+ public void onPendingEntryAdded(NotificationEntry entry) {
+ mNotifCachedContentViews.put(entry, new SparseArray<>());
+ }
+
+ @Override
+ public void onEntryRemoved(
+ NotificationEntry entry,
+ @Nullable NotificationVisibility visibility,
+ boolean removedByUser) {
+ mNotifCachedContentViews.remove(entry);
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 172b72e..e1a6747 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.row;
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
@@ -26,7 +27,6 @@
import android.os.AsyncTask;
import android.os.CancellationSignal;
import android.service.notification.StatusBarNotification;
-import android.util.ArrayMap;
import android.util.Log;
import android.view.View;
import android.widget.RemoteViews;
@@ -35,6 +35,7 @@
import com.android.internal.widget.ImageMessageConsumer;
import com.android.systemui.Dependency;
import com.android.systemui.statusbar.InflationTask;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.MediaNotificationProcessor;
@@ -49,17 +50,30 @@
import java.util.HashMap;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* {@link NotificationContentInflater} binds content to a {@link ExpandableNotificationRow} by
* asynchronously building the content's {@link RemoteViews} and applying it to the row.
*/
+@Singleton
+@VisibleForTesting(visibility = PACKAGE)
public class NotificationContentInflater implements NotificationRowContentBinder {
public static final String TAG = "NotifContentInflater";
- private RemoteViews.OnClickHandler mRemoteViewClickHandler;
private boolean mInflateSynchronously = false;
- private final ArrayMap<Integer, RemoteViews> mCachedContentViews = new ArrayMap<>();
+ private final NotificationRemoteInputManager mRemoteInputManager;
+ private final NotifRemoteViewCache mRemoteViewCache;
+
+ @Inject
+ public NotificationContentInflater(
+ NotifRemoteViewCache remoteViewCache,
+ NotificationRemoteInputManager remoteInputManager) {
+ mRemoteViewCache = remoteViewCache;
+ mRemoteInputManager = remoteInputManager;
+ }
@Override
public void bindContent(
@@ -76,27 +90,27 @@
return;
}
- StatusBarNotification sbn = row.getEntry().getSbn();
+ StatusBarNotification sbn = entry.getSbn();
// To check if the notification has inline image and preload inline image if necessary.
row.getImageResolver().preloadImages(sbn.getNotification());
if (forceInflate) {
- mCachedContentViews.clear();
+ mRemoteViewCache.clearCache(entry);
}
AsyncInflationTask task = new AsyncInflationTask(
- sbn,
mInflateSynchronously,
contentToBind,
- mCachedContentViews,
+ mRemoteViewCache,
+ entry,
row,
bindParams.isLowPriority,
bindParams.isChildInGroup,
bindParams.usesIncreasedHeight,
bindParams.usesIncreasedHeadsUpHeight,
callback,
- mRemoteViewClickHandler);
+ mRemoteInputManager.getRemoteViewsOnClickHandler());
if (mInflateSynchronously) {
task.onPostExecute(task.doInBackground());
} else {
@@ -123,13 +137,15 @@
result = inflateSmartReplyViews(result, reInflateFlags, entry,
row.getContext(), packageContext, row.getHeadsUpManager(),
row.getExistingSmartRepliesAndActions());
+
apply(
inflateSynchronously,
result,
reInflateFlags,
- mCachedContentViews,
+ mRemoteViewCache,
+ entry,
row,
- mRemoteViewClickHandler,
+ mRemoteInputManager.getRemoteViewsOnClickHandler(),
null);
return result;
}
@@ -149,7 +165,7 @@
int curFlag = 1;
while (contentToUnbind != 0) {
if ((contentToUnbind & curFlag) != 0) {
- freeNotificationView(row, curFlag);
+ freeNotificationView(entry, row, curFlag);
}
contentToUnbind &= ~curFlag;
curFlag = curFlag << 1;
@@ -157,34 +173,25 @@
}
/**
- * Set click handler for notification remote views
- *
- * @param remoteViewClickHandler click handler for remote views
- */
- public void setRemoteViewClickHandler(RemoteViews.OnClickHandler remoteViewClickHandler) {
- mRemoteViewClickHandler = remoteViewClickHandler;
- }
-
- /**
* Frees the content view associated with the inflation flag. Will only succeed if the
* view is safe to remove.
*
* @param inflateFlag the flag corresponding to the content view which should be freed
*/
- private void freeNotificationView(ExpandableNotificationRow row,
+ private void freeNotificationView(NotificationEntry entry, ExpandableNotificationRow row,
@InflationFlag int inflateFlag) {
switch (inflateFlag) {
case FLAG_CONTENT_VIEW_HEADS_UP:
if (row.getPrivateLayout().isContentViewInactive(VISIBLE_TYPE_HEADSUP)) {
row.getPrivateLayout().setHeadsUpChild(null);
- mCachedContentViews.remove(FLAG_CONTENT_VIEW_HEADS_UP);
+ mRemoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP);
row.getPrivateLayout().setHeadsUpInflatedSmartReplies(null);
}
break;
case FLAG_CONTENT_VIEW_PUBLIC:
if (row.getPublicLayout().isContentViewInactive(VISIBLE_TYPE_CONTRACTED)) {
row.getPublicLayout().setContractedChild(null);
- mCachedContentViews.remove(FLAG_CONTENT_VIEW_PUBLIC);
+ mRemoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC);
}
break;
case FLAG_CONTENT_VIEW_CONTRACTED:
@@ -245,11 +252,12 @@
return result;
}
- public static CancellationSignal apply(
+ private static CancellationSignal apply(
boolean inflateSynchronously,
InflationProgress result,
@InflationFlag int reInflateFlags,
- ArrayMap<Integer, RemoteViews> cachedContentViews,
+ NotifRemoteViewCache remoteViewCache,
+ NotificationEntry entry,
ExpandableNotificationRow row,
RemoteViews.OnClickHandler remoteViewClickHandler,
@Nullable InflationCallback callback) {
@@ -261,7 +269,7 @@
if ((reInflateFlags & flag) != 0) {
boolean isNewView =
!canReapplyRemoteView(result.newContentView,
- cachedContentViews.get(FLAG_CONTENT_VIEW_CONTRACTED));
+ remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED));
ApplyCallback applyCallback = new ApplyCallback() {
@Override
public void setResultView(View v) {
@@ -273,8 +281,8 @@
return result.newContentView;
}
};
- applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, cachedContentViews,
- row, isNewView, remoteViewClickHandler, callback, privateLayout,
+ applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, remoteViewCache,
+ entry, row, isNewView, remoteViewClickHandler, callback, privateLayout,
privateLayout.getContractedChild(), privateLayout.getVisibleWrapper(
NotificationContentView.VISIBLE_TYPE_CONTRACTED),
runningInflations, applyCallback);
@@ -285,7 +293,7 @@
if (result.newExpandedView != null) {
boolean isNewView =
!canReapplyRemoteView(result.newExpandedView,
- cachedContentViews.get(FLAG_CONTENT_VIEW_EXPANDED));
+ remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED));
ApplyCallback applyCallback = new ApplyCallback() {
@Override
public void setResultView(View v) {
@@ -297,8 +305,8 @@
return result.newExpandedView;
}
};
- applyRemoteView(inflateSynchronously, result, reInflateFlags, flag,
- cachedContentViews, row, isNewView, remoteViewClickHandler,
+ applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, remoteViewCache,
+ entry, row, isNewView, remoteViewClickHandler,
callback, privateLayout, privateLayout.getExpandedChild(),
privateLayout.getVisibleWrapper(
NotificationContentView.VISIBLE_TYPE_EXPANDED), runningInflations,
@@ -311,7 +319,7 @@
if (result.newHeadsUpView != null) {
boolean isNewView =
!canReapplyRemoteView(result.newHeadsUpView,
- cachedContentViews.get(FLAG_CONTENT_VIEW_HEADS_UP));
+ remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP));
ApplyCallback applyCallback = new ApplyCallback() {
@Override
public void setResultView(View v) {
@@ -323,8 +331,8 @@
return result.newHeadsUpView;
}
};
- applyRemoteView(inflateSynchronously, result, reInflateFlags, flag,
- cachedContentViews, row, isNewView, remoteViewClickHandler,
+ applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, remoteViewCache,
+ entry, row, isNewView, remoteViewClickHandler,
callback, privateLayout, privateLayout.getHeadsUpChild(),
privateLayout.getVisibleWrapper(
VISIBLE_TYPE_HEADSUP), runningInflations,
@@ -336,7 +344,7 @@
if ((reInflateFlags & flag) != 0) {
boolean isNewView =
!canReapplyRemoteView(result.newPublicView,
- cachedContentViews.get(FLAG_CONTENT_VIEW_PUBLIC));
+ remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC));
ApplyCallback applyCallback = new ApplyCallback() {
@Override
public void setResultView(View v) {
@@ -348,15 +356,16 @@
return result.newPublicView;
}
};
- applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, cachedContentViews,
- row, isNewView, remoteViewClickHandler, callback,
+ applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, remoteViewCache,
+ entry, row, isNewView, remoteViewClickHandler, callback,
publicLayout, publicLayout.getContractedChild(),
publicLayout.getVisibleWrapper(NotificationContentView.VISIBLE_TYPE_CONTRACTED),
runningInflations, applyCallback);
}
// Let's try to finish, maybe nobody is even inflating anything
- finishIfDone(result, reInflateFlags, cachedContentViews, runningInflations, callback, row);
+ finishIfDone(result, reInflateFlags, remoteViewCache, runningInflations, callback, entry,
+ row);
CancellationSignal cancellationSignal = new CancellationSignal();
cancellationSignal.setOnCancelListener(
() -> runningInflations.values().forEach(CancellationSignal::cancel));
@@ -369,7 +378,8 @@
final InflationProgress result,
final @InflationFlag int reInflateFlags,
@InflationFlag int inflationId,
- final ArrayMap<Integer, RemoteViews> cachedContentViews,
+ final NotifRemoteViewCache remoteViewCache,
+ final NotificationEntry entry,
final ExpandableNotificationRow row,
boolean isNewView,
RemoteViews.OnClickHandler remoteViewClickHandler,
@@ -397,7 +407,7 @@
existingWrapper.onReinflated();
}
} catch (Exception e) {
- handleInflationError(runningInflations, e, row.getEntry().getSbn(), callback);
+ handleInflationError(runningInflations, e, row.getEntry(), callback);
// Add a running inflation to make sure we don't trigger callbacks.
// Safe to do because only happens in tests.
runningInflations.put(inflationId, new CancellationSignal());
@@ -422,8 +432,8 @@
existingWrapper.onReinflated();
}
runningInflations.remove(inflationId);
- finishIfDone(result, reInflateFlags, cachedContentViews, runningInflations,
- callback, row);
+ finishIfDone(result, reInflateFlags, remoteViewCache, runningInflations,
+ callback, entry, row);
}
@Override
@@ -448,7 +458,7 @@
onViewApplied(newView);
} catch (Exception anotherException) {
runningInflations.remove(inflationId);
- handleInflationError(runningInflations, e, row.getEntry().getSbn(),
+ handleInflationError(runningInflations, e, row.getEntry(),
callback);
}
}
@@ -474,7 +484,7 @@
private static void handleInflationError(
HashMap<Integer, CancellationSignal> runningInflations, Exception e,
- StatusBarNotification notification, @Nullable InflationCallback callback) {
+ NotificationEntry notification, @Nullable InflationCallback callback) {
Assert.isMainThread();
runningInflations.values().forEach(CancellationSignal::cancel);
if (callback != null) {
@@ -488,11 +498,11 @@
* @return true if the inflation was finished
*/
private static boolean finishIfDone(InflationProgress result,
- @InflationFlag int reInflateFlags, ArrayMap<Integer, RemoteViews> cachedContentViews,
+ @InflationFlag int reInflateFlags, NotifRemoteViewCache remoteViewCache,
HashMap<Integer, CancellationSignal> runningInflations,
- @Nullable InflationCallback endListener, ExpandableNotificationRow row) {
+ @Nullable InflationCallback endListener, NotificationEntry entry,
+ ExpandableNotificationRow row) {
Assert.isMainThread();
- NotificationEntry entry = row.getEntry();
NotificationContentView privateLayout = row.getPrivateLayout();
NotificationContentView publicLayout = row.getPublicLayout();
if (runningInflations.isEmpty()) {
@@ -500,23 +510,27 @@
if (result.inflatedContentView != null) {
// New view case
privateLayout.setContractedChild(result.inflatedContentView);
- cachedContentViews.put(FLAG_CONTENT_VIEW_CONTRACTED, result.newContentView);
- } else if (cachedContentViews.get(FLAG_CONTENT_VIEW_CONTRACTED) != null) {
+ remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED,
+ result.newContentView);
+ } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED)) {
// Reinflation case. Only update if it's still cached (i.e. view has not been
// freed while inflating).
- cachedContentViews.put(FLAG_CONTENT_VIEW_CONTRACTED, result.newContentView);
+ remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED,
+ result.newContentView);
}
}
if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0) {
if (result.inflatedExpandedView != null) {
privateLayout.setExpandedChild(result.inflatedExpandedView);
- cachedContentViews.put(FLAG_CONTENT_VIEW_EXPANDED, result.newExpandedView);
+ remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED,
+ result.newExpandedView);
} else if (result.newExpandedView == null) {
privateLayout.setExpandedChild(null);
- cachedContentViews.put(FLAG_CONTENT_VIEW_EXPANDED, null);
- } else if (cachedContentViews.get(FLAG_CONTENT_VIEW_EXPANDED) != null) {
- cachedContentViews.put(FLAG_CONTENT_VIEW_EXPANDED, result.newExpandedView);
+ remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED);
+ } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED)) {
+ remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED,
+ result.newExpandedView);
}
if (result.newExpandedView != null) {
privateLayout.setExpandedInflatedSmartReplies(
@@ -530,12 +544,14 @@
if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
if (result.inflatedHeadsUpView != null) {
privateLayout.setHeadsUpChild(result.inflatedHeadsUpView);
- cachedContentViews.put(FLAG_CONTENT_VIEW_HEADS_UP, result.newHeadsUpView);
+ remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP,
+ result.newHeadsUpView);
} else if (result.newHeadsUpView == null) {
privateLayout.setHeadsUpChild(null);
- cachedContentViews.put(FLAG_CONTENT_VIEW_HEADS_UP, null);
- } else if (cachedContentViews.get(FLAG_CONTENT_VIEW_HEADS_UP) != null) {
- cachedContentViews.put(FLAG_CONTENT_VIEW_HEADS_UP, result.newHeadsUpView);
+ remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP);
+ } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP)) {
+ remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP,
+ result.newHeadsUpView);
}
if (result.newHeadsUpView != null) {
privateLayout.setHeadsUpInflatedSmartReplies(
@@ -548,16 +564,18 @@
if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) {
if (result.inflatedPublicView != null) {
publicLayout.setContractedChild(result.inflatedPublicView);
- cachedContentViews.put(FLAG_CONTENT_VIEW_PUBLIC, result.newPublicView);
- } else if (cachedContentViews.get(FLAG_CONTENT_VIEW_PUBLIC) != null) {
- cachedContentViews.put(FLAG_CONTENT_VIEW_PUBLIC, result.newPublicView);
+ remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC,
+ result.newPublicView);
+ } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC)) {
+ remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC,
+ result.newPublicView);
}
}
entry.headsUpStatusBarText = result.headsUpStatusBarText;
entry.headsUpStatusBarTextPublic = result.headsUpStatusBarTextPublic;
if (endListener != null) {
- endListener.onAsyncInflationFinished(row.getEntry(), reInflateFlags);
+ endListener.onAsyncInflationFinished(entry, reInflateFlags);
}
return true;
}
@@ -615,7 +633,7 @@
public static class AsyncInflationTask extends AsyncTask<Void, Void, InflationProgress>
implements InflationCallback, InflationTask {
- private final StatusBarNotification mSbn;
+ private final NotificationEntry mEntry;
private final Context mContext;
private final boolean mInflateSynchronously;
private final boolean mIsLowPriority;
@@ -624,17 +642,17 @@
private final InflationCallback mCallback;
private final boolean mUsesIncreasedHeadsUpHeight;
private @InflationFlag int mReInflateFlags;
- private final ArrayMap<Integer, RemoteViews> mCachedContentViews;
+ private final NotifRemoteViewCache mRemoteViewCache;
private ExpandableNotificationRow mRow;
private Exception mError;
private RemoteViews.OnClickHandler mRemoteViewClickHandler;
private CancellationSignal mCancellationSignal;
private AsyncInflationTask(
- StatusBarNotification notification,
boolean inflateSynchronously,
@InflationFlag int reInflateFlags,
- ArrayMap<Integer, RemoteViews> cachedContentViews,
+ NotifRemoteViewCache cache,
+ NotificationEntry entry,
ExpandableNotificationRow row,
boolean isLowPriority,
boolean isChildInGroup,
@@ -642,11 +660,11 @@
boolean usesIncreasedHeadsUpHeight,
InflationCallback callback,
RemoteViews.OnClickHandler remoteViewClickHandler) {
+ mEntry = entry;
mRow = row;
- mSbn = notification;
mInflateSynchronously = inflateSynchronously;
mReInflateFlags = reInflateFlags;
- mCachedContentViews = cachedContentViews;
+ mRemoteViewCache = cache;
mContext = mRow.getContext();
mIsLowPriority = isLowPriority;
mIsChildInGroup = isChildInGroup;
@@ -654,7 +672,6 @@
mUsesIncreasedHeadsUpHeight = usesIncreasedHeadsUpHeight;
mRemoteViewClickHandler = remoteViewClickHandler;
mCallback = callback;
- NotificationEntry entry = row.getEntry();
entry.setInflationTask(this);
}
@@ -667,12 +684,13 @@
@Override
protected InflationProgress doInBackground(Void... params) {
try {
+ final StatusBarNotification sbn = mEntry.getSbn();
final Notification.Builder recoveredBuilder
= Notification.Builder.recoverBuilder(mContext,
- mSbn.getNotification());
+ sbn.getNotification());
- Context packageContext = mSbn.getPackageContext(mContext);
- Notification notification = mSbn.getNotification();
+ Context packageContext = sbn.getPackageContext(mContext);
+ Notification notification = sbn.getNotification();
if (notification.isMediaNotification()) {
MediaNotificationProcessor processor = new MediaNotificationProcessor(mContext,
packageContext);
@@ -681,7 +699,7 @@
InflationProgress inflationProgress = createRemoteViews(mReInflateFlags,
recoveredBuilder, mIsLowPriority, mIsChildInGroup, mUsesIncreasedHeight,
mUsesIncreasedHeadsUpHeight, packageContext);
- return inflateSmartReplyViews(inflationProgress, mReInflateFlags, mRow.getEntry(),
+ return inflateSmartReplyViews(inflationProgress, mReInflateFlags, mEntry,
mRow.getContext(), packageContext, mRow.getHeadsUpManager(),
mRow.getExistingSmartRepliesAndActions());
} catch (Exception e) {
@@ -694,20 +712,20 @@
protected void onPostExecute(InflationProgress result) {
if (mError == null) {
mCancellationSignal = apply(mInflateSynchronously, result, mReInflateFlags,
- mCachedContentViews, mRow, mRemoteViewClickHandler, this);
+ mRemoteViewCache, mEntry, mRow, mRemoteViewClickHandler, this);
} else {
handleError(mError);
}
}
private void handleError(Exception e) {
- mRow.getEntry().onInflationTaskFinished();
- StatusBarNotification sbn = mRow.getEntry().getSbn();
+ mEntry.onInflationTaskFinished();
+ StatusBarNotification sbn = mEntry.getSbn();
final String ident = sbn.getPackageName() + "/0x"
+ Integer.toHexString(sbn.getId());
Log.e(StatusBar.TAG, "couldn't inflate view for notification " + ident, e);
if (mCallback != null) {
- mCallback.handleInflationException(sbn,
+ mCallback.handleInflationException(mRow.getEntry(),
new InflationException("Couldn't inflate contentViews" + e));
}
}
@@ -729,17 +747,17 @@
}
@Override
- public void handleInflationException(StatusBarNotification notification, Exception e) {
+ public void handleInflationException(NotificationEntry entry, Exception e) {
handleError(e);
}
@Override
public void onAsyncInflationFinished(NotificationEntry entry,
@InflationFlag int inflatedFlags) {
- mRow.getEntry().onInflationTaskFinished();
+ mEntry.onInflationTaskFinished();
mRow.onNotificationUpdated();
if (mCallback != null) {
- mCallback.onAsyncInflationFinished(mRow.getEntry(), inflatedFlags);
+ mCallback.onAsyncInflationFinished(mEntry, inflatedFlags);
}
// Notify the resolver that the inflation task has finished,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java
index 2fe54c0..9b95bff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java
@@ -19,7 +19,6 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.service.notification.StatusBarNotification;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -138,10 +137,10 @@
/**
* Callback for when there is an inflation exception
*
- * @param notification notification which failed to inflate content
+ * @param entry notification which failed to inflate content
* @param e exception
*/
- void handleInflationException(StatusBarNotification notification, Exception e);
+ void handleInflationException(NotificationEntry entry, Exception e);
/**
* Callback for after the content views finish inflating.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
new file mode 100644
index 0000000..df8653c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import javax.inject.Singleton;
+
+import dagger.Binds;
+import dagger.Module;
+
+/**
+ * Dagger Module containing notification row and view inflation implementations.
+ */
+@Module
+public abstract class NotificationRowModule {
+ /**
+ * Provides notification row content binder instance.
+ */
+ @Binds
+ @Singleton
+ public abstract NotificationRowContentBinder provideNotificationRowContentBinder(
+ NotificationContentInflater contentBinderImpl);
+
+ /**
+ * Provides notification remote view cache instance.
+ */
+ @Binds
+ @Singleton
+ public abstract NotifRemoteViewCache provideNotifRemoteViewCache(
+ NotifRemoteViewCacheImpl cacheImpl);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
index 2761689..09c1fad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
@@ -187,18 +187,18 @@
@Override
public boolean beginsSection(@NonNull View view, @Nullable View previous) {
boolean begin = false;
- if (view instanceof ExpandableNotificationRow) {
- if (previous instanceof ExpandableNotificationRow) {
+ if (view instanceof ActivatableNotificationView) {
+ if (previous instanceof ActivatableNotificationView) {
// If we're drawing the first non-person notification, break out a section
- ExpandableNotificationRow curr = (ExpandableNotificationRow) view;
- ExpandableNotificationRow prev = (ExpandableNotificationRow) previous;
+ ActivatableNotificationView curr = (ActivatableNotificationView) view;
+ ActivatableNotificationView prev = (ActivatableNotificationView) previous;
- begin = curr.getEntry().getBucket() != prev.getEntry().getBucket();
+ begin = getBucket(curr) != getBucket(prev);
}
}
if (!begin) {
- begin = view == mGentleHeader || previous == mPeopleHubView;
+ begin = view == mGentleHeader || view == mPeopleHubView;
}
return begin;
@@ -230,29 +230,42 @@
return;
}
- int lastPersonIndex = -1;
- int firstGentleNotifIndex = -1;
+ boolean peopleNotificationsPresent = false;
+ int firstNonHeadsUpIndex = -1;
+ int firstGentleIndex = -1;
+ int notifCount = 0;
final int n = mParent.getChildCount();
for (int i = 0; i < n; i++) {
View child = mParent.getChildAt(i);
- if (child instanceof ExpandableNotificationRow
- && child.getVisibility() != View.GONE) {
+ if (child instanceof ExpandableNotificationRow && child.getVisibility() != View.GONE) {
+ notifCount++;
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ if (firstNonHeadsUpIndex == -1 && !row.isHeadsUp()) {
+ firstNonHeadsUpIndex = i;
+ }
if (row.getEntry().getBucket() == BUCKET_PEOPLE) {
- lastPersonIndex = i;
+ peopleNotificationsPresent = true;
}
if (row.getEntry().getBucket() == BUCKET_SILENT) {
- firstGentleNotifIndex = i;
+ firstGentleIndex = i;
break;
}
}
}
- // make room for peopleHub
- firstGentleNotifIndex += adjustPeopleHubVisibilityAndPosition(lastPersonIndex);
+ if (firstNonHeadsUpIndex == -1) {
+ firstNonHeadsUpIndex = firstGentleIndex != -1 ? firstGentleIndex : notifCount;
+ }
- adjustGentleHeaderVisibilityAndPosition(firstGentleNotifIndex);
+ // make room for peopleHub
+ int offset = adjustPeopleHubVisibilityAndPosition(
+ firstNonHeadsUpIndex, peopleNotificationsPresent);
+ if (firstGentleIndex != -1) {
+ firstGentleIndex += offset;
+ }
+
+ adjustGentleHeaderVisibilityAndPosition(firstGentleIndex);
mGentleHeader.setAreThereDismissableGentleNotifs(
mParent.hasActiveClearableNotifications(ROWS_GENTLE));
@@ -294,13 +307,15 @@
}
}
- private int adjustPeopleHubVisibilityAndPosition(int lastPersonIndex) {
- final boolean showPeopleHeader = mPeopleHubVisible
- && mNumberOfSections > 2
- && mStatusBarStateController.getState() != StatusBarState.KEYGUARD;
+ private int adjustPeopleHubVisibilityAndPosition(
+ int targetIndex, boolean peopleNotificationsPresent) {
+ final boolean showPeopleHeader = mNumberOfSections > 2
+ && mStatusBarStateController.getState() != StatusBarState.KEYGUARD
+ && (peopleNotificationsPresent || mPeopleHubVisible);
final int currentHubIndex = mParent.indexOfChild(mPeopleHubView);
final boolean currentlyVisible = currentHubIndex >= 0;
- int targetIndex = lastPersonIndex + 1;
+
+ mPeopleHubView.setCanSwipe(showPeopleHeader && !peopleNotificationsPresent);
if (!showPeopleHeader) {
if (currentlyVisible) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 71342c5..823dd5a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -134,7 +134,7 @@
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
-import com.android.systemui.statusbar.phone.NotificationPanelView;
+import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -496,8 +496,7 @@
protected boolean mClearAllEnabled;
private Interpolator mHideXInterpolator = Interpolators.FAST_OUT_SLOW_IN;
- private NotificationPanelView mNotificationPanel;
- private final ShadeController mShadeController = Dependency.get(ShadeController.class);
+ private NotificationPanelViewController mNotificationPanelController;
private final NotificationGutsManager mNotificationGutsManager;
private final NotificationSectionsManager mSectionsManager;
@@ -5519,7 +5518,8 @@
if (viewsToRemove.isEmpty()) {
if (closeShade) {
- mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
+ Dependency.get(ShadeController.class).animateCollapsePanels(
+ CommandQueue.FLAG_EXCLUDE_NONE);
}
return;
}
@@ -5577,11 +5577,12 @@
final Runnable onSlideAwayAnimationComplete = () -> {
if (closeShade) {
- mShadeController.addPostCollapseAction(() -> {
+ Dependency.get(ShadeController.class).addPostCollapseAction(() -> {
setDismissAllInProgress(false);
onAnimationComplete.run();
});
- mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
+ Dependency.get(ShadeController.class).animateCollapsePanels(
+ CommandQueue.FLAG_EXCLUDE_NONE);
} else {
setDismissAllInProgress(false);
onAnimationComplete.run();
@@ -5657,8 +5658,9 @@
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void setNotificationPanel(NotificationPanelView notificationPanelView) {
- mNotificationPanel = notificationPanelView;
+ public void setNotificationPanelController(
+ NotificationPanelViewController notificationPanelViewController) {
+ mNotificationPanelController = notificationPanelViewController;
}
public void updateIconAreaViews() {
@@ -6402,7 +6404,7 @@
if (!mAmbientState.isDozing() || startingChild != null) {
// We have notifications, go to locked shade.
- mShadeController.goToLockedShade(startingChild);
+ Dependency.get(ShadeController.class).goToLockedShade(startingChild);
if (startingChild instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) startingChild;
row.onExpandedByGesture(true /* drag down is always an open */);
@@ -6441,7 +6443,7 @@
@Override
public void setEmptyDragAmount(float amount) {
- mNotificationPanel.setEmptyDragAmount(amount);
+ mNotificationPanelController.setEmptyDragAmount(amount);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
index 6d0fcc3..4845ea1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -31,6 +31,7 @@
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeActionHelper {
@@ -298,8 +299,8 @@
@Override
public Animator getViewTranslationAnimator(View v, float target,
ValueAnimator.AnimatorUpdateListener listener) {
- if (v instanceof SwipeableView) {
- return ((SwipeableView) v).getTranslateViewAnimator(target, listener);
+ if (v instanceof ExpandableNotificationRow) {
+ return ((ExpandableNotificationRow) v).getTranslateViewAnimator(target, listener);
} else {
return superGetViewTranslationAnimator(v, target, listener);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt
index a079606..e5717ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt
@@ -16,38 +16,22 @@
package com.android.systemui.statusbar.notification.stack
-import android.animation.Animator
-import android.animation.AnimatorListenerAdapter
-import android.animation.ObjectAnimator
-import android.animation.ValueAnimator
import android.content.Context
import android.util.AttributeSet
-import android.util.FloatProperty
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
-import android.widget.LinearLayout
-import android.widget.TextView
import com.android.systemui.R
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin
import com.android.systemui.statusbar.notification.people.DataListener
import com.android.systemui.statusbar.notification.people.PersonViewModel
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView
-private val TRANSLATE_CONTENT = object : FloatProperty<PeopleHubView>("translate") {
- override fun setValue(view: PeopleHubView, value: Float) {
- view.translation = value
- }
-
- override fun get(view: PeopleHubView) = view.translation
-}
-
class PeopleHubView(context: Context, attrs: AttributeSet) :
ActivatableNotificationView(context, attrs), SwipeableView {
private lateinit var contents: ViewGroup
private lateinit var personControllers: List<PersonDataListenerImpl>
- private var translateAnim: ObjectAnimator? = null
val personViewAdapters: Sequence<DataListener<PersonViewModel?>>
get() = personControllers.asSequence()
@@ -56,9 +40,10 @@
super.onFinishInflate()
contents = requireViewById(R.id.people_list)
personControllers = (0 until contents.childCount)
+ .reversed()
.asSequence()
.mapNotNull { idx ->
- (contents.getChildAt(idx) as? LinearLayout)?.let(::PersonDataListenerImpl)
+ (contents.getChildAt(idx) as? ImageView)?.let(::PersonDataListenerImpl)
}
.toList()
}
@@ -69,41 +54,32 @@
override fun createMenu(): NotificationMenuRowPlugin? = null
- override fun getTranslateViewAnimator(
- leftTarget: Float,
- listener: ValueAnimator.AnimatorUpdateListener?
- ): Animator =
- ObjectAnimator
- .ofFloat(this, TRANSLATE_CONTENT, leftTarget)
- .apply {
- listener?.let { addUpdateListener(listener) }
- addListener(object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(anim: Animator) {
- translateAnim = null
- }
- })
- }
- .also {
- translateAnim?.cancel()
- translateAnim = it
- }
-
override fun resetTranslation() {
- translateAnim?.cancel()
translationX = 0f
}
- private inner class PersonDataListenerImpl(val viewGroup: ViewGroup) :
+ override fun setTranslation(translation: Float) {
+ if (canSwipe) {
+ super.setTranslation(translation)
+ }
+ }
+
+ var canSwipe: Boolean = true
+ set(value) {
+ if (field != value) {
+ if (field) {
+ resetTranslation()
+ }
+ field = value
+ }
+ }
+
+ private inner class PersonDataListenerImpl(val avatarView: ImageView) :
DataListener<PersonViewModel?> {
- val nameView = viewGroup.requireViewById<TextView>(R.id.person_name)
- val avatarView = viewGroup.requireViewById<ImageView>(R.id.person_icon)
-
override fun onDataChanged(data: PersonViewModel?) {
- viewGroup.visibility = data?.let { View.VISIBLE } ?: View.INVISIBLE
- nameView.text = data?.name
avatarView.setImageDrawable(data?.icon)
- viewGroup.setOnClickListener { data?.onClick?.invoke() }
+ avatarView.setOnClickListener { data?.onClick?.invoke() }
}
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SwipeableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SwipeableView.java
index 6c6ef61..49e59a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SwipeableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SwipeableView.java
@@ -16,9 +16,6 @@
package com.android.systemui.statusbar.notification.stack;
-import android.animation.Animator;
-import android.animation.ValueAnimator;
-
import androidx.annotation.Nullable;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -32,10 +29,6 @@
/** Optionally creates a menu for this view. */
@Nullable NotificationMenuRowPlugin createMenu();
- /** Animator for translating the view, simulating a swipe. */
- Animator getTranslateViewAnimator(
- float leftTarget, ValueAnimator.AnimatorUpdateListener listener);
-
/** Sets the translation amount for an in-progress swipe. */
void setTranslation(float translate);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 8b31da4..e03db2c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -230,7 +230,7 @@
// The shelf will be hidden when dozing with a custom clock, we must show notification
// icons in this occasion.
if (mStatusBarStateController.isDozing()
- && mStatusBarComponent.getPanel().hasCustomClock()) {
+ && mStatusBarComponent.getPanelController().hasCustomClock()) {
state |= DISABLE_CLOCK | DISABLE_SYSTEM_INFO;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index 09ebb64..accd2a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -70,7 +70,6 @@
boolean mWakeLockScreenPerformsAuth = SystemProperties.getBoolean(
"persist.sysui.wake_performs_auth", true);
private boolean mDozingRequested;
- private boolean mDozing;
private boolean mPulsing;
private WakefulnessLifecycle mWakefulnessLifecycle;
private final SysuiStatusBarStateController mStatusBarStateController;
@@ -92,7 +91,7 @@
private final LockscreenLockIconController mLockscreenLockIconController;
private NotificationIconAreaController mNotificationIconAreaController;
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
- private NotificationPanelView mNotificationPanel;
+ private NotificationPanelViewController mNotificationPanel;
private View mAmbientIndicationContainer;
private StatusBar mStatusBar;
@@ -142,7 +141,7 @@
NotificationIconAreaController notificationIconAreaController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
StatusBarWindowViewController statusBarWindowViewController,
- NotificationPanelView notificationPanel, View ambientIndicationContainer) {
+ NotificationPanelViewController notificationPanel, View ambientIndicationContainer) {
mStatusBar = statusBar;
mNotificationIconAreaController = notificationIconAreaController;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
@@ -196,8 +195,8 @@
public void startDozing() {
if (!mDozingRequested) {
mDozingRequested = true;
- mDozeLog.traceDozing(mDozing);
updateDozing();
+ mDozeLog.traceDozing(mStatusBarStateController.isDozing());
mStatusBar.updateIsKeyguard();
}
}
@@ -281,8 +280,8 @@
public void stopDozing() {
if (mDozingRequested) {
mDozingRequested = false;
- mDozeLog.traceDozing(mDozing);
updateDozing();
+ mDozeLog.traceDozing(mStatusBarStateController.isDozing());
}
}
@@ -292,7 +291,7 @@
mDozeLog.tracePulseTouchDisabledByProx(ignore);
}
mIgnoreTouchWhilePulsing = ignore;
- if (mDozing && ignore) {
+ if (mStatusBarStateController.isDozing() && ignore) {
mStatusBarWindowViewController.cancelCurrentTouch();
}
}
@@ -448,10 +447,6 @@
return mAnimateScreenOff;
}
- public void setDozing(boolean dozing) {
- mDozing = dozing;
- }
-
boolean getIgnoreTouchWhilePulsing() {
return mIgnoreTouchWhilePulsing;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 8e5a912..7b20a7b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -58,7 +58,7 @@
private final View mClockView;
private final View mOperatorNameView;
private final DarkIconDispatcher mDarkIconDispatcher;
- private final NotificationPanelView mPanelView;
+ private final NotificationPanelViewController mNotificationPanelViewController;
private final Consumer<ExpandableNotificationRow>
mSetTrackingHeadsUp = this::setTrackingHeadsUp;
private final Runnable mUpdatePanelTranslation = this::updatePanelTranslation;
@@ -96,13 +96,14 @@
SysuiStatusBarStateController statusBarStateController,
KeyguardBypassController keyguardBypassController,
KeyguardStateController keyguardStateController,
- NotificationWakeUpCoordinator wakeUpCoordinator, CommandQueue commandQueue) {
+ NotificationWakeUpCoordinator wakeUpCoordinator, CommandQueue commandQueue,
+ NotificationPanelViewController notificationPanelViewController) {
this(notificationIconAreaController, headsUpManager, statusBarStateController,
keyguardBypassController, wakeUpCoordinator, keyguardStateController,
commandQueue,
statusbarView.findViewById(R.id.heads_up_status_bar_view),
statusbarView.findViewById(R.id.notification_stack_scroller),
- statusbarView.findViewById(R.id.notification_panel),
+ notificationPanelViewController,
statusbarView.findViewById(R.id.clock),
statusbarView.findViewById(R.id.operator_name_frame),
statusbarView.findViewById(R.id.centered_icon_area));
@@ -119,7 +120,7 @@
CommandQueue commandQueue,
HeadsUpStatusBarView headsUpStatusBarView,
NotificationStackScrollLayout stackScroller,
- NotificationPanelView panelView,
+ NotificationPanelViewController notificationPanelViewController,
View clockView,
View operatorNameView,
View centeredIconView) {
@@ -131,10 +132,10 @@
headsUpStatusBarView.setOnDrawingRectChangedListener(
() -> updateIsolatedIconLocation(true /* requireUpdate */));
mStackScroller = stackScroller;
- mPanelView = panelView;
- panelView.addTrackingHeadsUpListener(mSetTrackingHeadsUp);
- panelView.addVerticalTranslationListener(mUpdatePanelTranslation);
- panelView.setHeadsUpAppearanceController(this);
+ mNotificationPanelViewController = notificationPanelViewController;
+ notificationPanelViewController.addTrackingHeadsUpListener(mSetTrackingHeadsUp);
+ notificationPanelViewController.addVerticalTranslationListener(mUpdatePanelTranslation);
+ notificationPanelViewController.setHeadsUpAppearanceController(this);
mStackScroller.addOnExpandedHeightChangedListener(mSetExpandedHeight);
mStackScroller.addOnLayoutChangeListener(mStackScrollLayoutChangeListener);
mStackScroller.setHeadsUpAppearanceController(this);
@@ -169,9 +170,9 @@
mHeadsUpManager.removeListener(this);
mHeadsUpStatusBarView.setOnDrawingRectChangedListener(null);
mWakeUpCoordinator.removeListener(this);
- mPanelView.removeTrackingHeadsUpListener(mSetTrackingHeadsUp);
- mPanelView.removeVerticalTranslationListener(mUpdatePanelTranslation);
- mPanelView.setHeadsUpAppearanceController(null);
+ mNotificationPanelViewController.removeTrackingHeadsUpListener(mSetTrackingHeadsUp);
+ mNotificationPanelViewController.removeVerticalTranslationListener(mUpdatePanelTranslation);
+ mNotificationPanelViewController.setHeadsUpAppearanceController(null);
mStackScroller.removeOnExpandedHeightChangedListener(mSetExpandedHeight);
mStackScroller.removeOnLayoutChangeListener(mStackScrollLayoutChangeListener);
mDarkIconDispatcher.removeDarkReceiver(this);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index ac06d9d..c282cb8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -39,12 +39,12 @@
private boolean mTouchingHeadsUpView;
private boolean mTrackingHeadsUp;
private boolean mCollapseSnoozes;
- private NotificationPanelView mPanel;
+ private NotificationPanelViewController mPanel;
private ExpandableNotificationRow mPickedChild;
public HeadsUpTouchHelper(HeadsUpManagerPhone headsUpManager,
Callback callback,
- NotificationPanelView notificationPanelView) {
+ NotificationPanelViewController notificationPanelView) {
mHeadsUpManager = headsUpManager;
mCallback = callback;
mPanel = notificationPanelView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index d95d2b7..d3e44ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -155,7 +155,6 @@
};
private boolean mLeftIsVoiceAssist;
- private AssistManager mAssistManager;
private Drawable mLeftAssistIcon;
private IntentButton mRightButton = new DefaultRightButton();
@@ -254,7 +253,6 @@
mActivityStarter = Dependency.get(ActivityStarter.class);
mFlashlightController = Dependency.get(FlashlightController.class);
mAccessibilityController = Dependency.get(AccessibilityController.class);
- mAssistManager = Dependency.get(AssistManager.class);
mActivityIntentHelper = new ActivityIntentHelper(getContext());
updateLeftAffordance();
}
@@ -551,7 +549,7 @@
Runnable runnable = new Runnable() {
@Override
public void run() {
- mAssistManager.launchVoiceAssistFromKeyguard();
+ Dependency.get(AssistManager.class).launchVoiceAssistFromKeyguard();
}
};
if (!mKeyguardStateController.canDismissLockScreen()) {
@@ -565,7 +563,7 @@
}
private boolean canLaunchVoiceAssist() {
- return mAssistManager.canVoiceAssistBeLaunchedFromKeyguard();
+ return Dependency.get(AssistManager.class).canVoiceAssistBeLaunchedFromKeyguard();
}
private void launchPhone() {
@@ -647,7 +645,7 @@
}
if (mLeftIsVoiceAssist) {
mLeftPreview = mPreviewInflater.inflatePreviewFromService(
- mAssistManager.getVoiceInteractorComponentName());
+ Dependency.get(AssistManager.class).getVoiceInteractorComponentName());
} else {
mLeftPreview = mPreviewInflater.inflatePreview(mLeftButton.getIntent());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
index f7d52b5..ad1aa83 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -98,7 +98,12 @@
bypassEnabled = tunerService.getValue(key, dismissByDefault) != 0
}
}, Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD)
- lockscreenUserManager.addUserChangedListener { pendingUnlockType = null }
+ lockscreenUserManager.addUserChangedListener(
+ object : NotificationLockscreenUserManager.UserChangedListener {
+ override fun onUserChanged(userId: Int) {
+ pendingUnlockType = null
+ }
+ })
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 4e91e4c..a3f14ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -90,7 +90,7 @@
private int mContainerTopPadding;
/**
- * @see NotificationPanelView#getExpandedFraction()
+ * @see NotificationPanelViewController#getExpandedFraction()
*/
private float mPanelExpansion;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index d4cf272..a3b1b5f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -350,7 +350,7 @@
mIsOnDefaultDisplay = mDisplayId == Display.DEFAULT_DISPLAY;
}
- mNavigationBarView.setComponents(mStatusBarLazy.get().getPanel(), mAssistManager);
+ mNavigationBarView.setComponents(mStatusBarLazy.get().getPanelController());
mNavigationBarView.setDisabledFlags(mDisabledFlags1);
mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged);
mNavigationBarView.setOnTouchListener(this::onNavigationTouch);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 5a1b20d..ba9ba6c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -67,7 +67,6 @@
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.assist.AssistHandleViewController;
-import com.android.systemui.assist.AssistManager;
import com.android.systemui.model.SysUiState;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
@@ -148,7 +147,7 @@
private NavigationBarInflaterView mNavigationInflaterView;
private RecentsOnboarding mRecentsOnboarding;
- private NotificationPanelView mPanelView;
+ private NotificationPanelViewController mPanelView;
private FloatingRotationButton mFloatingRotationButton;
private RotationButtonController mRotationButtonController;
@@ -349,7 +348,7 @@
return mBarTransitions.getLightTransitionsController();
}
- public void setComponents(NotificationPanelView panel, AssistManager assistManager) {
+ public void setComponents(NotificationPanelViewController panel) {
mPanelView = panel;
updatePanelSystemUiStateFlags();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index fe0739f..896b6e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -32,7 +32,6 @@
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationContentInflater.AsyncInflationTask;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.statusbar.phone.NotificationGroupManager.NotificationGroup;
import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener;
@@ -428,7 +427,7 @@
* The notification is still pending inflation but we've decided that we no longer need
* the content view (e.g. suppression might have changed and we decided we need to transfer
* back). However, there is no way to abort just this inflation if other inflation requests
- * have started (see {@link AsyncInflationTask#supersedeTask(InflationTask)}). So instead
+ * have started (see {@link InflationTask#supersedeTask(InflationTask)}). So instead
* we just flag it as aborted and free when it's inflated.
*/
boolean mAbortOnInflation;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index c74286d..0f3af09 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -16,118 +16,15 @@
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
-import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
-import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.app.ActivityManager;
-import android.app.Fragment;
-import android.app.StatusBarManager;
import android.content.Context;
-import android.content.pm.ResolveInfo;
-import android.content.res.Configuration;
-import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
-import android.graphics.PointF;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.hardware.biometrics.BiometricSourceType;
-import android.os.PowerManager;
-import android.os.SystemClock;
-import android.provider.DeviceConfig;
-import android.provider.Settings;
import android.util.AttributeSet;
-import android.util.Log;
-import android.util.MathUtils;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowInsets;
-import android.view.accessibility.AccessibilityManager;
-import android.widget.FrameLayout;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.keyguard.KeyguardClockSwitch;
-import com.android.keyguard.KeyguardStatusView;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.systemui.DejankUtils;
-import com.android.systemui.Dependency;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.doze.DozeLog;
-import com.android.systemui.fragments.FragmentHostManager;
-import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.HomeControlsPlugin;
-import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.qs.QS;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.qs.QSFragment;
-import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FlingAnimationUtils;
-import com.android.systemui.statusbar.GestureRecorder;
-import com.android.systemui.statusbar.KeyguardAffordanceView;
-import com.android.systemui.statusbar.KeyguardIndicationController;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationShelf;
-import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.RemoteInputController;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
-import com.android.systemui.statusbar.notification.AnimatableProperty;
-import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
-import com.android.systemui.statusbar.notification.PropertyAnimator;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.ExpandableView;
-import com.android.systemui.statusbar.notification.stack.AnimationProperties;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
-import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
-import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.util.InjectionInflationController;
-import com.android.systemui.util.Utils;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.function.Consumer;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-
-public class NotificationPanelView extends PanelView implements
- ExpandableView.OnHeightChangedListener,
- View.OnClickListener, NotificationStackScrollLayout.OnOverscrollTopChangedListener,
- KeyguardAffordanceHelper.Callback, NotificationStackScrollLayout.OnEmptySpaceClickListener,
- OnHeadsUpChangedListener, QS.HeightListener, ZenModeController.Callback,
- ConfigurationController.ConfigurationListener, StateListener,
- PulseExpansionHandler.ExpansionCallback, DynamicPrivacyController.Listener,
- NotificationWakeUpCoordinator.WakeUpListener {
+public class NotificationPanelView extends PanelView {
private static final boolean DEBUG = false;
@@ -136,2873 +33,34 @@
*/
public static final int FLING_EXPAND = 0;
- /**
- * Fling collapsing QS, potentially stopping when QS becomes QQS.
- */
- public static final int FLING_COLLAPSE = 1;
-
- /**
- * Fling until QS is completely hidden.
- */
- public static final int FLING_HIDE = 2;
- private final DozeParameters mDozeParameters;
-
- private double mQqsSplitFraction;
-
- // Cap and total height of Roboto font. Needs to be adjusted when font for the big clock is
- // changed.
- private static final int CAP_HEIGHT = 1456;
- private static final int FONT_HEIGHT = 2163;
-
- /**
- * Maximum time before which we will expand the panel even for slow motions when getting a
- * touch passed over from launcher.
- */
- private static final int MAX_TIME_TO_OPEN_WHEN_FLINGING_FROM_LAUNCHER = 300;
-
static final String COUNTER_PANEL_OPEN = "panel_open";
static final String COUNTER_PANEL_OPEN_QS = "panel_open_qs";
- private static final String COUNTER_PANEL_OPEN_PEEK = "panel_open_peek";
- private static final Rect mDummyDirtyRect = new Rect(0, 0, 1, 1);
- private static final Rect mEmptyRect = new Rect();
-
- private static final AnimationProperties CLOCK_ANIMATION_PROPERTIES = new AnimationProperties()
- .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
- private static final AnimatableProperty KEYGUARD_HEADS_UP_SHOWING_AMOUNT
- = AnimatableProperty.from("KEYGUARD_HEADS_UP_SHOWING_AMOUNT",
- NotificationPanelView::setKeyguardHeadsUpShowingAmount,
- NotificationPanelView::getKeyguardHeadsUpShowingAmount,
- R.id.keyguard_hun_animator_tag,
- R.id.keyguard_hun_animator_end_tag,
- R.id.keyguard_hun_animator_start_tag);
- private static final AnimationProperties KEYGUARD_HUN_PROPERTIES =
- new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
- @VisibleForTesting
- final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
- new KeyguardUpdateMonitorCallback() {
-
- @Override
- public void onBiometricAuthenticated(int userId,
- BiometricSourceType biometricSourceType) {
- if (mFirstBypassAttempt && mUpdateMonitor.isUnlockingWithBiometricAllowed()) {
- mDelayShowingKeyguardStatusBar = true;
- }
- }
-
- @Override
- public void onBiometricRunningStateChanged(boolean running,
- BiometricSourceType biometricSourceType) {
- boolean keyguardOrShadeLocked = mBarState == StatusBarState.KEYGUARD
- || mBarState == StatusBarState.SHADE_LOCKED;
- if (!running && mFirstBypassAttempt && keyguardOrShadeLocked && !mDozing
- && !mDelayShowingKeyguardStatusBar) {
- mFirstBypassAttempt = false;
- animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
- }
- }
-
- @Override
- public void onFinishedGoingToSleep(int why) {
- mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
- mDelayShowingKeyguardStatusBar = false;
- }
- };
- private final KeyguardStateController.Callback mKeyguardMonitorCallback =
- new KeyguardStateController.Callback() {
- @Override
- public void onKeyguardFadingAwayChanged() {
- if (!mKeyguardStateController.isKeyguardFadingAway()) {
- mFirstBypassAttempt = false;
- mDelayShowingKeyguardStatusBar = false;
- }
- }
- };
-
- private final InjectionInflationController mInjectionInflationController;
- private final PowerManager mPowerManager;
- private final AccessibilityManager mAccessibilityManager;
- private final NotificationWakeUpCoordinator mWakeUpCoordinator;
- private final PulseExpansionHandler mPulseExpansionHandler;
- private final KeyguardBypassController mKeyguardBypassController;
- private final KeyguardUpdateMonitor mUpdateMonitor;
-
- @VisibleForTesting
- protected KeyguardAffordanceHelper mAffordanceHelper;
- private KeyguardUserSwitcher mKeyguardUserSwitcher;
- @VisibleForTesting
- protected KeyguardStatusBarView mKeyguardStatusBar;
- @VisibleForTesting
- protected ViewGroup mBigClockContainer;
- private QS mQs;
- @VisibleForTesting
- protected FrameLayout mQsFrame;
- @VisibleForTesting
- protected KeyguardStatusView mKeyguardStatusView;
- private View mQsNavbarScrim;
- protected NotificationsQuickSettingsContainer mNotificationContainerParent;
- protected NotificationStackScrollLayout mNotificationStackScroller;
- protected FrameLayout mHomeControlsLayout;
- private boolean mAnimateNextPositionUpdate;
-
- private int mTrackingPointer;
- private VelocityTracker mQsVelocityTracker;
- private boolean mQsTracking;
-
- /**
- * If set, the ongoing touch gesture might both trigger the expansion in {@link PanelView} and
- * the expansion for quick settings.
- */
- private boolean mConflictingQsExpansionGesture;
-
- /**
- * Whether we are currently handling a motion gesture in #onInterceptTouchEvent, but haven't
- * intercepted yet.
- */
- private boolean mIntercepting;
- private boolean mPanelExpanded;
- private boolean mQsExpanded;
- private boolean mQsExpandedWhenExpandingStarted;
- private boolean mQsFullyExpanded;
- private boolean mKeyguardShowing;
- private boolean mDozing;
- private boolean mDozingOnDown;
- protected int mBarState;
- private float mInitialHeightOnTouch;
- private float mInitialTouchX;
- private float mInitialTouchY;
- private float mLastTouchX;
- private float mLastTouchY;
- protected float mQsExpansionHeight;
- protected int mQsMinExpansionHeight;
- protected int mQsMaxExpansionHeight;
- private int mQsPeekHeight;
- private boolean mStackScrollerOverscrolling;
- private boolean mQsExpansionFromOverscroll;
- private float mLastOverscroll;
- protected boolean mQsExpansionEnabled = true;
- private ValueAnimator mQsExpansionAnimator;
- private FlingAnimationUtils mFlingAnimationUtils;
- private int mStatusBarMinHeight;
- private int mNotificationsHeaderCollideDistance;
- private int mUnlockMoveDistance;
- private float mEmptyDragAmount;
- private float mDownX;
- private float mDownY;
-
- private final KeyguardClockPositionAlgorithm mClockPositionAlgorithm =
- new KeyguardClockPositionAlgorithm();
- private final KeyguardClockPositionAlgorithm.Result mClockPositionResult =
- new KeyguardClockPositionAlgorithm.Result();
- private boolean mIsExpanding;
-
- private boolean mBlockTouches;
- // Used for two finger gesture as well as accessibility shortcut to QS.
- private boolean mQsExpandImmediate;
- private boolean mTwoFingerQsExpandPossible;
-
- /**
- * If we are in a panel collapsing motion, we reset scrollY of our scroll view but still
- * need to take this into account in our panel height calculation.
- */
- private boolean mQsAnimatorExpand;
- private boolean mIsLaunchTransitionFinished;
- private boolean mIsLaunchTransitionRunning;
- private Runnable mLaunchAnimationEndRunnable;
- private boolean mOnlyAffordanceInThisMotion;
- private boolean mKeyguardStatusViewAnimating;
- private ValueAnimator mQsSizeChangeAnimator;
-
- private boolean mShowEmptyShadeView;
-
- private boolean mQsScrimEnabled = true;
- private boolean mLastAnnouncementWasQuickSettings;
- private boolean mQsTouchAboveFalsingThreshold;
- private int mQsFalsingThreshold;
-
- private float mKeyguardStatusBarAnimateAlpha = 1f;
- private int mOldLayoutDirection;
- private HeadsUpTouchHelper mHeadsUpTouchHelper;
- private boolean mIsExpansionFromHeadsUp;
- private boolean mListenForHeadsUp;
- private int mNavigationBarBottomHeight;
- private boolean mExpandingFromHeadsUp;
- private boolean mCollapsedOnDown;
- private int mPositionMinSideMargin;
- private int mMaxFadeoutHeight;
- private int mLastOrientation = -1;
- private boolean mClosingWithAlphaFadeOut;
- private boolean mHeadsUpAnimatingAway;
- private boolean mLaunchingAffordance;
- private boolean mAffordanceHasPreview;
- private FalsingManager mFalsingManager;
- private String mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
-
- private Runnable mHeadsUpExistenceChangedRunnable = new Runnable() {
- @Override
- public void run() {
- setHeadsUpAnimatingAway(false);
- notifyBarPanelExpansionChanged();
- }
- };
- private NotificationGroupManager mGroupManager;
- private boolean mShowIconsWhenExpanded;
- private int mIndicationBottomPadding;
- private int mAmbientIndicationBottomPadding;
- private boolean mIsFullWidth;
- private boolean mBlockingExpansionForCurrentTouch;
-
- /**
- * Following variables maintain state of events when input focus transfer may occur.
- */
- private boolean mExpectingSynthesizedDown; // expecting to see synthesized DOWN event
- private boolean mLastEventSynthesizedDown; // last event was synthesized DOWN event
-
- /**
- * Current dark amount that follows regular interpolation curve of animation.
- */
- private float mInterpolatedDarkAmount;
-
- /**
- * Dark amount that animates from 0 to 1 or vice-versa in linear manner, even if the
- * interpolation curve is different.
- */
- private float mLinearDarkAmount;
-
- private boolean mPulsing;
- private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
- private boolean mNoVisibleNotifications = true;
- private boolean mUserSetupComplete;
- private int mQsNotificationTopPadding;
- private float mExpandOffset;
- private boolean mHideIconsDuringNotificationLaunch = true;
- private int mStackScrollerMeasuringPass;
- private ArrayList<Consumer<ExpandableNotificationRow>> mTrackingHeadsUpListeners
- = new ArrayList<>();
- private ArrayList<Runnable> mVerticalTranslationListener = new ArrayList<>();
- private HeadsUpAppearanceController mHeadsUpAppearanceController;
-
- private int mPanelAlpha;
private int mCurrentPanelAlpha;
private final Paint mAlphaPaint = new Paint();
- private Runnable mPanelAlphaEndAction;
- private float mBottomAreaShadeAlpha;
- private final ValueAnimator mBottomAreaShadeAlphaAnimator;
- private AnimatorListenerAdapter mAnimatorListenerAdapter = new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (mPanelAlphaEndAction != null) {
- mPanelAlphaEndAction.run();
- }
- }
- };
- private final AnimatableProperty PANEL_ALPHA = AnimatableProperty.from(
- "panelAlpha",
- NotificationPanelView::setPanelAlphaInternal,
- NotificationPanelView::getCurrentPanelAlpha,
- R.id.panel_alpha_animator_tag,
- R.id.panel_alpha_animator_start_tag,
- R.id.panel_alpha_animator_end_tag);
- private final AnimationProperties PANEL_ALPHA_OUT_PROPERTIES = new AnimationProperties()
- .setDuration(150)
- .setCustomInterpolator(PANEL_ALPHA.getProperty(), Interpolators.ALPHA_OUT);
- private final AnimationProperties PANEL_ALPHA_IN_PROPERTIES = new AnimationProperties()
- .setDuration(200)
- .setAnimationFinishListener(mAnimatorListenerAdapter)
- .setCustomInterpolator(PANEL_ALPHA.getProperty(), Interpolators.ALPHA_IN);
- private final NotificationEntryManager mEntryManager;
+ private boolean mDozing;
+ private RtlChangeListener mRtlChangeListener;
- private final CommandQueue mCommandQueue;
- private final NotificationLockscreenUserManager mLockscreenUserManager;
- private final ShadeController mShadeController;
- private int mDisplayId;
-
- /**
- * Cache the resource id of the theme to avoid unnecessary work in onThemeChanged.
- *
- * onThemeChanged is forced when the theme might not have changed. So, to avoid unncessary
- * work, check the current id with the cached id.
- */
- private int mThemeResId;
- private KeyguardIndicationController mKeyguardIndicationController;
- private Consumer<Boolean> mAffordanceLaunchListener;
- private int mShelfHeight;
- private Runnable mOnReinflationListener;
- private int mDarkIconSize;
- private int mHeadsUpInset;
- private boolean mHeadsUpPinnedMode;
- private float mKeyguardHeadsUpShowingAmount = 0.0f;
- private boolean mShowingKeyguardHeadsUp;
- private boolean mAllowExpandForSmallExpansion;
- private Runnable mExpandAfterLayoutRunnable;
-
- /**
- * If face auth with bypass is running for the first time after you turn on the screen.
- * (From aod or screen off)
- */
- private boolean mFirstBypassAttempt;
- /**
- * If auth happens successfully during {@code mFirstBypassAttempt}, and we should wait until
- * the keyguard is dismissed to show the status bar.
- */
- private boolean mDelayShowingKeyguardStatusBar;
-
- private PluginManager mPluginManager;
- private FrameLayout mPluginFrame;
- private NPVPluginManager mNPVPluginManager;
-
- @Inject
- public NotificationPanelView(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
- InjectionInflationController injectionInflationController,
- NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler,
- DynamicPrivacyController dynamicPrivacyController,
- KeyguardBypassController bypassController, FalsingManager falsingManager,
- PluginManager pluginManager, ShadeController shadeController,
- NotificationLockscreenUserManager notificationLockscreenUserManager,
- NotificationEntryManager notificationEntryManager,
- KeyguardStateController keyguardStateController,
- StatusBarStateController statusBarStateController, DozeLog dozeLog,
- DozeParameters dozeParameters, CommandQueue commandQueue) {
- super(context, attrs, falsingManager, dozeLog, keyguardStateController,
- (SysuiStatusBarStateController) statusBarStateController);
+ public NotificationPanelView(Context context, AttributeSet attrs) {
+ super(context, attrs);
setWillNotDraw(!DEBUG);
- mInjectionInflationController = injectionInflationController;
- mFalsingManager = falsingManager;
- mPowerManager = context.getSystemService(PowerManager.class);
- mWakeUpCoordinator = coordinator;
- mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
- setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
mAlphaPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
- setPanelAlpha(255, false /* animate */);
- mCommandQueue = commandQueue;
- mDisplayId = context.getDisplayId();
- mPulseExpansionHandler = pulseExpansionHandler;
- mDozeParameters = dozeParameters;
- pulseExpansionHandler.setPulseExpandAbortListener(() -> {
- if (mQs != null) {
- mQs.animateHeaderSlidingOut();
- }
- });
- mThemeResId = context.getThemeResId();
- mKeyguardBypassController = bypassController;
- mUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
- mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
- mKeyguardStateController.addCallback(mKeyguardMonitorCallback);
- dynamicPrivacyController.addListener(this);
-
- mBottomAreaShadeAlphaAnimator = ValueAnimator.ofFloat(1f, 0);
- mBottomAreaShadeAlphaAnimator.addUpdateListener(animation -> {
- mBottomAreaShadeAlpha = (float) animation.getAnimatedValue();
- updateKeyguardBottomAreaAlpha();
- });
- mBottomAreaShadeAlphaAnimator.setDuration(160);
- mBottomAreaShadeAlphaAnimator.setInterpolator(Interpolators.ALPHA_OUT);
- mPluginManager = pluginManager;
- mShadeController = shadeController;
- mLockscreenUserManager = notificationLockscreenUserManager;
- mEntryManager = notificationEntryManager;
setBackgroundColor(Color.TRANSPARENT);
}
- /**
- * Returns if there's a custom clock being presented.
- */
- public boolean hasCustomClock() {
- return mKeyguardStatusView.hasCustomClock();
- }
-
- private void setStatusBar(StatusBar bar) {
- mStatusBar = bar;
- mKeyguardBottomArea.setStatusBar(mStatusBar);
- }
-
- /**
- * Call after this view has been fully inflated and had its children attached.
- */
- public void onChildrenAttached() {
- loadDimens();
- mKeyguardStatusBar = findViewById(R.id.keyguard_header);
- mKeyguardStatusView = findViewById(R.id.keyguard_status_view);
-
- KeyguardClockSwitch keyguardClockSwitch = findViewById(R.id.keyguard_clock_container);
- mBigClockContainer = findViewById(R.id.big_clock_container);
- keyguardClockSwitch.setBigClockContainer(mBigClockContainer);
-
- mHomeControlsLayout = findViewById(R.id.home_controls_layout);
- mNotificationContainerParent = findViewById(R.id.notification_container_parent);
- mNotificationStackScroller = findViewById(R.id.notification_stack_scroller);
- mNotificationStackScroller.setOnHeightChangedListener(this);
- mNotificationStackScroller.setOverscrollTopChangedListener(this);
- mNotificationStackScroller.setOnEmptySpaceClickListener(this);
- addTrackingHeadsUpListener(mNotificationStackScroller::setTrackingHeadsUp);
- mKeyguardBottomArea = findViewById(R.id.keyguard_bottom_area);
- mQsNavbarScrim = findViewById(R.id.qs_navbar_scrim);
- mLastOrientation = getResources().getConfiguration().orientation;
- mPluginFrame = findViewById(R.id.plugin_frame);
- if (Settings.System.getInt(
- mContext.getContentResolver(), "npv_plugin_flag", 0) == 1) {
- mNPVPluginManager = new NPVPluginManager(mPluginFrame, mPluginManager);
- }
-
-
- initBottomArea();
-
- mWakeUpCoordinator.setStackScroller(mNotificationStackScroller);
- mQsFrame = findViewById(R.id.qs_frame);
- mPulseExpansionHandler.setUp(mNotificationStackScroller, this, mShadeController);
- mWakeUpCoordinator.addListener(new NotificationWakeUpCoordinator.WakeUpListener() {
- @Override
- public void onFullyHiddenChanged(boolean isFullyHidden) {
- updateKeyguardStatusBarForHeadsUp();
- }
-
- @Override
- public void onPulseExpansionChanged(boolean expandingChanged) {
- if (mKeyguardBypassController.getBypassEnabled()) {
- // Position the notifications while dragging down while pulsing
- requestScrollerTopPaddingUpdate(false /* animate */);
- updateQSPulseExpansion();
- }
- }
- });
-
- mPluginManager.addPluginListener(
- new PluginListener<HomeControlsPlugin>() {
-
- @Override
- public void onPluginConnected(HomeControlsPlugin plugin,
- Context pluginContext) {
- plugin.sendParentGroup(mHomeControlsLayout);
- }
-
- @Override
- public void onPluginDisconnected(HomeControlsPlugin plugin) {
-
- }
- }, HomeControlsPlugin.class, false);
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- FragmentHostManager.get(this).addTagListener(QS.TAG, mFragmentListener);
- Dependency.get(StatusBarStateController.class).addCallback(this);
- Dependency.get(ZenModeController.class).addCallback(this);
- Dependency.get(ConfigurationController.class).addCallback(this);
- mUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
- // Theme might have changed between inflating this view and attaching it to the window, so
- // force a call to onThemeChanged
- onThemeChanged();
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- FragmentHostManager.get(this).removeTagListener(QS.TAG, mFragmentListener);
- Dependency.get(StatusBarStateController.class).removeCallback(this);
- Dependency.get(ZenModeController.class).removeCallback(this);
- Dependency.get(ConfigurationController.class).removeCallback(this);
- mUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
- }
-
- @Override
- protected void loadDimens() {
- super.loadDimens();
- mFlingAnimationUtils = new FlingAnimationUtils(getResources().getDisplayMetrics(), 0.4f);
- mStatusBarMinHeight = getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height);
- mQsPeekHeight = getResources().getDimensionPixelSize(R.dimen.qs_peek_height);
- mNotificationsHeaderCollideDistance =
- getResources().getDimensionPixelSize(R.dimen.header_notifications_collide_distance);
- mUnlockMoveDistance = getResources().getDimensionPixelOffset(R.dimen.unlock_move_distance);
- mClockPositionAlgorithm.loadDimens(getResources());
- mQsFalsingThreshold = getResources().getDimensionPixelSize(
- R.dimen.qs_falsing_threshold);
- mPositionMinSideMargin = getResources().getDimensionPixelSize(
- R.dimen.notification_panel_min_side_margin);
- mMaxFadeoutHeight = getResources().getDimensionPixelSize(
- R.dimen.max_notification_fadeout_height);
- mIndicationBottomPadding = getResources().getDimensionPixelSize(
- R.dimen.keyguard_indication_bottom_padding);
- mQsNotificationTopPadding = getResources().getDimensionPixelSize(
- R.dimen.qs_notification_padding);
- mShelfHeight = getResources().getDimensionPixelSize(R.dimen.notification_shelf_height);
- mDarkIconSize = getResources().getDimensionPixelSize(
- R.dimen.status_bar_icon_drawing_size_dark);
- int statusbarHeight = getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height);
- mHeadsUpInset = statusbarHeight + getResources().getDimensionPixelSize(
- R.dimen.heads_up_status_bar_padding);
- mQqsSplitFraction = ((float) getResources().getInteger(R.integer.qqs_split_fraction)) / (
- getResources().getInteger(R.integer.qqs_split_fraction)
- + getResources().getInteger(R.integer.qs_split_fraction));
- }
-
- /**
- * @see #launchCamera(boolean, int)
- * @see #setLaunchingAffordance(boolean)
- */
- public void setLaunchAffordanceListener(Consumer<Boolean> listener) {
- mAffordanceLaunchListener = listener;
- }
-
- public void updateResources() {
- Resources res = getResources();
- int qsWidth = res.getDimensionPixelSize(R.dimen.qs_panel_width);
- int panelGravity = getResources().getInteger(R.integer.notification_panel_layout_gravity);
- FrameLayout.LayoutParams lp =
- (FrameLayout.LayoutParams) mQsFrame.getLayoutParams();
- if (lp.width != qsWidth || lp.gravity != panelGravity) {
- lp.width = qsWidth;
- lp.gravity = panelGravity;
- mQsFrame.setLayoutParams(lp);
- }
-
- int panelWidth = res.getDimensionPixelSize(R.dimen.notification_panel_width);
- lp = (FrameLayout.LayoutParams) mNotificationStackScroller.getLayoutParams();
- if (lp.width != panelWidth || lp.gravity != panelGravity) {
- lp.width = panelWidth;
- lp.gravity = panelGravity;
- mNotificationStackScroller.setLayoutParams(lp);
- }
- int sideMargin = res.getDimensionPixelOffset(R.dimen.notification_side_paddings);
- int topMargin = sideMargin;
- lp = (FrameLayout.LayoutParams) mPluginFrame.getLayoutParams();
- if (lp.width != qsWidth || lp.gravity != panelGravity || lp.leftMargin != sideMargin
- || lp.rightMargin != sideMargin || lp.topMargin != topMargin) {
- lp.width = qsWidth;
- lp.gravity = panelGravity;
- lp.leftMargin = sideMargin;
- lp.rightMargin = sideMargin;
- lp.topMargin = topMargin;
- mPluginFrame.setLayoutParams(lp);
- }
- }
-
- @Override
- public void onDensityOrFontScaleChanged() {
- updateShowEmptyShadeView();
- }
-
- @Override
- public void onThemeChanged() {
- final int themeResId = getContext().getThemeResId();
- if (mThemeResId == themeResId) {
- return;
- }
- mThemeResId = themeResId;
-
- reInflateViews();
- }
-
- @Override
- public void onOverlayChanged() {
- reInflateViews();
- }
-
- private void reInflateViews() {
- updateShowEmptyShadeView();
-
- // Re-inflate the status view group.
- int index = indexOfChild(mKeyguardStatusView);
- removeView(mKeyguardStatusView);
- mKeyguardStatusView = (KeyguardStatusView) mInjectionInflationController
- .injectable(LayoutInflater.from(mContext)).inflate(
- R.layout.keyguard_status_view,
- this,
- false);
- addView(mKeyguardStatusView, index);
-
- // Re-associate the clock container with the keyguard clock switch.
- mBigClockContainer.removeAllViews();
- KeyguardClockSwitch keyguardClockSwitch = findViewById(R.id.keyguard_clock_container);
- keyguardClockSwitch.setBigClockContainer(mBigClockContainer);
-
- // Update keyguard bottom area
- index = indexOfChild(mKeyguardBottomArea);
- removeView(mKeyguardBottomArea);
- KeyguardBottomAreaView oldBottomArea = mKeyguardBottomArea;
- mKeyguardBottomArea = (KeyguardBottomAreaView) mInjectionInflationController
- .injectable(LayoutInflater.from(mContext)).inflate(
- R.layout.keyguard_bottom_area,
- this,
- false);
- mKeyguardBottomArea.initFrom(oldBottomArea);
- addView(mKeyguardBottomArea, index);
- initBottomArea();
- mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea);
- onDozeAmountChanged(mStatusBarStateController.getDozeAmount(),
- mStatusBarStateController.getInterpolatedDozeAmount());
-
- if (mKeyguardStatusBar != null) {
- mKeyguardStatusBar.onThemeChanged();
- }
-
- setKeyguardStatusViewVisibility(mBarState, false, false);
- setKeyguardBottomAreaVisibility(mBarState, false);
- if (mOnReinflationListener != null) {
- mOnReinflationListener.run();
- }
- reinflatePluginContainer();
- }
-
- @Override
- public void onUiModeChanged() {
- reinflatePluginContainer();
- }
-
- private void reinflatePluginContainer() {
- int index = indexOfChild(mPluginFrame);
- removeView(mPluginFrame);
- mPluginFrame = (FrameLayout) mInjectionInflationController
- .injectable(LayoutInflater.from(mContext)).inflate(
- R.layout.status_bar_expanded_plugin_frame,
- this,
- false);
- addView(mPluginFrame, index);
-
- Resources res = getResources();
- int qsWidth = res.getDimensionPixelSize(R.dimen.qs_panel_width);
- int panelGravity = getResources().getInteger(R.integer.notification_panel_layout_gravity);
- FrameLayout.LayoutParams lp;
- int sideMargin = res.getDimensionPixelOffset(R.dimen.notification_side_paddings);
- int topMargin =
- res.getDimensionPixelOffset(com.android.internal.R.dimen.quick_qs_total_height);
- if (Utils.useQsMediaPlayer(mContext)) {
- topMargin = res.getDimensionPixelOffset(
- com.android.internal.R.dimen.quick_qs_total_height_with_media);
- }
- lp = (FrameLayout.LayoutParams) mPluginFrame.getLayoutParams();
- if (lp.width != qsWidth || lp.gravity != panelGravity || lp.leftMargin != sideMargin
- || lp.rightMargin != sideMargin || lp.topMargin != topMargin) {
- lp.width = qsWidth;
- lp.gravity = panelGravity;
- lp.leftMargin = sideMargin;
- lp.rightMargin = sideMargin;
- lp.topMargin = topMargin;
- mPluginFrame.setLayoutParams(lp);
- }
-
- if (mNPVPluginManager != null) mNPVPluginManager.replaceFrameLayout(mPluginFrame);
- }
-
- private void initBottomArea() {
- mAffordanceHelper = new KeyguardAffordanceHelper(this, getContext(), mFalsingManager);
- mKeyguardBottomArea.setAffordanceHelper(mAffordanceHelper);
- mKeyguardBottomArea.setStatusBar(mStatusBar);
- mKeyguardBottomArea.setUserSetupComplete(mUserSetupComplete);
- }
-
- public void setKeyguardIndicationController(KeyguardIndicationController indicationController) {
- mKeyguardIndicationController = indicationController;
- mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea);
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- DejankUtils.startDetectingBlockingIpcs("NVP#onLayout");
- super.onLayout(changed, left, top, right, bottom);
- setIsFullWidth(mNotificationStackScroller.getWidth() == getWidth());
-
- // Update Clock Pivot
- mKeyguardStatusView.setPivotX(getWidth() / 2);
- mKeyguardStatusView.setPivotY((FONT_HEIGHT - CAP_HEIGHT) / 2048f *
- mKeyguardStatusView.getClockTextSize());
-
- // Calculate quick setting heights.
- int oldMaxHeight = mQsMaxExpansionHeight;
- if (mQs != null) {
- mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQs.getQsMinExpansionHeight();
- if (mNPVPluginManager != null) {
- mNPVPluginManager.setYOffset(mQsMinExpansionHeight);
- mQsMinExpansionHeight += mNPVPluginManager.getHeight();
- }
- mQsMaxExpansionHeight = mQs.getDesiredHeight();
- mNotificationStackScroller.setMaxTopPadding(
- mQsMaxExpansionHeight + mQsNotificationTopPadding);
- }
- positionClockAndNotifications();
- if (mQsExpanded && mQsFullyExpanded) {
- mQsExpansionHeight = mQsMaxExpansionHeight;
- requestScrollerTopPaddingUpdate(false /* animate */);
- requestPanelHeightUpdate();
-
- // Size has changed, start an animation.
- if (mQsMaxExpansionHeight != oldMaxHeight) {
- startQsSizeChangeAnimation(oldMaxHeight, mQsMaxExpansionHeight);
- }
- } else if (!mQsExpanded) {
- setQsExpansion(mQsMinExpansionHeight + mLastOverscroll);
- }
- updateExpandedHeight(getExpandedHeight());
- updateHeader();
-
- // If we are running a size change animation, the animation takes care of the height of
- // the container. However, if we are not animating, we always need to make the QS container
- // the desired height so when closing the QS detail, it stays smaller after the size change
- // animation is finished but the detail view is still being animated away (this animation
- // takes longer than the size change animation).
- if (mQsSizeChangeAnimator == null && mQs != null) {
- mQs.setHeightOverride(mQs.getDesiredHeight());
- }
- updateMaxHeadsUpTranslation();
- updateGestureExclusionRect();
- if (mExpandAfterLayoutRunnable != null) {
- mExpandAfterLayoutRunnable.run();
- mExpandAfterLayoutRunnable = null;
- }
- DejankUtils.stopDetectingBlockingIpcs("NVP#onLayout");
- }
-
- private void updateGestureExclusionRect() {
- Rect exclusionRect = calculateGestureExclusionRect();
- setSystemGestureExclusionRects(exclusionRect.isEmpty()
- ? Collections.EMPTY_LIST
- : Collections.singletonList(exclusionRect));
- }
-
- private Rect calculateGestureExclusionRect() {
- Rect exclusionRect = null;
- Region touchableRegion = mHeadsUpManager.calculateTouchableRegion();
- if (isFullyCollapsed() && touchableRegion != null) {
- // Note: The heads up manager also calculates the non-pinned touchable region
- exclusionRect = touchableRegion.getBounds();
- }
- return exclusionRect != null
- ? exclusionRect
- : mEmptyRect;
- }
-
- private void setIsFullWidth(boolean isFullWidth) {
- mIsFullWidth = isFullWidth;
- mNotificationStackScroller.setIsFullWidth(isFullWidth);
- }
-
- private void startQsSizeChangeAnimation(int oldHeight, final int newHeight) {
- if (mQsSizeChangeAnimator != null) {
- oldHeight = (int) mQsSizeChangeAnimator.getAnimatedValue();
- mQsSizeChangeAnimator.cancel();
- }
- mQsSizeChangeAnimator = ValueAnimator.ofInt(oldHeight, newHeight);
- mQsSizeChangeAnimator.setDuration(300);
- mQsSizeChangeAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
- mQsSizeChangeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- requestScrollerTopPaddingUpdate(false /* animate */);
- requestPanelHeightUpdate();
- int height = (int) mQsSizeChangeAnimator.getAnimatedValue();
- mQs.setHeightOverride(height);
- }
- });
- mQsSizeChangeAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mQsSizeChangeAnimator = null;
- }
- });
- mQsSizeChangeAnimator.start();
- }
-
- /**
- * Positions the clock and notifications dynamically depending on how many notifications are
- * showing.
- */
- private void positionClockAndNotifications() {
- boolean animate = mNotificationStackScroller.isAddOrRemoveAnimationPending();
- boolean animateClock = animate || mAnimateNextPositionUpdate;
- int stackScrollerPadding;
- if (mBarState != StatusBarState.KEYGUARD) {
- stackScrollerPadding = getUnlockedStackScrollerPadding();
- } else {
- int totalHeight = getHeight();
- int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
- int clockPreferredY = mKeyguardStatusView.getClockPreferredY(totalHeight);
- boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
- final boolean hasVisibleNotifications =
- !bypassEnabled && mNotificationStackScroller.getVisibleNotificationCount() != 0;
- mKeyguardStatusView.setHasVisibleNotifications(hasVisibleNotifications);
- mClockPositionAlgorithm.setup(
- mStatusBarMinHeight,
- totalHeight - bottomPadding,
- mNotificationStackScroller.getIntrinsicContentHeight(),
- getExpandedFraction(),
- totalHeight,
- (int) (mKeyguardStatusView.getHeight()
- - mShelfHeight / 2.0f - mDarkIconSize / 2.0f),
- clockPreferredY,
- hasCustomClock(),
- hasVisibleNotifications,
- mInterpolatedDarkAmount,
- mEmptyDragAmount,
- bypassEnabled,
- getUnlockedStackScrollerPadding());
- mClockPositionAlgorithm.run(mClockPositionResult);
- PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.X,
- mClockPositionResult.clockX, CLOCK_ANIMATION_PROPERTIES, animateClock);
- PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.Y,
- mClockPositionResult.clockY, CLOCK_ANIMATION_PROPERTIES, animateClock);
- updateNotificationTranslucency();
- updateClock();
- stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded;
- }
- mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding);
- mKeyguardBottomArea.setAntiBurnInOffsetX(mClockPositionResult.clockX);
-
- mStackScrollerMeasuringPass++;
- requestScrollerTopPaddingUpdate(animate);
- mStackScrollerMeasuringPass = 0;
- mAnimateNextPositionUpdate = false;
- }
-
- /**
- * @return the padding of the stackscroller when unlocked
- */
- private int getUnlockedStackScrollerPadding() {
- return (mQs != null ? mQs.getHeader().getHeight() : 0) + mQsPeekHeight
- + mQsNotificationTopPadding;
- }
-
- /**
- * @param maximum the maximum to return at most
- * @return the maximum keyguard notifications that can fit on the screen
- */
- public int computeMaxKeyguardNotifications(int maximum) {
- float minPadding = mClockPositionAlgorithm.getMinStackScrollerPadding();
- int notificationPadding = Math.max(1, getResources().getDimensionPixelSize(
- R.dimen.notification_divider_height));
- NotificationShelf shelf = mNotificationStackScroller.getNotificationShelf();
- float shelfSize = shelf.getVisibility() == GONE ? 0
- : shelf.getIntrinsicHeight() + notificationPadding;
- float availableSpace = mNotificationStackScroller.getHeight() - minPadding - shelfSize
- - Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding)
- - mKeyguardStatusView.getLogoutButtonHeight();
- int count = 0;
- for (int i = 0; i < mNotificationStackScroller.getChildCount(); i++) {
- ExpandableView child = (ExpandableView) mNotificationStackScroller.getChildAt(i);
- if (!(child instanceof ExpandableNotificationRow)) {
- continue;
- }
- ExpandableNotificationRow row = (ExpandableNotificationRow) child;
- boolean suppressedSummary = mGroupManager != null
- && mGroupManager.isSummaryOfSuppressedGroup(row.getEntry().getSbn());
- if (suppressedSummary) {
- continue;
- }
- if (!mLockscreenUserManager.shouldShowOnKeyguard(row.getEntry())) {
- continue;
- }
- if (row.isRemoved()) {
- continue;
- }
- availableSpace -= child.getMinHeight(true /* ignoreTemporaryStates */)
- + notificationPadding;
- if (availableSpace >= 0 && count < maximum) {
- count++;
- } else if (availableSpace > -shelfSize) {
- // if we are exactly the last view, then we can show us still!
- for (int j = i + 1; j < mNotificationStackScroller.getChildCount(); j++) {
- if (mNotificationStackScroller.getChildAt(j)
- instanceof ExpandableNotificationRow) {
- return count;
- }
- }
- count++;
- return count;
- } else {
- return count;
- }
- }
- return count;
- }
-
- private void updateClock() {
- if (!mKeyguardStatusViewAnimating) {
- mKeyguardStatusView.setAlpha(mClockPositionResult.clockAlpha);
- }
- }
-
- public void animateToFullShade(long delay) {
- mNotificationStackScroller.goToFullShade(delay);
- requestLayout();
- mAnimateNextPositionUpdate = true;
- }
-
- public void setQsExpansionEnabled(boolean qsExpansionEnabled) {
- mQsExpansionEnabled = qsExpansionEnabled;
- if (mQs == null) return;
- mQs.setHeaderClickable(qsExpansionEnabled);
- }
-
- @Override
- public void resetViews(boolean animate) {
- mIsLaunchTransitionFinished = false;
- mBlockTouches = false;
- if (!mLaunchingAffordance) {
- mAffordanceHelper.reset(false);
- mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
- }
- mStatusBar.getGutsManager().closeAndSaveGuts(true /* leavebehind */, true /* force */,
- true /* controls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
- if (animate) {
- animateCloseQs(true /* animateAway */);
- } else {
- closeQs();
- }
- mNotificationStackScroller.setOverScrollAmount(0f, true /* onTop */, animate,
- !animate /* cancelAnimators */);
- mNotificationStackScroller.resetScrollPosition();
- }
-
- @Override
- public void collapse(boolean delayed, float speedUpFactor) {
- if (!canPanelBeCollapsed()) {
- return;
- }
-
- if (mQsExpanded) {
- mQsExpandImmediate = true;
- mNotificationStackScroller.setShouldShowShelfOnly(true);
- }
- super.collapse(delayed, speedUpFactor);
- }
-
- public void closeQs() {
- cancelQsAnimation();
- setQsExpansion(mQsMinExpansionHeight);
- }
-
- /**
- * Animate QS closing by flinging it.
- * If QS is expanded, it will collapse into QQS and stop.
- *
- * @param animateAway Do not stop when QS becomes QQS. Fling until QS isn't visible anymore.
- */
- public void animateCloseQs(boolean animateAway) {
- if (mQsExpansionAnimator != null) {
- if (!mQsAnimatorExpand) {
- return;
- }
- float height = mQsExpansionHeight;
- mQsExpansionAnimator.cancel();
- setQsExpansion(height);
- }
- flingSettings(0 /* vel */, animateAway ? FLING_HIDE : FLING_COLLAPSE);
- }
-
- public void expandWithQs() {
- if (mQsExpansionEnabled) {
- mQsExpandImmediate = true;
- mNotificationStackScroller.setShouldShowShelfOnly(true);
- }
- if (isFullyCollapsed()) {
- expand(true /* animate */);
- } else {
- flingSettings(0 /* velocity */, FLING_EXPAND);
- }
- }
-
- public void expandWithoutQs() {
- if (isQsExpanded()) {
- flingSettings(0 /* velocity */, FLING_COLLAPSE);
- } else {
- expand(true /* animate */);
- }
- }
-
- @Override
- public void fling(float vel, boolean expand) {
- GestureRecorder gr = ((PhoneStatusBarView) mBar).mBar.getGestureRecorder();
- if (gr != null) {
- gr.tag("fling " + ((vel > 0) ? "open" : "closed"), "notifications,v=" + vel);
- }
- super.fling(vel, expand);
- }
-
- @Override
- protected void flingToHeight(float vel, boolean expand, float target,
- float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) {
- mHeadsUpTouchHelper.notifyFling(!expand);
- setClosingWithAlphaFadeout(!expand && !isOnKeyguard() && getFadeoutAlpha() == 1.0f);
- super.flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing);
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent event) {
- if (mBlockTouches || mQsFullyExpanded && mQs.onInterceptTouchEvent(event)) {
- return false;
- }
- initDownStates(event);
- // Do not let touches go to shade or QS if the bouncer is visible,
- // but still let user swipe down to expand the panel, dismissing the bouncer.
- if (mStatusBar.isBouncerShowing()) {
- return true;
- }
- if (mBar.panelEnabled() && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
- mIsExpansionFromHeadsUp = true;
- MetricsLogger.count(mContext, COUNTER_PANEL_OPEN, 1);
- MetricsLogger.count(mContext, COUNTER_PANEL_OPEN_PEEK, 1);
- return true;
- }
- if (!shouldQuickSettingsIntercept(mDownX, mDownY, 0)
- && mPulseExpansionHandler.onInterceptTouchEvent(event)) {
- return true;
- }
-
- if (!isFullyCollapsed() && onQsIntercept(event)) {
- return true;
- }
- return super.onInterceptTouchEvent(event);
- }
-
- private boolean onQsIntercept(MotionEvent event) {
- int pointerIndex = event.findPointerIndex(mTrackingPointer);
- if (pointerIndex < 0) {
- pointerIndex = 0;
- mTrackingPointer = event.getPointerId(pointerIndex);
- }
- final float x = event.getX(pointerIndex);
- final float y = event.getY(pointerIndex);
-
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- mIntercepting = true;
- mInitialTouchY = y;
- mInitialTouchX = x;
- initVelocityTracker();
- trackMovement(event);
- if (shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) {
- getParent().requestDisallowInterceptTouchEvent(true);
- }
- if (mQsExpansionAnimator != null) {
- onQsExpansionStarted();
- mInitialHeightOnTouch = mQsExpansionHeight;
- mQsTracking = true;
- mIntercepting = false;
- mNotificationStackScroller.cancelLongPress();
- }
- break;
- case MotionEvent.ACTION_POINTER_UP:
- final int upPointer = event.getPointerId(event.getActionIndex());
- if (mTrackingPointer == upPointer) {
- // gesture is ongoing, find a new pointer to track
- final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
- mTrackingPointer = event.getPointerId(newIndex);
- mInitialTouchX = event.getX(newIndex);
- mInitialTouchY = event.getY(newIndex);
- }
- break;
-
- case MotionEvent.ACTION_MOVE:
- final float h = y - mInitialTouchY;
- trackMovement(event);
- if (mQsTracking) {
-
- // Already tracking because onOverscrolled was called. We need to update here
- // so we don't stop for a frame until the next touch event gets handled in
- // onTouchEvent.
- setQsExpansion(h + mInitialHeightOnTouch);
- trackMovement(event);
- mIntercepting = false;
- return true;
- }
- if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX)
- && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) {
- mQsTracking = true;
- onQsExpansionStarted();
- notifyExpandingFinished();
- mInitialHeightOnTouch = mQsExpansionHeight;
- mInitialTouchY = y;
- mInitialTouchX = x;
- mIntercepting = false;
- mNotificationStackScroller.cancelLongPress();
- return true;
- }
- break;
-
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP:
- trackMovement(event);
- if (mQsTracking) {
- flingQsWithCurrentVelocity(y,
- event.getActionMasked() == MotionEvent.ACTION_CANCEL);
- mQsTracking = false;
- }
- mIntercepting = false;
- break;
- }
- return false;
- }
-
- @Override
- protected boolean isInContentBounds(float x, float y) {
- float stackScrollerX = mNotificationStackScroller.getX();
- return !mNotificationStackScroller.isBelowLastNotification(x - stackScrollerX, y)
- && stackScrollerX < x && x < stackScrollerX + mNotificationStackScroller.getWidth();
- }
-
- private void initDownStates(MotionEvent event) {
- if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
- mOnlyAffordanceInThisMotion = false;
- mQsTouchAboveFalsingThreshold = mQsFullyExpanded;
- mDozingOnDown = isDozing();
- mDownX = event.getX();
- mDownY = event.getY();
- mCollapsedOnDown = isFullyCollapsed();
- mListenForHeadsUp = mCollapsedOnDown && mHeadsUpManager.hasPinnedHeadsUp();
- mAllowExpandForSmallExpansion = mExpectingSynthesizedDown;
- mTouchSlopExceededBeforeDown = mExpectingSynthesizedDown;
- if (mExpectingSynthesizedDown) {
- mLastEventSynthesizedDown = true;
- } else {
- // down but not synthesized motion event.
- mLastEventSynthesizedDown = false;
- }
- } else {
- // not down event at all.
- mLastEventSynthesizedDown = false;
- }
- }
-
- private void flingQsWithCurrentVelocity(float y, boolean isCancelMotionEvent) {
- float vel = getCurrentQSVelocity();
- final boolean expandsQs = flingExpandsQs(vel);
- if (expandsQs) {
- logQsSwipeDown(y);
- }
- flingSettings(vel, expandsQs && !isCancelMotionEvent ? FLING_EXPAND : FLING_COLLAPSE);
- }
-
- private void logQsSwipeDown(float y) {
- float vel = getCurrentQSVelocity();
- final int gesture = mBarState == StatusBarState.KEYGUARD
- ? MetricsEvent.ACTION_LS_QS
- : MetricsEvent.ACTION_SHADE_QS_PULL;
- mLockscreenGestureLogger.write(gesture,
- (int) ((y - mInitialTouchY) / mStatusBar.getDisplayDensity()),
- (int) (vel / mStatusBar.getDisplayDensity()));
- }
-
- private boolean flingExpandsQs(float vel) {
- if (mFalsingManager.isUnlockingDisabled() || isFalseTouch()) {
- return false;
- }
- if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
- return getQsExpansionFraction() > 0.5f;
- } else {
- return vel > 0;
- }
- }
-
- private boolean isFalseTouch() {
- if (!needsAntiFalsing()) {
- return false;
- }
- if (mFalsingManager.isClassifierEnabled()) {
- return mFalsingManager.isFalseTouch();
- }
- return !mQsTouchAboveFalsingThreshold;
- }
-
- private float getQsExpansionFraction() {
- return Math.min(1f, (mQsExpansionHeight - mQsMinExpansionHeight)
- / (mQsMaxExpansionHeight - mQsMinExpansionHeight));
- }
-
- @Override
- protected boolean shouldExpandWhenNotFlinging() {
- if (super.shouldExpandWhenNotFlinging()) {
- return true;
- }
- if (mAllowExpandForSmallExpansion) {
- // When we get a touch that came over from launcher, the velocity isn't always correct
- // Let's err on expanding if the gesture has been reasonably slow
- long timeSinceDown = SystemClock.uptimeMillis() - mDownTime;
- return timeSinceDown <= MAX_TIME_TO_OPEN_WHEN_FLINGING_FROM_LAUNCHER;
- }
- return false;
- }
-
- @Override
- protected float getOpeningHeight() {
- return mNotificationStackScroller.getOpeningHeight();
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (mBlockTouches || (mQs != null && mQs.isCustomizing())) {
- return false;
- }
-
- // Do not allow panel expansion if bouncer is scrimmed, otherwise user would be able to
- // pull down QS or expand the shade.
- if (mStatusBar.isBouncerShowingScrimmed()) {
- return false;
- }
-
- // Make sure the next touch won't the blocked after the current ends.
- if (event.getAction() == MotionEvent.ACTION_UP
- || event.getAction() == MotionEvent.ACTION_CANCEL) {
- mBlockingExpansionForCurrentTouch = false;
- }
- // When touch focus transfer happens, ACTION_DOWN->ACTION_UP may happen immediately
- // without any ACTION_MOVE event.
- // In such case, simply expand the panel instead of being stuck at the bottom bar.
- if (mLastEventSynthesizedDown && event.getAction() == MotionEvent.ACTION_UP) {
- expand(true /* animate */);
- }
- initDownStates(event);
- if (!mIsExpanding && !shouldQuickSettingsIntercept(mDownX, mDownY, 0)
- && mPulseExpansionHandler.onTouchEvent(event)) {
- // We're expanding all the other ones shouldn't get this anymore
- return true;
- }
- if (mListenForHeadsUp && !mHeadsUpTouchHelper.isTrackingHeadsUp()
- && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
- mIsExpansionFromHeadsUp = true;
- MetricsLogger.count(mContext, COUNTER_PANEL_OPEN_PEEK, 1);
- }
- boolean handled = false;
- if ((!mIsExpanding || mHintAnimationRunning)
- && !mQsExpanded
- && mBarState != StatusBarState.SHADE
- && !mDozing) {
- handled |= mAffordanceHelper.onTouchEvent(event);
- }
- if (mOnlyAffordanceInThisMotion) {
- return true;
- }
- handled |= mHeadsUpTouchHelper.onTouchEvent(event);
-
- if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && handleQsTouch(event)) {
- return true;
- }
- if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyCollapsed()) {
- MetricsLogger.count(mContext, COUNTER_PANEL_OPEN, 1);
- updateVerticalPanelPosition(event.getX());
- handled = true;
- }
- handled |= super.onTouchEvent(event);
- return !mDozing || mPulsing || handled;
- }
-
- private boolean handleQsTouch(MotionEvent event) {
- final int action = event.getActionMasked();
- if (action == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f
- && mBarState != StatusBarState.KEYGUARD && !mQsExpanded
- && mQsExpansionEnabled) {
-
- // Down in the empty area while fully expanded - go to QS.
- mQsTracking = true;
- mConflictingQsExpansionGesture = true;
- onQsExpansionStarted();
- mInitialHeightOnTouch = mQsExpansionHeight;
- mInitialTouchY = event.getX();
- mInitialTouchX = event.getY();
- }
- if (!isFullyCollapsed()) {
- handleQsDown(event);
- }
- if (!mQsExpandImmediate && mQsTracking) {
- onQsTouch(event);
- if (!mConflictingQsExpansionGesture) {
- return true;
- }
- }
- if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
- mConflictingQsExpansionGesture = false;
- }
- if (action == MotionEvent.ACTION_DOWN && isFullyCollapsed()
- && mQsExpansionEnabled) {
- mTwoFingerQsExpandPossible = true;
- }
- if (mTwoFingerQsExpandPossible && isOpenQsEvent(event)
- && event.getY(event.getActionIndex()) < mStatusBarMinHeight) {
- MetricsLogger.count(mContext, COUNTER_PANEL_OPEN_QS, 1);
- mQsExpandImmediate = true;
- mNotificationStackScroller.setShouldShowShelfOnly(true);
- requestPanelHeightUpdate();
-
- // Normally, we start listening when the panel is expanded, but here we need to start
- // earlier so the state is already up to date when dragging down.
- setListening(true);
- }
- if (isQsSplitEnabled() && !mKeyguardShowing) {
- if (mQsExpandImmediate) {
- mNotificationStackScroller.setVisibility(View.GONE);
- mQsFrame.setVisibility(View.VISIBLE);
- mHomeControlsLayout.setVisibility(View.VISIBLE);
- } else {
- mNotificationStackScroller.setVisibility(View.VISIBLE);
- mQsFrame.setVisibility(View.GONE);
- mHomeControlsLayout.setVisibility(View.GONE);
- }
- }
- return false;
- }
-
- private boolean isInQsArea(float x, float y) {
- return (x >= mQsFrame.getX()
- && x <= mQsFrame.getX() + mQsFrame.getWidth())
- && (y <= mNotificationStackScroller.getBottomMostNotificationBottom()
- || y <= mQs.getView().getY() + mQs.getView().getHeight());
- }
-
- private boolean isOnQsEndArea(float x) {
- if (!isQsSplitEnabled()) return false;
- if (getLayoutDirection() == LAYOUT_DIRECTION_LTR) {
- return x >= mQsFrame.getX() + mQqsSplitFraction * mQsFrame.getWidth()
- && x <= mQsFrame.getX() + mQsFrame.getWidth();
- } else {
- return x >= mQsFrame.getX()
- && x <= mQsFrame.getX() + (1 - mQqsSplitFraction) * mQsFrame.getWidth();
- }
- }
-
- private boolean isOpenQsEvent(MotionEvent event) {
- final int pointerCount = event.getPointerCount();
- final int action = event.getActionMasked();
-
- final boolean twoFingerDrag = action == MotionEvent.ACTION_POINTER_DOWN
- && pointerCount == 2;
-
- final boolean stylusButtonClickDrag = action == MotionEvent.ACTION_DOWN
- && (event.isButtonPressed(MotionEvent.BUTTON_STYLUS_PRIMARY)
- || event.isButtonPressed(MotionEvent.BUTTON_STYLUS_SECONDARY));
-
- final boolean mouseButtonClickDrag = action == MotionEvent.ACTION_DOWN
- && (event.isButtonPressed(MotionEvent.BUTTON_SECONDARY)
- || event.isButtonPressed(MotionEvent.BUTTON_TERTIARY));
-
- final boolean onHeaderRight = isOnQsEndArea(event.getX());
-
- return twoFingerDrag || stylusButtonClickDrag || mouseButtonClickDrag || onHeaderRight;
- }
-
- private void handleQsDown(MotionEvent event) {
- if (event.getActionMasked() == MotionEvent.ACTION_DOWN
- && shouldQuickSettingsIntercept(event.getX(), event.getY(), -1)) {
- mFalsingManager.onQsDown();
- mQsTracking = true;
- onQsExpansionStarted();
- mInitialHeightOnTouch = mQsExpansionHeight;
- mInitialTouchY = event.getX();
- mInitialTouchX = event.getY();
-
- // If we interrupt an expansion gesture here, make sure to update the state correctly.
- notifyExpandingFinished();
- }
- }
-
- /**
- * Input focus transfer is about to happen.
- */
- public void startWaitingForOpenPanelGesture() {
- if (!isFullyCollapsed()) {
- return;
- }
- mExpectingSynthesizedDown = true;
- onTrackingStarted();
- updatePanelExpanded();
- }
-
- /**
- * Called when this view is no longer waiting for input focus transfer.
- *
- * There are two scenarios behind this function call. First, input focus transfer
- * has successfully happened and this view already received synthetic DOWN event.
- * (mExpectingSynthesizedDown == false). Do nothing.
- *
- * Second, before input focus transfer finished, user may have lifted finger
- * in previous window and this window never received synthetic DOWN event.
- * (mExpectingSynthesizedDown == true).
- * In this case, we use the velocity to trigger fling event.
- *
- * @param velocity unit is in px / millis
- */
- public void stopWaitingForOpenPanelGesture(final float velocity) {
- if (mExpectingSynthesizedDown) {
- mExpectingSynthesizedDown = false;
- maybeVibrateOnOpening();
- Runnable runnable = () -> fling(velocity > 1f ? 1000f * velocity : 0,
- true /* expand */);
- if (mStatusBar.getStatusBarWindow().getHeight()
- != mStatusBar.getStatusBarHeight()) {
- // The panel is already expanded to its full size, let's expand directly
- runnable.run();
- } else {
- mExpandAfterLayoutRunnable = runnable;
- }
- onTrackingStopped(false);
- }
- }
-
- @Override
- protected boolean flingExpands(float vel, float vectorVel, float x, float y) {
- boolean expands = super.flingExpands(vel, vectorVel, x, y);
-
- // If we are already running a QS expansion, make sure that we keep the panel open.
- if (mQsExpansionAnimator != null) {
- expands = true;
- }
- return expands;
- }
-
- @Override
- protected boolean shouldGestureWaitForTouchSlop() {
- if (mExpectingSynthesizedDown) {
- mExpectingSynthesizedDown = false;
- return false;
- }
- return isFullyCollapsed() || mBarState != StatusBarState.SHADE;
- }
-
- @Override
- protected boolean shouldGestureIgnoreXTouchSlop(float x, float y) {
- return !mAffordanceHelper.isOnAffordanceIcon(x, y);
- }
-
- private void onQsTouch(MotionEvent event) {
- int pointerIndex = event.findPointerIndex(mTrackingPointer);
- if (pointerIndex < 0) {
- pointerIndex = 0;
- mTrackingPointer = event.getPointerId(pointerIndex);
- }
- final float y = event.getY(pointerIndex);
- final float x = event.getX(pointerIndex);
- final float h = y - mInitialTouchY;
-
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- mQsTracking = true;
- mInitialTouchY = y;
- mInitialTouchX = x;
- onQsExpansionStarted();
- mInitialHeightOnTouch = mQsExpansionHeight;
- initVelocityTracker();
- trackMovement(event);
- break;
-
- case MotionEvent.ACTION_POINTER_UP:
- final int upPointer = event.getPointerId(event.getActionIndex());
- if (mTrackingPointer == upPointer) {
- // gesture is ongoing, find a new pointer to track
- final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
- final float newY = event.getY(newIndex);
- final float newX = event.getX(newIndex);
- mTrackingPointer = event.getPointerId(newIndex);
- mInitialHeightOnTouch = mQsExpansionHeight;
- mInitialTouchY = newY;
- mInitialTouchX = newX;
- }
- break;
-
- case MotionEvent.ACTION_MOVE:
- setQsExpansion(h + mInitialHeightOnTouch);
- if (h >= getFalsingThreshold()) {
- mQsTouchAboveFalsingThreshold = true;
- }
- trackMovement(event);
- break;
-
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- mQsTracking = false;
- mTrackingPointer = -1;
- trackMovement(event);
- float fraction = getQsExpansionFraction();
- if (fraction != 0f || y >= mInitialTouchY) {
- flingQsWithCurrentVelocity(y,
- event.getActionMasked() == MotionEvent.ACTION_CANCEL);
- }
- if (mQsVelocityTracker != null) {
- mQsVelocityTracker.recycle();
- mQsVelocityTracker = null;
- }
- break;
- }
- }
-
- private int getFalsingThreshold() {
- float factor = mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
- return (int) (mQsFalsingThreshold * factor);
- }
-
- @Override
- public void onOverscrollTopChanged(float amount, boolean isRubberbanded) {
- cancelQsAnimation();
- if (!mQsExpansionEnabled) {
- amount = 0f;
- }
- float rounded = amount >= 1f ? amount : 0f;
- setOverScrolling(rounded != 0f && isRubberbanded);
- mQsExpansionFromOverscroll = rounded != 0f;
- mLastOverscroll = rounded;
- updateQsState();
- setQsExpansion(mQsMinExpansionHeight + rounded);
- }
-
- @Override
- public void flingTopOverscroll(float velocity, boolean open) {
- mLastOverscroll = 0f;
- mQsExpansionFromOverscroll = false;
- setQsExpansion(mQsExpansionHeight);
- flingSettings(!mQsExpansionEnabled && open ? 0f : velocity,
- open && mQsExpansionEnabled ? FLING_EXPAND : FLING_COLLAPSE,
- new Runnable() {
- @Override
- public void run() {
- mStackScrollerOverscrolling = false;
- setOverScrolling(false);
- updateQsState();
- }
- }, false /* isClick */);
- }
-
- private void setOverScrolling(boolean overscrolling) {
- mStackScrollerOverscrolling = overscrolling;
- if (mQs == null) return;
- mQs.setOverscrolling(overscrolling);
- }
-
- private void onQsExpansionStarted() {
- onQsExpansionStarted(0);
- }
-
- protected void onQsExpansionStarted(int overscrollAmount) {
- cancelQsAnimation();
- cancelHeightAnimator();
-
- // Reset scroll position and apply that position to the expanded height.
- float height = mQsExpansionHeight - overscrollAmount;
- setQsExpansion(height);
- requestPanelHeightUpdate();
- mNotificationStackScroller.checkSnoozeLeavebehind();
-
- // When expanding QS, let's authenticate the user if possible,
- // this will speed up notification actions.
- if (height == 0) {
- mStatusBar.requestFaceAuth();
- }
- }
-
- private void setQsExpanded(boolean expanded) {
- boolean changed = mQsExpanded != expanded;
- if (changed) {
- mQsExpanded = expanded;
- updateQsState();
- requestPanelHeightUpdate();
- mFalsingManager.setQsExpanded(expanded);
- mStatusBar.setQsExpanded(expanded);
- mNotificationContainerParent.setQsExpanded(expanded);
- mPulseExpansionHandler.setQsExpanded(expanded);
- mKeyguardBypassController.setQSExpanded(expanded);
- }
- }
-
- @Override
- public void onStateChanged(int statusBarState) {
- boolean goingToFullShade = mStatusBarStateController.goingToFullShade();
- boolean keyguardFadingAway = mKeyguardStateController.isKeyguardFadingAway();
- int oldState = mBarState;
- boolean keyguardShowing = statusBarState == StatusBarState.KEYGUARD;
- setKeyguardStatusViewVisibility(statusBarState, keyguardFadingAway, goingToFullShade);
- setKeyguardBottomAreaVisibility(statusBarState, goingToFullShade);
-
- mBarState = statusBarState;
- mKeyguardShowing = keyguardShowing;
- if (mKeyguardShowing && isQsSplitEnabled()) {
- mNotificationStackScroller.setVisibility(View.VISIBLE);
- mQsFrame.setVisibility(View.VISIBLE);
- mHomeControlsLayout.setVisibility(View.GONE);
- }
-
- if (oldState == StatusBarState.KEYGUARD
- && (goingToFullShade || statusBarState == StatusBarState.SHADE_LOCKED)) {
- animateKeyguardStatusBarOut();
- long delay = mBarState == StatusBarState.SHADE_LOCKED
- ? 0 : mKeyguardStateController.calculateGoingToFullShadeDelay();
- mQs.animateHeaderSlidingIn(delay);
- } else if (oldState == StatusBarState.SHADE_LOCKED
- && statusBarState == StatusBarState.KEYGUARD) {
- animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
- mNotificationStackScroller.resetScrollPosition();
- // Only animate header if the header is visible. If not, it will partially animate out
- // the top of QS
- if (!mQsExpanded) {
- mQs.animateHeaderSlidingOut();
- }
- } else {
- mKeyguardStatusBar.setAlpha(1f);
- mKeyguardStatusBar.setVisibility(keyguardShowing ? View.VISIBLE : View.INVISIBLE);
- ((PhoneStatusBarView) mBar).maybeShowDivider(keyguardShowing);
- if (keyguardShowing && oldState != mBarState) {
- if (mQs != null) {
- mQs.hideImmediately();
- }
- }
- }
- updateKeyguardStatusBarForHeadsUp();
- if (keyguardShowing) {
- updateDozingVisibilities(false /* animate */);
- }
- // THe update needs to happen after the headerSlide in above, otherwise the translation
- // would reset
- updateQSPulseExpansion();
- maybeAnimateBottomAreaAlpha();
- resetHorizontalPanelPosition();
- updateQsState();
- }
-
- private void maybeAnimateBottomAreaAlpha() {
- mBottomAreaShadeAlphaAnimator.cancel();
- if (mBarState == StatusBarState.SHADE_LOCKED) {
- mBottomAreaShadeAlphaAnimator.start();
- } else {
- mBottomAreaShadeAlpha = 1f;
- }
- }
-
- private final Runnable mAnimateKeyguardStatusViewInvisibleEndRunnable = new Runnable() {
- @Override
- public void run() {
- mKeyguardStatusViewAnimating = false;
- mKeyguardStatusView.setVisibility(View.INVISIBLE);
- }
- };
-
- private final Runnable mAnimateKeyguardStatusViewGoneEndRunnable = new Runnable() {
- @Override
- public void run() {
- mKeyguardStatusViewAnimating = false;
- mKeyguardStatusView.setVisibility(View.GONE);
- }
- };
-
- private final Runnable mAnimateKeyguardStatusViewVisibleEndRunnable = new Runnable() {
- @Override
- public void run() {
- mKeyguardStatusViewAnimating = false;
- }
- };
-
- private final Runnable mAnimateKeyguardStatusBarInvisibleEndRunnable = new Runnable() {
- @Override
- public void run() {
- mKeyguardStatusBar.setVisibility(View.INVISIBLE);
- mKeyguardStatusBar.setAlpha(1f);
- mKeyguardStatusBarAnimateAlpha = 1f;
- }
- };
-
- private void animateKeyguardStatusBarOut() {
- ValueAnimator anim = ValueAnimator.ofFloat(mKeyguardStatusBar.getAlpha(), 0f);
- anim.addUpdateListener(mStatusBarAnimateAlphaListener);
- anim.setStartDelay(mKeyguardStateController.isKeyguardFadingAway()
- ? mKeyguardStateController.getKeyguardFadingAwayDelay()
- : 0);
-
- long duration;
- if (mKeyguardStateController.isKeyguardFadingAway()) {
- duration = mKeyguardStateController.getShortenedFadingAwayDuration();
- } else {
- duration = StackStateAnimator.ANIMATION_DURATION_STANDARD;
- }
- anim.setDuration(duration);
-
- anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mAnimateKeyguardStatusBarInvisibleEndRunnable.run();
- }
- });
- anim.start();
- }
-
- private final ValueAnimator.AnimatorUpdateListener mStatusBarAnimateAlphaListener =
- new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mKeyguardStatusBarAnimateAlpha = (float) animation.getAnimatedValue();
- updateHeaderKeyguardAlpha();
- }
- };
-
- private void animateKeyguardStatusBarIn(long duration) {
- mKeyguardStatusBar.setVisibility(View.VISIBLE);
- mKeyguardStatusBar.setAlpha(0f);
- ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
- anim.addUpdateListener(mStatusBarAnimateAlphaListener);
- anim.setDuration(duration);
- anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- anim.start();
- }
-
- private final Runnable mAnimateKeyguardBottomAreaInvisibleEndRunnable = new Runnable() {
- @Override
- public void run() {
- mKeyguardBottomArea.setVisibility(View.GONE);
- }
- };
-
- private void setKeyguardBottomAreaVisibility(int statusBarState, boolean goingToFullShade) {
- mKeyguardBottomArea.animate().cancel();
- if (goingToFullShade) {
- mKeyguardBottomArea.animate()
- .alpha(0f)
- .setStartDelay(mKeyguardStateController.getKeyguardFadingAwayDelay())
- .setDuration(mKeyguardStateController.getShortenedFadingAwayDuration())
- .setInterpolator(Interpolators.ALPHA_OUT)
- .withEndAction(mAnimateKeyguardBottomAreaInvisibleEndRunnable)
- .start();
- } else if (statusBarState == StatusBarState.KEYGUARD
- || statusBarState == StatusBarState.SHADE_LOCKED) {
- mKeyguardBottomArea.setVisibility(View.VISIBLE);
- mKeyguardBottomArea.setAlpha(1f);
- } else {
- mKeyguardBottomArea.setVisibility(View.GONE);
- }
- }
-
- private void setKeyguardStatusViewVisibility(int statusBarState, boolean keyguardFadingAway,
- boolean goingToFullShade) {
- mKeyguardStatusView.animate().cancel();
- mKeyguardStatusViewAnimating = false;
- if ((!keyguardFadingAway && mBarState == StatusBarState.KEYGUARD
- && statusBarState != StatusBarState.KEYGUARD) || goingToFullShade) {
- mKeyguardStatusViewAnimating = true;
- mKeyguardStatusView.animate()
- .alpha(0f)
- .setStartDelay(0)
- .setDuration(160)
- .setInterpolator(Interpolators.ALPHA_OUT)
- .withEndAction(mAnimateKeyguardStatusViewGoneEndRunnable);
- if (keyguardFadingAway) {
- mKeyguardStatusView.animate()
- .setStartDelay(mKeyguardStateController.getKeyguardFadingAwayDelay())
- .setDuration(mKeyguardStateController.getShortenedFadingAwayDuration())
- .start();
- }
- } else if (mBarState == StatusBarState.SHADE_LOCKED
- && statusBarState == StatusBarState.KEYGUARD) {
- mKeyguardStatusView.setVisibility(View.VISIBLE);
- mKeyguardStatusViewAnimating = true;
- mKeyguardStatusView.setAlpha(0f);
- mKeyguardStatusView.animate()
- .alpha(1f)
- .setStartDelay(0)
- .setDuration(320)
- .setInterpolator(Interpolators.ALPHA_IN)
- .withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable);
- } else if (statusBarState == StatusBarState.KEYGUARD) {
- if (keyguardFadingAway) {
- mKeyguardStatusViewAnimating = true;
- mKeyguardStatusView.animate()
- .alpha(0)
- .translationYBy(-getHeight() * 0.05f)
- .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN)
- .setDuration(125)
- .setStartDelay(0)
- .withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable)
- .start();
- } else {
- mKeyguardStatusView.setVisibility(View.VISIBLE);
- mKeyguardStatusView.setAlpha(1f);
- }
- } else {
- mKeyguardStatusView.setVisibility(View.GONE);
- mKeyguardStatusView.setAlpha(1f);
- }
- }
-
- private void updateQsState() {
- mNotificationStackScroller.setQsExpanded(mQsExpanded);
- mNotificationStackScroller.setScrollingEnabled(
- mBarState != StatusBarState.KEYGUARD && (!mQsExpanded
- || mQsExpansionFromOverscroll));
- updateEmptyShadeView();
- if (mNPVPluginManager != null) {
- mNPVPluginManager.changeVisibility((mBarState != StatusBarState.KEYGUARD)
- ? View.VISIBLE
- : View.INVISIBLE);
- }
- mQsNavbarScrim.setVisibility(mBarState == StatusBarState.SHADE && mQsExpanded
- && !mStackScrollerOverscrolling && mQsScrimEnabled
- ? View.VISIBLE
- : View.INVISIBLE);
- if (mKeyguardUserSwitcher != null && mQsExpanded && !mStackScrollerOverscrolling) {
- mKeyguardUserSwitcher.hideIfNotSimple(true /* animate */);
- }
- if (mQs == null) return;
- mQs.setExpanded(mQsExpanded);
- }
-
- private void setQsExpansion(float height) {
- height = Math.min(Math.max(height, mQsMinExpansionHeight), mQsMaxExpansionHeight);
- mQsFullyExpanded = height == mQsMaxExpansionHeight && mQsMaxExpansionHeight != 0;
- if (height > mQsMinExpansionHeight && !mQsExpanded && !mStackScrollerOverscrolling
- && !mDozing) {
- setQsExpanded(true);
- } else if (height <= mQsMinExpansionHeight && mQsExpanded) {
- setQsExpanded(false);
- }
- mQsExpansionHeight = height;
- updateQsExpansion();
- requestScrollerTopPaddingUpdate(false /* animate */);
- updateHeaderKeyguardAlpha();
- if (mBarState == StatusBarState.SHADE_LOCKED
- || mBarState == StatusBarState.KEYGUARD) {
- updateKeyguardBottomAreaAlpha();
- updateBigClockAlpha();
- }
- if (mBarState == StatusBarState.SHADE && mQsExpanded
- && !mStackScrollerOverscrolling && mQsScrimEnabled) {
- mQsNavbarScrim.setAlpha(getQsExpansionFraction());
- }
-
- if (mAccessibilityManager.isEnabled()) {
- setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
- }
-
- if (!mFalsingManager.isUnlockingDisabled() && mQsFullyExpanded
- && mFalsingManager.shouldEnforceBouncer()) {
- mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */,
- false /* dismissShade */, true /* afterKeyguardGone */, false /* deferred */);
- }
- for (int i = 0; i < mExpansionListeners.size(); i++) {
- mExpansionListeners.get(i).onQsExpansionChanged(mQsMaxExpansionHeight != 0
- ? mQsExpansionHeight / mQsMaxExpansionHeight : 0);
- }
- if (DEBUG) {
- invalidate();
- }
- }
-
- protected void updateQsExpansion() {
- if (mQs == null) return;
- float qsExpansionFraction = getQsExpansionFraction();
- mQs.setQsExpansion(qsExpansionFraction, getHeaderTranslation());
- int heightDiff = mQs.getDesiredHeight() - mQs.getQsMinExpansionHeight();
- if (mNPVPluginManager != null) {
- mNPVPluginManager.setExpansion(qsExpansionFraction, getHeaderTranslation(), heightDiff);
- }
- mNotificationStackScroller.setQsExpansionFraction(qsExpansionFraction);
- }
-
- private String determineAccessibilityPaneTitle() {
- if (mQs != null && mQs.isCustomizing()) {
- return getContext().getString(R.string.accessibility_desc_quick_settings_edit);
- } else if (mQsExpansionHeight != 0.0f && mQsFullyExpanded) {
- // Upon initialisation when we are not layouted yet we don't want to announce that we
- // are fully expanded, hence the != 0.0f check.
- return getContext().getString(R.string.accessibility_desc_quick_settings);
- } else if (mBarState == StatusBarState.KEYGUARD) {
- return getContext().getString(R.string.accessibility_desc_lock_screen);
- } else {
- return getContext().getString(R.string.accessibility_desc_notification_shade);
- }
- }
-
- private float calculateQsTopPadding() {
- if (mKeyguardShowing
- && (mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted)) {
-
- // Either QS pushes the notifications down when fully expanded, or QS is fully above the
- // notifications (mostly on tablets). maxNotificationPadding denotes the normal top
- // padding on Keyguard, maxQsPadding denotes the top padding from the quick settings
- // panel. We need to take the maximum and linearly interpolate with the panel expansion
- // for a nice motion.
- int maxNotificationPadding = getKeyguardNotificationStaticPadding();
- int maxQsPadding = mQsMaxExpansionHeight + mQsNotificationTopPadding;
- int max = mBarState == StatusBarState.KEYGUARD
- ? Math.max(maxNotificationPadding, maxQsPadding)
- : maxQsPadding;
- return (int) MathUtils.lerp((float) mQsMinExpansionHeight, (float) max,
- getExpandedFraction());
- } else if (mQsSizeChangeAnimator != null) {
- return Math.max((int) mQsSizeChangeAnimator.getAnimatedValue(),
- getKeyguardNotificationStaticPadding());
- } else if (mKeyguardShowing) {
- // We can only do the smoother transition on Keyguard when we also are not collapsing
- // from a scrolled quick settings.
- return MathUtils.lerp((float) getKeyguardNotificationStaticPadding(),
- (float) (mQsMaxExpansionHeight + mQsNotificationTopPadding),
- getQsExpansionFraction());
- } else {
- return mQsExpansionHeight + mQsNotificationTopPadding;
- }
- }
-
- /**
- * @return the topPadding of notifications when on keyguard not respecting quick settings
- * expansion
- */
- private int getKeyguardNotificationStaticPadding() {
- if (!mKeyguardShowing) {
- return 0;
- }
- if (!mKeyguardBypassController.getBypassEnabled()) {
- return mClockPositionResult.stackScrollerPadding;
- }
- int collapsedPosition = mHeadsUpInset;
- if (!mNotificationStackScroller.isPulseExpanding()) {
- return collapsedPosition;
- } else {
- int expandedPosition = mClockPositionResult.stackScrollerPadding;
- return (int) MathUtils.lerp(collapsedPosition, expandedPosition,
- mNotificationStackScroller.calculateAppearFractionBypass());
- }
- }
-
-
- protected void requestScrollerTopPaddingUpdate(boolean animate) {
- mNotificationStackScroller.updateTopPadding(calculateQsTopPadding(), animate);
- if (mKeyguardShowing && mKeyguardBypassController.getBypassEnabled()) {
- // update the position of the header
- updateQsExpansion();
- }
- }
-
-
- private void updateQSPulseExpansion() {
- if (mQs != null) {
- mQs.setShowCollapsedOnKeyguard(mKeyguardShowing
- && mKeyguardBypassController.getBypassEnabled()
- && mNotificationStackScroller.isPulseExpanding());
- }
- }
-
- private void trackMovement(MotionEvent event) {
- if (mQsVelocityTracker != null) mQsVelocityTracker.addMovement(event);
- mLastTouchX = event.getX();
- mLastTouchY = event.getY();
- }
-
- private void initVelocityTracker() {
- if (mQsVelocityTracker != null) {
- mQsVelocityTracker.recycle();
- }
- mQsVelocityTracker = VelocityTracker.obtain();
- }
-
- private float getCurrentQSVelocity() {
- if (mQsVelocityTracker == null) {
- return 0;
- }
- mQsVelocityTracker.computeCurrentVelocity(1000);
- return mQsVelocityTracker.getYVelocity();
- }
-
- private void cancelQsAnimation() {
- if (mQsExpansionAnimator != null) {
- mQsExpansionAnimator.cancel();
- }
- }
-
- /**
- * @see #flingSettings(float, int, Runnable, boolean)
- */
- public void flingSettings(float vel, int type) {
- flingSettings(vel, type, null, false /* isClick */);
- }
-
- /**
- * Animates QS or QQS as if the user had swiped up or down.
- *
- * @param vel Finger velocity or 0 when not initiated by touch events.
- * @param type Either {@link #FLING_EXPAND}, {@link #FLING_COLLAPSE} or {@link #FLING_HIDE}.
- * @param onFinishRunnable Runnable to be executed at the end of animation.
- * @param isClick If originated by click (different interpolator and duration.)
- */
- protected void flingSettings(float vel, int type, final Runnable onFinishRunnable,
- boolean isClick) {
- float target;
- switch (type) {
- case FLING_EXPAND:
- target = mQsMaxExpansionHeight;
- break;
- case FLING_COLLAPSE:
- target = mQsMinExpansionHeight;
- break;
- case FLING_HIDE:
- default:
- target = 0;
- }
- if (target == mQsExpansionHeight) {
- if (onFinishRunnable != null) {
- onFinishRunnable.run();
- }
- return;
- }
-
- // If we move in the opposite direction, reset velocity and use a different duration.
- boolean oppositeDirection = false;
- boolean expanding = type == FLING_EXPAND;
- if (vel > 0 && !expanding || vel < 0 && expanding) {
- vel = 0;
- oppositeDirection = true;
- }
- ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target);
- if (isClick) {
- animator.setInterpolator(Interpolators.TOUCH_RESPONSE);
- animator.setDuration(368);
- } else {
- mFlingAnimationUtils.apply(animator, mQsExpansionHeight, target, vel);
- }
- if (oppositeDirection) {
- animator.setDuration(350);
- }
- animator.addUpdateListener(animation -> {
- setQsExpansion((Float) animation.getAnimatedValue());
- });
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mNotificationStackScroller.resetCheckSnoozeLeavebehind();
- mQsExpansionAnimator = null;
- if (onFinishRunnable != null) {
- onFinishRunnable.run();
- }
- }
- });
- animator.start();
- mQsExpansionAnimator = animator;
- mQsAnimatorExpand = expanding;
- }
-
- /**
- * @return Whether we should intercept a gesture to open Quick Settings.
- */
- private boolean shouldQuickSettingsIntercept(float x, float y, float yDiff) {
- if (!mQsExpansionEnabled || mCollapsedOnDown
- || (mKeyguardShowing && mKeyguardBypassController.getBypassEnabled())) {
- return false;
- }
- View header = mKeyguardShowing || mQs == null ? mKeyguardStatusBar : mQs.getHeader();
- final boolean onHeader = x >= mQsFrame.getX()
- && x <= mQsFrame.getX() + mQsFrame.getWidth()
- && y >= header.getTop() && y <= header.getBottom();
- if (mQsExpanded) {
- return onHeader || (yDiff < 0 && isInQsArea(x, y));
- } else {
- return onHeader;
- }
- }
-
- @Override
- protected boolean isScrolledToBottom() {
- if (!isInSettings()) {
- return mBarState == StatusBarState.KEYGUARD
- || mNotificationStackScroller.isScrolledToBottom();
- } else {
- return true;
- }
- }
-
- @Override
- protected int getMaxPanelHeight() {
- if (mKeyguardBypassController.getBypassEnabled() && mBarState == StatusBarState.KEYGUARD) {
- return getMaxPanelHeightBypass();
- } else {
- return getMaxPanelHeightNonBypass();
- }
- }
-
- private int getMaxPanelHeightNonBypass() {
- int min = mStatusBarMinHeight;
- if (!(mBarState == StatusBarState.KEYGUARD)
- && mNotificationStackScroller.getNotGoneChildCount() == 0) {
- int minHeight = (int) (mQsMinExpansionHeight + getOverExpansionAmount());
- min = Math.max(min, minHeight);
- }
- int maxHeight;
- if (mQsExpandImmediate || mQsExpanded || mIsExpanding && mQsExpandedWhenExpandingStarted
- || mPulsing) {
- maxHeight = calculatePanelHeightQsExpanded();
- } else {
- maxHeight = calculatePanelHeightShade();
- }
- maxHeight = Math.max(maxHeight, min);
- return maxHeight;
- }
-
- private int getMaxPanelHeightBypass() {
- int position = mClockPositionAlgorithm.getExpandedClockPosition()
- + mKeyguardStatusView.getHeight();
- if (mNotificationStackScroller.getVisibleNotificationCount() != 0) {
- position += mShelfHeight / 2.0f + mDarkIconSize / 2.0f;
- }
- return position;
- }
-
- public boolean isInSettings() {
- return mQsExpanded;
- }
-
- public boolean isExpanding() {
- return mIsExpanding;
- }
-
- @Override
- protected void onHeightUpdated(float expandedHeight) {
- if (!mQsExpanded || mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted) {
- // Updating the clock position will set the top padding which might
- // trigger a new panel height and re-position the clock.
- // This is a circular dependency and should be avoided, otherwise we'll have
- // a stack overflow.
- if (mStackScrollerMeasuringPass > 2) {
- if (DEBUG) Log.d(TAG, "Unstable notification panel height. Aborting.");
- } else {
- positionClockAndNotifications();
- }
- }
- if (mQsExpandImmediate || mQsExpanded && !mQsTracking && mQsExpansionAnimator == null
- && !mQsExpansionFromOverscroll) {
- float t;
- if (mKeyguardShowing) {
-
- // On Keyguard, interpolate the QS expansion linearly to the panel expansion
- t = expandedHeight / (getMaxPanelHeight());
- } else {
- // In Shade, interpolate linearly such that QS is closed whenever panel height is
- // minimum QS expansion + minStackHeight
- float panelHeightQsCollapsed = mNotificationStackScroller.getIntrinsicPadding()
- + mNotificationStackScroller.getLayoutMinHeight();
- float panelHeightQsExpanded = calculatePanelHeightQsExpanded();
- t = (expandedHeight - panelHeightQsCollapsed)
- / (panelHeightQsExpanded - panelHeightQsCollapsed);
- }
- float targetHeight = mQsMinExpansionHeight
- + t * (mQsMaxExpansionHeight - mQsMinExpansionHeight);
- setQsExpansion(targetHeight);
- mHomeControlsLayout.setTranslationY(targetHeight);
- }
- updateExpandedHeight(expandedHeight);
- updateHeader();
- updateNotificationTranslucency();
- updatePanelExpanded();
- updateGestureExclusionRect();
- if (DEBUG) {
- invalidate();
- }
- }
-
- private void updatePanelExpanded() {
- boolean isExpanded = !isFullyCollapsed() || mExpectingSynthesizedDown;
- if (mPanelExpanded != isExpanded) {
- mHeadsUpManager.setIsPanelExpanded(isExpanded);
- mStatusBar.setPanelExpanded(isExpanded);
- mPanelExpanded = isExpanded;
- }
- }
-
- private int calculatePanelHeightShade() {
- int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin();
- int maxHeight = mNotificationStackScroller.getHeight() - emptyBottomMargin;
- maxHeight += mNotificationStackScroller.getTopPaddingOverflow();
-
- if (mBarState == StatusBarState.KEYGUARD) {
- int minKeyguardPanelBottom = mClockPositionAlgorithm.getExpandedClockPosition()
- + mKeyguardStatusView.getHeight()
- + mNotificationStackScroller.getIntrinsicContentHeight();
- return Math.max(maxHeight, minKeyguardPanelBottom);
- } else {
- return maxHeight;
- }
- }
-
- private int calculatePanelHeightQsExpanded() {
- float notificationHeight = mNotificationStackScroller.getHeight()
- - mNotificationStackScroller.getEmptyBottomMargin()
- - mNotificationStackScroller.getTopPadding();
-
- // When only empty shade view is visible in QS collapsed state, simulate that we would have
- // it in expanded QS state as well so we don't run into troubles when fading the view in/out
- // and expanding/collapsing the whole panel from/to quick settings.
- if (mNotificationStackScroller.getNotGoneChildCount() == 0
- && mShowEmptyShadeView) {
- notificationHeight = mNotificationStackScroller.getEmptyShadeViewHeight();
- }
- int maxQsHeight = mQsMaxExpansionHeight;
-
- if (mKeyguardShowing) {
- maxQsHeight += mQsNotificationTopPadding;
- }
-
- // If an animation is changing the size of the QS panel, take the animated value.
- if (mQsSizeChangeAnimator != null) {
- maxQsHeight = (int) mQsSizeChangeAnimator.getAnimatedValue();
- }
- float totalHeight = Math.max(
- maxQsHeight, mBarState == StatusBarState.KEYGUARD
- ? mClockPositionResult.stackScrollerPadding : 0)
- + notificationHeight + mNotificationStackScroller.getTopPaddingOverflow();
- if (totalHeight > mNotificationStackScroller.getHeight()) {
- float fullyCollapsedHeight = maxQsHeight
- + mNotificationStackScroller.getLayoutMinHeight();
- totalHeight = Math.max(fullyCollapsedHeight, mNotificationStackScroller.getHeight());
- }
- return (int) totalHeight;
- }
-
- private void updateNotificationTranslucency() {
- float alpha = 1f;
- if (mClosingWithAlphaFadeOut && !mExpandingFromHeadsUp &&
- !mHeadsUpManager.hasPinnedHeadsUp()) {
- alpha = getFadeoutAlpha();
- }
- if (mBarState == StatusBarState.KEYGUARD && !mHintAnimationRunning
- && !mKeyguardBypassController.getBypassEnabled()) {
- alpha *= mClockPositionResult.clockAlpha;
- }
- mNotificationStackScroller.setAlpha(alpha);
- }
-
- private float getFadeoutAlpha() {
- float alpha;
- if (mQsMinExpansionHeight == 0) {
- return 1.0f;
- }
- alpha = getExpandedHeight() / mQsMinExpansionHeight;
- alpha = Math.max(0, Math.min(alpha, 1));
- alpha = (float) Math.pow(alpha, 0.75);
- return alpha;
- }
-
- @Override
- protected float getOverExpansionAmount() {
- return mNotificationStackScroller.getCurrentOverScrollAmount(true /* top */);
- }
-
- @Override
- protected float getOverExpansionPixels() {
- return mNotificationStackScroller.getCurrentOverScrolledPixels(true /* top */);
- }
-
- /**
- * Hides the header when notifications are colliding with it.
- */
- private void updateHeader() {
- if (mBarState == StatusBarState.KEYGUARD) {
- updateHeaderKeyguardAlpha();
- }
- updateQsExpansion();
- }
-
- protected float getHeaderTranslation() {
- if (mBarState == StatusBarState.KEYGUARD && !mKeyguardBypassController.getBypassEnabled()) {
- return -mQs.getQsMinExpansionHeight();
- }
- float appearAmount = mNotificationStackScroller.calculateAppearFraction(mExpandedHeight);
- float startHeight = -mQsExpansionHeight;
- if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()
- && mNotificationStackScroller.isPulseExpanding()) {
- if (!mPulseExpansionHandler.isExpanding()
- && !mPulseExpansionHandler.getLeavingLockscreen()) {
- // If we aborted the expansion we need to make sure the header doesn't reappear
- // again after the header has animated away
- appearAmount = 0;
- } else {
- appearAmount = mNotificationStackScroller.calculateAppearFractionBypass();
- }
- startHeight = -mQs.getQsMinExpansionHeight();
- if (mNPVPluginManager != null) startHeight -= mNPVPluginManager.getHeight();
- }
- float translation = MathUtils.lerp(startHeight, 0,
- Math.min(1.0f, appearAmount))
- + mExpandOffset;
- return Math.min(0, translation);
- }
-
- /**
- * @return the alpha to be used to fade out the contents on Keyguard (status bar, bottom area)
- * during swiping up
- */
- private float getKeyguardContentsAlpha() {
- float alpha;
- if (mBarState == StatusBarState.KEYGUARD) {
-
- // When on Keyguard, we hide the header as soon as we expanded close enough to the
- // header
- alpha = getExpandedHeight()
- /
- (mKeyguardStatusBar.getHeight() + mNotificationsHeaderCollideDistance);
- } else {
-
- // In SHADE_LOCKED, the top card is already really close to the header. Hide it as
- // soon as we start translating the stack.
- alpha = getExpandedHeight() / mKeyguardStatusBar.getHeight();
- }
- alpha = MathUtils.saturate(alpha);
- alpha = (float) Math.pow(alpha, 0.75);
- return alpha;
- }
-
- private void updateHeaderKeyguardAlpha() {
- if (!mKeyguardShowing) {
- return;
- }
- float alphaQsExpansion = 1 - Math.min(1, getQsExpansionFraction() * 2);
- float newAlpha = Math.min(getKeyguardContentsAlpha(), alphaQsExpansion)
- * mKeyguardStatusBarAnimateAlpha;
- newAlpha *= 1.0f - mKeyguardHeadsUpShowingAmount;
- mKeyguardStatusBar.setAlpha(newAlpha);
- boolean hideForBypass = mFirstBypassAttempt && mUpdateMonitor.shouldListenForFace()
- || mDelayShowingKeyguardStatusBar;
- mKeyguardStatusBar.setVisibility(newAlpha != 0f && !mDozing && !hideForBypass
- ? VISIBLE : INVISIBLE);
- }
-
- private void updateKeyguardBottomAreaAlpha() {
- // There are two possible panel expansion behaviors:
- // • User dragging up to unlock: we want to fade out as quick as possible
- // (ALPHA_EXPANSION_THRESHOLD) to avoid seeing the bouncer over the bottom area.
- // • User tapping on lock screen: bouncer won't be visible but panel expansion will
- // change due to "unlock hint animation." In this case, fading out the bottom area
- // would also hide the message that says "swipe to unlock," we don't want to do that.
- float expansionAlpha = MathUtils.map(isUnlockHintRunning()
- ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f,
- 0f, 1f, getExpandedFraction());
- float alpha = Math.min(expansionAlpha, 1 - getQsExpansionFraction());
- alpha *= mBottomAreaShadeAlpha;
- mKeyguardBottomArea.setAffordanceAlpha(alpha);
- mKeyguardBottomArea.setImportantForAccessibility(alpha == 0f
- ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
- : IMPORTANT_FOR_ACCESSIBILITY_AUTO);
- View ambientIndicationContainer = mStatusBar.getAmbientIndicationContainer();
- if (ambientIndicationContainer != null) {
- ambientIndicationContainer.setAlpha(alpha);
- }
- }
-
- /**
- * Custom clock fades away when user drags up to unlock or pulls down quick settings.
- *
- * Updates alpha of custom clock to match the alpha of the KeyguardBottomArea. See
- * {@link updateKeyguardBottomAreaAlpha}.
- */
- private void updateBigClockAlpha() {
- float expansionAlpha = MathUtils.map(isUnlockHintRunning()
- ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, 0f, 1f, getExpandedFraction());
- float alpha = Math.min(expansionAlpha, 1 - getQsExpansionFraction());
- mBigClockContainer.setAlpha(alpha);
- }
-
- @Override
- protected void onExpandingStarted() {
- super.onExpandingStarted();
- mNotificationStackScroller.onExpansionStarted();
- mIsExpanding = true;
- mQsExpandedWhenExpandingStarted = mQsFullyExpanded;
- if (mQsExpanded) {
- onQsExpansionStarted();
- }
- // Since there are QS tiles in the header now, we need to make sure we start listening
- // immediately so they can be up to date.
- if (mQs == null) return;
- mQs.setHeaderListening(true);
- }
-
- @Override
- protected void onExpandingFinished() {
- super.onExpandingFinished();
- mNotificationStackScroller.onExpansionStopped();
- mHeadsUpManager.onExpandingFinished();
- mIsExpanding = false;
- if (isFullyCollapsed()) {
- DejankUtils.postAfterTraversal(new Runnable() {
- @Override
- public void run() {
- setListening(false);
- }
- });
-
- // Workaround b/22639032: Make sure we invalidate something because else RenderThread
- // thinks we are actually drawing a frame put in reality we don't, so RT doesn't go
- // ahead with rendering and we jank.
- postOnAnimation(new Runnable() {
- @Override
- public void run() {
- getParent().invalidateChild(NotificationPanelView.this, mDummyDirtyRect);
- }
- });
- } else {
- setListening(true);
- }
- mQsExpandImmediate = false;
- mNotificationStackScroller.setShouldShowShelfOnly(false);
- mTwoFingerQsExpandPossible = false;
- mIsExpansionFromHeadsUp = false;
- notifyListenersTrackingHeadsUp(null);
- mExpandingFromHeadsUp = false;
- setPanelScrimMinFraction(0.0f);
- }
-
- private void notifyListenersTrackingHeadsUp(ExpandableNotificationRow pickedChild) {
- for (int i = 0; i < mTrackingHeadsUpListeners.size(); i++) {
- Consumer<ExpandableNotificationRow> listener
- = mTrackingHeadsUpListeners.get(i);
- listener.accept(pickedChild);
- }
- }
-
- private void setListening(boolean listening) {
- mKeyguardStatusBar.setListening(listening);
- if (mQs == null) return;
- mQs.setListening(listening);
- if (mNPVPluginManager != null) mNPVPluginManager.setListening(listening);
- }
-
- @Override
- public void expand(boolean animate) {
- super.expand(animate);
- setListening(true);
- }
-
- @Override
- protected void setOverExpansion(float overExpansion, boolean isPixels) {
- if (mConflictingQsExpansionGesture || mQsExpandImmediate) {
- return;
- }
- if (mBarState != StatusBarState.KEYGUARD) {
- mNotificationStackScroller.setOnHeightChangedListener(null);
- if (isPixels) {
- mNotificationStackScroller.setOverScrolledPixels(
- overExpansion, true /* onTop */, false /* animate */);
- } else {
- mNotificationStackScroller.setOverScrollAmount(
- overExpansion, true /* onTop */, false /* animate */);
- }
- mNotificationStackScroller.setOnHeightChangedListener(this);
- }
- }
-
- @Override
- protected void onTrackingStarted() {
- mFalsingManager.onTrackingStarted(!mKeyguardStateController.canDismissLockScreen());
- super.onTrackingStarted();
- if (mQsFullyExpanded) {
- mQsExpandImmediate = true;
- mNotificationStackScroller.setShouldShowShelfOnly(true);
- }
- if (mBarState == StatusBarState.KEYGUARD
- || mBarState == StatusBarState.SHADE_LOCKED) {
- mAffordanceHelper.animateHideLeftRightIcon();
- }
- mNotificationStackScroller.onPanelTrackingStarted();
- }
-
- @Override
- protected void onTrackingStopped(boolean expand) {
- mFalsingManager.onTrackingStopped();
- super.onTrackingStopped(expand);
- if (expand) {
- mNotificationStackScroller.setOverScrolledPixels(
- 0.0f, true /* onTop */, true /* animate */);
- }
- mNotificationStackScroller.onPanelTrackingStopped();
- if (expand && (mBarState == StatusBarState.KEYGUARD
- || mBarState == StatusBarState.SHADE_LOCKED)) {
- if (!mHintAnimationRunning) {
- mAffordanceHelper.reset(true);
- }
- }
- }
-
- @Override
- public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
-
- // Block update if we are in quick settings and just the top padding changed
- // (i.e. view == null).
- if (view == null && mQsExpanded) {
- return;
- }
- if (needsAnimation && mInterpolatedDarkAmount == 0) {
- mAnimateNextPositionUpdate = true;
- }
- ExpandableView firstChildNotGone = mNotificationStackScroller.getFirstChildNotGone();
- ExpandableNotificationRow firstRow = firstChildNotGone instanceof ExpandableNotificationRow
- ? (ExpandableNotificationRow) firstChildNotGone
- : null;
- if (firstRow != null
- && (view == firstRow || (firstRow.getNotificationParent() == firstRow))) {
- requestScrollerTopPaddingUpdate(false /* animate */);
- }
- requestPanelHeightUpdate();
- }
-
- @Override
- public void onReset(ExpandableView view) {
- }
-
- public void onQsHeightChanged() {
- mQsMaxExpansionHeight = mQs != null ? mQs.getDesiredHeight() : 0;
- if (mQsExpanded && mQsFullyExpanded) {
- mQsExpansionHeight = mQsMaxExpansionHeight;
- requestScrollerTopPaddingUpdate(false /* animate */);
- requestPanelHeightUpdate();
- }
- if (mAccessibilityManager.isEnabled()) {
- setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
- }
- mNotificationStackScroller.setMaxTopPadding(
- mQsMaxExpansionHeight + mQsNotificationTopPadding);
- }
-
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- mAffordanceHelper.onConfigurationChanged();
- if (newConfig.orientation != mLastOrientation) {
- resetHorizontalPanelPosition();
- }
- mLastOrientation = newConfig.orientation;
- }
-
- @Override
- public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- mNavigationBarBottomHeight = insets.getStableInsetBottom();
- updateMaxHeadsUpTranslation();
- return insets;
- }
-
- private void updateMaxHeadsUpTranslation() {
- mNotificationStackScroller.setHeadsUpBoundaries(getHeight(), mNavigationBarBottomHeight);
- }
-
@Override
public void onRtlPropertiesChanged(int layoutDirection) {
- if (layoutDirection != mOldLayoutDirection) {
- mAffordanceHelper.onRtlPropertiesChanged();
- mOldLayoutDirection = layoutDirection;
+ if (mRtlChangeListener != null) {
+ mRtlChangeListener.onRtlPropertielsChanged(layoutDirection);
}
}
@Override
- public void onClick(View v) {
- onQsExpansionStarted();
- if (mQsExpanded) {
- flingSettings(0 /* vel */, FLING_COLLAPSE, null /* onFinishRunnable */,
- true /* isClick */);
- } else if (mQsExpansionEnabled) {
- mLockscreenGestureLogger.write(MetricsEvent.ACTION_SHADE_QS_TAP, 0, 0);
- flingSettings(0 /* vel */, FLING_EXPAND, null /* onFinishRunnable */,
- true /* isClick */);
- }
- }
-
- @Override
- public void onAnimationToSideStarted(boolean rightPage, float translation, float vel) {
- boolean start = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? rightPage : !rightPage;
- mIsLaunchTransitionRunning = true;
- mLaunchAnimationEndRunnable = null;
- float displayDensity = mStatusBar.getDisplayDensity();
- int lengthDp = Math.abs((int) (translation / displayDensity));
- int velocityDp = Math.abs((int) (vel / displayDensity));
- if (start) {
- mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_DIALER, lengthDp, velocityDp);
-
- mFalsingManager.onLeftAffordanceOn();
- if (mFalsingManager.shouldEnforceBouncer()) {
- mStatusBar.executeRunnableDismissingKeyguard(new Runnable() {
- @Override
- public void run() {
- mKeyguardBottomArea.launchLeftAffordance();
- }
- }, null, true /* dismissShade */, false /* afterKeyguardGone */,
- true /* deferred */);
- } else {
- mKeyguardBottomArea.launchLeftAffordance();
- }
- } else {
- if (KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE.equals(
- mLastCameraLaunchSource)) {
- mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_CAMERA, lengthDp, velocityDp);
- }
- mFalsingManager.onCameraOn();
- if (mFalsingManager.shouldEnforceBouncer()) {
- mStatusBar.executeRunnableDismissingKeyguard(new Runnable() {
- @Override
- public void run() {
- mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource);
- }
- }, null, true /* dismissShade */, false /* afterKeyguardGone */,
- true /* deferred */);
- }
- else {
- mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource);
- }
- }
- mStatusBar.startLaunchTransitionTimeout();
- mBlockTouches = true;
- }
-
- @Override
- public void onAnimationToSideEnded() {
- mIsLaunchTransitionRunning = false;
- mIsLaunchTransitionFinished = true;
- if (mLaunchAnimationEndRunnable != null) {
- mLaunchAnimationEndRunnable.run();
- mLaunchAnimationEndRunnable = null;
- }
- mStatusBar.readyForKeyguardDone();
- }
-
- @Override
- protected void startUnlockHintAnimation() {
- if (mPowerManager.isPowerSaveMode()) {
- onUnlockHintStarted();
- onUnlockHintFinished();
- return;
- }
- super.startUnlockHintAnimation();
- }
-
- @Override
- public float getMaxTranslationDistance() {
- return (float) Math.hypot(getWidth(), getHeight());
- }
-
- @Override
- public void onSwipingStarted(boolean rightIcon) {
- mFalsingManager.onAffordanceSwipingStarted(rightIcon);
- boolean camera = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? !rightIcon
- : rightIcon;
- if (camera) {
- mKeyguardBottomArea.bindCameraPrewarmService();
- }
- requestDisallowInterceptTouchEvent(true);
- mOnlyAffordanceInThisMotion = true;
- mQsTracking = false;
- }
-
- @Override
- public void onSwipingAborted() {
- mFalsingManager.onAffordanceSwipingAborted();
- mKeyguardBottomArea.unbindCameraPrewarmService(false /* launched */);
- }
-
- @Override
- public void onIconClicked(boolean rightIcon) {
- if (mHintAnimationRunning) {
- return;
- }
- mHintAnimationRunning = true;
- mAffordanceHelper.startHintAnimation(rightIcon, new Runnable() {
- @Override
- public void run() {
- mHintAnimationRunning = false;
- mStatusBar.onHintFinished();
- }
- });
- rightIcon = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? !rightIcon : rightIcon;
- if (rightIcon) {
- mStatusBar.onCameraHintStarted();
- } else {
- if (mKeyguardBottomArea.isLeftVoiceAssist()) {
- mStatusBar.onVoiceAssistHintStarted();
- } else {
- mStatusBar.onPhoneHintStarted();
- }
- }
- }
-
- @Override
- protected void onUnlockHintFinished() {
- super.onUnlockHintFinished();
- mNotificationStackScroller.setUnlockHintRunning(false);
- }
-
- @Override
- protected void onUnlockHintStarted() {
- super.onUnlockHintStarted();
- mNotificationStackScroller.setUnlockHintRunning(true);
- }
-
- @Override
- public KeyguardAffordanceView getLeftIcon() {
- return getLayoutDirection() == LAYOUT_DIRECTION_RTL
- ? mKeyguardBottomArea.getRightView()
- : mKeyguardBottomArea.getLeftView();
- }
-
- @Override
- public KeyguardAffordanceView getRightIcon() {
- return getLayoutDirection() == LAYOUT_DIRECTION_RTL
- ? mKeyguardBottomArea.getLeftView()
- : mKeyguardBottomArea.getRightView();
- }
-
- @Override
- public View getLeftPreview() {
- return getLayoutDirection() == LAYOUT_DIRECTION_RTL
- ? mKeyguardBottomArea.getRightPreview()
- : mKeyguardBottomArea.getLeftPreview();
- }
-
- @Override
- public View getRightPreview() {
- return getLayoutDirection() == LAYOUT_DIRECTION_RTL
- ? mKeyguardBottomArea.getLeftPreview()
- : mKeyguardBottomArea.getRightPreview();
- }
-
- @Override
- public float getAffordanceFalsingFactor() {
- return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
- }
-
- @Override
- public boolean needsAntiFalsing() {
- return mBarState == StatusBarState.KEYGUARD;
- }
-
- @Override
- protected float getPeekHeight() {
- if (mNotificationStackScroller.getNotGoneChildCount() > 0) {
- return mNotificationStackScroller.getPeekHeight();
- } else {
- return mQsMinExpansionHeight;
- }
- }
-
- @Override
- protected boolean shouldUseDismissingAnimation() {
- return mBarState != StatusBarState.SHADE
- && (mKeyguardStateController.canDismissLockScreen() || !isTracking());
- }
-
- @Override
- protected boolean fullyExpandedClearAllVisible() {
- return mNotificationStackScroller.isFooterViewNotGone()
- && mNotificationStackScroller.isScrolledToBottom() && !mQsExpandImmediate;
- }
-
- @Override
- protected boolean isClearAllVisible() {
- return mNotificationStackScroller.isFooterViewContentVisible();
- }
-
- @Override
- protected int getClearAllHeight() {
- return mNotificationStackScroller.getFooterViewHeight();
- }
-
- @Override
- protected boolean isTrackingBlocked() {
- return mConflictingQsExpansionGesture && mQsExpanded || mBlockingExpansionForCurrentTouch;
- }
-
- public boolean isQsExpanded() {
- return mQsExpanded;
- }
-
- public boolean isQsDetailShowing() {
- return mQs.isShowingDetail();
- }
-
- public void closeQsDetail() {
- mQs.closeDetail();
- }
-
- @Override
public boolean shouldDelayChildPressedState() {
return true;
}
- public boolean isLaunchTransitionFinished() {
- return mIsLaunchTransitionFinished;
- }
-
- public boolean isLaunchTransitionRunning() {
- return mIsLaunchTransitionRunning;
- }
-
- public void setLaunchTransitionEndRunnable(Runnable r) {
- mLaunchAnimationEndRunnable = r;
- }
-
- public void setEmptyDragAmount(float amount) {
- mEmptyDragAmount = amount * 0.2f;
- positionClockAndNotifications();
- }
-
- private void updateDozingVisibilities(boolean animate) {
- mKeyguardBottomArea.setDozing(mDozing, animate);
- if (!mDozing && animate) {
- animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
- }
- }
-
- @Override
- public boolean isDozing() {
- return mDozing;
- }
-
- public void showEmptyShadeView(boolean emptyShadeViewVisible) {
- mShowEmptyShadeView = emptyShadeViewVisible;
- updateEmptyShadeView();
- }
-
- private void updateEmptyShadeView() {
- // Hide "No notifications" in QS.
- mNotificationStackScroller.updateEmptyShadeView(mShowEmptyShadeView && !mQsExpanded);
- }
-
- public void setQsScrimEnabled(boolean qsScrimEnabled) {
- boolean changed = mQsScrimEnabled != qsScrimEnabled;
- mQsScrimEnabled = qsScrimEnabled;
- if (changed) {
- updateQsState();
- }
- }
-
- public void setKeyguardUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher) {
- mKeyguardUserSwitcher = keyguardUserSwitcher;
- }
-
- public void onScreenTurningOn() {
- mKeyguardStatusView.dozeTimeTick();
- }
-
- @Override
- public void onEmptySpaceClicked(float x, float y) {
- onEmptySpaceClick(x);
- }
-
- @Override
- protected boolean onMiddleClicked() {
- switch (mBarState) {
- case StatusBarState.KEYGUARD:
- if (!mDozingOnDown) {
- if (mKeyguardBypassController.getBypassEnabled()) {
- mUpdateMonitor.requestFaceAuth();
- } else {
- mLockscreenGestureLogger.write(
- MetricsEvent.ACTION_LS_HINT,
- 0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
- startUnlockHintAnimation();
- }
- }
- return true;
- case StatusBarState.SHADE_LOCKED:
- if (!mQsExpanded) {
- mStatusBarStateController.setState(StatusBarState.KEYGUARD);
- }
- return true;
- case StatusBarState.SHADE:
-
- // This gets called in the middle of the touch handling, where the state is still
- // that we are tracking the panel. Collapse the panel after this is done.
- post(mPostCollapseRunnable);
- return false;
- default:
- return true;
- }
- }
-
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
@@ -3011,250 +69,18 @@
}
}
- public float getCurrentPanelAlpha() {
+ float getCurrentPanelAlpha() {
return mCurrentPanelAlpha;
}
- public boolean setPanelAlpha(int alpha, boolean animate) {
- if (mPanelAlpha != alpha) {
- mPanelAlpha = alpha;
- PropertyAnimator.setProperty(this, PANEL_ALPHA, alpha,
- alpha == 255 ? PANEL_ALPHA_IN_PROPERTIES : PANEL_ALPHA_OUT_PROPERTIES, animate);
- return true;
- }
- return false;
- }
-
- public void setPanelAlphaInternal(float alpha) {
+ void setPanelAlphaInternal(float alpha) {
mCurrentPanelAlpha = (int) alpha;
mAlphaPaint.setARGB(mCurrentPanelAlpha, 255, 255, 255);
invalidate();
}
- public void setPanelAlphaEndAction(Runnable r) {
- mPanelAlphaEndAction = r;
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- if (DEBUG) {
- Paint p = new Paint();
- p.setColor(Color.RED);
- p.setStrokeWidth(2);
- p.setStyle(Paint.Style.STROKE);
- canvas.drawLine(0, getMaxPanelHeight(), getWidth(), getMaxPanelHeight(), p);
- p.setColor(Color.BLUE);
- canvas.drawLine(0, getExpandedHeight(), getWidth(), getExpandedHeight(), p);
- p.setColor(Color.GREEN);
- canvas.drawLine(0, calculatePanelHeightQsExpanded(), getWidth(),
- calculatePanelHeightQsExpanded(), p);
- p.setColor(Color.YELLOW);
- canvas.drawLine(0, calculatePanelHeightShade(), getWidth(),
- calculatePanelHeightShade(), p);
- p.setColor(Color.MAGENTA);
- canvas.drawLine(0, calculateQsTopPadding(), getWidth(),
- calculateQsTopPadding(), p);
- p.setColor(Color.CYAN);
- canvas.drawLine(0, mClockPositionResult.stackScrollerPadding, getWidth(),
- mNotificationStackScroller.getTopPadding(), p);
- p.setColor(Color.GRAY);
- canvas.drawLine(0, mClockPositionResult.clockY, getWidth(),
- mClockPositionResult.clockY, p);
- }
- }
-
- @Override
- public void onHeadsUpPinnedModeChanged(final boolean inPinnedMode) {
- mNotificationStackScroller.setInHeadsUpPinnedMode(inPinnedMode);
- if (inPinnedMode) {
- mHeadsUpExistenceChangedRunnable.run();
- updateNotificationTranslucency();
- } else {
- setHeadsUpAnimatingAway(true);
- mNotificationStackScroller.runAfterAnimationFinished(
- mHeadsUpExistenceChangedRunnable);
- }
- updateGestureExclusionRect();
- mHeadsUpPinnedMode = inPinnedMode;
- updateHeadsUpVisibility();
- updateKeyguardStatusBarForHeadsUp();
- }
-
- private void updateKeyguardStatusBarForHeadsUp() {
- boolean showingKeyguardHeadsUp = mKeyguardShowing
- && mHeadsUpAppearanceController.shouldBeVisible();
- if (mShowingKeyguardHeadsUp != showingKeyguardHeadsUp) {
- mShowingKeyguardHeadsUp = showingKeyguardHeadsUp;
- if (mKeyguardShowing) {
- PropertyAnimator.setProperty(this, KEYGUARD_HEADS_UP_SHOWING_AMOUNT,
- showingKeyguardHeadsUp ? 1.0f : 0.0f, KEYGUARD_HUN_PROPERTIES,
- true /* animate */);
- } else {
- PropertyAnimator.applyImmediately(this, KEYGUARD_HEADS_UP_SHOWING_AMOUNT, 0.0f);
- }
- }
- }
-
- private void setKeyguardHeadsUpShowingAmount(float amount) {
- mKeyguardHeadsUpShowingAmount = amount;
- updateHeaderKeyguardAlpha();
- }
-
- private float getKeyguardHeadsUpShowingAmount() {
- return mKeyguardHeadsUpShowingAmount;
- }
-
- public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
- mHeadsUpAnimatingAway = headsUpAnimatingAway;
- mNotificationStackScroller.setHeadsUpAnimatingAway(headsUpAnimatingAway);
- updateHeadsUpVisibility();
- }
-
- private void updateHeadsUpVisibility() {
- ((PhoneStatusBarView) mBar).setHeadsUpVisible(mHeadsUpAnimatingAway || mHeadsUpPinnedMode);
- }
-
- @Override
- public void onHeadsUpPinned(NotificationEntry entry) {
- if (!isOnKeyguard()) {
- mNotificationStackScroller.generateHeadsUpAnimation(entry.getHeadsUpAnimationView(),
- true);
- }
- }
-
- @Override
- public void onHeadsUpUnPinned(NotificationEntry entry) {
-
- // When we're unpinning the notification via active edge they remain heads-upped,
- // we need to make sure that an animation happens in this case, otherwise the notification
- // will stick to the top without any interaction.
- if (isFullyCollapsed() && entry.isRowHeadsUp() && !isOnKeyguard()) {
- mNotificationStackScroller.generateHeadsUpAnimation(
- entry.getHeadsUpAnimationView(), false);
- entry.setHeadsUpIsVisible();
- }
- }
-
- @Override
- public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
- mNotificationStackScroller.generateHeadsUpAnimation(entry, isHeadsUp);
- }
-
- @Override
- public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
- super.setHeadsUpManager(headsUpManager);
- mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager,
- mNotificationStackScroller.getHeadsUpCallback(), this);
- }
-
- public void setTrackedHeadsUp(ExpandableNotificationRow pickedChild) {
- if (pickedChild != null) {
- notifyListenersTrackingHeadsUp(pickedChild);
- mExpandingFromHeadsUp = true;
- }
- // otherwise we update the state when the expansion is finished
- }
-
- @Override
- protected void onClosingFinished() {
- super.onClosingFinished();
- resetHorizontalPanelPosition();
- setClosingWithAlphaFadeout(false);
- }
-
- private void setClosingWithAlphaFadeout(boolean closing) {
- mClosingWithAlphaFadeOut = closing;
- mNotificationStackScroller.forceNoOverlappingRendering(closing);
- }
-
- /**
- * Updates the vertical position of the panel so it is positioned closer to the touch
- * responsible for opening the panel.
- *
- * @param x the x-coordinate the touch event
- */
- protected void updateVerticalPanelPosition(float x) {
- if (mNotificationStackScroller.getWidth() * 1.75f > getWidth()) {
- resetHorizontalPanelPosition();
- return;
- }
- float leftMost = mPositionMinSideMargin + mNotificationStackScroller.getWidth() / 2;
- float rightMost = getWidth() - mPositionMinSideMargin
- - mNotificationStackScroller.getWidth() / 2;
- if (Math.abs(x - getWidth() / 2) < mNotificationStackScroller.getWidth() / 4) {
- x = getWidth() / 2;
- }
- x = Math.min(rightMost, Math.max(leftMost, x));
- float center =
- mNotificationStackScroller.getLeft() + mNotificationStackScroller.getWidth() / 2;
- setHorizontalPanelTranslation(x - center);
- }
-
- private void resetHorizontalPanelPosition() {
- setHorizontalPanelTranslation(0f);
- }
-
- protected void setHorizontalPanelTranslation(float translation) {
- mNotificationStackScroller.setTranslationX(translation);
- mQsFrame.setTranslationX(translation);
- int size = mVerticalTranslationListener.size();
- for (int i = 0; i < size; i++) {
- mVerticalTranslationListener.get(i).run();
- }
- }
-
- protected void updateExpandedHeight(float expandedHeight) {
- if (mTracking) {
- mNotificationStackScroller.setExpandingVelocity(getCurrentExpandVelocity());
- }
- if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()) {
- // The expandedHeight is always the full panel Height when bypassing
- expandedHeight = getMaxPanelHeightNonBypass();
- }
- mNotificationStackScroller.setExpandedHeight(expandedHeight);
- updateKeyguardBottomAreaAlpha();
- updateBigClockAlpha();
- updateStatusBarIcons();
- }
-
- /**
- * @return whether the notifications are displayed full width and don't have any margins on
- * the side.
- */
- public boolean isFullWidth() {
- return mIsFullWidth;
- }
-
- private void updateStatusBarIcons() {
- boolean showIconsWhenExpanded = (isPanelVisibleBecauseOfHeadsUp() || isFullWidth())
- && getExpandedHeight() < getOpeningHeight();
- if (showIconsWhenExpanded && mNoVisibleNotifications && isOnKeyguard()) {
- showIconsWhenExpanded = false;
- }
- if (showIconsWhenExpanded != mShowIconsWhenExpanded) {
- mShowIconsWhenExpanded = showIconsWhenExpanded;
- mCommandQueue.recomputeDisableFlags(mDisplayId, false);
- }
- }
-
- private boolean isOnKeyguard() {
- return mBarState == StatusBarState.KEYGUARD;
- }
-
- public void setPanelScrimMinFraction(float minFraction) {
- mBar.panelScrimMinFractionChanged(minFraction);
- }
-
- public void clearNotificationEffects() {
- mStatusBar.clearNotificationEffects();
- }
-
- @Override
- protected boolean isPanelVisibleBecauseOfHeadsUp() {
- return (mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway)
- && mBarState == StatusBarState.SHADE;
+ public void setDozing(boolean dozing) {
+ mDozing = dozing;
}
@Override
@@ -3262,382 +88,11 @@
return !mDozing;
}
- public void launchCamera(boolean animate, int source) {
- if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) {
- mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP;
- } else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE) {
- mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_WIGGLE;
- } else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER) {
- mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER;
- } else {
-
- // Default.
- mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
- }
-
- // If we are launching it when we are occluded already we don't want it to animate,
- // nor setting these flags, since the occluded state doesn't change anymore, hence it's
- // never reset.
- if (!isFullyCollapsed()) {
- setLaunchingAffordance(true);
- } else {
- animate = false;
- }
- mAffordanceHasPreview = mKeyguardBottomArea.getRightPreview() != null;
- mAffordanceHelper.launchAffordance(animate, getLayoutDirection() == LAYOUT_DIRECTION_RTL);
+ void setRtlChangeListener(RtlChangeListener listener) {
+ mRtlChangeListener = listener;
}
- public void onAffordanceLaunchEnded() {
- setLaunchingAffordance(false);
- }
-
- /**
- * Set whether we are currently launching an affordance. This is currently only set when
- * launched via a camera gesture.
- */
- private void setLaunchingAffordance(boolean launchingAffordance) {
- mLaunchingAffordance = launchingAffordance;
- getLeftIcon().setLaunchingAffordance(launchingAffordance);
- getRightIcon().setLaunchingAffordance(launchingAffordance);
- mKeyguardBypassController.setLaunchingAffordance(launchingAffordance);
- if (mAffordanceLaunchListener != null) {
- mAffordanceLaunchListener.accept(launchingAffordance);
- }
- }
-
- /**
- * Return true when a bottom affordance is launching an occluded activity with a splash screen.
- */
- public boolean isLaunchingAffordanceWithPreview() {
- return mLaunchingAffordance && mAffordanceHasPreview;
- }
-
- /**
- * Whether the camera application can be launched for the camera launch gesture.
- */
- public boolean canCameraGestureBeLaunched() {
- if (!mStatusBar.isCameraAllowedByAdmin()) {
- return false;
- }
-
- ResolveInfo resolveInfo = mKeyguardBottomArea.resolveCameraIntent();
- String packageToLaunch = (resolveInfo == null || resolveInfo.activityInfo == null)
- ? null : resolveInfo.activityInfo.packageName;
- return packageToLaunch != null &&
- (mBarState != StatusBarState.SHADE || !isForegroundApp(packageToLaunch))
- && !mAffordanceHelper.isSwipingInProgress();
- }
-
- /**
- * Return true if the applications with the package name is running in foreground.
- *
- * @param pkgName application package name.
- */
- private boolean isForegroundApp(String pkgName) {
- ActivityManager am = getContext().getSystemService(ActivityManager.class);
- List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
- return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName());
- }
-
- private void setGroupManager(NotificationGroupManager groupManager) {
- mGroupManager = groupManager;
- }
-
- public boolean hideStatusBarIconsWhenExpanded() {
- if (mLaunchingNotification) {
- return mHideIconsDuringNotificationLaunch;
- }
- if (mHeadsUpAppearanceController != null
- && mHeadsUpAppearanceController.shouldBeVisible()) {
- return false;
- }
- return !isFullWidth() || !mShowIconsWhenExpanded;
- }
-
- private final FragmentListener mFragmentListener = new FragmentListener() {
- @Override
- public void onFragmentViewCreated(String tag, Fragment fragment) {
- mQs = (QS) fragment;
- mQs.setPanelView(NotificationPanelView.this);
- mQs.setExpandClickListener(NotificationPanelView.this);
- mQs.setHeaderClickable(mQsExpansionEnabled);
- updateQSPulseExpansion();
- mQs.setOverscrolling(mStackScrollerOverscrolling);
-
- // recompute internal state when qspanel height changes
- mQs.getView().addOnLayoutChangeListener(
- (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
- final int height = bottom - top;
- final int oldHeight = oldBottom - oldTop;
- if (height != oldHeight) {
- onQsHeightChanged();
- }
- });
- mNotificationStackScroller.setQsContainer((ViewGroup) mQs.getView());
- if (mQs instanceof QSFragment) {
- mKeyguardStatusBar.setQSPanel(((QSFragment) mQs).getQsPanel());
- }
- updateQsExpansion();
- }
-
- @Override
- public void onFragmentViewDestroyed(String tag, Fragment fragment) {
- // Manual handling of fragment lifecycle is only required because this bridges
- // non-fragment and fragment code. Once we are using a fragment for the notification
- // panel, mQs will not need to be null cause it will be tied to the same lifecycle.
- if (fragment == mQs) {
- mQs = null;
- }
- }
- };
-
- @Override
- public void setTouchAndAnimationDisabled(boolean disabled) {
- super.setTouchAndAnimationDisabled(disabled);
- if (disabled && mAffordanceHelper.isSwipingInProgress() && !mIsLaunchTransitionRunning) {
- mAffordanceHelper.reset(false /* animate */);
- }
- mNotificationStackScroller.setAnimationsEnabled(!disabled);
- }
-
- /**
- * Sets the dozing state.
- *
- * @param dozing {@code true} when dozing.
- * @param animate if transition should be animated.
- * @param wakeUpTouchLocation touch event location - if woken up by SLPI sensor.
- */
- public void setDozing(boolean dozing, boolean animate, PointF wakeUpTouchLocation) {
- if (dozing == mDozing) return;
- mDozing = dozing;
- mNotificationStackScroller.setDozing(mDozing, animate, wakeUpTouchLocation);
- mKeyguardBottomArea.setDozing(mDozing, animate);
-
- if (dozing) {
- mBottomAreaShadeAlphaAnimator.cancel();
- }
-
- if (mBarState == StatusBarState.KEYGUARD
- || mBarState == StatusBarState.SHADE_LOCKED) {
- updateDozingVisibilities(animate);
- }
-
- final float dozeAmount = dozing ? 1 : 0;
- mStatusBarStateController.setDozeAmount(dozeAmount, animate);
- }
-
- @Override
- public void onDozeAmountChanged(float linearAmount, float amount) {
- mInterpolatedDarkAmount = amount;
- mLinearDarkAmount = linearAmount;
- mKeyguardStatusView.setDarkAmount(mInterpolatedDarkAmount);
- mKeyguardBottomArea.setDarkAmount(mInterpolatedDarkAmount);
- positionClockAndNotifications();
- }
-
- public void setPulsing(boolean pulsing) {
- mPulsing = pulsing;
- final boolean animatePulse = !mDozeParameters.getDisplayNeedsBlanking()
- && mDozeParameters.getAlwaysOn();
- if (animatePulse) {
- mAnimateNextPositionUpdate = true;
- }
- // Do not animate the clock when waking up from a pulse.
- // The height callback will take care of pushing the clock to the right position.
- if (!mPulsing && !mDozing) {
- mAnimateNextPositionUpdate = false;
- }
- mNotificationStackScroller.setPulsing(pulsing, animatePulse);
- mKeyguardStatusView.setPulsing(pulsing);
- }
-
- public void setAmbientIndicationBottomPadding(int ambientIndicationBottomPadding) {
- if (mAmbientIndicationBottomPadding != ambientIndicationBottomPadding) {
- mAmbientIndicationBottomPadding = ambientIndicationBottomPadding;
- mStatusBar.updateKeyguardMaxNotifications();
- }
- }
-
- public void dozeTimeTick() {
- mKeyguardBottomArea.dozeTimeTick();
- mKeyguardStatusView.dozeTimeTick();
- if (mInterpolatedDarkAmount > 0) {
- positionClockAndNotifications();
- }
- }
-
- public void setStatusAccessibilityImportance(int mode) {
- mKeyguardStatusView.setImportantForAccessibility(mode);
- }
-
- /**
- * TODO: this should be removed.
- * It's not correct to pass this view forward because other classes will end up adding
- * children to it. Theme will be out of sync.
- *
- * @return bottom area view
- */
- public KeyguardBottomAreaView getKeyguardBottomAreaView() {
- return mKeyguardBottomArea;
- }
-
- public void setUserSetupComplete(boolean userSetupComplete) {
- mUserSetupComplete = userSetupComplete;
- mKeyguardBottomArea.setUserSetupComplete(userSetupComplete);
- }
-
- public void applyExpandAnimationParams(ExpandAnimationParameters params) {
- mExpandOffset = params != null ? params.getTopChange() : 0;
- updateQsExpansion();
- if (params != null) {
- boolean hideIcons = params.getProgress(
- ActivityLaunchAnimator.ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f;
- if (hideIcons != mHideIconsDuringNotificationLaunch) {
- mHideIconsDuringNotificationLaunch = hideIcons;
- if (!hideIcons) {
- mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */);
- }
- }
- }
- }
-
- public void addTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) {
- mTrackingHeadsUpListeners.add(listener);
- }
-
- public void removeTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) {
- mTrackingHeadsUpListeners.remove(listener);
- }
-
- public void addVerticalTranslationListener(Runnable verticalTranslationListener) {
- mVerticalTranslationListener.add(verticalTranslationListener);
- }
-
- public void removeVerticalTranslationListener(Runnable verticalTranslationListener) {
- mVerticalTranslationListener.remove(verticalTranslationListener);
- }
-
- public void setHeadsUpAppearanceController(
- HeadsUpAppearanceController headsUpAppearanceController) {
- mHeadsUpAppearanceController = headsUpAppearanceController;
- }
-
- /**
- * Starts the animation before we dismiss Keyguard, i.e. an disappearing animation on the
- * security view of the bouncer.
- */
- public void onBouncerPreHideAnimation() {
- setKeyguardStatusViewVisibility(mBarState, true /* keyguardFadingAway */,
- false /* goingToFullShade */);
- }
-
- /**
- * Do not let the user drag the shade up and down for the current touch session.
- * This is necessary to avoid shade expansion while/after the bouncer is dismissed.
- */
- public void blockExpansionForCurrentTouch() {
- mBlockingExpansionForCurrentTouch = mTracking;
- }
-
- @Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- super.dump(fd, pw, args);
- pw.println(" gestureExclusionRect: " + calculateGestureExclusionRect());
- if (mKeyguardStatusBar != null) {
- mKeyguardStatusBar.dump(fd, pw, args);
- }
- if (mKeyguardStatusView != null) {
- mKeyguardStatusView.dump(fd, pw, args);
- }
- }
-
- public boolean hasActiveClearableNotifications() {
- return mNotificationStackScroller.hasActiveClearableNotifications(ROWS_ALL);
- }
-
- @Override
- public void onZenChanged(int zen) {
- updateShowEmptyShadeView();
- }
-
- private void updateShowEmptyShadeView() {
- boolean showEmptyShadeView =
- mBarState != StatusBarState.KEYGUARD && !mEntryManager.hasActiveNotifications();
- showEmptyShadeView(showEmptyShadeView);
- }
-
- public RemoteInputController.Delegate createRemoteInputDelegate() {
- return mNotificationStackScroller.createDelegate();
- }
-
- public void updateNotificationViews() {
- mNotificationStackScroller.updateSectionBoundaries();
- mNotificationStackScroller.updateSpeedBumpIndex();
- mNotificationStackScroller.updateFooter();
- updateShowEmptyShadeView();
- mNotificationStackScroller.updateIconAreaViews();
- }
-
- public void onUpdateRowStates() {
- mNotificationStackScroller.onUpdateRowStates();
- }
-
- public boolean hasPulsingNotifications() {
- return mNotificationStackScroller.hasPulsingNotifications();
- }
-
- public ActivatableNotificationView getActivatedChild() {
- return mNotificationStackScroller.getActivatedChild();
- }
-
- public void setActivatedChild(ActivatableNotificationView o) {
- mNotificationStackScroller.setActivatedChild(o);
- }
-
- public void runAfterAnimationFinished(Runnable r) {
- mNotificationStackScroller.runAfterAnimationFinished(r);
- }
-
- public void setScrollingEnabled(boolean b) {
- mNotificationStackScroller.setScrollingEnabled(b);
- }
-
- public void initDependencies(StatusBar statusBar, NotificationGroupManager groupManager,
- NotificationShelf notificationShelf,
- HeadsUpManagerPhone headsUpManager,
- NotificationIconAreaController notificationIconAreaController,
- ScrimController scrimController) {
- setStatusBar(statusBar);
- setGroupManager(mGroupManager);
- mNotificationStackScroller.setNotificationPanel(this);
- mNotificationStackScroller.setIconAreaController(notificationIconAreaController);
- mNotificationStackScroller.setStatusBar(statusBar);
- mNotificationStackScroller.setGroupManager(groupManager);
- mNotificationStackScroller.setShelf(notificationShelf);
- mNotificationStackScroller.setScrimController(scrimController);
- updateShowEmptyShadeView();
- }
-
- public void showTransientIndication(int id) {
- mKeyguardIndicationController.showTransientIndication(id);
- }
-
- @Override
- public void onDynamicPrivacyChanged() {
- // Do not request animation when pulsing or waking up, otherwise the clock wiill be out
- // of sync with the notification panel.
- if (mLinearDarkAmount != 0) {
- return;
- }
- mAnimateNextPositionUpdate = true;
- }
-
- public void setOnReinflationListener(Runnable onReinflationListener) {
- mOnReinflationListener = onReinflationListener;
- }
-
- public static boolean isQsSplitEnabled() {
- return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.QS_SPLIT_ENABLED, false);
+ interface RtlChangeListener {
+ void onRtlPropertielsChanged(int layoutDirection);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
new file mode 100644
index 0000000..90ec2a0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -0,0 +1,3741 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.app.ActivityManager;
+import android.app.Fragment;
+import android.app.StatusBarManager;
+import android.content.Context;
+import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.drawable.Drawable;
+import android.hardware.biometrics.BiometricSourceType;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.provider.DeviceConfig;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.MathUtils;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewPropertyAnimator;
+import android.view.ViewTreeObserver;
+import android.view.WindowInsets;
+import android.view.accessibility.AccessibilityManager;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.LatencyTracker;
+import com.android.keyguard.KeyguardClockSwitch;
+import com.android.keyguard.KeyguardStatusView;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.DejankUtils;
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.DisplayId;
+import com.android.systemui.doze.DozeLog;
+import com.android.systemui.fragments.FragmentHostManager;
+import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.HomeControlsPlugin;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
+import com.android.systemui.qs.QSFragment;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.FlingAnimationUtils;
+import com.android.systemui.statusbar.GestureRecorder;
+import com.android.systemui.statusbar.KeyguardAffordanceView;
+import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationShelf;
+import com.android.systemui.statusbar.PulseExpansionHandler;
+import com.android.systemui.statusbar.RemoteInputController;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
+import com.android.systemui.statusbar.notification.AnimatableProperty;
+import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.PropertyAnimator;
+import com.android.systemui.statusbar.notification.ViewGroupFadeHelper;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
+import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
+import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.util.InjectionInflationController;
+import com.android.systemui.util.Utils;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+import javax.inject.Inject;
+
+@StatusBarComponent.StatusBarScope
+public class NotificationPanelViewController extends PanelViewController {
+
+ private static final boolean DEBUG = false;
+
+ /**
+ * Fling expanding QS.
+ */
+ private static final int FLING_EXPAND = 0;
+
+ /**
+ * Fling collapsing QS, potentially stopping when QS becomes QQS.
+ */
+ private static final int FLING_COLLAPSE = 1;
+
+ /**
+ * Fling until QS is completely hidden.
+ */
+ private static final int FLING_HIDE = 2;
+ private final DozeParameters mDozeParameters;
+ private final OnHeightChangedListener mOnHeightChangedListener = new OnHeightChangedListener();
+ private final OnClickListener mOnClickListener = new OnClickListener();
+ private final OnOverscrollTopChangedListener
+ mOnOverscrollTopChangedListener =
+ new OnOverscrollTopChangedListener();
+ private final KeyguardAffordanceHelperCallback
+ mKeyguardAffordanceHelperCallback =
+ new KeyguardAffordanceHelperCallback();
+ private final OnEmptySpaceClickListener
+ mOnEmptySpaceClickListener =
+ new OnEmptySpaceClickListener();
+ private final MyOnHeadsUpChangedListener
+ mOnHeadsUpChangedListener =
+ new MyOnHeadsUpChangedListener();
+ private final HeightListener mHeightListener = new HeightListener();
+ private final ZenModeControllerCallback
+ mZenModeControllerCallback =
+ new ZenModeControllerCallback();
+ private final ConfigurationListener mConfigurationListener = new ConfigurationListener();
+ private final StatusBarStateListener mStatusBarStateListener = new StatusBarStateListener();
+ private final ExpansionCallback mExpansionCallback = new ExpansionCallback();
+ private final NotificationPanelView mView;
+ private final MetricsLogger mMetricsLogger;
+ private final ActivityManager mActivityManager;
+ private final ZenModeController mZenModeController;
+ private final ConfigurationController mConfigurationController;
+ private final FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder;
+
+ private double mQqsSplitFraction;
+
+ // Cap and total height of Roboto font. Needs to be adjusted when font for the big clock is
+ // changed.
+ private static final int CAP_HEIGHT = 1456;
+ private static final int FONT_HEIGHT = 2163;
+
+ /**
+ * Maximum time before which we will expand the panel even for slow motions when getting a
+ * touch passed over from launcher.
+ */
+ private static final int MAX_TIME_TO_OPEN_WHEN_FLINGING_FROM_LAUNCHER = 300;
+
+ private static final String COUNTER_PANEL_OPEN = "panel_open";
+ private static final String COUNTER_PANEL_OPEN_QS = "panel_open_qs";
+ private static final String COUNTER_PANEL_OPEN_PEEK = "panel_open_peek";
+
+ private static final Rect M_DUMMY_DIRTY_RECT = new Rect(0, 0, 1, 1);
+ private static final Rect EMPTY_RECT = new Rect();
+
+ private static final AnimationProperties
+ CLOCK_ANIMATION_PROPERTIES =
+ new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ private final AnimatableProperty KEYGUARD_HEADS_UP_SHOWING_AMOUNT = AnimatableProperty.from(
+ "KEYGUARD_HEADS_UP_SHOWING_AMOUNT",
+ (notificationPanelView, aFloat) -> setKeyguardHeadsUpShowingAmount(aFloat),
+ (Function<NotificationPanelView, Float>) notificationPanelView ->
+ getKeyguardHeadsUpShowingAmount(),
+ R.id.keyguard_hun_animator_tag, R.id.keyguard_hun_animator_end_tag,
+ R.id.keyguard_hun_animator_start_tag);
+ private static final AnimationProperties
+ KEYGUARD_HUN_PROPERTIES =
+ new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ @VisibleForTesting
+ final KeyguardUpdateMonitorCallback
+ mKeyguardUpdateCallback =
+ new KeyguardUpdateMonitorCallback() {
+
+ @Override
+ public void onBiometricAuthenticated(int userId,
+ BiometricSourceType biometricSourceType) {
+ if (mFirstBypassAttempt && mUpdateMonitor.isUnlockingWithBiometricAllowed()) {
+ mDelayShowingKeyguardStatusBar = true;
+ }
+ }
+
+ @Override
+ public void onBiometricRunningStateChanged(boolean running,
+ BiometricSourceType biometricSourceType) {
+ boolean
+ keyguardOrShadeLocked =
+ mBarState == StatusBarState.KEYGUARD
+ || mBarState == StatusBarState.SHADE_LOCKED;
+ if (!running && mFirstBypassAttempt && keyguardOrShadeLocked && !mDozing
+ && !mDelayShowingKeyguardStatusBar) {
+ mFirstBypassAttempt = false;
+ animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ }
+ }
+
+ @Override
+ public void onFinishedGoingToSleep(int why) {
+ mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
+ mDelayShowingKeyguardStatusBar = false;
+ }
+ };
+
+ private final InjectionInflationController mInjectionInflationController;
+ private final PowerManager mPowerManager;
+ private final AccessibilityManager mAccessibilityManager;
+ private final NotificationWakeUpCoordinator mWakeUpCoordinator;
+ private final PulseExpansionHandler mPulseExpansionHandler;
+ private final KeyguardBypassController mKeyguardBypassController;
+ private final KeyguardUpdateMonitor mUpdateMonitor;
+
+ private KeyguardAffordanceHelper mAffordanceHelper;
+ private KeyguardUserSwitcher mKeyguardUserSwitcher;
+ private KeyguardStatusBarView mKeyguardStatusBar;
+ private ViewGroup mBigClockContainer;
+ private QS mQs;
+ private FrameLayout mQsFrame;
+ private KeyguardStatusView mKeyguardStatusView;
+ private View mQsNavbarScrim;
+ private NotificationsQuickSettingsContainer mNotificationContainerParent;
+ private NotificationStackScrollLayout mNotificationStackScroller;
+ private FrameLayout mHomeControlsLayout;
+ private boolean mAnimateNextPositionUpdate;
+
+ private int mTrackingPointer;
+ private VelocityTracker mQsVelocityTracker;
+ private boolean mQsTracking;
+
+ /**
+ * If set, the ongoing touch gesture might both trigger the expansion in {@link PanelView} and
+ * the expansion for quick settings.
+ */
+ private boolean mConflictingQsExpansionGesture;
+
+ private boolean mPanelExpanded;
+ private boolean mQsExpanded;
+ private boolean mQsExpandedWhenExpandingStarted;
+ private boolean mQsFullyExpanded;
+ private boolean mKeyguardShowing;
+ private boolean mDozing;
+ private boolean mDozingOnDown;
+ private int mBarState;
+ private float mInitialHeightOnTouch;
+ private float mInitialTouchX;
+ private float mInitialTouchY;
+ private float mQsExpansionHeight;
+ private int mQsMinExpansionHeight;
+ private int mQsMaxExpansionHeight;
+ private int mQsPeekHeight;
+ private boolean mStackScrollerOverscrolling;
+ private boolean mQsExpansionFromOverscroll;
+ private float mLastOverscroll;
+ private boolean mQsExpansionEnabled = true;
+ private ValueAnimator mQsExpansionAnimator;
+ private FlingAnimationUtils mFlingAnimationUtils;
+ private int mStatusBarMinHeight;
+ private int mNotificationsHeaderCollideDistance;
+ private float mEmptyDragAmount;
+ private float mDownX;
+ private float mDownY;
+
+ private final KeyguardClockPositionAlgorithm
+ mClockPositionAlgorithm =
+ new KeyguardClockPositionAlgorithm();
+ private final KeyguardClockPositionAlgorithm.Result
+ mClockPositionResult =
+ new KeyguardClockPositionAlgorithm.Result();
+ private boolean mIsExpanding;
+
+ private boolean mBlockTouches;
+ // Used for two finger gesture as well as accessibility shortcut to QS.
+ private boolean mQsExpandImmediate;
+ private boolean mTwoFingerQsExpandPossible;
+
+ /**
+ * If we are in a panel collapsing motion, we reset scrollY of our scroll view but still
+ * need to take this into account in our panel height calculation.
+ */
+ private boolean mQsAnimatorExpand;
+ private boolean mIsLaunchTransitionFinished;
+ private boolean mIsLaunchTransitionRunning;
+ private Runnable mLaunchAnimationEndRunnable;
+ private boolean mOnlyAffordanceInThisMotion;
+ private boolean mKeyguardStatusViewAnimating;
+ private ValueAnimator mQsSizeChangeAnimator;
+
+ private boolean mShowEmptyShadeView;
+
+ private boolean mQsScrimEnabled = true;
+ private boolean mQsTouchAboveFalsingThreshold;
+ private int mQsFalsingThreshold;
+
+ private float mKeyguardStatusBarAnimateAlpha = 1f;
+ private HeadsUpTouchHelper mHeadsUpTouchHelper;
+ private boolean mListenForHeadsUp;
+ private int mNavigationBarBottomHeight;
+ private boolean mExpandingFromHeadsUp;
+ private boolean mCollapsedOnDown;
+ private int mPositionMinSideMargin;
+ private int mLastOrientation = -1;
+ private boolean mClosingWithAlphaFadeOut;
+ private boolean mHeadsUpAnimatingAway;
+ private boolean mLaunchingAffordance;
+ private boolean mAffordanceHasPreview;
+ private FalsingManager mFalsingManager;
+ private String mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
+
+ private Runnable mHeadsUpExistenceChangedRunnable = () -> {
+ setHeadsUpAnimatingAway(false);
+ notifyBarPanelExpansionChanged();
+ };
+ private NotificationGroupManager mGroupManager;
+ private boolean mShowIconsWhenExpanded;
+ private int mIndicationBottomPadding;
+ private int mAmbientIndicationBottomPadding;
+ private boolean mIsFullWidth;
+ private boolean mBlockingExpansionForCurrentTouch;
+
+ /**
+ * Following variables maintain state of events when input focus transfer may occur.
+ */
+ private boolean mExpectingSynthesizedDown; // expecting to see synthesized DOWN event
+ private boolean mLastEventSynthesizedDown; // last event was synthesized DOWN event
+
+ /**
+ * Current dark amount that follows regular interpolation curve of animation.
+ */
+ private float mInterpolatedDarkAmount;
+
+ /**
+ * Dark amount that animates from 0 to 1 or vice-versa in linear manner, even if the
+ * interpolation curve is different.
+ */
+ private float mLinearDarkAmount;
+
+ private boolean mPulsing;
+ private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
+ private boolean mUserSetupComplete;
+ private int mQsNotificationTopPadding;
+ private float mExpandOffset;
+ private boolean mHideIconsDuringNotificationLaunch = true;
+ private int mStackScrollerMeasuringPass;
+ private ArrayList<Consumer<ExpandableNotificationRow>>
+ mTrackingHeadsUpListeners =
+ new ArrayList<>();
+ private ArrayList<Runnable> mVerticalTranslationListener = new ArrayList<>();
+ private HeadsUpAppearanceController mHeadsUpAppearanceController;
+
+ private int mPanelAlpha;
+ private Runnable mPanelAlphaEndAction;
+ private float mBottomAreaShadeAlpha;
+ private final ValueAnimator mBottomAreaShadeAlphaAnimator;
+ private AnimatorListenerAdapter mAnimatorListenerAdapter = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mPanelAlphaEndAction != null) {
+ mPanelAlphaEndAction.run();
+ }
+ }
+ };
+ private final AnimatableProperty mPanelAlphaAnimator = AnimatableProperty.from("panelAlpha",
+ NotificationPanelView::setPanelAlphaInternal,
+ NotificationPanelView::getCurrentPanelAlpha,
+ R.id.panel_alpha_animator_tag, R.id.panel_alpha_animator_start_tag,
+ R.id.panel_alpha_animator_end_tag);
+ private final AnimationProperties mPanelAlphaOutPropertiesAnimator =
+ new AnimationProperties().setDuration(150).setCustomInterpolator(
+ mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_OUT);
+ private final AnimationProperties mPanelAlphaInPropertiesAnimator =
+ new AnimationProperties().setDuration(200).setAnimationFinishListener(
+ mAnimatorListenerAdapter).setCustomInterpolator(
+ mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_IN);
+ private final NotificationEntryManager mEntryManager;
+
+ private final CommandQueue mCommandQueue;
+ private final NotificationLockscreenUserManager mLockscreenUserManager;
+ private final ShadeController mShadeController;
+ private int mDisplayId;
+
+ /**
+ * Cache the resource id of the theme to avoid unnecessary work in onThemeChanged.
+ *
+ * onThemeChanged is forced when the theme might not have changed. So, to avoid unncessary
+ * work, check the current id with the cached id.
+ */
+ private int mThemeResId;
+ private KeyguardIndicationController mKeyguardIndicationController;
+ private Consumer<Boolean> mAffordanceLaunchListener;
+ private int mShelfHeight;
+ private Runnable mOnReinflationListener;
+ private int mDarkIconSize;
+ private int mHeadsUpInset;
+ private boolean mHeadsUpPinnedMode;
+ private float mKeyguardHeadsUpShowingAmount = 0.0f;
+ private boolean mShowingKeyguardHeadsUp;
+ private boolean mAllowExpandForSmallExpansion;
+ private Runnable mExpandAfterLayoutRunnable;
+
+ /**
+ * If face auth with bypass is running for the first time after you turn on the screen.
+ * (From aod or screen off)
+ */
+ private boolean mFirstBypassAttempt;
+ /**
+ * If auth happens successfully during {@code mFirstBypassAttempt}, and we should wait until
+ * the keyguard is dismissed to show the status bar.
+ */
+ private boolean mDelayShowingKeyguardStatusBar;
+
+ private PluginManager mPluginManager;
+ private FrameLayout mPluginFrame;
+ private NPVPluginManager mNPVPluginManager;
+ private int mOldLayoutDirection;
+
+ @Inject
+ public NotificationPanelViewController(NotificationPanelView view,
+ InjectionInflationController injectionInflationController,
+ NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler,
+ DynamicPrivacyController dynamicPrivacyController,
+ KeyguardBypassController bypassController, FalsingManager falsingManager,
+ PluginManager pluginManager, ShadeController shadeController,
+ NotificationLockscreenUserManager notificationLockscreenUserManager,
+ NotificationEntryManager notificationEntryManager,
+ KeyguardStateController keyguardStateController,
+ StatusBarStateController statusBarStateController, DozeLog dozeLog,
+ DozeParameters dozeParameters, CommandQueue commandQueue, VibratorHelper vibratorHelper,
+ LatencyTracker latencyTracker, PowerManager powerManager,
+ AccessibilityManager accessibilityManager, @DisplayId int displayId,
+ KeyguardUpdateMonitor keyguardUpdateMonitor, MetricsLogger metricsLogger,
+ ActivityManager activityManager, ZenModeController zenModeController,
+ ConfigurationController configurationController,
+ FlingAnimationUtils.Builder flingAnimationUtilsBuilder) {
+ super(view, falsingManager, dozeLog, keyguardStateController,
+ (SysuiStatusBarStateController) statusBarStateController, vibratorHelper,
+ latencyTracker, flingAnimationUtilsBuilder);
+ mView = view;
+ mMetricsLogger = metricsLogger;
+ mActivityManager = activityManager;
+ mZenModeController = zenModeController;
+ mConfigurationController = configurationController;
+ mFlingAnimationUtilsBuilder = flingAnimationUtilsBuilder;
+ mView.setWillNotDraw(!DEBUG);
+ mInjectionInflationController = injectionInflationController;
+ mFalsingManager = falsingManager;
+ mPowerManager = powerManager;
+ mWakeUpCoordinator = coordinator;
+ mAccessibilityManager = accessibilityManager;
+ mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
+ setPanelAlpha(255, false /* animate */);
+ mCommandQueue = commandQueue;
+ mDisplayId = displayId;
+ mPulseExpansionHandler = pulseExpansionHandler;
+ mDozeParameters = dozeParameters;
+ pulseExpansionHandler.setPulseExpandAbortListener(() -> {
+ if (mQs != null) {
+ mQs.animateHeaderSlidingOut();
+ }
+ });
+ mThemeResId = mView.getContext().getThemeResId();
+ mKeyguardBypassController = bypassController;
+ mUpdateMonitor = keyguardUpdateMonitor;
+ mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
+ KeyguardStateController.Callback
+ keyguardMonitorCallback =
+ new KeyguardStateController.Callback() {
+ @Override
+ public void onKeyguardFadingAwayChanged() {
+ if (!mKeyguardStateController.isKeyguardFadingAway()) {
+ mFirstBypassAttempt = false;
+ mDelayShowingKeyguardStatusBar = false;
+ }
+ }
+ };
+ mKeyguardStateController.addCallback(keyguardMonitorCallback);
+ DynamicPrivacyControlListener
+ dynamicPrivacyControlListener =
+ new DynamicPrivacyControlListener();
+ dynamicPrivacyController.addListener(dynamicPrivacyControlListener);
+
+ mBottomAreaShadeAlphaAnimator = ValueAnimator.ofFloat(1f, 0);
+ mBottomAreaShadeAlphaAnimator.addUpdateListener(animation -> {
+ mBottomAreaShadeAlpha = (float) animation.getAnimatedValue();
+ updateKeyguardBottomAreaAlpha();
+ });
+ mBottomAreaShadeAlphaAnimator.setDuration(160);
+ mBottomAreaShadeAlphaAnimator.setInterpolator(Interpolators.ALPHA_OUT);
+ mPluginManager = pluginManager;
+ mShadeController = shadeController;
+ mLockscreenUserManager = notificationLockscreenUserManager;
+ mEntryManager = notificationEntryManager;
+
+ mView.setBackgroundColor(Color.TRANSPARENT);
+ OnAttachStateChangeListener onAttachStateChangeListener = new OnAttachStateChangeListener();
+ mView.addOnAttachStateChangeListener(onAttachStateChangeListener);
+ if (mView.isAttachedToWindow()) {
+ onAttachStateChangeListener.onViewAttachedToWindow(mView);
+ }
+
+ mView.setOnApplyWindowInsetsListener(new OnApplyWindowInsetsListener());
+
+ if (DEBUG) {
+ mView.getOverlay().add(new DebugDrawable());
+ }
+
+ onFinishInflate();
+ }
+
+ private void onFinishInflate() {
+ loadDimens();
+ mKeyguardStatusBar = mView.findViewById(R.id.keyguard_header);
+ mKeyguardStatusView = mView.findViewById(R.id.keyguard_status_view);
+
+ KeyguardClockSwitch keyguardClockSwitch = mView.findViewById(R.id.keyguard_clock_container);
+ mBigClockContainer = mView.findViewById(R.id.big_clock_container);
+ keyguardClockSwitch.setBigClockContainer(mBigClockContainer);
+
+ mHomeControlsLayout = mView.findViewById(R.id.home_controls_layout);
+ mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent);
+ mNotificationStackScroller = mView.findViewById(R.id.notification_stack_scroller);
+ mNotificationStackScroller.setOnHeightChangedListener(mOnHeightChangedListener);
+ mNotificationStackScroller.setOverscrollTopChangedListener(mOnOverscrollTopChangedListener);
+ mNotificationStackScroller.setOnEmptySpaceClickListener(mOnEmptySpaceClickListener);
+ addTrackingHeadsUpListener(mNotificationStackScroller::setTrackingHeadsUp);
+ mKeyguardBottomArea = mView.findViewById(R.id.keyguard_bottom_area);
+ mQsNavbarScrim = mView.findViewById(R.id.qs_navbar_scrim);
+ mLastOrientation = mResources.getConfiguration().orientation;
+ mPluginFrame = mView.findViewById(R.id.plugin_frame);
+ if (Settings.System.getInt(mView.getContext().getContentResolver(), "npv_plugin_flag", 0)
+ == 1) {
+ mNPVPluginManager = new NPVPluginManager(mPluginFrame, mPluginManager);
+ }
+
+
+ initBottomArea();
+
+ mWakeUpCoordinator.setStackScroller(mNotificationStackScroller);
+ mQsFrame = mView.findViewById(R.id.qs_frame);
+ mPulseExpansionHandler.setUp(
+ mNotificationStackScroller, mExpansionCallback, mShadeController);
+ mWakeUpCoordinator.addListener(new NotificationWakeUpCoordinator.WakeUpListener() {
+ @Override
+ public void onFullyHiddenChanged(boolean isFullyHidden) {
+ updateKeyguardStatusBarForHeadsUp();
+ }
+
+ @Override
+ public void onPulseExpansionChanged(boolean expandingChanged) {
+ if (mKeyguardBypassController.getBypassEnabled()) {
+ // Position the notifications while dragging down while pulsing
+ requestScrollerTopPaddingUpdate(false /* animate */);
+ updateQSPulseExpansion();
+ }
+ }
+ });
+
+ mPluginManager.addPluginListener(new PluginListener<HomeControlsPlugin>() {
+
+ @Override
+ public void onPluginConnected(HomeControlsPlugin plugin, Context pluginContext) {
+ plugin.sendParentGroup(mHomeControlsLayout);
+ }
+
+ @Override
+ public void onPluginDisconnected(HomeControlsPlugin plugin) {
+
+ }
+ }, HomeControlsPlugin.class, false);
+
+ mView.setRtlChangeListener(layoutDirection -> {
+ if (layoutDirection != mOldLayoutDirection) {
+ mAffordanceHelper.onRtlPropertiesChanged();
+ mOldLayoutDirection = layoutDirection;
+ }
+ });
+ }
+
+ @Override
+ protected void loadDimens() {
+ super.loadDimens();
+ mFlingAnimationUtils = mFlingAnimationUtilsBuilder.reset()
+ .setMaxLengthSeconds(0.4f).build();
+ mStatusBarMinHeight = mResources.getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height);
+ mQsPeekHeight = mResources.getDimensionPixelSize(R.dimen.qs_peek_height);
+ mNotificationsHeaderCollideDistance = mResources.getDimensionPixelSize(
+ R.dimen.header_notifications_collide_distance);
+ mClockPositionAlgorithm.loadDimens(mResources);
+ mQsFalsingThreshold = mResources.getDimensionPixelSize(R.dimen.qs_falsing_threshold);
+ mPositionMinSideMargin = mResources.getDimensionPixelSize(
+ R.dimen.notification_panel_min_side_margin);
+ mIndicationBottomPadding = mResources.getDimensionPixelSize(
+ R.dimen.keyguard_indication_bottom_padding);
+ mQsNotificationTopPadding = mResources.getDimensionPixelSize(
+ R.dimen.qs_notification_padding);
+ mShelfHeight = mResources.getDimensionPixelSize(R.dimen.notification_shelf_height);
+ mDarkIconSize = mResources.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size_dark);
+ int statusbarHeight = mResources.getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height);
+ mHeadsUpInset = statusbarHeight + mResources.getDimensionPixelSize(
+ R.dimen.heads_up_status_bar_padding);
+ mQqsSplitFraction = ((float) mResources.getInteger(R.integer.qqs_split_fraction)) / (
+ mResources.getInteger(R.integer.qqs_split_fraction) + mResources.getInteger(
+ R.integer.qs_split_fraction));
+ }
+
+ /**
+ * Returns if there's a custom clock being presented.
+ */
+ public boolean hasCustomClock() {
+ return mKeyguardStatusView.hasCustomClock();
+ }
+
+ private void setStatusBar(StatusBar bar) {
+ // TODO: this can be injected.
+ mStatusBar = bar;
+ mKeyguardBottomArea.setStatusBar(mStatusBar);
+ }
+ /**
+ * @see #launchCamera(boolean, int)
+ * @see #setLaunchingAffordance(boolean)
+ */
+ public void setLaunchAffordanceListener(Consumer<Boolean> listener) {
+ mAffordanceLaunchListener = listener;
+ }
+
+ public void updateResources() {
+ int qsWidth = mResources.getDimensionPixelSize(R.dimen.qs_panel_width);
+ int panelGravity = mResources.getInteger(R.integer.notification_panel_layout_gravity);
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mQsFrame.getLayoutParams();
+ if (lp.width != qsWidth || lp.gravity != panelGravity) {
+ lp.width = qsWidth;
+ lp.gravity = panelGravity;
+ mQsFrame.setLayoutParams(lp);
+ }
+
+ int panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width);
+ lp = (FrameLayout.LayoutParams) mNotificationStackScroller.getLayoutParams();
+ if (lp.width != panelWidth || lp.gravity != panelGravity) {
+ lp.width = panelWidth;
+ lp.gravity = panelGravity;
+ mNotificationStackScroller.setLayoutParams(lp);
+ }
+ int sideMargin = mResources.getDimensionPixelOffset(R.dimen.notification_side_paddings);
+ int topMargin = sideMargin;
+ lp = (FrameLayout.LayoutParams) mPluginFrame.getLayoutParams();
+ if (lp.width != qsWidth || lp.gravity != panelGravity || lp.leftMargin != sideMargin
+ || lp.rightMargin != sideMargin || lp.topMargin != topMargin) {
+ lp.width = qsWidth;
+ lp.gravity = panelGravity;
+ lp.leftMargin = sideMargin;
+ lp.rightMargin = sideMargin;
+ lp.topMargin = topMargin;
+ mPluginFrame.setLayoutParams(lp);
+ }
+ }
+
+ private void reInflateViews() {
+ updateShowEmptyShadeView();
+
+ // Re-inflate the status view group.
+ int index = mView.indexOfChild(mKeyguardStatusView);
+ mView.removeView(mKeyguardStatusView);
+ mKeyguardStatusView = (KeyguardStatusView) mInjectionInflationController.injectable(
+ LayoutInflater.from(mView.getContext())).inflate(
+ R.layout.keyguard_status_view, mView, false);
+ mView.addView(mKeyguardStatusView, index);
+
+ // Re-associate the clock container with the keyguard clock switch.
+ mBigClockContainer.removeAllViews();
+ KeyguardClockSwitch keyguardClockSwitch = mView.findViewById(R.id.keyguard_clock_container);
+ keyguardClockSwitch.setBigClockContainer(mBigClockContainer);
+
+ // Update keyguard bottom area
+ index = mView.indexOfChild(mKeyguardBottomArea);
+ mView.removeView(mKeyguardBottomArea);
+ KeyguardBottomAreaView oldBottomArea = mKeyguardBottomArea;
+ mKeyguardBottomArea = (KeyguardBottomAreaView) mInjectionInflationController.injectable(
+ LayoutInflater.from(mView.getContext())).inflate(
+ R.layout.keyguard_bottom_area, mView, false);
+ mKeyguardBottomArea.initFrom(oldBottomArea);
+ mView.addView(mKeyguardBottomArea, index);
+ initBottomArea();
+ mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea);
+ mStatusBarStateListener.onDozeAmountChanged(mStatusBarStateController.getDozeAmount(),
+ mStatusBarStateController.getInterpolatedDozeAmount());
+
+ if (mKeyguardStatusBar != null) {
+ mKeyguardStatusBar.onThemeChanged();
+ }
+
+ setKeyguardStatusViewVisibility(mBarState, false, false);
+ setKeyguardBottomAreaVisibility(mBarState, false);
+ if (mOnReinflationListener != null) {
+ mOnReinflationListener.run();
+ }
+ reinflatePluginContainer();
+ }
+
+ private void reinflatePluginContainer() {
+ int index = mView.indexOfChild(mPluginFrame);
+ mView.removeView(mPluginFrame);
+ mPluginFrame = (FrameLayout) mInjectionInflationController.injectable(
+ LayoutInflater.from(mView.getContext())).inflate(
+ R.layout.status_bar_expanded_plugin_frame, mView, false);
+ mView.addView(mPluginFrame, index);
+
+ Resources res = mView.getResources();
+ int qsWidth = res.getDimensionPixelSize(R.dimen.qs_panel_width);
+ int panelGravity = mView.getResources().getInteger(
+ R.integer.notification_panel_layout_gravity);
+ FrameLayout.LayoutParams lp;
+ int sideMargin = res.getDimensionPixelOffset(R.dimen.notification_side_paddings);
+ int topMargin = res.getDimensionPixelOffset(
+ com.android.internal.R.dimen.quick_qs_total_height);
+ if (Utils.useQsMediaPlayer(mView.getContext())) {
+ topMargin = res.getDimensionPixelOffset(
+ com.android.internal.R.dimen.quick_qs_total_height_with_media);
+ }
+ lp = (FrameLayout.LayoutParams) mPluginFrame.getLayoutParams();
+ if (lp.width != qsWidth || lp.gravity != panelGravity || lp.leftMargin != sideMargin
+ || lp.rightMargin != sideMargin || lp.topMargin != topMargin) {
+ lp.width = qsWidth;
+ lp.gravity = panelGravity;
+ lp.leftMargin = sideMargin;
+ lp.rightMargin = sideMargin;
+ lp.topMargin = topMargin;
+ mPluginFrame.setLayoutParams(lp);
+ }
+
+ if (mNPVPluginManager != null) mNPVPluginManager.replaceFrameLayout(mPluginFrame);
+ }
+
+ private void initBottomArea() {
+ mAffordanceHelper = new KeyguardAffordanceHelper(
+ mKeyguardAffordanceHelperCallback, mView.getContext(), mFalsingManager);
+ mKeyguardBottomArea.setAffordanceHelper(mAffordanceHelper);
+ mKeyguardBottomArea.setStatusBar(mStatusBar);
+ mKeyguardBottomArea.setUserSetupComplete(mUserSetupComplete);
+ }
+
+ public void setKeyguardIndicationController(KeyguardIndicationController indicationController) {
+ mKeyguardIndicationController = indicationController;
+ mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea);
+ }
+
+ private void updateGestureExclusionRect() {
+ Rect exclusionRect = calculateGestureExclusionRect();
+ mView.setSystemGestureExclusionRects(exclusionRect.isEmpty() ? Collections.EMPTY_LIST
+ : Collections.singletonList(exclusionRect));
+ }
+
+ private Rect calculateGestureExclusionRect() {
+ Rect exclusionRect = null;
+ Region touchableRegion = mHeadsUpManager.calculateTouchableRegion();
+ if (isFullyCollapsed() && touchableRegion != null) {
+ // Note: The heads up manager also calculates the non-pinned touchable region
+ exclusionRect = touchableRegion.getBounds();
+ }
+ return exclusionRect != null ? exclusionRect : EMPTY_RECT;
+ }
+
+ private void setIsFullWidth(boolean isFullWidth) {
+ mIsFullWidth = isFullWidth;
+ mNotificationStackScroller.setIsFullWidth(isFullWidth);
+ }
+
+ private void startQsSizeChangeAnimation(int oldHeight, final int newHeight) {
+ if (mQsSizeChangeAnimator != null) {
+ oldHeight = (int) mQsSizeChangeAnimator.getAnimatedValue();
+ mQsSizeChangeAnimator.cancel();
+ }
+ mQsSizeChangeAnimator = ValueAnimator.ofInt(oldHeight, newHeight);
+ mQsSizeChangeAnimator.setDuration(300);
+ mQsSizeChangeAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ mQsSizeChangeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ requestScrollerTopPaddingUpdate(false /* animate */);
+ requestPanelHeightUpdate();
+ int height = (int) mQsSizeChangeAnimator.getAnimatedValue();
+ mQs.setHeightOverride(height);
+ }
+ });
+ mQsSizeChangeAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mQsSizeChangeAnimator = null;
+ }
+ });
+ mQsSizeChangeAnimator.start();
+ }
+
+ /**
+ * Positions the clock and notifications dynamically depending on how many notifications are
+ * showing.
+ */
+ private void positionClockAndNotifications() {
+ boolean animate = mNotificationStackScroller.isAddOrRemoveAnimationPending();
+ boolean animateClock = animate || mAnimateNextPositionUpdate;
+ int stackScrollerPadding;
+ if (mBarState != StatusBarState.KEYGUARD) {
+ stackScrollerPadding = getUnlockedStackScrollerPadding();
+ } else {
+ int totalHeight = mView.getHeight();
+ int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
+ int clockPreferredY = mKeyguardStatusView.getClockPreferredY(totalHeight);
+ boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
+ final boolean
+ hasVisibleNotifications =
+ !bypassEnabled && mNotificationStackScroller.getVisibleNotificationCount() != 0;
+ mKeyguardStatusView.setHasVisibleNotifications(hasVisibleNotifications);
+ mClockPositionAlgorithm.setup(mStatusBarMinHeight, totalHeight - bottomPadding,
+ mNotificationStackScroller.getIntrinsicContentHeight(), getExpandedFraction(),
+ totalHeight, (int) (mKeyguardStatusView.getHeight() - mShelfHeight / 2.0f
+ - mDarkIconSize / 2.0f), clockPreferredY, hasCustomClock(),
+ hasVisibleNotifications, mInterpolatedDarkAmount, mEmptyDragAmount,
+ bypassEnabled, getUnlockedStackScrollerPadding());
+ mClockPositionAlgorithm.run(mClockPositionResult);
+ PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.X,
+ mClockPositionResult.clockX, CLOCK_ANIMATION_PROPERTIES, animateClock);
+ PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.Y,
+ mClockPositionResult.clockY, CLOCK_ANIMATION_PROPERTIES, animateClock);
+ updateNotificationTranslucency();
+ updateClock();
+ stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded;
+ }
+ mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding);
+ mKeyguardBottomArea.setAntiBurnInOffsetX(mClockPositionResult.clockX);
+
+ mStackScrollerMeasuringPass++;
+ requestScrollerTopPaddingUpdate(animate);
+ mStackScrollerMeasuringPass = 0;
+ mAnimateNextPositionUpdate = false;
+ }
+
+ /**
+ * @return the padding of the stackscroller when unlocked
+ */
+ private int getUnlockedStackScrollerPadding() {
+ return (mQs != null ? mQs.getHeader().getHeight() : 0) + mQsPeekHeight
+ + mQsNotificationTopPadding;
+ }
+
+ /**
+ * @param maximum the maximum to return at most
+ * @return the maximum keyguard notifications that can fit on the screen
+ */
+ public int computeMaxKeyguardNotifications(int maximum) {
+ float minPadding = mClockPositionAlgorithm.getMinStackScrollerPadding();
+ int notificationPadding = Math.max(
+ 1, mResources.getDimensionPixelSize(R.dimen.notification_divider_height));
+ NotificationShelf shelf = mNotificationStackScroller.getNotificationShelf();
+ float
+ shelfSize =
+ shelf.getVisibility() == View.GONE ? 0
+ : shelf.getIntrinsicHeight() + notificationPadding;
+ float
+ availableSpace =
+ mNotificationStackScroller.getHeight() - minPadding - shelfSize - Math.max(
+ mIndicationBottomPadding, mAmbientIndicationBottomPadding)
+ - mKeyguardStatusView.getLogoutButtonHeight();
+ int count = 0;
+ for (int i = 0; i < mNotificationStackScroller.getChildCount(); i++) {
+ ExpandableView child = (ExpandableView) mNotificationStackScroller.getChildAt(i);
+ if (!(child instanceof ExpandableNotificationRow)) {
+ continue;
+ }
+ ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ boolean
+ suppressedSummary =
+ mGroupManager != null && mGroupManager.isSummaryOfSuppressedGroup(
+ row.getEntry().getSbn());
+ if (suppressedSummary) {
+ continue;
+ }
+ if (!mLockscreenUserManager.shouldShowOnKeyguard(row.getEntry())) {
+ continue;
+ }
+ if (row.isRemoved()) {
+ continue;
+ }
+ availableSpace -= child.getMinHeight(true /* ignoreTemporaryStates */)
+ + notificationPadding;
+ if (availableSpace >= 0 && count < maximum) {
+ count++;
+ } else if (availableSpace > -shelfSize) {
+ // if we are exactly the last view, then we can show us still!
+ for (int j = i + 1; j < mNotificationStackScroller.getChildCount(); j++) {
+ if (mNotificationStackScroller.getChildAt(
+ j) instanceof ExpandableNotificationRow) {
+ return count;
+ }
+ }
+ count++;
+ return count;
+ } else {
+ return count;
+ }
+ }
+ return count;
+ }
+
+ private void updateClock() {
+ if (!mKeyguardStatusViewAnimating) {
+ mKeyguardStatusView.setAlpha(mClockPositionResult.clockAlpha);
+ }
+ }
+
+ public void animateToFullShade(long delay) {
+ mNotificationStackScroller.goToFullShade(delay);
+ mView.requestLayout();
+ mAnimateNextPositionUpdate = true;
+ }
+
+ public void setQsExpansionEnabled(boolean qsExpansionEnabled) {
+ mQsExpansionEnabled = qsExpansionEnabled;
+ if (mQs == null) return;
+ mQs.setHeaderClickable(qsExpansionEnabled);
+ }
+
+ @Override
+ public void resetViews(boolean animate) {
+ mIsLaunchTransitionFinished = false;
+ mBlockTouches = false;
+ if (!mLaunchingAffordance) {
+ mAffordanceHelper.reset(false);
+ mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
+ }
+ mStatusBar.getGutsManager().closeAndSaveGuts(true /* leavebehind */, true /* force */,
+ true /* controls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
+ if (animate) {
+ animateCloseQs(true /* animateAway */);
+ } else {
+ closeQs();
+ }
+ mNotificationStackScroller.setOverScrollAmount(0f, true /* onTop */, animate,
+ !animate /* cancelAnimators */);
+ mNotificationStackScroller.resetScrollPosition();
+ }
+
+ @Override
+ public void collapse(boolean delayed, float speedUpFactor) {
+ if (!canPanelBeCollapsed()) {
+ return;
+ }
+
+ if (mQsExpanded) {
+ mQsExpandImmediate = true;
+ mNotificationStackScroller.setShouldShowShelfOnly(true);
+ }
+ super.collapse(delayed, speedUpFactor);
+ }
+
+ public void closeQs() {
+ cancelQsAnimation();
+ setQsExpansion(mQsMinExpansionHeight);
+ }
+
+ public void cancelAnimation() {
+ mView.animate().cancel();
+ }
+
+
+ /**
+ * Animate QS closing by flinging it.
+ * If QS is expanded, it will collapse into QQS and stop.
+ *
+ * @param animateAway Do not stop when QS becomes QQS. Fling until QS isn't visible anymore.
+ */
+ public void animateCloseQs(boolean animateAway) {
+ if (mQsExpansionAnimator != null) {
+ if (!mQsAnimatorExpand) {
+ return;
+ }
+ float height = mQsExpansionHeight;
+ mQsExpansionAnimator.cancel();
+ setQsExpansion(height);
+ }
+ flingSettings(0 /* vel */, animateAway ? FLING_HIDE : FLING_COLLAPSE);
+ }
+
+ public void expandWithQs() {
+ if (mQsExpansionEnabled) {
+ mQsExpandImmediate = true;
+ mNotificationStackScroller.setShouldShowShelfOnly(true);
+ }
+ if (isFullyCollapsed()) {
+ expand(true /* animate */);
+ } else {
+ flingSettings(0 /* velocity */, FLING_EXPAND);
+ }
+ }
+
+ public void expandWithoutQs() {
+ if (isQsExpanded()) {
+ flingSettings(0 /* velocity */, FLING_COLLAPSE);
+ } else {
+ expand(true /* animate */);
+ }
+ }
+
+ @Override
+ public void fling(float vel, boolean expand) {
+ GestureRecorder gr = ((PhoneStatusBarView) mBar).mBar.getGestureRecorder();
+ if (gr != null) {
+ gr.tag("fling " + ((vel > 0) ? "open" : "closed"), "notifications,v=" + vel);
+ }
+ super.fling(vel, expand);
+ }
+
+ @Override
+ protected void flingToHeight(float vel, boolean expand, float target,
+ float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) {
+ mHeadsUpTouchHelper.notifyFling(!expand);
+ setClosingWithAlphaFadeout(!expand && !isOnKeyguard() && getFadeoutAlpha() == 1.0f);
+ super.flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing);
+ }
+
+
+ private boolean onQsIntercept(MotionEvent event) {
+ int pointerIndex = event.findPointerIndex(mTrackingPointer);
+ if (pointerIndex < 0) {
+ pointerIndex = 0;
+ mTrackingPointer = event.getPointerId(pointerIndex);
+ }
+ final float x = event.getX(pointerIndex);
+ final float y = event.getY(pointerIndex);
+
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ mInitialTouchY = y;
+ mInitialTouchX = x;
+ initVelocityTracker();
+ trackMovement(event);
+ if (shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) {
+ mView.getParent().requestDisallowInterceptTouchEvent(true);
+ }
+ if (mQsExpansionAnimator != null) {
+ onQsExpansionStarted();
+ mInitialHeightOnTouch = mQsExpansionHeight;
+ mQsTracking = true;
+ mNotificationStackScroller.cancelLongPress();
+ }
+ break;
+ case MotionEvent.ACTION_POINTER_UP:
+ final int upPointer = event.getPointerId(event.getActionIndex());
+ if (mTrackingPointer == upPointer) {
+ // gesture is ongoing, find a new pointer to track
+ final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
+ mTrackingPointer = event.getPointerId(newIndex);
+ mInitialTouchX = event.getX(newIndex);
+ mInitialTouchY = event.getY(newIndex);
+ }
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ final float h = y - mInitialTouchY;
+ trackMovement(event);
+ if (mQsTracking) {
+
+ // Already tracking because onOverscrolled was called. We need to update here
+ // so we don't stop for a frame until the next touch event gets handled in
+ // onTouchEvent.
+ setQsExpansion(h + mInitialHeightOnTouch);
+ trackMovement(event);
+ return true;
+ }
+ if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX)
+ && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) {
+ mQsTracking = true;
+ onQsExpansionStarted();
+ notifyExpandingFinished();
+ mInitialHeightOnTouch = mQsExpansionHeight;
+ mInitialTouchY = y;
+ mInitialTouchX = x;
+ mNotificationStackScroller.cancelLongPress();
+ return true;
+ }
+ break;
+
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ trackMovement(event);
+ if (mQsTracking) {
+ flingQsWithCurrentVelocity(y,
+ event.getActionMasked() == MotionEvent.ACTION_CANCEL);
+ mQsTracking = false;
+ }
+ break;
+ }
+ return false;
+ }
+
+ @Override
+ protected boolean isInContentBounds(float x, float y) {
+ float stackScrollerX = mNotificationStackScroller.getX();
+ return !mNotificationStackScroller.isBelowLastNotification(x - stackScrollerX, y)
+ && stackScrollerX < x && x < stackScrollerX + mNotificationStackScroller.getWidth();
+ }
+
+ private void initDownStates(MotionEvent event) {
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ mOnlyAffordanceInThisMotion = false;
+ mQsTouchAboveFalsingThreshold = mQsFullyExpanded;
+ mDozingOnDown = isDozing();
+ mDownX = event.getX();
+ mDownY = event.getY();
+ mCollapsedOnDown = isFullyCollapsed();
+ mListenForHeadsUp = mCollapsedOnDown && mHeadsUpManager.hasPinnedHeadsUp();
+ mAllowExpandForSmallExpansion = mExpectingSynthesizedDown;
+ mTouchSlopExceededBeforeDown = mExpectingSynthesizedDown;
+ if (mExpectingSynthesizedDown) {
+ mLastEventSynthesizedDown = true;
+ } else {
+ // down but not synthesized motion event.
+ mLastEventSynthesizedDown = false;
+ }
+ } else {
+ // not down event at all.
+ mLastEventSynthesizedDown = false;
+ }
+ }
+
+ private void flingQsWithCurrentVelocity(float y, boolean isCancelMotionEvent) {
+ float vel = getCurrentQSVelocity();
+ final boolean expandsQs = flingExpandsQs(vel);
+ if (expandsQs) {
+ logQsSwipeDown(y);
+ }
+ flingSettings(vel, expandsQs && !isCancelMotionEvent ? FLING_EXPAND : FLING_COLLAPSE);
+ }
+
+ private void logQsSwipeDown(float y) {
+ float vel = getCurrentQSVelocity();
+ final int
+ gesture =
+ mBarState == StatusBarState.KEYGUARD ? MetricsEvent.ACTION_LS_QS
+ : MetricsEvent.ACTION_SHADE_QS_PULL;
+ mLockscreenGestureLogger.write(gesture,
+ (int) ((y - mInitialTouchY) / mStatusBar.getDisplayDensity()),
+ (int) (vel / mStatusBar.getDisplayDensity()));
+ }
+
+ private boolean flingExpandsQs(float vel) {
+ if (mFalsingManager.isUnlockingDisabled() || isFalseTouch()) {
+ return false;
+ }
+ if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
+ return getQsExpansionFraction() > 0.5f;
+ } else {
+ return vel > 0;
+ }
+ }
+
+ private boolean isFalseTouch() {
+ if (!mKeyguardAffordanceHelperCallback.needsAntiFalsing()) {
+ return false;
+ }
+ if (mFalsingManager.isClassifierEnabled()) {
+ return mFalsingManager.isFalseTouch();
+ }
+ return !mQsTouchAboveFalsingThreshold;
+ }
+
+ private float getQsExpansionFraction() {
+ return Math.min(
+ 1f, (mQsExpansionHeight - mQsMinExpansionHeight) / (mQsMaxExpansionHeight
+ - mQsMinExpansionHeight));
+ }
+
+ @Override
+ protected boolean shouldExpandWhenNotFlinging() {
+ if (super.shouldExpandWhenNotFlinging()) {
+ return true;
+ }
+ if (mAllowExpandForSmallExpansion) {
+ // When we get a touch that came over from launcher, the velocity isn't always correct
+ // Let's err on expanding if the gesture has been reasonably slow
+ long timeSinceDown = SystemClock.uptimeMillis() - mDownTime;
+ return timeSinceDown <= MAX_TIME_TO_OPEN_WHEN_FLINGING_FROM_LAUNCHER;
+ }
+ return false;
+ }
+
+ @Override
+ protected float getOpeningHeight() {
+ return mNotificationStackScroller.getOpeningHeight();
+ }
+
+
+ private boolean handleQsTouch(MotionEvent event) {
+ final int action = event.getActionMasked();
+ if (action == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f
+ && mBarState != StatusBarState.KEYGUARD && !mQsExpanded && mQsExpansionEnabled) {
+
+ // Down in the empty area while fully expanded - go to QS.
+ mQsTracking = true;
+ mConflictingQsExpansionGesture = true;
+ onQsExpansionStarted();
+ mInitialHeightOnTouch = mQsExpansionHeight;
+ mInitialTouchY = event.getX();
+ mInitialTouchX = event.getY();
+ }
+ if (!isFullyCollapsed()) {
+ handleQsDown(event);
+ }
+ if (!mQsExpandImmediate && mQsTracking) {
+ onQsTouch(event);
+ if (!mConflictingQsExpansionGesture) {
+ return true;
+ }
+ }
+ if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
+ mConflictingQsExpansionGesture = false;
+ }
+ if (action == MotionEvent.ACTION_DOWN && isFullyCollapsed() && mQsExpansionEnabled) {
+ mTwoFingerQsExpandPossible = true;
+ }
+ if (mTwoFingerQsExpandPossible && isOpenQsEvent(event) && event.getY(event.getActionIndex())
+ < mStatusBarMinHeight) {
+ mMetricsLogger.count(COUNTER_PANEL_OPEN_QS, 1);
+ mQsExpandImmediate = true;
+ mNotificationStackScroller.setShouldShowShelfOnly(true);
+ requestPanelHeightUpdate();
+
+ // Normally, we start listening when the panel is expanded, but here we need to start
+ // earlier so the state is already up to date when dragging down.
+ setListening(true);
+ }
+ if (isQsSplitEnabled() && !mKeyguardShowing) {
+ if (mQsExpandImmediate) {
+ mNotificationStackScroller.setVisibility(View.GONE);
+ mQsFrame.setVisibility(View.VISIBLE);
+ mHomeControlsLayout.setVisibility(View.VISIBLE);
+ } else {
+ mNotificationStackScroller.setVisibility(View.VISIBLE);
+ mQsFrame.setVisibility(View.GONE);
+ mHomeControlsLayout.setVisibility(View.GONE);
+ }
+ }
+ return false;
+ }
+
+ private boolean isInQsArea(float x, float y) {
+ return (x >= mQsFrame.getX() && x <= mQsFrame.getX() + mQsFrame.getWidth()) && (
+ y <= mNotificationStackScroller.getBottomMostNotificationBottom()
+ || y <= mQs.getView().getY() + mQs.getView().getHeight());
+ }
+
+ private boolean isOnQsEndArea(float x) {
+ if (!isQsSplitEnabled()) return false;
+ if (mView.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR) {
+ return x >= mQsFrame.getX() + mQqsSplitFraction * mQsFrame.getWidth()
+ && x <= mQsFrame.getX() + mQsFrame.getWidth();
+ } else {
+ return x >= mQsFrame.getX()
+ && x <= mQsFrame.getX() + (1 - mQqsSplitFraction) * mQsFrame.getWidth();
+ }
+ }
+
+ private boolean isOpenQsEvent(MotionEvent event) {
+ final int pointerCount = event.getPointerCount();
+ final int action = event.getActionMasked();
+
+ final boolean
+ twoFingerDrag =
+ action == MotionEvent.ACTION_POINTER_DOWN && pointerCount == 2;
+
+ final boolean
+ stylusButtonClickDrag =
+ action == MotionEvent.ACTION_DOWN && (event.isButtonPressed(
+ MotionEvent.BUTTON_STYLUS_PRIMARY) || event.isButtonPressed(
+ MotionEvent.BUTTON_STYLUS_SECONDARY));
+
+ final boolean
+ mouseButtonClickDrag =
+ action == MotionEvent.ACTION_DOWN && (event.isButtonPressed(
+ MotionEvent.BUTTON_SECONDARY) || event.isButtonPressed(
+ MotionEvent.BUTTON_TERTIARY));
+
+ final boolean onHeaderRight = isOnQsEndArea(event.getX());
+
+ return twoFingerDrag || stylusButtonClickDrag || mouseButtonClickDrag || onHeaderRight;
+ }
+
+ private void handleQsDown(MotionEvent event) {
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN && shouldQuickSettingsIntercept(
+ event.getX(), event.getY(), -1)) {
+ mFalsingManager.onQsDown();
+ mQsTracking = true;
+ onQsExpansionStarted();
+ mInitialHeightOnTouch = mQsExpansionHeight;
+ mInitialTouchY = event.getX();
+ mInitialTouchX = event.getY();
+
+ // If we interrupt an expansion gesture here, make sure to update the state correctly.
+ notifyExpandingFinished();
+ }
+ }
+
+ /**
+ * Input focus transfer is about to happen.
+ */
+ public void startWaitingForOpenPanelGesture() {
+ if (!isFullyCollapsed()) {
+ return;
+ }
+ mExpectingSynthesizedDown = true;
+ onTrackingStarted();
+ updatePanelExpanded();
+ }
+
+ /**
+ * Called when this view is no longer waiting for input focus transfer.
+ *
+ * There are two scenarios behind this function call. First, input focus transfer
+ * has successfully happened and this view already received synthetic DOWN event.
+ * (mExpectingSynthesizedDown == false). Do nothing.
+ *
+ * Second, before input focus transfer finished, user may have lifted finger
+ * in previous window and this window never received synthetic DOWN event.
+ * (mExpectingSynthesizedDown == true).
+ * In this case, we use the velocity to trigger fling event.
+ *
+ * @param velocity unit is in px / millis
+ */
+ public void stopWaitingForOpenPanelGesture(final float velocity) {
+ if (mExpectingSynthesizedDown) {
+ mExpectingSynthesizedDown = false;
+ maybeVibrateOnOpening();
+ Runnable runnable = () -> fling(velocity > 1f ? 1000f * velocity : 0,
+ true /* expand */);
+ if (mStatusBar.getStatusBarWindow().getHeight() != mStatusBar.getStatusBarHeight()) {
+ // The panel is already expanded to its full size, let's expand directly
+ runnable.run();
+ } else {
+ mExpandAfterLayoutRunnable = runnable;
+ }
+ onTrackingStopped(false);
+ }
+ }
+
+ @Override
+ protected boolean flingExpands(float vel, float vectorVel, float x, float y) {
+ boolean expands = super.flingExpands(vel, vectorVel, x, y);
+
+ // If we are already running a QS expansion, make sure that we keep the panel open.
+ if (mQsExpansionAnimator != null) {
+ expands = true;
+ }
+ return expands;
+ }
+
+ @Override
+ protected boolean shouldGestureWaitForTouchSlop() {
+ if (mExpectingSynthesizedDown) {
+ mExpectingSynthesizedDown = false;
+ return false;
+ }
+ return isFullyCollapsed() || mBarState != StatusBarState.SHADE;
+ }
+
+ @Override
+ protected boolean shouldGestureIgnoreXTouchSlop(float x, float y) {
+ return !mAffordanceHelper.isOnAffordanceIcon(x, y);
+ }
+
+ private void onQsTouch(MotionEvent event) {
+ int pointerIndex = event.findPointerIndex(mTrackingPointer);
+ if (pointerIndex < 0) {
+ pointerIndex = 0;
+ mTrackingPointer = event.getPointerId(pointerIndex);
+ }
+ final float y = event.getY(pointerIndex);
+ final float x = event.getX(pointerIndex);
+ final float h = y - mInitialTouchY;
+
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ mQsTracking = true;
+ mInitialTouchY = y;
+ mInitialTouchX = x;
+ onQsExpansionStarted();
+ mInitialHeightOnTouch = mQsExpansionHeight;
+ initVelocityTracker();
+ trackMovement(event);
+ break;
+
+ case MotionEvent.ACTION_POINTER_UP:
+ final int upPointer = event.getPointerId(event.getActionIndex());
+ if (mTrackingPointer == upPointer) {
+ // gesture is ongoing, find a new pointer to track
+ final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
+ final float newY = event.getY(newIndex);
+ final float newX = event.getX(newIndex);
+ mTrackingPointer = event.getPointerId(newIndex);
+ mInitialHeightOnTouch = mQsExpansionHeight;
+ mInitialTouchY = newY;
+ mInitialTouchX = newX;
+ }
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ setQsExpansion(h + mInitialHeightOnTouch);
+ if (h >= getFalsingThreshold()) {
+ mQsTouchAboveFalsingThreshold = true;
+ }
+ trackMovement(event);
+ break;
+
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ mQsTracking = false;
+ mTrackingPointer = -1;
+ trackMovement(event);
+ float fraction = getQsExpansionFraction();
+ if (fraction != 0f || y >= mInitialTouchY) {
+ flingQsWithCurrentVelocity(y,
+ event.getActionMasked() == MotionEvent.ACTION_CANCEL);
+ }
+ if (mQsVelocityTracker != null) {
+ mQsVelocityTracker.recycle();
+ mQsVelocityTracker = null;
+ }
+ break;
+ }
+ }
+
+ private int getFalsingThreshold() {
+ float factor = mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
+ return (int) (mQsFalsingThreshold * factor);
+ }
+
+ private void setOverScrolling(boolean overscrolling) {
+ mStackScrollerOverscrolling = overscrolling;
+ if (mQs == null) return;
+ mQs.setOverscrolling(overscrolling);
+ }
+
+ private void onQsExpansionStarted() {
+ onQsExpansionStarted(0);
+ }
+
+ protected void onQsExpansionStarted(int overscrollAmount) {
+ cancelQsAnimation();
+ cancelHeightAnimator();
+
+ // Reset scroll position and apply that position to the expanded height.
+ float height = mQsExpansionHeight - overscrollAmount;
+ setQsExpansion(height);
+ requestPanelHeightUpdate();
+ mNotificationStackScroller.checkSnoozeLeavebehind();
+
+ // When expanding QS, let's authenticate the user if possible,
+ // this will speed up notification actions.
+ if (height == 0) {
+ mStatusBar.requestFaceAuth();
+ }
+ }
+
+ private void setQsExpanded(boolean expanded) {
+ boolean changed = mQsExpanded != expanded;
+ if (changed) {
+ mQsExpanded = expanded;
+ updateQsState();
+ requestPanelHeightUpdate();
+ mFalsingManager.setQsExpanded(expanded);
+ mStatusBar.setQsExpanded(expanded);
+ mNotificationContainerParent.setQsExpanded(expanded);
+ mPulseExpansionHandler.setQsExpanded(expanded);
+ mKeyguardBypassController.setQSExpanded(expanded);
+ }
+ }
+
+ private void maybeAnimateBottomAreaAlpha() {
+ mBottomAreaShadeAlphaAnimator.cancel();
+ if (mBarState == StatusBarState.SHADE_LOCKED) {
+ mBottomAreaShadeAlphaAnimator.start();
+ } else {
+ mBottomAreaShadeAlpha = 1f;
+ }
+ }
+
+ private final Runnable mAnimateKeyguardStatusViewInvisibleEndRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mKeyguardStatusViewAnimating = false;
+ mKeyguardStatusView.setVisibility(View.INVISIBLE);
+ }
+ };
+
+ private final Runnable mAnimateKeyguardStatusViewGoneEndRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mKeyguardStatusViewAnimating = false;
+ mKeyguardStatusView.setVisibility(View.GONE);
+ }
+ };
+
+ private final Runnable mAnimateKeyguardStatusViewVisibleEndRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mKeyguardStatusViewAnimating = false;
+ }
+ };
+
+ private final Runnable mAnimateKeyguardStatusBarInvisibleEndRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mKeyguardStatusBar.setVisibility(View.INVISIBLE);
+ mKeyguardStatusBar.setAlpha(1f);
+ mKeyguardStatusBarAnimateAlpha = 1f;
+ }
+ };
+
+ private void animateKeyguardStatusBarOut() {
+ ValueAnimator anim = ValueAnimator.ofFloat(mKeyguardStatusBar.getAlpha(), 0f);
+ anim.addUpdateListener(mStatusBarAnimateAlphaListener);
+ anim.setStartDelay(mKeyguardStateController.isKeyguardFadingAway()
+ ? mKeyguardStateController.getKeyguardFadingAwayDelay() : 0);
+
+ long duration;
+ if (mKeyguardStateController.isKeyguardFadingAway()) {
+ duration = mKeyguardStateController.getShortenedFadingAwayDuration();
+ } else {
+ duration = StackStateAnimator.ANIMATION_DURATION_STANDARD;
+ }
+ anim.setDuration(duration);
+
+ anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mAnimateKeyguardStatusBarInvisibleEndRunnable.run();
+ }
+ });
+ anim.start();
+ }
+
+ private final ValueAnimator.AnimatorUpdateListener
+ mStatusBarAnimateAlphaListener =
+ new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mKeyguardStatusBarAnimateAlpha = (float) animation.getAnimatedValue();
+ updateHeaderKeyguardAlpha();
+ }
+ };
+
+ private void animateKeyguardStatusBarIn(long duration) {
+ mKeyguardStatusBar.setVisibility(View.VISIBLE);
+ mKeyguardStatusBar.setAlpha(0f);
+ ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
+ anim.addUpdateListener(mStatusBarAnimateAlphaListener);
+ anim.setDuration(duration);
+ anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+ anim.start();
+ }
+
+ private final Runnable mAnimateKeyguardBottomAreaInvisibleEndRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mKeyguardBottomArea.setVisibility(View.GONE);
+ }
+ };
+
+ private void setKeyguardBottomAreaVisibility(int statusBarState, boolean goingToFullShade) {
+ mKeyguardBottomArea.animate().cancel();
+ if (goingToFullShade) {
+ mKeyguardBottomArea.animate().alpha(0f).setStartDelay(
+ mKeyguardStateController.getKeyguardFadingAwayDelay()).setDuration(
+ mKeyguardStateController.getShortenedFadingAwayDuration()).setInterpolator(
+ Interpolators.ALPHA_OUT).withEndAction(
+ mAnimateKeyguardBottomAreaInvisibleEndRunnable).start();
+ } else if (statusBarState == StatusBarState.KEYGUARD
+ || statusBarState == StatusBarState.SHADE_LOCKED) {
+ mKeyguardBottomArea.setVisibility(View.VISIBLE);
+ mKeyguardBottomArea.setAlpha(1f);
+ } else {
+ mKeyguardBottomArea.setVisibility(View.GONE);
+ }
+ }
+
+ private void setKeyguardStatusViewVisibility(int statusBarState, boolean keyguardFadingAway,
+ boolean goingToFullShade) {
+ mKeyguardStatusView.animate().cancel();
+ mKeyguardStatusViewAnimating = false;
+ if ((!keyguardFadingAway && mBarState == StatusBarState.KEYGUARD
+ && statusBarState != StatusBarState.KEYGUARD) || goingToFullShade) {
+ mKeyguardStatusViewAnimating = true;
+ mKeyguardStatusView.animate().alpha(0f).setStartDelay(0).setDuration(
+ 160).setInterpolator(Interpolators.ALPHA_OUT).withEndAction(
+ mAnimateKeyguardStatusViewGoneEndRunnable);
+ if (keyguardFadingAway) {
+ mKeyguardStatusView.animate().setStartDelay(
+ mKeyguardStateController.getKeyguardFadingAwayDelay()).setDuration(
+ mKeyguardStateController.getShortenedFadingAwayDuration()).start();
+ }
+ } else if (mBarState == StatusBarState.SHADE_LOCKED
+ && statusBarState == StatusBarState.KEYGUARD) {
+ mKeyguardStatusView.setVisibility(View.VISIBLE);
+ mKeyguardStatusViewAnimating = true;
+ mKeyguardStatusView.setAlpha(0f);
+ mKeyguardStatusView.animate().alpha(1f).setStartDelay(0).setDuration(
+ 320).setInterpolator(Interpolators.ALPHA_IN).withEndAction(
+ mAnimateKeyguardStatusViewVisibleEndRunnable);
+ } else if (statusBarState == StatusBarState.KEYGUARD) {
+ if (keyguardFadingAway) {
+ mKeyguardStatusViewAnimating = true;
+ mKeyguardStatusView.animate().alpha(0).translationYBy(
+ -getHeight() * 0.05f).setInterpolator(
+ Interpolators.FAST_OUT_LINEAR_IN).setDuration(125).setStartDelay(
+ 0).withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable).start();
+ } else {
+ mKeyguardStatusView.setVisibility(View.VISIBLE);
+ mKeyguardStatusView.setAlpha(1f);
+ }
+ } else {
+ mKeyguardStatusView.setVisibility(View.GONE);
+ mKeyguardStatusView.setAlpha(1f);
+ }
+ }
+
+ private void updateQsState() {
+ mNotificationStackScroller.setQsExpanded(mQsExpanded);
+ mNotificationStackScroller.setScrollingEnabled(
+ mBarState != StatusBarState.KEYGUARD && (!mQsExpanded
+ || mQsExpansionFromOverscroll));
+ updateEmptyShadeView();
+ if (mNPVPluginManager != null) {
+ mNPVPluginManager.changeVisibility(
+ (mBarState != StatusBarState.KEYGUARD) ? View.VISIBLE : View.INVISIBLE);
+ }
+ mQsNavbarScrim.setVisibility(
+ mBarState == StatusBarState.SHADE && mQsExpanded && !mStackScrollerOverscrolling
+ && mQsScrimEnabled ? View.VISIBLE : View.INVISIBLE);
+ if (mKeyguardUserSwitcher != null && mQsExpanded && !mStackScrollerOverscrolling) {
+ mKeyguardUserSwitcher.hideIfNotSimple(true /* animate */);
+ }
+ if (mQs == null) return;
+ mQs.setExpanded(mQsExpanded);
+ }
+
+ private void setQsExpansion(float height) {
+ height = Math.min(Math.max(height, mQsMinExpansionHeight), mQsMaxExpansionHeight);
+ mQsFullyExpanded = height == mQsMaxExpansionHeight && mQsMaxExpansionHeight != 0;
+ if (height > mQsMinExpansionHeight && !mQsExpanded && !mStackScrollerOverscrolling
+ && !mDozing) {
+ setQsExpanded(true);
+ } else if (height <= mQsMinExpansionHeight && mQsExpanded) {
+ setQsExpanded(false);
+ }
+ mQsExpansionHeight = height;
+ updateQsExpansion();
+ requestScrollerTopPaddingUpdate(false /* animate */);
+ updateHeaderKeyguardAlpha();
+ if (mBarState == StatusBarState.SHADE_LOCKED || mBarState == StatusBarState.KEYGUARD) {
+ updateKeyguardBottomAreaAlpha();
+ updateBigClockAlpha();
+ }
+ if (mBarState == StatusBarState.SHADE && mQsExpanded && !mStackScrollerOverscrolling
+ && mQsScrimEnabled) {
+ mQsNavbarScrim.setAlpha(getQsExpansionFraction());
+ }
+
+ if (mAccessibilityManager.isEnabled()) {
+ mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
+ }
+
+ if (!mFalsingManager.isUnlockingDisabled() && mQsFullyExpanded
+ && mFalsingManager.shouldEnforceBouncer()) {
+ mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */,
+ false /* dismissShade */, true /* afterKeyguardGone */, false /* deferred */);
+ }
+ for (int i = 0; i < mExpansionListeners.size(); i++) {
+ mExpansionListeners.get(i).onQsExpansionChanged(
+ mQsMaxExpansionHeight != 0 ? mQsExpansionHeight / mQsMaxExpansionHeight : 0);
+ }
+ if (DEBUG) {
+ mView.invalidate();
+ }
+ }
+
+ protected void updateQsExpansion() {
+ if (mQs == null) return;
+ float qsExpansionFraction = getQsExpansionFraction();
+ mQs.setQsExpansion(qsExpansionFraction, getHeaderTranslation());
+ int heightDiff = mQs.getDesiredHeight() - mQs.getQsMinExpansionHeight();
+ if (mNPVPluginManager != null) {
+ mNPVPluginManager.setExpansion(qsExpansionFraction, getHeaderTranslation(), heightDiff);
+ }
+ mNotificationStackScroller.setQsExpansionFraction(qsExpansionFraction);
+ }
+
+ private String determineAccessibilityPaneTitle() {
+ if (mQs != null && mQs.isCustomizing()) {
+ return mResources.getString(R.string.accessibility_desc_quick_settings_edit);
+ } else if (mQsExpansionHeight != 0.0f && mQsFullyExpanded) {
+ // Upon initialisation when we are not layouted yet we don't want to announce that we
+ // are fully expanded, hence the != 0.0f check.
+ return mResources.getString(R.string.accessibility_desc_quick_settings);
+ } else if (mBarState == StatusBarState.KEYGUARD) {
+ return mResources.getString(R.string.accessibility_desc_lock_screen);
+ } else {
+ return mResources.getString(R.string.accessibility_desc_notification_shade);
+ }
+ }
+
+ private float calculateQsTopPadding() {
+ if (mKeyguardShowing && (mQsExpandImmediate
+ || mIsExpanding && mQsExpandedWhenExpandingStarted)) {
+
+ // Either QS pushes the notifications down when fully expanded, or QS is fully above the
+ // notifications (mostly on tablets). maxNotificationPadding denotes the normal top
+ // padding on Keyguard, maxQsPadding denotes the top padding from the quick settings
+ // panel. We need to take the maximum and linearly interpolate with the panel expansion
+ // for a nice motion.
+ int maxNotificationPadding = getKeyguardNotificationStaticPadding();
+ int maxQsPadding = mQsMaxExpansionHeight + mQsNotificationTopPadding;
+ int max = mBarState == StatusBarState.KEYGUARD ? Math.max(
+ maxNotificationPadding, maxQsPadding) : maxQsPadding;
+ return (int) MathUtils.lerp((float) mQsMinExpansionHeight, (float) max,
+ getExpandedFraction());
+ } else if (mQsSizeChangeAnimator != null) {
+ return Math.max(
+ (int) mQsSizeChangeAnimator.getAnimatedValue(),
+ getKeyguardNotificationStaticPadding());
+ } else if (mKeyguardShowing) {
+ // We can only do the smoother transition on Keyguard when we also are not collapsing
+ // from a scrolled quick settings.
+ return MathUtils.lerp((float) getKeyguardNotificationStaticPadding(),
+ (float) (mQsMaxExpansionHeight + mQsNotificationTopPadding),
+ getQsExpansionFraction());
+ } else {
+ return mQsExpansionHeight + mQsNotificationTopPadding;
+ }
+ }
+
+ /**
+ * @return the topPadding of notifications when on keyguard not respecting quick settings
+ * expansion
+ */
+ private int getKeyguardNotificationStaticPadding() {
+ if (!mKeyguardShowing) {
+ return 0;
+ }
+ if (!mKeyguardBypassController.getBypassEnabled()) {
+ return mClockPositionResult.stackScrollerPadding;
+ }
+ int collapsedPosition = mHeadsUpInset;
+ if (!mNotificationStackScroller.isPulseExpanding()) {
+ return collapsedPosition;
+ } else {
+ int expandedPosition = mClockPositionResult.stackScrollerPadding;
+ return (int) MathUtils.lerp(collapsedPosition, expandedPosition,
+ mNotificationStackScroller.calculateAppearFractionBypass());
+ }
+ }
+
+
+ protected void requestScrollerTopPaddingUpdate(boolean animate) {
+ mNotificationStackScroller.updateTopPadding(calculateQsTopPadding(), animate);
+ if (mKeyguardShowing && mKeyguardBypassController.getBypassEnabled()) {
+ // update the position of the header
+ updateQsExpansion();
+ }
+ }
+
+
+ private void updateQSPulseExpansion() {
+ if (mQs != null) {
+ mQs.setShowCollapsedOnKeyguard(
+ mKeyguardShowing && mKeyguardBypassController.getBypassEnabled()
+ && mNotificationStackScroller.isPulseExpanding());
+ }
+ }
+
+ private void trackMovement(MotionEvent event) {
+ if (mQsVelocityTracker != null) mQsVelocityTracker.addMovement(event);
+ }
+
+ private void initVelocityTracker() {
+ if (mQsVelocityTracker != null) {
+ mQsVelocityTracker.recycle();
+ }
+ mQsVelocityTracker = VelocityTracker.obtain();
+ }
+
+ private float getCurrentQSVelocity() {
+ if (mQsVelocityTracker == null) {
+ return 0;
+ }
+ mQsVelocityTracker.computeCurrentVelocity(1000);
+ return mQsVelocityTracker.getYVelocity();
+ }
+
+ private void cancelQsAnimation() {
+ if (mQsExpansionAnimator != null) {
+ mQsExpansionAnimator.cancel();
+ }
+ }
+
+ /**
+ * @see #flingSettings(float, int, Runnable, boolean)
+ */
+ public void flingSettings(float vel, int type) {
+ flingSettings(vel, type, null, false /* isClick */);
+ }
+
+ /**
+ * Animates QS or QQS as if the user had swiped up or down.
+ *
+ * @param vel Finger velocity or 0 when not initiated by touch events.
+ * @param type Either {@link #FLING_EXPAND}, {@link #FLING_COLLAPSE} or {@link
+ * #FLING_HIDE}.
+ * @param onFinishRunnable Runnable to be executed at the end of animation.
+ * @param isClick If originated by click (different interpolator and duration.)
+ */
+ protected void flingSettings(float vel, int type, final Runnable onFinishRunnable,
+ boolean isClick) {
+ float target;
+ switch (type) {
+ case FLING_EXPAND:
+ target = mQsMaxExpansionHeight;
+ break;
+ case FLING_COLLAPSE:
+ target = mQsMinExpansionHeight;
+ break;
+ case FLING_HIDE:
+ default:
+ target = 0;
+ }
+ if (target == mQsExpansionHeight) {
+ if (onFinishRunnable != null) {
+ onFinishRunnable.run();
+ }
+ return;
+ }
+
+ // If we move in the opposite direction, reset velocity and use a different duration.
+ boolean oppositeDirection = false;
+ boolean expanding = type == FLING_EXPAND;
+ if (vel > 0 && !expanding || vel < 0 && expanding) {
+ vel = 0;
+ oppositeDirection = true;
+ }
+ ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target);
+ if (isClick) {
+ animator.setInterpolator(Interpolators.TOUCH_RESPONSE);
+ animator.setDuration(368);
+ } else {
+ mFlingAnimationUtils.apply(animator, mQsExpansionHeight, target, vel);
+ }
+ if (oppositeDirection) {
+ animator.setDuration(350);
+ }
+ animator.addUpdateListener(animation -> {
+ setQsExpansion((Float) animation.getAnimatedValue());
+ });
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mNotificationStackScroller.resetCheckSnoozeLeavebehind();
+ mQsExpansionAnimator = null;
+ if (onFinishRunnable != null) {
+ onFinishRunnable.run();
+ }
+ }
+ });
+ animator.start();
+ mQsExpansionAnimator = animator;
+ mQsAnimatorExpand = expanding;
+ }
+
+ /**
+ * @return Whether we should intercept a gesture to open Quick Settings.
+ */
+ private boolean shouldQuickSettingsIntercept(float x, float y, float yDiff) {
+ if (!mQsExpansionEnabled || mCollapsedOnDown || (mKeyguardShowing
+ && mKeyguardBypassController.getBypassEnabled())) {
+ return false;
+ }
+ View header = mKeyguardShowing || mQs == null ? mKeyguardStatusBar : mQs.getHeader();
+ final boolean
+ onHeader =
+ x >= mQsFrame.getX() && x <= mQsFrame.getX() + mQsFrame.getWidth()
+ && y >= header.getTop() && y <= header.getBottom();
+ if (mQsExpanded) {
+ return onHeader || (yDiff < 0 && isInQsArea(x, y));
+ } else {
+ return onHeader;
+ }
+ }
+
+ @Override
+ protected boolean isScrolledToBottom() {
+ if (!isInSettings()) {
+ return mBarState == StatusBarState.KEYGUARD
+ || mNotificationStackScroller.isScrolledToBottom();
+ } else {
+ return true;
+ }
+ }
+
+ @Override
+ protected int getMaxPanelHeight() {
+ if (mKeyguardBypassController.getBypassEnabled() && mBarState == StatusBarState.KEYGUARD) {
+ return getMaxPanelHeightBypass();
+ } else {
+ return getMaxPanelHeightNonBypass();
+ }
+ }
+
+ private int getMaxPanelHeightNonBypass() {
+ int min = mStatusBarMinHeight;
+ if (!(mBarState == StatusBarState.KEYGUARD)
+ && mNotificationStackScroller.getNotGoneChildCount() == 0) {
+ int minHeight = (int) (mQsMinExpansionHeight + getOverExpansionAmount());
+ min = Math.max(min, minHeight);
+ }
+ int maxHeight;
+ if (mQsExpandImmediate || mQsExpanded || mIsExpanding && mQsExpandedWhenExpandingStarted
+ || mPulsing) {
+ maxHeight = calculatePanelHeightQsExpanded();
+ } else {
+ maxHeight = calculatePanelHeightShade();
+ }
+ maxHeight = Math.max(maxHeight, min);
+ return maxHeight;
+ }
+
+ private int getMaxPanelHeightBypass() {
+ int position =
+ mClockPositionAlgorithm.getExpandedClockPosition()
+ + mKeyguardStatusView.getHeight();
+ if (mNotificationStackScroller.getVisibleNotificationCount() != 0) {
+ position += mShelfHeight / 2.0f + mDarkIconSize / 2.0f;
+ }
+ return position;
+ }
+
+ public boolean isInSettings() {
+ return mQsExpanded;
+ }
+
+ public boolean isExpanding() {
+ return mIsExpanding;
+ }
+
+ @Override
+ protected void onHeightUpdated(float expandedHeight) {
+ if (!mQsExpanded || mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted) {
+ // Updating the clock position will set the top padding which might
+ // trigger a new panel height and re-position the clock.
+ // This is a circular dependency and should be avoided, otherwise we'll have
+ // a stack overflow.
+ if (mStackScrollerMeasuringPass > 2) {
+ if (DEBUG) Log.d(TAG, "Unstable notification panel height. Aborting.");
+ } else {
+ positionClockAndNotifications();
+ }
+ }
+ if (mQsExpandImmediate || mQsExpanded && !mQsTracking && mQsExpansionAnimator == null
+ && !mQsExpansionFromOverscroll) {
+ float t;
+ if (mKeyguardShowing) {
+
+ // On Keyguard, interpolate the QS expansion linearly to the panel expansion
+ t = expandedHeight / (getMaxPanelHeight());
+ } else {
+ // In Shade, interpolate linearly such that QS is closed whenever panel height is
+ // minimum QS expansion + minStackHeight
+ float
+ panelHeightQsCollapsed =
+ mNotificationStackScroller.getIntrinsicPadding()
+ + mNotificationStackScroller.getLayoutMinHeight();
+ float panelHeightQsExpanded = calculatePanelHeightQsExpanded();
+ t =
+ (expandedHeight - panelHeightQsCollapsed) / (panelHeightQsExpanded
+ - panelHeightQsCollapsed);
+ }
+ float
+ targetHeight =
+ mQsMinExpansionHeight + t * (mQsMaxExpansionHeight - mQsMinExpansionHeight);
+ setQsExpansion(targetHeight);
+ mHomeControlsLayout.setTranslationY(targetHeight);
+ }
+ updateExpandedHeight(expandedHeight);
+ updateHeader();
+ updateNotificationTranslucency();
+ updatePanelExpanded();
+ updateGestureExclusionRect();
+ if (DEBUG) {
+ mView.invalidate();
+ }
+ }
+
+ private void updatePanelExpanded() {
+ boolean isExpanded = !isFullyCollapsed() || mExpectingSynthesizedDown;
+ if (mPanelExpanded != isExpanded) {
+ mHeadsUpManager.setIsPanelExpanded(isExpanded);
+ mStatusBar.setPanelExpanded(isExpanded);
+ mPanelExpanded = isExpanded;
+ }
+ }
+
+ private int calculatePanelHeightShade() {
+ int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin();
+ int maxHeight = mNotificationStackScroller.getHeight() - emptyBottomMargin;
+ maxHeight += mNotificationStackScroller.getTopPaddingOverflow();
+
+ if (mBarState == StatusBarState.KEYGUARD) {
+ int
+ minKeyguardPanelBottom =
+ mClockPositionAlgorithm.getExpandedClockPosition()
+ + mKeyguardStatusView.getHeight()
+ + mNotificationStackScroller.getIntrinsicContentHeight();
+ return Math.max(maxHeight, minKeyguardPanelBottom);
+ } else {
+ return maxHeight;
+ }
+ }
+
+ private int calculatePanelHeightQsExpanded() {
+ float
+ notificationHeight =
+ mNotificationStackScroller.getHeight()
+ - mNotificationStackScroller.getEmptyBottomMargin()
+ - mNotificationStackScroller.getTopPadding();
+
+ // When only empty shade view is visible in QS collapsed state, simulate that we would have
+ // it in expanded QS state as well so we don't run into troubles when fading the view in/out
+ // and expanding/collapsing the whole panel from/to quick settings.
+ if (mNotificationStackScroller.getNotGoneChildCount() == 0 && mShowEmptyShadeView) {
+ notificationHeight = mNotificationStackScroller.getEmptyShadeViewHeight();
+ }
+ int maxQsHeight = mQsMaxExpansionHeight;
+
+ if (mKeyguardShowing) {
+ maxQsHeight += mQsNotificationTopPadding;
+ }
+
+ // If an animation is changing the size of the QS panel, take the animated value.
+ if (mQsSizeChangeAnimator != null) {
+ maxQsHeight = (int) mQsSizeChangeAnimator.getAnimatedValue();
+ }
+ float totalHeight = Math.max(maxQsHeight,
+ mBarState == StatusBarState.KEYGUARD ? mClockPositionResult.stackScrollerPadding
+ : 0) + notificationHeight
+ + mNotificationStackScroller.getTopPaddingOverflow();
+ if (totalHeight > mNotificationStackScroller.getHeight()) {
+ float
+ fullyCollapsedHeight =
+ maxQsHeight + mNotificationStackScroller.getLayoutMinHeight();
+ totalHeight = Math.max(fullyCollapsedHeight, mNotificationStackScroller.getHeight());
+ }
+ return (int) totalHeight;
+ }
+
+ private void updateNotificationTranslucency() {
+ float alpha = 1f;
+ if (mClosingWithAlphaFadeOut && !mExpandingFromHeadsUp
+ && !mHeadsUpManager.hasPinnedHeadsUp()) {
+ alpha = getFadeoutAlpha();
+ }
+ if (mBarState == StatusBarState.KEYGUARD && !mHintAnimationRunning
+ && !mKeyguardBypassController.getBypassEnabled()) {
+ alpha *= mClockPositionResult.clockAlpha;
+ }
+ mNotificationStackScroller.setAlpha(alpha);
+ }
+
+ private float getFadeoutAlpha() {
+ float alpha;
+ if (mQsMinExpansionHeight == 0) {
+ return 1.0f;
+ }
+ alpha = getExpandedHeight() / mQsMinExpansionHeight;
+ alpha = Math.max(0, Math.min(alpha, 1));
+ alpha = (float) Math.pow(alpha, 0.75);
+ return alpha;
+ }
+
+ @Override
+ protected float getOverExpansionAmount() {
+ return mNotificationStackScroller.getCurrentOverScrollAmount(true /* top */);
+ }
+
+ @Override
+ protected float getOverExpansionPixels() {
+ return mNotificationStackScroller.getCurrentOverScrolledPixels(true /* top */);
+ }
+
+ /**
+ * Hides the header when notifications are colliding with it.
+ */
+ private void updateHeader() {
+ if (mBarState == StatusBarState.KEYGUARD) {
+ updateHeaderKeyguardAlpha();
+ }
+ updateQsExpansion();
+ }
+
+ protected float getHeaderTranslation() {
+ if (mBarState == StatusBarState.KEYGUARD && !mKeyguardBypassController.getBypassEnabled()) {
+ return -mQs.getQsMinExpansionHeight();
+ }
+ float appearAmount = mNotificationStackScroller.calculateAppearFraction(mExpandedHeight);
+ float startHeight = -mQsExpansionHeight;
+ if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()
+ && mNotificationStackScroller.isPulseExpanding()) {
+ if (!mPulseExpansionHandler.isExpanding()
+ && !mPulseExpansionHandler.getLeavingLockscreen()) {
+ // If we aborted the expansion we need to make sure the header doesn't reappear
+ // again after the header has animated away
+ appearAmount = 0;
+ } else {
+ appearAmount = mNotificationStackScroller.calculateAppearFractionBypass();
+ }
+ startHeight = -mQs.getQsMinExpansionHeight();
+ if (mNPVPluginManager != null) startHeight -= mNPVPluginManager.getHeight();
+ }
+ float translation = MathUtils.lerp(startHeight, 0, Math.min(1.0f, appearAmount))
+ + mExpandOffset;
+ return Math.min(0, translation);
+ }
+
+ /**
+ * @return the alpha to be used to fade out the contents on Keyguard (status bar, bottom area)
+ * during swiping up
+ */
+ private float getKeyguardContentsAlpha() {
+ float alpha;
+ if (mBarState == StatusBarState.KEYGUARD) {
+
+ // When on Keyguard, we hide the header as soon as we expanded close enough to the
+ // header
+ alpha =
+ getExpandedHeight() / (mKeyguardStatusBar.getHeight()
+ + mNotificationsHeaderCollideDistance);
+ } else {
+
+ // In SHADE_LOCKED, the top card is already really close to the header. Hide it as
+ // soon as we start translating the stack.
+ alpha = getExpandedHeight() / mKeyguardStatusBar.getHeight();
+ }
+ alpha = MathUtils.saturate(alpha);
+ alpha = (float) Math.pow(alpha, 0.75);
+ return alpha;
+ }
+
+ private void updateHeaderKeyguardAlpha() {
+ if (!mKeyguardShowing) {
+ return;
+ }
+ float alphaQsExpansion = 1 - Math.min(1, getQsExpansionFraction() * 2);
+ float newAlpha = Math.min(getKeyguardContentsAlpha(), alphaQsExpansion)
+ * mKeyguardStatusBarAnimateAlpha;
+ newAlpha *= 1.0f - mKeyguardHeadsUpShowingAmount;
+ mKeyguardStatusBar.setAlpha(newAlpha);
+ boolean
+ hideForBypass =
+ mFirstBypassAttempt && mUpdateMonitor.shouldListenForFace()
+ || mDelayShowingKeyguardStatusBar;
+ mKeyguardStatusBar.setVisibility(
+ newAlpha != 0f && !mDozing && !hideForBypass ? View.VISIBLE : View.INVISIBLE);
+ }
+
+ private void updateKeyguardBottomAreaAlpha() {
+ // There are two possible panel expansion behaviors:
+ // • User dragging up to unlock: we want to fade out as quick as possible
+ // (ALPHA_EXPANSION_THRESHOLD) to avoid seeing the bouncer over the bottom area.
+ // • User tapping on lock screen: bouncer won't be visible but panel expansion will
+ // change due to "unlock hint animation." In this case, fading out the bottom area
+ // would also hide the message that says "swipe to unlock," we don't want to do that.
+ float expansionAlpha = MathUtils.map(
+ isUnlockHintRunning() ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, 0f, 1f,
+ getExpandedFraction());
+ float alpha = Math.min(expansionAlpha, 1 - getQsExpansionFraction());
+ alpha *= mBottomAreaShadeAlpha;
+ mKeyguardBottomArea.setAffordanceAlpha(alpha);
+ mKeyguardBottomArea.setImportantForAccessibility(
+ alpha == 0f ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+ : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+ View ambientIndicationContainer = mStatusBar.getAmbientIndicationContainer();
+ if (ambientIndicationContainer != null) {
+ ambientIndicationContainer.setAlpha(alpha);
+ }
+ }
+
+ /**
+ * Custom clock fades away when user drags up to unlock or pulls down quick settings.
+ *
+ * Updates alpha of custom clock to match the alpha of the KeyguardBottomArea. See
+ * {@link #updateKeyguardBottomAreaAlpha}.
+ */
+ private void updateBigClockAlpha() {
+ float expansionAlpha = MathUtils.map(
+ isUnlockHintRunning() ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, 0f, 1f,
+ getExpandedFraction());
+ float alpha = Math.min(expansionAlpha, 1 - getQsExpansionFraction());
+ mBigClockContainer.setAlpha(alpha);
+ }
+
+ @Override
+ protected void onExpandingStarted() {
+ super.onExpandingStarted();
+ mNotificationStackScroller.onExpansionStarted();
+ mIsExpanding = true;
+ mQsExpandedWhenExpandingStarted = mQsFullyExpanded;
+ if (mQsExpanded) {
+ onQsExpansionStarted();
+ }
+ // Since there are QS tiles in the header now, we need to make sure we start listening
+ // immediately so they can be up to date.
+ if (mQs == null) return;
+ mQs.setHeaderListening(true);
+ }
+
+ @Override
+ protected void onExpandingFinished() {
+ super.onExpandingFinished();
+ mNotificationStackScroller.onExpansionStopped();
+ mHeadsUpManager.onExpandingFinished();
+ mIsExpanding = false;
+ if (isFullyCollapsed()) {
+ DejankUtils.postAfterTraversal(new Runnable() {
+ @Override
+ public void run() {
+ setListening(false);
+ }
+ });
+
+ // Workaround b/22639032: Make sure we invalidate something because else RenderThread
+ // thinks we are actually drawing a frame put in reality we don't, so RT doesn't go
+ // ahead with rendering and we jank.
+ mView.postOnAnimation(new Runnable() {
+ @Override
+ public void run() {
+ mView.getParent().invalidateChild(mView, M_DUMMY_DIRTY_RECT);
+ }
+ });
+ } else {
+ setListening(true);
+ }
+ mQsExpandImmediate = false;
+ mNotificationStackScroller.setShouldShowShelfOnly(false);
+ mTwoFingerQsExpandPossible = false;
+ notifyListenersTrackingHeadsUp(null);
+ mExpandingFromHeadsUp = false;
+ setPanelScrimMinFraction(0.0f);
+ }
+
+ private void notifyListenersTrackingHeadsUp(ExpandableNotificationRow pickedChild) {
+ for (int i = 0; i < mTrackingHeadsUpListeners.size(); i++) {
+ Consumer<ExpandableNotificationRow> listener = mTrackingHeadsUpListeners.get(i);
+ listener.accept(pickedChild);
+ }
+ }
+
+ private void setListening(boolean listening) {
+ mKeyguardStatusBar.setListening(listening);
+ if (mQs == null) return;
+ mQs.setListening(listening);
+ if (mNPVPluginManager != null) mNPVPluginManager.setListening(listening);
+ }
+
+ @Override
+ public void expand(boolean animate) {
+ super.expand(animate);
+ setListening(true);
+ }
+
+ @Override
+ protected void setOverExpansion(float overExpansion, boolean isPixels) {
+ if (mConflictingQsExpansionGesture || mQsExpandImmediate) {
+ return;
+ }
+ if (mBarState != StatusBarState.KEYGUARD) {
+ mNotificationStackScroller.setOnHeightChangedListener(null);
+ if (isPixels) {
+ mNotificationStackScroller.setOverScrolledPixels(overExpansion, true /* onTop */,
+ false /* animate */);
+ } else {
+ mNotificationStackScroller.setOverScrollAmount(overExpansion, true /* onTop */,
+ false /* animate */);
+ }
+ mNotificationStackScroller.setOnHeightChangedListener(mOnHeightChangedListener);
+ }
+ }
+
+ @Override
+ protected void onTrackingStarted() {
+ mFalsingManager.onTrackingStarted(!mKeyguardStateController.canDismissLockScreen());
+ super.onTrackingStarted();
+ if (mQsFullyExpanded) {
+ mQsExpandImmediate = true;
+ mNotificationStackScroller.setShouldShowShelfOnly(true);
+ }
+ if (mBarState == StatusBarState.KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED) {
+ mAffordanceHelper.animateHideLeftRightIcon();
+ }
+ mNotificationStackScroller.onPanelTrackingStarted();
+ }
+
+ @Override
+ protected void onTrackingStopped(boolean expand) {
+ mFalsingManager.onTrackingStopped();
+ super.onTrackingStopped(expand);
+ if (expand) {
+ mNotificationStackScroller.setOverScrolledPixels(0.0f, true /* onTop */,
+ true /* animate */);
+ }
+ mNotificationStackScroller.onPanelTrackingStopped();
+ if (expand && (mBarState == StatusBarState.KEYGUARD
+ || mBarState == StatusBarState.SHADE_LOCKED)) {
+ if (!mHintAnimationRunning) {
+ mAffordanceHelper.reset(true);
+ }
+ }
+ }
+
+ private void updateMaxHeadsUpTranslation() {
+ mNotificationStackScroller.setHeadsUpBoundaries(getHeight(), mNavigationBarBottomHeight);
+ }
+
+ @Override
+ protected void startUnlockHintAnimation() {
+ if (mPowerManager.isPowerSaveMode()) {
+ onUnlockHintStarted();
+ onUnlockHintFinished();
+ return;
+ }
+ super.startUnlockHintAnimation();
+ }
+
+ @Override
+ protected void onUnlockHintFinished() {
+ super.onUnlockHintFinished();
+ mNotificationStackScroller.setUnlockHintRunning(false);
+ }
+
+ @Override
+ protected void onUnlockHintStarted() {
+ super.onUnlockHintStarted();
+ mNotificationStackScroller.setUnlockHintRunning(true);
+ }
+
+ @Override
+ protected float getPeekHeight() {
+ if (mNotificationStackScroller.getNotGoneChildCount() > 0) {
+ return mNotificationStackScroller.getPeekHeight();
+ } else {
+ return mQsMinExpansionHeight;
+ }
+ }
+
+ @Override
+ protected boolean shouldUseDismissingAnimation() {
+ return mBarState != StatusBarState.SHADE && (mKeyguardStateController.canDismissLockScreen()
+ || !isTracking());
+ }
+
+ @Override
+ protected boolean fullyExpandedClearAllVisible() {
+ return mNotificationStackScroller.isFooterViewNotGone()
+ && mNotificationStackScroller.isScrolledToBottom() && !mQsExpandImmediate;
+ }
+
+ @Override
+ protected boolean isClearAllVisible() {
+ return mNotificationStackScroller.isFooterViewContentVisible();
+ }
+
+ @Override
+ protected int getClearAllHeight() {
+ return mNotificationStackScroller.getFooterViewHeight();
+ }
+
+ @Override
+ protected boolean isTrackingBlocked() {
+ return mConflictingQsExpansionGesture && mQsExpanded || mBlockingExpansionForCurrentTouch;
+ }
+
+ public boolean isQsExpanded() {
+ return mQsExpanded;
+ }
+
+ public boolean isQsDetailShowing() {
+ return mQs.isShowingDetail();
+ }
+
+ public void closeQsDetail() {
+ mQs.closeDetail();
+ }
+
+ public boolean isLaunchTransitionFinished() {
+ return mIsLaunchTransitionFinished;
+ }
+
+ public boolean isLaunchTransitionRunning() {
+ return mIsLaunchTransitionRunning;
+ }
+
+ public void setLaunchTransitionEndRunnable(Runnable r) {
+ mLaunchAnimationEndRunnable = r;
+ }
+
+ private void updateDozingVisibilities(boolean animate) {
+ mKeyguardBottomArea.setDozing(mDozing, animate);
+ if (!mDozing && animate) {
+ animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ }
+ }
+
+ @Override
+ public boolean isDozing() {
+ return mDozing;
+ }
+
+ public void showEmptyShadeView(boolean emptyShadeViewVisible) {
+ mShowEmptyShadeView = emptyShadeViewVisible;
+ updateEmptyShadeView();
+ }
+
+ private void updateEmptyShadeView() {
+ // Hide "No notifications" in QS.
+ mNotificationStackScroller.updateEmptyShadeView(mShowEmptyShadeView && !mQsExpanded);
+ }
+
+ public void setQsScrimEnabled(boolean qsScrimEnabled) {
+ boolean changed = mQsScrimEnabled != qsScrimEnabled;
+ mQsScrimEnabled = qsScrimEnabled;
+ if (changed) {
+ updateQsState();
+ }
+ }
+
+ public void setKeyguardUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher) {
+ mKeyguardUserSwitcher = keyguardUserSwitcher;
+ }
+
+ public void onScreenTurningOn() {
+ mKeyguardStatusView.dozeTimeTick();
+ }
+
+ @Override
+ protected boolean onMiddleClicked() {
+ switch (mBarState) {
+ case StatusBarState.KEYGUARD:
+ if (!mDozingOnDown) {
+ if (mKeyguardBypassController.getBypassEnabled()) {
+ mUpdateMonitor.requestFaceAuth();
+ } else {
+ mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_HINT,
+ 0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
+ startUnlockHintAnimation();
+ }
+ }
+ return true;
+ case StatusBarState.SHADE_LOCKED:
+ if (!mQsExpanded) {
+ mStatusBarStateController.setState(StatusBarState.KEYGUARD);
+ }
+ return true;
+ case StatusBarState.SHADE:
+
+ // This gets called in the middle of the touch handling, where the state is still
+ // that we are tracking the panel. Collapse the panel after this is done.
+ mView.post(mPostCollapseRunnable);
+ return false;
+ default:
+ return true;
+ }
+ }
+
+ public void setPanelAlpha(int alpha, boolean animate) {
+ if (mPanelAlpha != alpha) {
+ mPanelAlpha = alpha;
+ PropertyAnimator.setProperty(mView, mPanelAlphaAnimator, alpha, alpha == 255
+ ? mPanelAlphaInPropertiesAnimator : mPanelAlphaOutPropertiesAnimator,
+ animate);
+ }
+ }
+
+ public void setPanelAlphaEndAction(Runnable r) {
+ mPanelAlphaEndAction = r;
+ }
+
+ private void updateKeyguardStatusBarForHeadsUp() {
+ boolean
+ showingKeyguardHeadsUp =
+ mKeyguardShowing && mHeadsUpAppearanceController.shouldBeVisible();
+ if (mShowingKeyguardHeadsUp != showingKeyguardHeadsUp) {
+ mShowingKeyguardHeadsUp = showingKeyguardHeadsUp;
+ if (mKeyguardShowing) {
+ PropertyAnimator.setProperty(mView, KEYGUARD_HEADS_UP_SHOWING_AMOUNT,
+ showingKeyguardHeadsUp ? 1.0f : 0.0f, KEYGUARD_HUN_PROPERTIES,
+ true /* animate */);
+ } else {
+ PropertyAnimator.applyImmediately(mView, KEYGUARD_HEADS_UP_SHOWING_AMOUNT, 0.0f);
+ }
+ }
+ }
+
+ private void setKeyguardHeadsUpShowingAmount(float amount) {
+ mKeyguardHeadsUpShowingAmount = amount;
+ updateHeaderKeyguardAlpha();
+ }
+
+ private float getKeyguardHeadsUpShowingAmount() {
+ return mKeyguardHeadsUpShowingAmount;
+ }
+
+ public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
+ mHeadsUpAnimatingAway = headsUpAnimatingAway;
+ mNotificationStackScroller.setHeadsUpAnimatingAway(headsUpAnimatingAway);
+ updateHeadsUpVisibility();
+ }
+
+ private void updateHeadsUpVisibility() {
+ ((PhoneStatusBarView) mBar).setHeadsUpVisible(mHeadsUpAnimatingAway || mHeadsUpPinnedMode);
+ }
+
+ @Override
+ public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
+ super.setHeadsUpManager(headsUpManager);
+ mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager,
+ mNotificationStackScroller.getHeadsUpCallback(),
+ NotificationPanelViewController.this);
+ }
+
+ public void setTrackedHeadsUp(ExpandableNotificationRow pickedChild) {
+ if (pickedChild != null) {
+ notifyListenersTrackingHeadsUp(pickedChild);
+ mExpandingFromHeadsUp = true;
+ }
+ // otherwise we update the state when the expansion is finished
+ }
+
+ @Override
+ protected void onClosingFinished() {
+ super.onClosingFinished();
+ resetHorizontalPanelPosition();
+ setClosingWithAlphaFadeout(false);
+ }
+
+ private void setClosingWithAlphaFadeout(boolean closing) {
+ mClosingWithAlphaFadeOut = closing;
+ mNotificationStackScroller.forceNoOverlappingRendering(closing);
+ }
+
+ /**
+ * Updates the vertical position of the panel so it is positioned closer to the touch
+ * responsible for opening the panel.
+ *
+ * @param x the x-coordinate the touch event
+ */
+ protected void updateVerticalPanelPosition(float x) {
+ if (mNotificationStackScroller.getWidth() * 1.75f > mView.getWidth()) {
+ resetHorizontalPanelPosition();
+ return;
+ }
+ float leftMost = mPositionMinSideMargin + mNotificationStackScroller.getWidth() / 2;
+ float
+ rightMost =
+ mView.getWidth() - mPositionMinSideMargin
+ - mNotificationStackScroller.getWidth() / 2;
+ if (Math.abs(x - mView.getWidth() / 2) < mNotificationStackScroller.getWidth() / 4) {
+ x = mView.getWidth() / 2;
+ }
+ x = Math.min(rightMost, Math.max(leftMost, x));
+ float
+ center =
+ mNotificationStackScroller.getLeft() + mNotificationStackScroller.getWidth() / 2;
+ setHorizontalPanelTranslation(x - center);
+ }
+
+ private void resetHorizontalPanelPosition() {
+ setHorizontalPanelTranslation(0f);
+ }
+
+ protected void setHorizontalPanelTranslation(float translation) {
+ mNotificationStackScroller.setTranslationX(translation);
+ mQsFrame.setTranslationX(translation);
+ int size = mVerticalTranslationListener.size();
+ for (int i = 0; i < size; i++) {
+ mVerticalTranslationListener.get(i).run();
+ }
+ }
+
+ protected void updateExpandedHeight(float expandedHeight) {
+ if (mTracking) {
+ mNotificationStackScroller.setExpandingVelocity(getCurrentExpandVelocity());
+ }
+ if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()) {
+ // The expandedHeight is always the full panel Height when bypassing
+ expandedHeight = getMaxPanelHeightNonBypass();
+ }
+ mNotificationStackScroller.setExpandedHeight(expandedHeight);
+ updateKeyguardBottomAreaAlpha();
+ updateBigClockAlpha();
+ updateStatusBarIcons();
+ }
+
+ /**
+ * @return whether the notifications are displayed full width and don't have any margins on
+ * the side.
+ */
+ public boolean isFullWidth() {
+ return mIsFullWidth;
+ }
+
+ private void updateStatusBarIcons() {
+ boolean
+ showIconsWhenExpanded =
+ (isPanelVisibleBecauseOfHeadsUp() || isFullWidth())
+ && getExpandedHeight() < getOpeningHeight();
+ boolean noVisibleNotifications = true;
+ if (showIconsWhenExpanded && noVisibleNotifications && isOnKeyguard()) {
+ showIconsWhenExpanded = false;
+ }
+ if (showIconsWhenExpanded != mShowIconsWhenExpanded) {
+ mShowIconsWhenExpanded = showIconsWhenExpanded;
+ mCommandQueue.recomputeDisableFlags(mDisplayId, false);
+ }
+ }
+
+ private boolean isOnKeyguard() {
+ return mBarState == StatusBarState.KEYGUARD;
+ }
+
+ public void setPanelScrimMinFraction(float minFraction) {
+ mBar.panelScrimMinFractionChanged(minFraction);
+ }
+
+ public void clearNotificationEffects() {
+ mStatusBar.clearNotificationEffects();
+ }
+
+ @Override
+ protected boolean isPanelVisibleBecauseOfHeadsUp() {
+ return (mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway)
+ && mBarState == StatusBarState.SHADE;
+ }
+
+ public void launchCamera(boolean animate, int source) {
+ if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) {
+ mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP;
+ } else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE) {
+ mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_WIGGLE;
+ } else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER) {
+ mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER;
+ } else {
+
+ // Default.
+ mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
+ }
+
+ // If we are launching it when we are occluded already we don't want it to animate,
+ // nor setting these flags, since the occluded state doesn't change anymore, hence it's
+ // never reset.
+ if (!isFullyCollapsed()) {
+ setLaunchingAffordance(true);
+ } else {
+ animate = false;
+ }
+ mAffordanceHasPreview = mKeyguardBottomArea.getRightPreview() != null;
+ mAffordanceHelper.launchAffordance(
+ animate, mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
+ }
+
+ public void onAffordanceLaunchEnded() {
+ setLaunchingAffordance(false);
+ }
+
+ /**
+ * Set whether we are currently launching an affordance. This is currently only set when
+ * launched via a camera gesture.
+ */
+ private void setLaunchingAffordance(boolean launchingAffordance) {
+ mLaunchingAffordance = launchingAffordance;
+ mKeyguardAffordanceHelperCallback.getLeftIcon().setLaunchingAffordance(launchingAffordance);
+ mKeyguardAffordanceHelperCallback.getRightIcon().setLaunchingAffordance(
+ launchingAffordance);
+ mKeyguardBypassController.setLaunchingAffordance(launchingAffordance);
+ if (mAffordanceLaunchListener != null) {
+ mAffordanceLaunchListener.accept(launchingAffordance);
+ }
+ }
+
+ /**
+ * Return true when a bottom affordance is launching an occluded activity with a splash screen.
+ */
+ public boolean isLaunchingAffordanceWithPreview() {
+ return mLaunchingAffordance && mAffordanceHasPreview;
+ }
+
+ /**
+ * Whether the camera application can be launched for the camera launch gesture.
+ */
+ public boolean canCameraGestureBeLaunched() {
+ if (!mStatusBar.isCameraAllowedByAdmin()) {
+ return false;
+ }
+
+ ResolveInfo resolveInfo = mKeyguardBottomArea.resolveCameraIntent();
+ String
+ packageToLaunch =
+ (resolveInfo == null || resolveInfo.activityInfo == null) ? null
+ : resolveInfo.activityInfo.packageName;
+ return packageToLaunch != null && (mBarState != StatusBarState.SHADE || !isForegroundApp(
+ packageToLaunch)) && !mAffordanceHelper.isSwipingInProgress();
+ }
+
+ /**
+ * Return true if the applications with the package name is running in foreground.
+ *
+ * @param pkgName application package name.
+ */
+ private boolean isForegroundApp(String pkgName) {
+ List<ActivityManager.RunningTaskInfo> tasks = mActivityManager.getRunningTasks(1);
+ return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName());
+ }
+
+ private void setGroupManager(NotificationGroupManager groupManager) {
+ mGroupManager = groupManager;
+ }
+
+ public boolean hideStatusBarIconsWhenExpanded() {
+ if (mLaunchingNotification) {
+ return mHideIconsDuringNotificationLaunch;
+ }
+ if (mHeadsUpAppearanceController != null
+ && mHeadsUpAppearanceController.shouldBeVisible()) {
+ return false;
+ }
+ return !isFullWidth() || !mShowIconsWhenExpanded;
+ }
+
+ private final FragmentListener mFragmentListener = new FragmentListener() {
+ @Override
+ public void onFragmentViewCreated(String tag, Fragment fragment) {
+ mQs = (QS) fragment;
+ mQs.setPanelView(mHeightListener);
+ mQs.setExpandClickListener(mOnClickListener);
+ mQs.setHeaderClickable(mQsExpansionEnabled);
+ updateQSPulseExpansion();
+ mQs.setOverscrolling(mStackScrollerOverscrolling);
+
+ // recompute internal state when qspanel height changes
+ mQs.getView().addOnLayoutChangeListener(
+ (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+ final int height = bottom - top;
+ final int oldHeight = oldBottom - oldTop;
+ if (height != oldHeight) {
+ mHeightListener.onQsHeightChanged();
+ }
+ });
+ mNotificationStackScroller.setQsContainer((ViewGroup) mQs.getView());
+ if (mQs instanceof QSFragment) {
+ mKeyguardStatusBar.setQSPanel(((QSFragment) mQs).getQsPanel());
+ }
+ updateQsExpansion();
+ }
+
+ @Override
+ public void onFragmentViewDestroyed(String tag, Fragment fragment) {
+ // Manual handling of fragment lifecycle is only required because this bridges
+ // non-fragment and fragment code. Once we are using a fragment for the notification
+ // panel, mQs will not need to be null cause it will be tied to the same lifecycle.
+ if (fragment == mQs) {
+ mQs = null;
+ }
+ }
+ };
+
+ @Override
+ public void setTouchAndAnimationDisabled(boolean disabled) {
+ super.setTouchAndAnimationDisabled(disabled);
+ if (disabled && mAffordanceHelper.isSwipingInProgress() && !mIsLaunchTransitionRunning) {
+ mAffordanceHelper.reset(false /* animate */);
+ }
+ mNotificationStackScroller.setAnimationsEnabled(!disabled);
+ }
+
+ /**
+ * Sets the dozing state.
+ *
+ * @param dozing {@code true} when dozing.
+ * @param animate if transition should be animated.
+ * @param wakeUpTouchLocation touch event location - if woken up by SLPI sensor.
+ */
+ public void setDozing(boolean dozing, boolean animate, PointF wakeUpTouchLocation) {
+ if (dozing == mDozing) return;
+ mView.setDozing(dozing);
+ mDozing = dozing;
+ mNotificationStackScroller.setDozing(mDozing, animate, wakeUpTouchLocation);
+ mKeyguardBottomArea.setDozing(mDozing, animate);
+
+ if (dozing) {
+ mBottomAreaShadeAlphaAnimator.cancel();
+ }
+
+ if (mBarState == StatusBarState.KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED) {
+ updateDozingVisibilities(animate);
+ }
+
+ final float dozeAmount = dozing ? 1 : 0;
+ mStatusBarStateController.setDozeAmount(dozeAmount, animate);
+ }
+
+ public void setPulsing(boolean pulsing) {
+ mPulsing = pulsing;
+ final boolean
+ animatePulse =
+ !mDozeParameters.getDisplayNeedsBlanking() && mDozeParameters.getAlwaysOn();
+ if (animatePulse) {
+ mAnimateNextPositionUpdate = true;
+ }
+ // Do not animate the clock when waking up from a pulse.
+ // The height callback will take care of pushing the clock to the right position.
+ if (!mPulsing && !mDozing) {
+ mAnimateNextPositionUpdate = false;
+ }
+ mNotificationStackScroller.setPulsing(pulsing, animatePulse);
+ mKeyguardStatusView.setPulsing(pulsing);
+ }
+
+ public void setAmbientIndicationBottomPadding(int ambientIndicationBottomPadding) {
+ if (mAmbientIndicationBottomPadding != ambientIndicationBottomPadding) {
+ mAmbientIndicationBottomPadding = ambientIndicationBottomPadding;
+ mStatusBar.updateKeyguardMaxNotifications();
+ }
+ }
+
+ public void dozeTimeTick() {
+ mKeyguardBottomArea.dozeTimeTick();
+ mKeyguardStatusView.dozeTimeTick();
+ if (mInterpolatedDarkAmount > 0) {
+ positionClockAndNotifications();
+ }
+ }
+
+ public void setStatusAccessibilityImportance(int mode) {
+ mKeyguardStatusView.setImportantForAccessibility(mode);
+ }
+
+ /**
+ * TODO: this should be removed.
+ * It's not correct to pass this view forward because other classes will end up adding
+ * children to it. Theme will be out of sync.
+ *
+ * @return bottom area view
+ */
+ public KeyguardBottomAreaView getKeyguardBottomAreaView() {
+ return mKeyguardBottomArea;
+ }
+
+ public void setUserSetupComplete(boolean userSetupComplete) {
+ mUserSetupComplete = userSetupComplete;
+ mKeyguardBottomArea.setUserSetupComplete(userSetupComplete);
+ }
+
+ public void applyExpandAnimationParams(ExpandAnimationParameters params) {
+ mExpandOffset = params != null ? params.getTopChange() : 0;
+ updateQsExpansion();
+ if (params != null) {
+ boolean hideIcons = params.getProgress(
+ ActivityLaunchAnimator.ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f;
+ if (hideIcons != mHideIconsDuringNotificationLaunch) {
+ mHideIconsDuringNotificationLaunch = hideIcons;
+ if (!hideIcons) {
+ mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */);
+ }
+ }
+ }
+ }
+
+ public void addTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) {
+ mTrackingHeadsUpListeners.add(listener);
+ }
+
+ public void removeTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) {
+ mTrackingHeadsUpListeners.remove(listener);
+ }
+
+ public void addVerticalTranslationListener(Runnable verticalTranslationListener) {
+ mVerticalTranslationListener.add(verticalTranslationListener);
+ }
+
+ public void removeVerticalTranslationListener(Runnable verticalTranslationListener) {
+ mVerticalTranslationListener.remove(verticalTranslationListener);
+ }
+
+ public void setHeadsUpAppearanceController(
+ HeadsUpAppearanceController headsUpAppearanceController) {
+ mHeadsUpAppearanceController = headsUpAppearanceController;
+ }
+
+ /**
+ * Starts the animation before we dismiss Keyguard, i.e. an disappearing animation on the
+ * security view of the bouncer.
+ */
+ public void onBouncerPreHideAnimation() {
+ setKeyguardStatusViewVisibility(mBarState, true /* keyguardFadingAway */,
+ false /* goingToFullShade */);
+ }
+
+ /**
+ * Do not let the user drag the shade up and down for the current touch session.
+ * This is necessary to avoid shade expansion while/after the bouncer is dismissed.
+ */
+ public void blockExpansionForCurrentTouch() {
+ mBlockingExpansionForCurrentTouch = mTracking;
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ super.dump(fd, pw, args);
+ pw.println(" gestureExclusionRect: " + calculateGestureExclusionRect());
+ if (mKeyguardStatusBar != null) {
+ mKeyguardStatusBar.dump(fd, pw, args);
+ }
+ if (mKeyguardStatusView != null) {
+ mKeyguardStatusView.dump(fd, pw, args);
+ }
+ }
+
+ public boolean hasActiveClearableNotifications() {
+ return mNotificationStackScroller.hasActiveClearableNotifications(ROWS_ALL);
+ }
+
+ private void updateShowEmptyShadeView() {
+ boolean
+ showEmptyShadeView =
+ mBarState != StatusBarState.KEYGUARD && !mEntryManager.hasActiveNotifications();
+ showEmptyShadeView(showEmptyShadeView);
+ }
+
+ public RemoteInputController.Delegate createRemoteInputDelegate() {
+ return mNotificationStackScroller.createDelegate();
+ }
+
+ public void updateNotificationViews() {
+ mNotificationStackScroller.updateSectionBoundaries();
+ mNotificationStackScroller.updateSpeedBumpIndex();
+ mNotificationStackScroller.updateFooter();
+ updateShowEmptyShadeView();
+ mNotificationStackScroller.updateIconAreaViews();
+ }
+
+ public void onUpdateRowStates() {
+ mNotificationStackScroller.onUpdateRowStates();
+ }
+
+ public boolean hasPulsingNotifications() {
+ return mNotificationStackScroller.hasPulsingNotifications();
+ }
+
+ public ActivatableNotificationView getActivatedChild() {
+ return mNotificationStackScroller.getActivatedChild();
+ }
+
+ public void setActivatedChild(ActivatableNotificationView o) {
+ mNotificationStackScroller.setActivatedChild(o);
+ }
+
+ public void runAfterAnimationFinished(Runnable r) {
+ mNotificationStackScroller.runAfterAnimationFinished(r);
+ }
+
+ public void setScrollingEnabled(boolean b) {
+ mNotificationStackScroller.setScrollingEnabled(b);
+ }
+
+ public void initDependencies(StatusBar statusBar, NotificationGroupManager groupManager,
+ NotificationShelf notificationShelf,
+ NotificationIconAreaController notificationIconAreaController,
+ ScrimController scrimController) {
+ setStatusBar(statusBar);
+ setGroupManager(mGroupManager);
+ mNotificationStackScroller.setNotificationPanelController(this);
+ mNotificationStackScroller.setIconAreaController(notificationIconAreaController);
+ mNotificationStackScroller.setStatusBar(statusBar);
+ mNotificationStackScroller.setGroupManager(groupManager);
+ mNotificationStackScroller.setShelf(notificationShelf);
+ mNotificationStackScroller.setScrimController(scrimController);
+ updateShowEmptyShadeView();
+ }
+
+ public void showTransientIndication(int id) {
+ mKeyguardIndicationController.showTransientIndication(id);
+ }
+
+ public void setOnReinflationListener(Runnable onReinflationListener) {
+ mOnReinflationListener = onReinflationListener;
+ }
+
+ public static boolean isQsSplitEnabled() {
+ return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.QS_SPLIT_ENABLED, false);
+ }
+
+ public void setAlpha(float alpha) {
+ mView.setAlpha(alpha);
+ }
+
+ public ViewPropertyAnimator fadeOut(long startDelayMs, long durationMs, Runnable endAction) {
+ return mView.animate().alpha(0).setStartDelay(startDelayMs).setDuration(
+ durationMs).setInterpolator(Interpolators.ALPHA_OUT).withLayer().withEndAction(
+ endAction);
+ }
+
+ public void resetViewGroupFade() {
+ ViewGroupFadeHelper.reset(mView);
+ }
+
+ public void addOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) {
+ mView.getViewTreeObserver().addOnGlobalLayoutListener(listener);
+ }
+
+ public void removeOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) {
+ mView.getViewTreeObserver().removeOnGlobalLayoutListener(listener);
+ }
+
+ public MyOnHeadsUpChangedListener getOnHeadsUpChangedListener() {
+ return mOnHeadsUpChangedListener;
+ }
+
+ public int getHeight() {
+ return mView.getHeight();
+ }
+
+ public TextView getHeaderDebugInfo() {
+ return mView.findViewById(R.id.header_debug_info);
+ }
+
+ public void onThemeChanged() {
+ mConfigurationListener.onThemeChanged();
+ }
+
+ @Override
+ public OnLayoutChangeListener createLayoutChangeListener() {
+ return new OnLayoutChangeListener();
+ }
+
+ public void setEmptyDragAmount(float amount) {
+ mExpansionCallback.setEmptyDragAmount(amount);
+ }
+
+ @Override
+ protected TouchHandler createTouchHandler() {
+ return new TouchHandler() {
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ if (mBlockTouches || mQsFullyExpanded && mQs.onInterceptTouchEvent(event)) {
+ return false;
+ }
+ initDownStates(event);
+ // Do not let touches go to shade or QS if the bouncer is visible,
+ // but still let user swipe down to expand the panel, dismissing the bouncer.
+ if (mStatusBar.isBouncerShowing()) {
+ return true;
+ }
+ if (mBar.panelEnabled() && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
+ mMetricsLogger.count(COUNTER_PANEL_OPEN, 1);
+ mMetricsLogger.count(COUNTER_PANEL_OPEN_PEEK, 1);
+ return true;
+ }
+ if (!shouldQuickSettingsIntercept(mDownX, mDownY, 0)
+ && mPulseExpansionHandler.onInterceptTouchEvent(event)) {
+ return true;
+ }
+
+ if (!isFullyCollapsed() && onQsIntercept(event)) {
+ return true;
+ }
+ return super.onInterceptTouchEvent(event);
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (mBlockTouches || (mQs != null && mQs.isCustomizing())) {
+ return false;
+ }
+
+ // Do not allow panel expansion if bouncer is scrimmed, otherwise user would be able
+ // to pull down QS or expand the shade.
+ if (mStatusBar.isBouncerShowingScrimmed()) {
+ return false;
+ }
+
+ // Make sure the next touch won't the blocked after the current ends.
+ if (event.getAction() == MotionEvent.ACTION_UP
+ || event.getAction() == MotionEvent.ACTION_CANCEL) {
+ mBlockingExpansionForCurrentTouch = false;
+ }
+ // When touch focus transfer happens, ACTION_DOWN->ACTION_UP may happen immediately
+ // without any ACTION_MOVE event.
+ // In such case, simply expand the panel instead of being stuck at the bottom bar.
+ if (mLastEventSynthesizedDown && event.getAction() == MotionEvent.ACTION_UP) {
+ expand(true /* animate */);
+ }
+ initDownStates(event);
+ if (!mIsExpanding && !shouldQuickSettingsIntercept(mDownX, mDownY, 0)
+ && mPulseExpansionHandler.onTouchEvent(event)) {
+ // We're expanding all the other ones shouldn't get this anymore
+ return true;
+ }
+ if (mListenForHeadsUp && !mHeadsUpTouchHelper.isTrackingHeadsUp()
+ && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
+ mMetricsLogger.count(COUNTER_PANEL_OPEN_PEEK, 1);
+ }
+ boolean handled = false;
+ if ((!mIsExpanding || mHintAnimationRunning) && !mQsExpanded
+ && mBarState != StatusBarState.SHADE && !mDozing) {
+ handled |= mAffordanceHelper.onTouchEvent(event);
+ }
+ if (mOnlyAffordanceInThisMotion) {
+ return true;
+ }
+ handled |= mHeadsUpTouchHelper.onTouchEvent(event);
+
+ if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && handleQsTouch(event)) {
+ return true;
+ }
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyCollapsed()) {
+ mMetricsLogger.count(COUNTER_PANEL_OPEN, 1);
+ updateVerticalPanelPosition(event.getX());
+ handled = true;
+ }
+ handled |= super.onTouch(v, event);
+ return !mDozing || mPulsing || handled;
+ }
+ };
+ }
+
+ @Override
+ protected PanelViewController.OnConfigurationChangedListener
+ createOnConfigurationChangedListener() {
+ return new OnConfigurationChangedListener();
+ }
+
+ private class OnHeightChangedListener implements ExpandableView.OnHeightChangedListener {
+ @Override
+ public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
+
+ // Block update if we are in quick settings and just the top padding changed
+ // (i.e. view == null).
+ if (view == null && mQsExpanded) {
+ return;
+ }
+ if (needsAnimation && mInterpolatedDarkAmount == 0) {
+ mAnimateNextPositionUpdate = true;
+ }
+ ExpandableView firstChildNotGone = mNotificationStackScroller.getFirstChildNotGone();
+ ExpandableNotificationRow
+ firstRow =
+ firstChildNotGone instanceof ExpandableNotificationRow
+ ? (ExpandableNotificationRow) firstChildNotGone : null;
+ if (firstRow != null && (view == firstRow || (firstRow.getNotificationParent()
+ == firstRow))) {
+ requestScrollerTopPaddingUpdate(false /* animate */);
+ }
+ requestPanelHeightUpdate();
+ }
+
+ @Override
+ public void onReset(ExpandableView view) {
+ }
+ }
+
+ private class OnClickListener implements View.OnClickListener {
+ @Override
+ public void onClick(View v) {
+ onQsExpansionStarted();
+ if (mQsExpanded) {
+ flingSettings(0 /* vel */, FLING_COLLAPSE, null /* onFinishRunnable */,
+ true /* isClick */);
+ } else if (mQsExpansionEnabled) {
+ mLockscreenGestureLogger.write(MetricsEvent.ACTION_SHADE_QS_TAP, 0, 0);
+ flingSettings(0 /* vel */, FLING_EXPAND, null /* onFinishRunnable */,
+ true /* isClick */);
+ }
+ }
+ }
+
+ private class OnOverscrollTopChangedListener implements
+ NotificationStackScrollLayout.OnOverscrollTopChangedListener {
+ @Override
+ public void onOverscrollTopChanged(float amount, boolean isRubberbanded) {
+ cancelQsAnimation();
+ if (!mQsExpansionEnabled) {
+ amount = 0f;
+ }
+ float rounded = amount >= 1f ? amount : 0f;
+ setOverScrolling(rounded != 0f && isRubberbanded);
+ mQsExpansionFromOverscroll = rounded != 0f;
+ mLastOverscroll = rounded;
+ updateQsState();
+ setQsExpansion(mQsMinExpansionHeight + rounded);
+ }
+
+ @Override
+ public void flingTopOverscroll(float velocity, boolean open) {
+ mLastOverscroll = 0f;
+ mQsExpansionFromOverscroll = false;
+ setQsExpansion(mQsExpansionHeight);
+ flingSettings(!mQsExpansionEnabled && open ? 0f : velocity,
+ open && mQsExpansionEnabled ? FLING_EXPAND : FLING_COLLAPSE, () -> {
+ mStackScrollerOverscrolling = false;
+ setOverScrolling(false);
+ updateQsState();
+ }, false /* isClick */);
+ }
+ }
+
+ private class DynamicPrivacyControlListener implements DynamicPrivacyController.Listener {
+ @Override
+ public void onDynamicPrivacyChanged() {
+ // Do not request animation when pulsing or waking up, otherwise the clock wiill be out
+ // of sync with the notification panel.
+ if (mLinearDarkAmount != 0) {
+ return;
+ }
+ mAnimateNextPositionUpdate = true;
+ }
+ }
+
+ private class KeyguardAffordanceHelperCallback implements KeyguardAffordanceHelper.Callback {
+ @Override
+ public void onAnimationToSideStarted(boolean rightPage, float translation, float vel) {
+ boolean
+ start =
+ mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? rightPage
+ : !rightPage;
+ mIsLaunchTransitionRunning = true;
+ mLaunchAnimationEndRunnable = null;
+ float displayDensity = mStatusBar.getDisplayDensity();
+ int lengthDp = Math.abs((int) (translation / displayDensity));
+ int velocityDp = Math.abs((int) (vel / displayDensity));
+ if (start) {
+ mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_DIALER, lengthDp, velocityDp);
+
+ mFalsingManager.onLeftAffordanceOn();
+ if (mFalsingManager.shouldEnforceBouncer()) {
+ mStatusBar.executeRunnableDismissingKeyguard(
+ () -> mKeyguardBottomArea.launchLeftAffordance(), null,
+ true /* dismissShade */, false /* afterKeyguardGone */,
+ true /* deferred */);
+ } else {
+ mKeyguardBottomArea.launchLeftAffordance();
+ }
+ } else {
+ if (KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE.equals(
+ mLastCameraLaunchSource)) {
+ mLockscreenGestureLogger.write(
+ MetricsEvent.ACTION_LS_CAMERA, lengthDp, velocityDp);
+ }
+ mFalsingManager.onCameraOn();
+ if (mFalsingManager.shouldEnforceBouncer()) {
+ mStatusBar.executeRunnableDismissingKeyguard(
+ () -> mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource), null,
+ true /* dismissShade */, false /* afterKeyguardGone */,
+ true /* deferred */);
+ } else {
+ mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource);
+ }
+ }
+ mStatusBar.startLaunchTransitionTimeout();
+ mBlockTouches = true;
+ }
+
+ @Override
+ public void onAnimationToSideEnded() {
+ mIsLaunchTransitionRunning = false;
+ mIsLaunchTransitionFinished = true;
+ if (mLaunchAnimationEndRunnable != null) {
+ mLaunchAnimationEndRunnable.run();
+ mLaunchAnimationEndRunnable = null;
+ }
+ mStatusBar.readyForKeyguardDone();
+ }
+
+ @Override
+ public float getMaxTranslationDistance() {
+ return (float) Math.hypot(mView.getWidth(), getHeight());
+ }
+
+ @Override
+ public void onSwipingStarted(boolean rightIcon) {
+ mFalsingManager.onAffordanceSwipingStarted(rightIcon);
+ boolean
+ camera =
+ mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? !rightIcon
+ : rightIcon;
+ if (camera) {
+ mKeyguardBottomArea.bindCameraPrewarmService();
+ }
+ mView.requestDisallowInterceptTouchEvent(true);
+ mOnlyAffordanceInThisMotion = true;
+ mQsTracking = false;
+ }
+
+ @Override
+ public void onSwipingAborted() {
+ mFalsingManager.onAffordanceSwipingAborted();
+ mKeyguardBottomArea.unbindCameraPrewarmService(false /* launched */);
+ }
+
+ @Override
+ public void onIconClicked(boolean rightIcon) {
+ if (mHintAnimationRunning) {
+ return;
+ }
+ mHintAnimationRunning = true;
+ mAffordanceHelper.startHintAnimation(rightIcon, () -> {
+ mHintAnimationRunning = false;
+ mStatusBar.onHintFinished();
+ });
+ rightIcon =
+ mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? !rightIcon
+ : rightIcon;
+ if (rightIcon) {
+ mStatusBar.onCameraHintStarted();
+ } else {
+ if (mKeyguardBottomArea.isLeftVoiceAssist()) {
+ mStatusBar.onVoiceAssistHintStarted();
+ } else {
+ mStatusBar.onPhoneHintStarted();
+ }
+ }
+ }
+
+ @Override
+ public KeyguardAffordanceView getLeftIcon() {
+ return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
+ ? mKeyguardBottomArea.getRightView() : mKeyguardBottomArea.getLeftView();
+ }
+
+ @Override
+ public KeyguardAffordanceView getRightIcon() {
+ return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
+ ? mKeyguardBottomArea.getLeftView() : mKeyguardBottomArea.getRightView();
+ }
+
+ @Override
+ public View getLeftPreview() {
+ return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
+ ? mKeyguardBottomArea.getRightPreview() : mKeyguardBottomArea.getLeftPreview();
+ }
+
+ @Override
+ public View getRightPreview() {
+ return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
+ ? mKeyguardBottomArea.getLeftPreview() : mKeyguardBottomArea.getRightPreview();
+ }
+
+ @Override
+ public float getAffordanceFalsingFactor() {
+ return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
+ }
+
+ @Override
+ public boolean needsAntiFalsing() {
+ return mBarState == StatusBarState.KEYGUARD;
+ }
+ }
+
+ private class OnEmptySpaceClickListener implements
+ NotificationStackScrollLayout.OnEmptySpaceClickListener {
+ @Override
+ public void onEmptySpaceClicked(float x, float y) {
+ onEmptySpaceClick(x);
+ }
+ }
+
+ private class MyOnHeadsUpChangedListener implements OnHeadsUpChangedListener {
+ @Override
+ public void onHeadsUpPinnedModeChanged(final boolean inPinnedMode) {
+ mNotificationStackScroller.setInHeadsUpPinnedMode(inPinnedMode);
+ if (inPinnedMode) {
+ mHeadsUpExistenceChangedRunnable.run();
+ updateNotificationTranslucency();
+ } else {
+ setHeadsUpAnimatingAway(true);
+ mNotificationStackScroller.runAfterAnimationFinished(
+ mHeadsUpExistenceChangedRunnable);
+ }
+ updateGestureExclusionRect();
+ mHeadsUpPinnedMode = inPinnedMode;
+ updateHeadsUpVisibility();
+ updateKeyguardStatusBarForHeadsUp();
+ }
+
+ @Override
+ public void onHeadsUpPinned(NotificationEntry entry) {
+ if (!isOnKeyguard()) {
+ mNotificationStackScroller.generateHeadsUpAnimation(entry.getHeadsUpAnimationView(),
+ true);
+ }
+ }
+
+ @Override
+ public void onHeadsUpUnPinned(NotificationEntry entry) {
+
+ // When we're unpinning the notification via active edge they remain heads-upped,
+ // we need to make sure that an animation happens in this case, otherwise the
+ // notification
+ // will stick to the top without any interaction.
+ if (isFullyCollapsed() && entry.isRowHeadsUp() && !isOnKeyguard()) {
+ mNotificationStackScroller.generateHeadsUpAnimation(
+ entry.getHeadsUpAnimationView(), false);
+ entry.setHeadsUpIsVisible();
+ }
+ }
+
+ @Override
+ public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
+ mNotificationStackScroller.generateHeadsUpAnimation(entry, isHeadsUp);
+ }
+ }
+
+ private class HeightListener implements QS.HeightListener {
+ public void onQsHeightChanged() {
+ mQsMaxExpansionHeight = mQs != null ? mQs.getDesiredHeight() : 0;
+ if (mQsExpanded && mQsFullyExpanded) {
+ mQsExpansionHeight = mQsMaxExpansionHeight;
+ requestScrollerTopPaddingUpdate(false /* animate */);
+ requestPanelHeightUpdate();
+ }
+ if (mAccessibilityManager.isEnabled()) {
+ mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
+ }
+ mNotificationStackScroller.setMaxTopPadding(
+ mQsMaxExpansionHeight + mQsNotificationTopPadding);
+ }
+ }
+
+ private class ZenModeControllerCallback implements ZenModeController.Callback {
+ @Override
+ public void onZenChanged(int zen) {
+ updateShowEmptyShadeView();
+ }
+ }
+
+ private class ConfigurationListener implements ConfigurationController.ConfigurationListener {
+ @Override
+ public void onDensityOrFontScaleChanged() {
+ updateShowEmptyShadeView();
+ }
+
+ @Override
+ public void onThemeChanged() {
+ final int themeResId = mView.getContext().getThemeResId();
+ if (mThemeResId == themeResId) {
+ return;
+ }
+ mThemeResId = themeResId;
+
+ reInflateViews();
+ }
+
+ @Override
+ public void onOverlayChanged() {
+ reInflateViews();
+ }
+
+ @Override
+ public void onUiModeChanged() {
+ reinflatePluginContainer();
+ }
+ }
+
+ private class StatusBarStateListener implements StateListener {
+ @Override
+ public void onStateChanged(int statusBarState) {
+ boolean goingToFullShade = mStatusBarStateController.goingToFullShade();
+ boolean keyguardFadingAway = mKeyguardStateController.isKeyguardFadingAway();
+ int oldState = mBarState;
+ boolean keyguardShowing = statusBarState == StatusBarState.KEYGUARD;
+ setKeyguardStatusViewVisibility(statusBarState, keyguardFadingAway, goingToFullShade);
+ setKeyguardBottomAreaVisibility(statusBarState, goingToFullShade);
+
+ mBarState = statusBarState;
+ mKeyguardShowing = keyguardShowing;
+ if (mKeyguardShowing && isQsSplitEnabled()) {
+ mNotificationStackScroller.setVisibility(View.VISIBLE);
+ mQsFrame.setVisibility(View.VISIBLE);
+ mHomeControlsLayout.setVisibility(View.GONE);
+ }
+
+ if (oldState == StatusBarState.KEYGUARD && (goingToFullShade
+ || statusBarState == StatusBarState.SHADE_LOCKED)) {
+ animateKeyguardStatusBarOut();
+ long
+ delay =
+ mBarState == StatusBarState.SHADE_LOCKED ? 0
+ : mKeyguardStateController.calculateGoingToFullShadeDelay();
+ mQs.animateHeaderSlidingIn(delay);
+ } else if (oldState == StatusBarState.SHADE_LOCKED
+ && statusBarState == StatusBarState.KEYGUARD) {
+ animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ mNotificationStackScroller.resetScrollPosition();
+ // Only animate header if the header is visible. If not, it will partially
+ // animate out
+ // the top of QS
+ if (!mQsExpanded) {
+ mQs.animateHeaderSlidingOut();
+ }
+ } else {
+ mKeyguardStatusBar.setAlpha(1f);
+ mKeyguardStatusBar.setVisibility(keyguardShowing ? View.VISIBLE : View.INVISIBLE);
+ ((PhoneStatusBarView) mBar).maybeShowDivider(keyguardShowing);
+ if (keyguardShowing && oldState != mBarState) {
+ if (mQs != null) {
+ mQs.hideImmediately();
+ }
+ }
+ }
+ updateKeyguardStatusBarForHeadsUp();
+ if (keyguardShowing) {
+ updateDozingVisibilities(false /* animate */);
+ }
+ // THe update needs to happen after the headerSlide in above, otherwise the translation
+ // would reset
+ updateQSPulseExpansion();
+ maybeAnimateBottomAreaAlpha();
+ resetHorizontalPanelPosition();
+ updateQsState();
+ }
+
+ @Override
+ public void onDozeAmountChanged(float linearAmount, float amount) {
+ mInterpolatedDarkAmount = amount;
+ mLinearDarkAmount = linearAmount;
+ mKeyguardStatusView.setDarkAmount(mInterpolatedDarkAmount);
+ mKeyguardBottomArea.setDarkAmount(mInterpolatedDarkAmount);
+ positionClockAndNotifications();
+ }
+ }
+
+ private class ExpansionCallback implements PulseExpansionHandler.ExpansionCallback {
+ public void setEmptyDragAmount(float amount) {
+ mEmptyDragAmount = amount * 0.2f;
+ positionClockAndNotifications();
+ }
+ }
+
+ private class OnAttachStateChangeListener implements View.OnAttachStateChangeListener {
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ FragmentHostManager.get(mView).addTagListener(QS.TAG, mFragmentListener);
+ mStatusBarStateController.addCallback(mStatusBarStateListener);
+ mZenModeController.addCallback(mZenModeControllerCallback);
+ mConfigurationController.addCallback(mConfigurationListener);
+ mUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
+ // Theme might have changed between inflating this view and attaching it to the
+ // window, so
+ // force a call to onThemeChanged
+ mConfigurationListener.onThemeChanged();
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ FragmentHostManager.get(mView).removeTagListener(QS.TAG, mFragmentListener);
+ mStatusBarStateController.removeCallback(mStatusBarStateListener);
+ mZenModeController.removeCallback(mZenModeControllerCallback);
+ mConfigurationController.removeCallback(mConfigurationListener);
+ mUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
+ }
+ }
+
+ private class OnLayoutChangeListener extends PanelViewController.OnLayoutChangeListener {
+
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
+ int oldTop, int oldRight, int oldBottom) {
+ DejankUtils.startDetectingBlockingIpcs("NVP#onLayout");
+ super.onLayoutChange(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom);
+ setIsFullWidth(mNotificationStackScroller.getWidth() == mView.getWidth());
+
+ // Update Clock Pivot
+ mKeyguardStatusView.setPivotX(mView.getWidth() / 2);
+ mKeyguardStatusView.setPivotY(
+ (FONT_HEIGHT - CAP_HEIGHT) / 2048f * mKeyguardStatusView.getClockTextSize());
+
+ // Calculate quick setting heights.
+ int oldMaxHeight = mQsMaxExpansionHeight;
+ if (mQs != null) {
+ mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQs.getQsMinExpansionHeight();
+ if (mNPVPluginManager != null) {
+ mNPVPluginManager.setYOffset(mQsMinExpansionHeight);
+ mQsMinExpansionHeight += mNPVPluginManager.getHeight();
+ }
+ mQsMaxExpansionHeight = mQs.getDesiredHeight();
+ mNotificationStackScroller.setMaxTopPadding(
+ mQsMaxExpansionHeight + mQsNotificationTopPadding);
+ }
+ positionClockAndNotifications();
+ if (mQsExpanded && mQsFullyExpanded) {
+ mQsExpansionHeight = mQsMaxExpansionHeight;
+ requestScrollerTopPaddingUpdate(false /* animate */);
+ requestPanelHeightUpdate();
+
+ // Size has changed, start an animation.
+ if (mQsMaxExpansionHeight != oldMaxHeight) {
+ startQsSizeChangeAnimation(oldMaxHeight, mQsMaxExpansionHeight);
+ }
+ } else if (!mQsExpanded) {
+ setQsExpansion(mQsMinExpansionHeight + mLastOverscroll);
+ }
+ updateExpandedHeight(getExpandedHeight());
+ updateHeader();
+
+ // If we are running a size change animation, the animation takes care of the height of
+ // the container. However, if we are not animating, we always need to make the QS
+ // container
+ // the desired height so when closing the QS detail, it stays smaller after the size
+ // change
+ // animation is finished but the detail view is still being animated away (this
+ // animation
+ // takes longer than the size change animation).
+ if (mQsSizeChangeAnimator == null && mQs != null) {
+ mQs.setHeightOverride(mQs.getDesiredHeight());
+ }
+ updateMaxHeadsUpTranslation();
+ updateGestureExclusionRect();
+ if (mExpandAfterLayoutRunnable != null) {
+ mExpandAfterLayoutRunnable.run();
+ mExpandAfterLayoutRunnable = null;
+ }
+ DejankUtils.stopDetectingBlockingIpcs("NVP#onLayout");
+ }
+ }
+
+ private class DebugDrawable extends Drawable {
+
+ @Override
+ public void draw(Canvas canvas) {
+ Paint p = new Paint();
+ p.setColor(Color.RED);
+ p.setStrokeWidth(2);
+ p.setStyle(Paint.Style.STROKE);
+ canvas.drawLine(0, getMaxPanelHeight(), mView.getWidth(), getMaxPanelHeight(), p);
+ p.setColor(Color.BLUE);
+ canvas.drawLine(0, getExpandedHeight(), mView.getWidth(), getExpandedHeight(), p);
+ p.setColor(Color.GREEN);
+ canvas.drawLine(0, calculatePanelHeightQsExpanded(), mView.getWidth(),
+ calculatePanelHeightQsExpanded(), p);
+ p.setColor(Color.YELLOW);
+ canvas.drawLine(0, calculatePanelHeightShade(), mView.getWidth(),
+ calculatePanelHeightShade(), p);
+ p.setColor(Color.MAGENTA);
+ canvas.drawLine(
+ 0, calculateQsTopPadding(), mView.getWidth(), calculateQsTopPadding(), p);
+ p.setColor(Color.CYAN);
+ canvas.drawLine(0, mClockPositionResult.stackScrollerPadding, mView.getWidth(),
+ mNotificationStackScroller.getTopPadding(), p);
+ p.setColor(Color.GRAY);
+ canvas.drawLine(0, mClockPositionResult.clockY, mView.getWidth(),
+ mClockPositionResult.clockY, p);
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+
+ }
+
+ @Override
+ public int getOpacity() {
+ return 0;
+ }
+ }
+
+ private class OnConfigurationChangedListener extends
+ PanelViewController.OnConfigurationChangedListener {
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ mAffordanceHelper.onConfigurationChanged();
+ if (newConfig.orientation != mLastOrientation) {
+ resetHorizontalPanelPosition();
+ }
+ mLastOrientation = newConfig.orientation;
+ }
+ }
+
+ private class OnApplyWindowInsetsListener implements View.OnApplyWindowInsetsListener {
+ public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
+ mNavigationBarBottomHeight = insets.getStableInsetBottom();
+ updateMaxHeadsUpTranslation();
+ return insets;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index 063d00b..8d8c8da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -43,7 +43,7 @@
public static final int STATE_OPENING = 1;
public static final int STATE_OPEN = 2;
- PanelView mPanel;
+ PanelViewController mPanel;
private int mState = STATE_CLOSED;
private boolean mTracking;
@@ -83,7 +83,8 @@
super.onFinishInflate();
}
- public void setPanel(PanelView pv) {
+ /** Set the PanelViewController */
+ public void setPanel(PanelViewController pv) {
mPanel = pv;
pv.setBar(this);
}
@@ -96,7 +97,7 @@
setImportantForAccessibility(important);
updateVisibility();
- if (mPanel != null) mPanel.setImportantForAccessibility(important);
+ if (mPanel != null) mPanel.getView().setImportantForAccessibility(important);
}
public float getExpansionFraction() {
@@ -108,7 +109,7 @@
}
protected void updateVisibility() {
- mPanel.setVisibility(shouldPanelBeVisible() ? VISIBLE : INVISIBLE);
+ mPanel.getView().setVisibility(shouldPanelBeVisible() ? VISIBLE : INVISIBLE);
}
protected boolean shouldPanelBeVisible() {
@@ -131,7 +132,7 @@
}
if (event.getAction() == MotionEvent.ACTION_DOWN) {
- final PanelView panel = mPanel;
+ final PanelViewController panel = mPanel;
if (panel == null) {
// panel is not there, so we'll eat the gesture
Log.v(TAG, String.format("onTouch: no panel for touch at (%d,%d)",
@@ -149,7 +150,7 @@
return true;
}
}
- return mPanel == null || mPanel.onTouchEvent(event);
+ return mPanel == null || mPanel.getView().dispatchTouchEvent(event);
}
public abstract void panelScrimMinFractionChanged(float minFraction);
@@ -163,7 +164,7 @@
boolean fullyClosed = true;
boolean fullyOpened = false;
if (SPEW) LOG("panelExpansionChanged: start state=%d", mState);
- PanelView pv = mPanel;
+ PanelViewController pv = mPanel;
mExpanded = expanded;
mPanelFraction = frac;
updateVisibility();
@@ -192,7 +193,7 @@
public void collapsePanel(boolean animate, boolean delayed, float speedUpFactor) {
boolean waiting = false;
- PanelView pv = mPanel;
+ PanelViewController pv = mPanel;
if (animate && !pv.isFullyCollapsed()) {
pv.collapse(delayed, speedUpFactor);
waiting = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index cd56d06..2719a32 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -16,1255 +16,62 @@
package com.android.systemui.statusbar.phone;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.os.SystemClock;
-import android.os.VibrationEffect;
import android.util.AttributeSet;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.InputDevice;
import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewTreeObserver;
-import android.view.animation.Interpolator;
import android.widget.FrameLayout;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.util.LatencyTracker;
-import com.android.systemui.DejankUtils;
-import com.android.systemui.Dependency;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.doze.DozeLog;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.statusbar.FlingAnimationUtils;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-
public abstract class PanelView extends FrameLayout {
public static final boolean DEBUG = PanelBar.DEBUG;
public static final String TAG = PanelView.class.getSimpleName();
- private static final int INITIAL_OPENING_PEEK_DURATION = 200;
- private static final int PEEK_ANIMATION_DURATION = 360;
- private static final int NO_FIXED_DURATION = -1;
- protected long mDownTime;
- protected boolean mTouchSlopExceededBeforeDown;
- private float mMinExpandHeight;
- private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
- private boolean mPanelUpdateWhenAnimatorEnds;
- private boolean mVibrateOnOpening;
- protected boolean mLaunchingNotification;
- private int mFixedDuration = NO_FIXED_DURATION;
- protected ArrayList<PanelExpansionListener> mExpansionListeners = new ArrayList<>();
-
- private final void logf(String fmt, Object... args) {
- Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
- }
+ private PanelViewController.TouchHandler mTouchHandler;
protected StatusBar mStatusBar;
protected HeadsUpManagerPhone mHeadsUpManager;
- private float mPeekHeight;
- private float mHintDistance;
- private float mInitialOffsetOnTouch;
- private boolean mCollapsedAndHeadsUpOnDown;
- private float mExpandedFraction = 0;
- protected float mExpandedHeight = 0;
- private boolean mPanelClosedOnDown;
- private boolean mHasLayoutedSinceDown;
- private float mUpdateFlingVelocity;
- private boolean mUpdateFlingOnLayout;
- private boolean mPeekTouching;
- private boolean mJustPeeked;
- private boolean mClosing;
- protected boolean mTracking;
- private boolean mTouchSlopExceeded;
- private int mTrackingPointer;
protected int mTouchSlop;
- protected boolean mHintAnimationRunning;
- private boolean mOverExpandedBeforeFling;
- private boolean mTouchAboveFalsingThreshold;
- private int mUnlockFalsingThreshold;
- private boolean mTouchStartedInEmptyArea;
- private boolean mMotionAborted;
- private boolean mUpwardsWhenTresholdReached;
- private boolean mAnimatingOnDown;
- private ValueAnimator mHeightAnimator;
- private ObjectAnimator mPeekAnimator;
- private final VelocityTracker mVelocityTracker = VelocityTracker.obtain();
- private FlingAnimationUtils mFlingAnimationUtils;
- private FlingAnimationUtils mFlingAnimationUtilsClosing;
- private FlingAnimationUtils mFlingAnimationUtilsDismissing;
- private final FalsingManager mFalsingManager;
- private final DozeLog mDozeLog;
- private final VibratorHelper mVibratorHelper;
-
- /**
- * Whether an instant expand request is currently pending and we are just waiting for layout.
- */
- private boolean mInstantExpanding;
- private boolean mAnimateAfterExpanding;
-
- PanelBar mBar;
-
- private String mViewName;
- private float mInitialTouchY;
- private float mInitialTouchX;
- private boolean mTouchDisabled;
-
- /**
- * Whether or not the PanelView can be expanded or collapsed with a drag.
- */
- private boolean mNotificationsDragEnabled;
-
- private Interpolator mBounceInterpolator;
protected KeyguardBottomAreaView mKeyguardBottomArea;
+ private OnConfigurationChangedListener mOnConfigurationChangedListener;
- /**
- * Speed-up factor to be used when {@link #mFlingCollapseRunnable} runs the next time.
- */
- private float mNextCollapseSpeedUpFactor = 1.0f;
-
- protected boolean mExpanding;
- private boolean mGestureWaitForTouchSlop;
- private boolean mIgnoreXTouchSlop;
- private boolean mExpandLatencyTracking;
- protected final KeyguardStateController mKeyguardStateController;
- protected final SysuiStatusBarStateController mStatusBarStateController;
-
- protected void onExpandingFinished() {
- mBar.onExpandingFinished();
+ public PanelView(Context context) {
+ super(context);
}
- protected void onExpandingStarted() {
- }
-
- private void notifyExpandingStarted() {
- if (!mExpanding) {
- mExpanding = true;
- onExpandingStarted();
- }
- }
-
- protected final void notifyExpandingFinished() {
- endClosing();
- if (mExpanding) {
- mExpanding = false;
- onExpandingFinished();
- }
- }
-
- private void runPeekAnimation(long duration, float peekHeight, boolean collapseWhenFinished) {
- mPeekHeight = peekHeight;
- if (DEBUG) logf("peek to height=%.1f", mPeekHeight);
- if (mHeightAnimator != null) {
- return;
- }
- if (mPeekAnimator != null) {
- mPeekAnimator.cancel();
- }
- mPeekAnimator = ObjectAnimator.ofFloat(this, "expandedHeight", mPeekHeight)
- .setDuration(duration);
- mPeekAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- mPeekAnimator.addListener(new AnimatorListenerAdapter() {
- private boolean mCancelled;
-
- @Override
- public void onAnimationCancel(Animator animation) {
- mCancelled = true;
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- mPeekAnimator = null;
- if (!mCancelled && collapseWhenFinished) {
- postOnAnimation(mPostCollapseRunnable);
- }
-
- }
- });
- notifyExpandingStarted();
- mPeekAnimator.start();
- mJustPeeked = true;
- }
-
- public PanelView(Context context, AttributeSet attrs, FalsingManager falsingManager,
- DozeLog dozeLog, KeyguardStateController keyguardStateController,
- SysuiStatusBarStateController statusBarStateController) {
+ public PanelView(Context context, AttributeSet attrs) {
super(context, attrs);
- mKeyguardStateController = keyguardStateController;
- mStatusBarStateController = statusBarStateController;
- DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
- mFlingAnimationUtils = new FlingAnimationUtils(displayMetrics,
- 0.6f /* maxLengthSeconds */, 0.6f /* speedUpFactor */);
- mFlingAnimationUtilsClosing = new FlingAnimationUtils(displayMetrics,
- 0.5f /* maxLengthSeconds */, 0.6f /* speedUpFactor */);
- mFlingAnimationUtilsDismissing = new FlingAnimationUtils(displayMetrics,
- 0.5f /* maxLengthSeconds */, 0.2f /* speedUpFactor */, 0.6f /* x2 */,
- 0.84f /* y2 */);
- mBounceInterpolator = new BounceInterpolator();
- mFalsingManager = falsingManager;
- mDozeLog = dozeLog;
- mNotificationsDragEnabled =
- getResources().getBoolean(R.bool.config_enableNotificationShadeDrag);
- mVibratorHelper = Dependency.get(VibratorHelper.class);
- mVibrateOnOpening = mContext.getResources().getBoolean(
- R.bool.config_vibrateOnIconAnimation);
}
- protected void loadDimens() {
- final Resources res = getContext().getResources();
- final ViewConfiguration configuration = ViewConfiguration.get(getContext());
- mTouchSlop = configuration.getScaledTouchSlop();
- mHintDistance = res.getDimension(R.dimen.hint_move_distance);
- mUnlockFalsingThreshold = res.getDimensionPixelSize(R.dimen.unlock_falsing_threshold);
+ public PanelView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
}
- private void addMovement(MotionEvent event) {
- // Add movement to velocity tracker using raw screen X and Y coordinates instead
- // of window coordinates because the window frame may be moving at the same time.
- float deltaX = event.getRawX() - event.getX();
- float deltaY = event.getRawY() - event.getY();
- event.offsetLocation(deltaX, deltaY);
- mVelocityTracker.addMovement(event);
- event.offsetLocation(-deltaX, -deltaY);
+ public PanelView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
}
- public void setTouchAndAnimationDisabled(boolean disabled) {
- mTouchDisabled = disabled;
- if (mTouchDisabled) {
- cancelHeightAnimator();
- if (mTracking) {
- onTrackingStopped(true /* expanded */);
- }
- notifyExpandingFinished();
- }
+ public void setOnTouchListener(PanelViewController.TouchHandler touchHandler) {
+ super.setOnTouchListener(touchHandler);
+ mTouchHandler = touchHandler;
}
- public void startExpandLatencyTracking() {
- if (LatencyTracker.isEnabled(mContext)) {
- LatencyTracker.getInstance(mContext).onActionStart(
- LatencyTracker.ACTION_EXPAND_PANEL);
- mExpandLatencyTracking = true;
- }
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (mInstantExpanding
- || (mTouchDisabled && event.getActionMasked() != MotionEvent.ACTION_CANCEL)
- || (mMotionAborted && event.getActionMasked() != MotionEvent.ACTION_DOWN)) {
- return false;
- }
-
- // If dragging should not expand the notifications shade, then return false.
- if (!mNotificationsDragEnabled) {
- if (mTracking) {
- // Turn off tracking if it's on or the shade can get stuck in the down position.
- onTrackingStopped(true /* expand */);
- }
- return false;
- }
-
- // On expanding, single mouse click expands the panel instead of dragging.
- if (isFullyCollapsed() && event.isFromSource(InputDevice.SOURCE_MOUSE)) {
- if (event.getAction() == MotionEvent.ACTION_UP) {
- expand(true);
- }
- return true;
- }
-
- /*
- * We capture touch events here and update the expand height here in case according to
- * the users fingers. This also handles multi-touch.
- *
- * If the user just clicks shortly, we show a quick peek of the shade.
- *
- * Flinging is also enabled in order to open or close the shade.
- */
-
- int pointerIndex = event.findPointerIndex(mTrackingPointer);
- if (pointerIndex < 0) {
- pointerIndex = 0;
- mTrackingPointer = event.getPointerId(pointerIndex);
- }
- final float x = event.getX(pointerIndex);
- final float y = event.getY(pointerIndex);
-
- if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
- mGestureWaitForTouchSlop = shouldGestureWaitForTouchSlop();
- mIgnoreXTouchSlop = isFullyCollapsed() || shouldGestureIgnoreXTouchSlop(x, y);
- }
-
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
- mJustPeeked = false;
- mMinExpandHeight = 0.0f;
- mPanelClosedOnDown = isFullyCollapsed();
- mHasLayoutedSinceDown = false;
- mUpdateFlingOnLayout = false;
- mMotionAborted = false;
- mPeekTouching = mPanelClosedOnDown;
- mDownTime = SystemClock.uptimeMillis();
- mTouchAboveFalsingThreshold = false;
- mCollapsedAndHeadsUpOnDown = isFullyCollapsed()
- && mHeadsUpManager.hasPinnedHeadsUp();
- addMovement(event);
- if (!mGestureWaitForTouchSlop || (mHeightAnimator != null && !mHintAnimationRunning)
- || mPeekAnimator != null) {
- mTouchSlopExceeded = (mHeightAnimator != null && !mHintAnimationRunning)
- || mPeekAnimator != null || mTouchSlopExceededBeforeDown;
- cancelHeightAnimator();
- cancelPeek();
- onTrackingStarted();
- }
- if (isFullyCollapsed() && !mHeadsUpManager.hasPinnedHeadsUp()
- && !mStatusBar.isBouncerShowing()) {
- startOpening(event);
- }
- break;
-
- case MotionEvent.ACTION_POINTER_UP:
- final int upPointer = event.getPointerId(event.getActionIndex());
- if (mTrackingPointer == upPointer) {
- // gesture is ongoing, find a new pointer to track
- final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
- final float newY = event.getY(newIndex);
- final float newX = event.getX(newIndex);
- mTrackingPointer = event.getPointerId(newIndex);
- startExpandMotion(newX, newY, true /* startTracking */, mExpandedHeight);
- }
- break;
- case MotionEvent.ACTION_POINTER_DOWN:
- if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
- mMotionAborted = true;
- endMotionEvent(event, x, y, true /* forceCancel */);
- return false;
- }
- break;
- case MotionEvent.ACTION_MOVE:
- addMovement(event);
- float h = y - mInitialTouchY;
-
- // If the panel was collapsed when touching, we only need to check for the
- // y-component of the gesture, as we have no conflicting horizontal gesture.
- if (Math.abs(h) > mTouchSlop
- && (Math.abs(h) > Math.abs(x - mInitialTouchX)
- || mIgnoreXTouchSlop)) {
- mTouchSlopExceeded = true;
- if (mGestureWaitForTouchSlop && !mTracking && !mCollapsedAndHeadsUpOnDown) {
- if (!mJustPeeked && mInitialOffsetOnTouch != 0f) {
- startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
- h = 0;
- }
- cancelHeightAnimator();
- onTrackingStarted();
- }
- }
- float newHeight = Math.max(0, h + mInitialOffsetOnTouch);
- if (newHeight > mPeekHeight) {
- if (mPeekAnimator != null) {
- mPeekAnimator.cancel();
- }
- mJustPeeked = false;
- } else if (mPeekAnimator == null && mJustPeeked) {
- // The initial peek has finished, but we haven't dragged as far yet, lets
- // speed it up by starting at the peek height.
- mInitialOffsetOnTouch = mExpandedHeight;
- mInitialTouchY = y;
- mMinExpandHeight = mExpandedHeight;
- mJustPeeked = false;
- }
- newHeight = Math.max(newHeight, mMinExpandHeight);
- if (-h >= getFalsingThreshold()) {
- mTouchAboveFalsingThreshold = true;
- mUpwardsWhenTresholdReached = isDirectionUpwards(x, y);
- }
- if (!mJustPeeked && (!mGestureWaitForTouchSlop || mTracking) &&
- !isTrackingBlocked()) {
- setExpandedHeightInternal(newHeight);
- }
- break;
-
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- addMovement(event);
- endMotionEvent(event, x, y, false /* forceCancel */);
- break;
- }
- return !mGestureWaitForTouchSlop || mTracking;
- }
-
- private void startOpening(MotionEvent event) {
- runPeekAnimation(INITIAL_OPENING_PEEK_DURATION, getOpeningHeight(),
- false /* collapseWhenFinished */);
- notifyBarPanelExpansionChanged();
- maybeVibrateOnOpening();
-
- //TODO: keyguard opens QS a different way; log that too?
-
- // Log the position of the swipe that opened the panel
- float width = mStatusBar.getDisplayWidth();
- float height = mStatusBar.getDisplayHeight();
- int rot = mStatusBar.getRotation();
-
- mLockscreenGestureLogger.writeAtFractionalPosition(MetricsEvent.ACTION_PANEL_VIEW_EXPAND,
- (int) (event.getX() / width * 100),
- (int) (event.getY() / height * 100),
- rot);
- }
-
- protected void maybeVibrateOnOpening() {
- if (mVibrateOnOpening) {
- mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
- }
- }
-
- protected abstract float getOpeningHeight();
-
- /**
- * @return whether the swiping direction is upwards and above a 45 degree angle compared to the
- * horizontal direction
- */
- private boolean isDirectionUpwards(float x, float y) {
- float xDiff = x - mInitialTouchX;
- float yDiff = y - mInitialTouchY;
- if (yDiff >= 0) {
- return false;
- }
- return Math.abs(yDiff) >= Math.abs(xDiff);
- }
-
- protected void startExpandingFromPeek() {
- mStatusBar.handlePeekToExpandTransistion();
- }
-
- protected void startExpandMotion(float newX, float newY, boolean startTracking,
- float expandedHeight) {
- mInitialOffsetOnTouch = expandedHeight;
- mInitialTouchY = newY;
- mInitialTouchX = newX;
- if (startTracking) {
- mTouchSlopExceeded = true;
- setExpandedHeight(mInitialOffsetOnTouch);
- onTrackingStarted();
- }
- }
-
- private void endMotionEvent(MotionEvent event, float x, float y, boolean forceCancel) {
- mTrackingPointer = -1;
- if ((mTracking && mTouchSlopExceeded)
- || Math.abs(x - mInitialTouchX) > mTouchSlop
- || Math.abs(y - mInitialTouchY) > mTouchSlop
- || event.getActionMasked() == MotionEvent.ACTION_CANCEL
- || forceCancel) {
- mVelocityTracker.computeCurrentVelocity(1000);
- float vel = mVelocityTracker.getYVelocity();
- float vectorVel = (float) Math.hypot(
- mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
-
- boolean expand = flingExpands(vel, vectorVel, x, y)
- || event.getActionMasked() == MotionEvent.ACTION_CANCEL
- || forceCancel;
- mDozeLog.traceFling(expand, mTouchAboveFalsingThreshold,
- mStatusBar.isFalsingThresholdNeeded(),
- mStatusBar.isWakeUpComingFromTouch());
- // Log collapse gesture if on lock screen.
- if (!expand && mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
- float displayDensity = mStatusBar.getDisplayDensity();
- int heightDp = (int) Math.abs((y - mInitialTouchY) / displayDensity);
- int velocityDp = (int) Math.abs(vel / displayDensity);
- mLockscreenGestureLogger.write(
- MetricsEvent.ACTION_LS_UNLOCK,
- heightDp, velocityDp);
- }
- fling(vel, expand, isFalseTouch(x, y));
- onTrackingStopped(expand);
- mUpdateFlingOnLayout = expand && mPanelClosedOnDown && !mHasLayoutedSinceDown;
- if (mUpdateFlingOnLayout) {
- mUpdateFlingVelocity = vel;
- }
- } else if (mPanelClosedOnDown && !mHeadsUpManager.hasPinnedHeadsUp() && !mTracking
- && !mStatusBar.isBouncerShowing()
- && !mKeyguardStateController.isKeyguardFadingAway()) {
- long timePassed = SystemClock.uptimeMillis() - mDownTime;
- if (timePassed < ViewConfiguration.getLongPressTimeout()) {
- // Lets show the user that he can actually expand the panel
- runPeekAnimation(PEEK_ANIMATION_DURATION, getPeekHeight(), true /* collapseWhenFinished */);
- } else {
- // We need to collapse the panel since we peeked to the small height.
- postOnAnimation(mPostCollapseRunnable);
- }
- } else if (!mStatusBar.isBouncerShowing()) {
- boolean expands = onEmptySpaceClick(mInitialTouchX);
- onTrackingStopped(expands);
- }
-
- mVelocityTracker.clear();
- mPeekTouching = false;
- }
-
- protected float getCurrentExpandVelocity() {
- mVelocityTracker.computeCurrentVelocity(1000);
- return mVelocityTracker.getYVelocity();
- }
-
- private int getFalsingThreshold() {
- float factor = mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
- return (int) (mUnlockFalsingThreshold * factor);
- }
-
- protected abstract boolean shouldGestureWaitForTouchSlop();
-
- protected abstract boolean shouldGestureIgnoreXTouchSlop(float x, float y);
-
- protected void onTrackingStopped(boolean expand) {
- mTracking = false;
- mBar.onTrackingStopped(expand);
- notifyBarPanelExpansionChanged();
- }
-
- protected void onTrackingStarted() {
- endClosing();
- mTracking = true;
- mBar.onTrackingStarted();
- notifyExpandingStarted();
- notifyBarPanelExpansionChanged();
+ public void setOnConfigurationChangedListener(OnConfigurationChangedListener listener) {
+ mOnConfigurationChangedListener = listener;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
- if (mInstantExpanding || !mNotificationsDragEnabled || mTouchDisabled
- || (mMotionAborted && event.getActionMasked() != MotionEvent.ACTION_DOWN)) {
- return false;
- }
-
- /*
- * If the user drags anywhere inside the panel we intercept it if the movement is
- * upwards. This allows closing the shade from anywhere inside the panel.
- *
- * We only do this if the current content is scrolled to the bottom,
- * i.e isScrolledToBottom() is true and therefore there is no conflicting scrolling gesture
- * possible.
- */
- int pointerIndex = event.findPointerIndex(mTrackingPointer);
- if (pointerIndex < 0) {
- pointerIndex = 0;
- mTrackingPointer = event.getPointerId(pointerIndex);
- }
- final float x = event.getX(pointerIndex);
- final float y = event.getY(pointerIndex);
- boolean scrolledToBottom = isScrolledToBottom();
-
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- mStatusBar.userActivity();
- mAnimatingOnDown = mHeightAnimator != null;
- mMinExpandHeight = 0.0f;
- mDownTime = SystemClock.uptimeMillis();
- if (mAnimatingOnDown && mClosing && !mHintAnimationRunning
- || mPeekAnimator != null) {
- cancelHeightAnimator();
- cancelPeek();
- mTouchSlopExceeded = true;
- return true;
- }
- mInitialTouchY = y;
- mInitialTouchX = x;
- mTouchStartedInEmptyArea = !isInContentBounds(x, y);
- mTouchSlopExceeded = mTouchSlopExceededBeforeDown;
- mJustPeeked = false;
- mMotionAborted = false;
- mPanelClosedOnDown = isFullyCollapsed();
- mCollapsedAndHeadsUpOnDown = false;
- mHasLayoutedSinceDown = false;
- mUpdateFlingOnLayout = false;
- mTouchAboveFalsingThreshold = false;
- addMovement(event);
- break;
- case MotionEvent.ACTION_POINTER_UP:
- final int upPointer = event.getPointerId(event.getActionIndex());
- if (mTrackingPointer == upPointer) {
- // gesture is ongoing, find a new pointer to track
- final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
- mTrackingPointer = event.getPointerId(newIndex);
- mInitialTouchX = event.getX(newIndex);
- mInitialTouchY = event.getY(newIndex);
- }
- break;
- case MotionEvent.ACTION_POINTER_DOWN:
- if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
- mMotionAborted = true;
- mVelocityTracker.clear();
- }
- break;
- case MotionEvent.ACTION_MOVE:
- final float h = y - mInitialTouchY;
- addMovement(event);
- if (scrolledToBottom || mTouchStartedInEmptyArea || mAnimatingOnDown) {
- float hAbs = Math.abs(h);
- if ((h < -mTouchSlop || (mAnimatingOnDown && hAbs > mTouchSlop))
- && hAbs > Math.abs(x - mInitialTouchX)) {
- cancelHeightAnimator();
- startExpandMotion(x, y, true /* startTracking */, mExpandedHeight);
- return true;
- }
- }
- break;
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP:
- mVelocityTracker.clear();
- break;
- }
- return false;
- }
-
- /**
- * @return Whether a pair of coordinates are inside the visible view content bounds.
- */
- protected abstract boolean isInContentBounds(float x, float y);
-
- protected void cancelHeightAnimator() {
- if (mHeightAnimator != null) {
- if (mHeightAnimator.isRunning()) {
- mPanelUpdateWhenAnimatorEnds = false;
- }
- mHeightAnimator.cancel();
- }
- endClosing();
- }
-
- private void endClosing() {
- if (mClosing) {
- mClosing = false;
- onClosingFinished();
- }
- }
-
- protected boolean isScrolledToBottom() {
- return true;
- }
-
- protected float getContentHeight() {
- return mExpandedHeight;
+ return mTouchHandler.onInterceptTouchEvent(event);
}
@Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- loadDimens();
+ public void dispatchConfigurationChanged(Configuration newConfig) {
+ super.dispatchConfigurationChanged(newConfig);
+ mOnConfigurationChangedListener.onConfigurationChanged(newConfig);
}
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- loadDimens();
- }
-
- /**
- * @param vel the current vertical velocity of the motion
- * @param vectorVel the length of the vectorial velocity
- * @return whether a fling should expands the panel; contracts otherwise
- */
- protected boolean flingExpands(float vel, float vectorVel, float x, float y) {
- if (mFalsingManager.isUnlockingDisabled()) {
- return true;
- }
-
- if (isFalseTouch(x, y)) {
- return true;
- }
- if (Math.abs(vectorVel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
- return shouldExpandWhenNotFlinging();
- } else {
- return vel > 0;
- }
- }
-
- protected boolean shouldExpandWhenNotFlinging() {
- return getExpandedFraction() > 0.5f;
- }
-
- /**
- * @param x the final x-coordinate when the finger was lifted
- * @param y the final y-coordinate when the finger was lifted
- * @return whether this motion should be regarded as a false touch
- */
- private boolean isFalseTouch(float x, float y) {
- if (!mStatusBar.isFalsingThresholdNeeded()) {
- return false;
- }
- if (mFalsingManager.isClassifierEnabled()) {
- return mFalsingManager.isFalseTouch();
- }
- if (!mTouchAboveFalsingThreshold) {
- return true;
- }
- if (mUpwardsWhenTresholdReached) {
- return false;
- }
- return !isDirectionUpwards(x, y);
- }
-
- protected void fling(float vel, boolean expand) {
- fling(vel, expand, 1.0f /* collapseSpeedUpFactor */, false);
- }
-
- protected void fling(float vel, boolean expand, boolean expandBecauseOfFalsing) {
- fling(vel, expand, 1.0f /* collapseSpeedUpFactor */, expandBecauseOfFalsing);
- }
-
- protected void fling(float vel, boolean expand, float collapseSpeedUpFactor,
- boolean expandBecauseOfFalsing) {
- cancelPeek();
- float target = expand ? getMaxPanelHeight() : 0;
- if (!expand) {
- mClosing = true;
- }
- flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing);
- }
-
- protected void flingToHeight(float vel, boolean expand, float target,
- float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) {
- // Hack to make the expand transition look nice when clear all button is visible - we make
- // the animation only to the last notification, and then jump to the maximum panel height so
- // clear all just fades in and the decelerating motion is towards the last notification.
- final boolean clearAllExpandHack = expand && fullyExpandedClearAllVisible()
- && mExpandedHeight < getMaxPanelHeight() - getClearAllHeight()
- && !isClearAllVisible();
- if (clearAllExpandHack) {
- target = getMaxPanelHeight() - getClearAllHeight();
- }
- if (target == mExpandedHeight || getOverExpansionAmount() > 0f && expand) {
- notifyExpandingFinished();
- return;
- }
- mOverExpandedBeforeFling = getOverExpansionAmount() > 0f;
- ValueAnimator animator = createHeightAnimator(target);
- if (expand) {
- if (expandBecauseOfFalsing && vel < 0) {
- vel = 0;
- }
- mFlingAnimationUtils.apply(animator, mExpandedHeight, target, vel, getHeight());
- if (vel == 0) {
- animator.setDuration(350);
- }
- } else {
- if (shouldUseDismissingAnimation()) {
- if (vel == 0) {
- animator.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED);
- long duration = (long) (200 + mExpandedHeight / getHeight() * 100);
- animator.setDuration(duration);
- } else {
- mFlingAnimationUtilsDismissing.apply(animator, mExpandedHeight, target, vel,
- getHeight());
- }
- } else {
- mFlingAnimationUtilsClosing
- .apply(animator, mExpandedHeight, target, vel, getHeight());
- }
-
- // Make it shorter if we run a canned animation
- if (vel == 0) {
- animator.setDuration((long) (animator.getDuration() / collapseSpeedUpFactor));
- }
- if (mFixedDuration != NO_FIXED_DURATION) {
- animator.setDuration(mFixedDuration);
- }
- }
- animator.addListener(new AnimatorListenerAdapter() {
- private boolean mCancelled;
-
- @Override
- public void onAnimationCancel(Animator animation) {
- mCancelled = true;
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (clearAllExpandHack && !mCancelled) {
- setExpandedHeightInternal(getMaxPanelHeight());
- }
- setAnimator(null);
- if (!mCancelled) {
- notifyExpandingFinished();
- }
- notifyBarPanelExpansionChanged();
- }
- });
- setAnimator(animator);
- animator.start();
- }
-
- protected abstract boolean shouldUseDismissingAnimation();
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- mViewName = getResources().getResourceName(getId());
- }
-
- public String getName() {
- return mViewName;
- }
-
- public void setExpandedHeight(float height) {
- if (DEBUG) logf("setExpandedHeight(%.1f)", height);
- setExpandedHeightInternal(height + getOverExpansionPixels());
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- mStatusBar.onPanelLaidOut();
- requestPanelHeightUpdate();
- mHasLayoutedSinceDown = true;
- if (mUpdateFlingOnLayout) {
- abortAnimations();
- fling(mUpdateFlingVelocity, true /* expands */);
- mUpdateFlingOnLayout = false;
- }
- }
-
- protected void requestPanelHeightUpdate() {
- float currentMaxPanelHeight = getMaxPanelHeight();
-
- if (isFullyCollapsed()) {
- return;
- }
-
- if (currentMaxPanelHeight == mExpandedHeight) {
- return;
- }
-
- if (mPeekAnimator != null || mPeekTouching) {
- return;
- }
-
- if (mTracking && !isTrackingBlocked()) {
- return;
- }
-
- if (mHeightAnimator != null) {
- mPanelUpdateWhenAnimatorEnds = true;
- return;
- }
-
- setExpandedHeight(currentMaxPanelHeight);
- }
-
- public void setExpandedHeightInternal(float h) {
- if (mExpandLatencyTracking && h != 0f) {
- DejankUtils.postAfterTraversal(() -> LatencyTracker.getInstance(mContext).onActionEnd(
- LatencyTracker.ACTION_EXPAND_PANEL));
- mExpandLatencyTracking = false;
- }
- float fhWithoutOverExpansion = getMaxPanelHeight() - getOverExpansionAmount();
- if (mHeightAnimator == null) {
- float overExpansionPixels = Math.max(0, h - fhWithoutOverExpansion);
- if (getOverExpansionPixels() != overExpansionPixels && mTracking) {
- setOverExpansion(overExpansionPixels, true /* isPixels */);
- }
- mExpandedHeight = Math.min(h, fhWithoutOverExpansion) + getOverExpansionAmount();
- } else {
- mExpandedHeight = h;
- if (mOverExpandedBeforeFling) {
- setOverExpansion(Math.max(0, h - fhWithoutOverExpansion), false /* isPixels */);
- }
- }
-
- // If we are closing the panel and we are almost there due to a slow decelerating
- // interpolator, abort the animation.
- if (mExpandedHeight < 1f && mExpandedHeight != 0f && mClosing) {
- mExpandedHeight = 0f;
- if (mHeightAnimator != null) {
- mHeightAnimator.end();
- }
- }
- mExpandedFraction = Math.min(1f,
- fhWithoutOverExpansion == 0 ? 0 : mExpandedHeight / fhWithoutOverExpansion);
- onHeightUpdated(mExpandedHeight);
- notifyBarPanelExpansionChanged();
- }
-
- /**
- * @return true if the panel tracking should be temporarily blocked; this is used when a
- * conflicting gesture (opening QS) is happening
- */
- protected abstract boolean isTrackingBlocked();
-
- protected abstract void setOverExpansion(float overExpansion, boolean isPixels);
-
- protected abstract void onHeightUpdated(float expandedHeight);
-
- protected abstract float getOverExpansionAmount();
-
- protected abstract float getOverExpansionPixels();
-
- /**
- * This returns the maximum height of the panel. Children should override this if their
- * desired height is not the full height.
- *
- * @return the default implementation simply returns the maximum height.
- */
- protected abstract int getMaxPanelHeight();
-
- public void setExpandedFraction(float frac) {
- setExpandedHeight(getMaxPanelHeight() * frac);
- }
-
- public float getExpandedHeight() {
- return mExpandedHeight;
- }
-
- public float getExpandedFraction() {
- return mExpandedFraction;
- }
-
- public boolean isFullyExpanded() {
- return mExpandedHeight >= getMaxPanelHeight();
- }
-
- public boolean isFullyCollapsed() {
- return mExpandedFraction <= 0.0f;
- }
-
- public boolean isCollapsing() {
- return mClosing || mLaunchingNotification;
- }
-
- public boolean isTracking() {
- return mTracking;
- }
-
- public void setBar(PanelBar panelBar) {
- mBar = panelBar;
- }
-
- public void collapse(boolean delayed, float speedUpFactor) {
- if (DEBUG) logf("collapse: " + this);
- if (canPanelBeCollapsed()) {
- cancelHeightAnimator();
- notifyExpandingStarted();
-
- // Set after notifyExpandingStarted, as notifyExpandingStarted resets the closing state.
- mClosing = true;
- if (delayed) {
- mNextCollapseSpeedUpFactor = speedUpFactor;
- postDelayed(mFlingCollapseRunnable, 120);
- } else {
- fling(0, false /* expand */, speedUpFactor, false /* expandBecauseOfFalsing */);
- }
- }
- }
-
- public boolean canPanelBeCollapsed() {
- return !isFullyCollapsed() && !mTracking && !mClosing;
- }
-
- private final Runnable mFlingCollapseRunnable = new Runnable() {
- @Override
- public void run() {
- fling(0, false /* expand */, mNextCollapseSpeedUpFactor,
- false /* expandBecauseOfFalsing */);
- }
- };
-
- public void cancelPeek() {
- boolean cancelled = false;
- if (mPeekAnimator != null) {
- cancelled = true;
- mPeekAnimator.cancel();
- }
-
- if (cancelled) {
- // When peeking, we already tell mBar that we expanded ourselves. Make sure that we also
- // notify mBar that we might have closed ourselves.
- notifyBarPanelExpansionChanged();
- }
- }
-
- public void expand(final boolean animate) {
- if (!isFullyCollapsed() && !isCollapsing()) {
- return;
- }
-
- mInstantExpanding = true;
- mAnimateAfterExpanding = animate;
- mUpdateFlingOnLayout = false;
- abortAnimations();
- cancelPeek();
- if (mTracking) {
- onTrackingStopped(true /* expands */); // The panel is expanded after this call.
- }
- if (mExpanding) {
- notifyExpandingFinished();
- }
- notifyBarPanelExpansionChanged();
-
- // Wait for window manager to pickup the change, so we know the maximum height of the panel
- // then.
- getViewTreeObserver().addOnGlobalLayoutListener(
- new ViewTreeObserver.OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- if (!mInstantExpanding) {
- getViewTreeObserver().removeOnGlobalLayoutListener(this);
- return;
- }
- if (mStatusBar.getStatusBarWindow().getHeight()
- != mStatusBar.getStatusBarHeight()) {
- getViewTreeObserver().removeOnGlobalLayoutListener(this);
- if (mAnimateAfterExpanding) {
- notifyExpandingStarted();
- fling(0, true /* expand */);
- } else {
- setExpandedFraction(1f);
- }
- mInstantExpanding = false;
- }
- }
- });
-
- // Make sure a layout really happens.
- requestLayout();
- }
-
- public void instantCollapse() {
- abortAnimations();
- setExpandedFraction(0f);
- if (mExpanding) {
- notifyExpandingFinished();
- }
- if (mInstantExpanding) {
- mInstantExpanding = false;
- notifyBarPanelExpansionChanged();
- }
- }
-
- private void abortAnimations() {
- cancelPeek();
- cancelHeightAnimator();
- removeCallbacks(mPostCollapseRunnable);
- removeCallbacks(mFlingCollapseRunnable);
- }
-
- protected void onClosingFinished() {
- mBar.onClosingFinished();
- }
-
-
- protected void startUnlockHintAnimation() {
-
- // We don't need to hint the user if an animation is already running or the user is changing
- // the expansion.
- if (mHeightAnimator != null || mTracking) {
- return;
- }
- cancelPeek();
- notifyExpandingStarted();
- startUnlockHintAnimationPhase1(() -> {
- notifyExpandingFinished();
- onUnlockHintFinished();
- mHintAnimationRunning = false;
- });
- onUnlockHintStarted();
- mHintAnimationRunning = true;
- }
-
- protected void onUnlockHintFinished() {
- mStatusBar.onHintFinished();
- }
-
- protected void onUnlockHintStarted() {
- mStatusBar.onUnlockHintStarted();
- }
-
- public boolean isUnlockHintRunning() {
- return mHintAnimationRunning;
- }
-
- /**
- * Phase 1: Move everything upwards.
- */
- private void startUnlockHintAnimationPhase1(final Runnable onAnimationFinished) {
- float target = Math.max(0, getMaxPanelHeight() - mHintDistance);
- ValueAnimator animator = createHeightAnimator(target);
- animator.setDuration(250);
- animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
- animator.addListener(new AnimatorListenerAdapter() {
- private boolean mCancelled;
-
- @Override
- public void onAnimationCancel(Animator animation) {
- mCancelled = true;
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (mCancelled) {
- setAnimator(null);
- onAnimationFinished.run();
- } else {
- startUnlockHintAnimationPhase2(onAnimationFinished);
- }
- }
- });
- animator.start();
- setAnimator(animator);
-
- View[] viewsToAnimate = {
- mKeyguardBottomArea.getIndicationArea(),
- mStatusBar.getAmbientIndicationContainer()};
- for (View v : viewsToAnimate) {
- if (v == null) {
- continue;
- }
- v.animate()
- .translationY(-mHintDistance)
- .setDuration(250)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .withEndAction(() -> v.animate()
- .translationY(0)
- .setDuration(450)
- .setInterpolator(mBounceInterpolator)
- .start())
- .start();
- }
- }
-
- private void setAnimator(ValueAnimator animator) {
- mHeightAnimator = animator;
- if (animator == null && mPanelUpdateWhenAnimatorEnds) {
- mPanelUpdateWhenAnimatorEnds = false;
- requestPanelHeightUpdate();
- }
- }
-
- /**
- * Phase 2: Bounce down.
- */
- private void startUnlockHintAnimationPhase2(final Runnable onAnimationFinished) {
- ValueAnimator animator = createHeightAnimator(getMaxPanelHeight());
- animator.setDuration(450);
- animator.setInterpolator(mBounceInterpolator);
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- setAnimator(null);
- onAnimationFinished.run();
- notifyBarPanelExpansionChanged();
- }
- });
- animator.start();
- setAnimator(animator);
- }
-
- private ValueAnimator createHeightAnimator(float targetHeight) {
- ValueAnimator animator = ValueAnimator.ofFloat(mExpandedHeight, targetHeight);
- animator.addUpdateListener(
- animation -> setExpandedHeightInternal((float) animation.getAnimatedValue()));
- return animator;
- }
-
- protected void notifyBarPanelExpansionChanged() {
- if (mBar != null) {
- mBar.panelExpansionChanged(mExpandedFraction, mExpandedFraction > 0f
- || mPeekAnimator != null || mInstantExpanding
- || isPanelVisibleBecauseOfHeadsUp() || mTracking || mHeightAnimator != null);
- }
- for (int i = 0; i < mExpansionListeners.size(); i++) {
- mExpansionListeners.get(i).onPanelExpansionChanged(mExpandedFraction, mTracking);
- }
- }
-
- public void addExpansionListener(PanelExpansionListener panelExpansionListener) {
- mExpansionListeners.add(panelExpansionListener);
- }
-
- protected abstract boolean isPanelVisibleBecauseOfHeadsUp();
-
- /**
- * Gets called when the user performs a click anywhere in the empty area of the panel.
- *
- * @return whether the panel will be expanded after the action performed by this method
- */
- protected boolean onEmptySpaceClick(float x) {
- if (mHintAnimationRunning) {
- return true;
- }
- return onMiddleClicked();
- }
-
- protected final Runnable mPostCollapseRunnable = new Runnable() {
- @Override
- public void run() {
- collapse(false /* delayed */, 1.0f /* speedUpFactor */);
- }
- };
-
- protected abstract boolean onMiddleClicked();
-
- protected abstract boolean isDozing();
-
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println(String.format("[PanelView(%s): expandedHeight=%f maxPanelHeight=%d closing=%s"
- + " tracking=%s justPeeked=%s peekAnim=%s%s timeAnim=%s%s touchDisabled=%s"
- + "]",
- this.getClass().getSimpleName(),
- getExpandedHeight(),
- getMaxPanelHeight(),
- mClosing?"T":"f",
- mTracking?"T":"f",
- mJustPeeked?"T":"f",
- mPeekAnimator, ((mPeekAnimator!=null && mPeekAnimator.isStarted())?" (started)":""),
- mHeightAnimator, ((mHeightAnimator !=null && mHeightAnimator.isStarted())?" (started)":""),
- mTouchDisabled?"T":"f"
- ));
- }
-
- public abstract void resetViews(boolean animate);
-
- protected abstract float getPeekHeight();
- /**
- * @return whether "Clear all" button will be visible when the panel is fully expanded
- */
- protected abstract boolean fullyExpandedClearAllVisible();
-
- protected abstract boolean isClearAllVisible();
-
- /**
- * @return the height of the clear all button, in pixels
- */
- protected abstract int getClearAllHeight();
-
- public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
- mHeadsUpManager = headsUpManager;
- }
-
- public void setLaunchingNotification(boolean launchingNotification) {
- mLaunchingNotification = launchingNotification;
- }
-
- public void collapseWithDuration(int animationDuration) {
- mFixedDuration = animationDuration;
- collapse(false /* delayed */, 1.0f /* speedUpFactor */);
- mFixedDuration = NO_FIXED_DURATION;
+ interface OnConfigurationChangedListener {
+ void onConfigurationChanged(Configuration newConfig);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
new file mode 100644
index 0000000..3d8e09a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -0,0 +1,1297 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.os.SystemClock;
+import android.os.VibrationEffect;
+import android.util.Log;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.animation.Interpolator;
+
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.LatencyTracker;
+import com.android.systemui.DejankUtils;
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.doze.DozeLog;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.statusbar.FlingAnimationUtils;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+public abstract class PanelViewController {
+ public static final boolean DEBUG = PanelBar.DEBUG;
+ public static final String TAG = PanelView.class.getSimpleName();
+ private static final int INITIAL_OPENING_PEEK_DURATION = 200;
+ private static final int PEEK_ANIMATION_DURATION = 360;
+ private static final int NO_FIXED_DURATION = -1;
+ protected long mDownTime;
+ protected boolean mTouchSlopExceededBeforeDown;
+ private float mMinExpandHeight;
+ private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
+ private boolean mPanelUpdateWhenAnimatorEnds;
+ private boolean mVibrateOnOpening;
+ protected boolean mLaunchingNotification;
+ private int mFixedDuration = NO_FIXED_DURATION;
+ protected ArrayList<PanelExpansionListener> mExpansionListeners = new ArrayList<>();
+
+ private void logf(String fmt, Object... args) {
+ Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
+ }
+
+ protected StatusBar mStatusBar;
+ protected HeadsUpManagerPhone mHeadsUpManager;
+
+ private float mPeekHeight;
+ private float mHintDistance;
+ private float mInitialOffsetOnTouch;
+ private boolean mCollapsedAndHeadsUpOnDown;
+ private float mExpandedFraction = 0;
+ protected float mExpandedHeight = 0;
+ private boolean mPanelClosedOnDown;
+ private boolean mHasLayoutedSinceDown;
+ private float mUpdateFlingVelocity;
+ private boolean mUpdateFlingOnLayout;
+ private boolean mPeekTouching;
+ private boolean mJustPeeked;
+ private boolean mClosing;
+ protected boolean mTracking;
+ private boolean mTouchSlopExceeded;
+ private int mTrackingPointer;
+ protected int mTouchSlop;
+ protected boolean mHintAnimationRunning;
+ private boolean mOverExpandedBeforeFling;
+ private boolean mTouchAboveFalsingThreshold;
+ private int mUnlockFalsingThreshold;
+ private boolean mTouchStartedInEmptyArea;
+ private boolean mMotionAborted;
+ private boolean mUpwardsWhenThresholdReached;
+ private boolean mAnimatingOnDown;
+
+ private ValueAnimator mHeightAnimator;
+ private ObjectAnimator mPeekAnimator;
+ private final VelocityTracker mVelocityTracker = VelocityTracker.obtain();
+ private FlingAnimationUtils mFlingAnimationUtils;
+ private FlingAnimationUtils mFlingAnimationUtilsClosing;
+ private FlingAnimationUtils mFlingAnimationUtilsDismissing;
+ private final LatencyTracker mLatencyTracker;
+ private final FalsingManager mFalsingManager;
+ private final DozeLog mDozeLog;
+ private final VibratorHelper mVibratorHelper;
+
+ /**
+ * Whether an instant expand request is currently pending and we are just waiting for layout.
+ */
+ private boolean mInstantExpanding;
+ private boolean mAnimateAfterExpanding;
+
+ PanelBar mBar;
+
+ private String mViewName;
+ private float mInitialTouchY;
+ private float mInitialTouchX;
+ private boolean mTouchDisabled;
+
+ /**
+ * Whether or not the PanelView can be expanded or collapsed with a drag.
+ */
+ private boolean mNotificationsDragEnabled;
+
+ private Interpolator mBounceInterpolator;
+ protected KeyguardBottomAreaView mKeyguardBottomArea;
+
+ /**
+ * Speed-up factor to be used when {@link #mFlingCollapseRunnable} runs the next time.
+ */
+ private float mNextCollapseSpeedUpFactor = 1.0f;
+
+ protected boolean mExpanding;
+ private boolean mGestureWaitForTouchSlop;
+ private boolean mIgnoreXTouchSlop;
+ private boolean mExpandLatencyTracking;
+ private final PanelView mView;
+ protected final Resources mResources;
+ protected final KeyguardStateController mKeyguardStateController;
+ protected final SysuiStatusBarStateController mStatusBarStateController;
+
+ protected void onExpandingFinished() {
+ mBar.onExpandingFinished();
+ }
+
+ protected void onExpandingStarted() {
+ }
+
+ private void notifyExpandingStarted() {
+ if (!mExpanding) {
+ mExpanding = true;
+ onExpandingStarted();
+ }
+ }
+
+ protected final void notifyExpandingFinished() {
+ endClosing();
+ if (mExpanding) {
+ mExpanding = false;
+ onExpandingFinished();
+ }
+ }
+
+ private void runPeekAnimation(long duration, float peekHeight, boolean collapseWhenFinished) {
+ mPeekHeight = peekHeight;
+ if (DEBUG) logf("peek to height=%.1f", mPeekHeight);
+ if (mHeightAnimator != null) {
+ return;
+ }
+ if (mPeekAnimator != null) {
+ mPeekAnimator.cancel();
+ }
+ mPeekAnimator = ObjectAnimator.ofFloat(this, "expandedHeight", mPeekHeight).setDuration(
+ duration);
+ mPeekAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+ mPeekAnimator.addListener(new AnimatorListenerAdapter() {
+ private boolean mCancelled;
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCancelled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mPeekAnimator = null;
+ if (!mCancelled && collapseWhenFinished) {
+ mView.postOnAnimation(mPostCollapseRunnable);
+ }
+
+ }
+ });
+ notifyExpandingStarted();
+ mPeekAnimator.start();
+ mJustPeeked = true;
+ }
+
+ public PanelViewController(PanelView view,
+ FalsingManager falsingManager, DozeLog dozeLog,
+ KeyguardStateController keyguardStateController,
+ SysuiStatusBarStateController statusBarStateController, VibratorHelper vibratorHelper,
+ LatencyTracker latencyTracker, FlingAnimationUtils.Builder flingAnimationUtilsBuilder) {
+ mView = view;
+ mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ mViewName = mResources.getResourceName(mView.getId());
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ }
+ });
+
+ mView.addOnLayoutChangeListener(createLayoutChangeListener());
+ mView.setOnTouchListener(createTouchHandler());
+ mView.setOnConfigurationChangedListener(createOnConfigurationChangedListener());
+
+ mResources = mView.getResources();
+ mKeyguardStateController = keyguardStateController;
+ mStatusBarStateController = statusBarStateController;
+ mFlingAnimationUtils = flingAnimationUtilsBuilder
+ .reset()
+ .setMaxLengthSeconds(0.6f)
+ .setSpeedUpFactor(0.6f)
+ .build();
+ mFlingAnimationUtilsClosing = flingAnimationUtilsBuilder
+ .reset()
+ .setMaxLengthSeconds(0.5f)
+ .setSpeedUpFactor(0.6f)
+ .build();
+ mFlingAnimationUtilsDismissing = flingAnimationUtilsBuilder
+ .reset()
+ .setMaxLengthSeconds(0.5f)
+ .setSpeedUpFactor(0.6f)
+ .setX2(0.6f)
+ .setY2(0.84f)
+ .build();
+ mLatencyTracker = latencyTracker;
+ mBounceInterpolator = new BounceInterpolator();
+ mFalsingManager = falsingManager;
+ mDozeLog = dozeLog;
+ mNotificationsDragEnabled = mResources.getBoolean(
+ R.bool.config_enableNotificationShadeDrag);
+ mVibratorHelper = vibratorHelper;
+ mVibrateOnOpening = mResources.getBoolean(R.bool.config_vibrateOnIconAnimation);
+ }
+
+ protected void loadDimens() {
+ final ViewConfiguration configuration = ViewConfiguration.get(mView.getContext());
+ mTouchSlop = configuration.getScaledTouchSlop();
+ mHintDistance = mResources.getDimension(R.dimen.hint_move_distance);
+ mUnlockFalsingThreshold = mResources.getDimensionPixelSize(
+ R.dimen.unlock_falsing_threshold);
+ }
+
+ private void addMovement(MotionEvent event) {
+ // Add movement to velocity tracker using raw screen X and Y coordinates instead
+ // of window coordinates because the window frame may be moving at the same time.
+ float deltaX = event.getRawX() - event.getX();
+ float deltaY = event.getRawY() - event.getY();
+ event.offsetLocation(deltaX, deltaY);
+ mVelocityTracker.addMovement(event);
+ event.offsetLocation(-deltaX, -deltaY);
+ }
+
+ public void setTouchAndAnimationDisabled(boolean disabled) {
+ mTouchDisabled = disabled;
+ if (mTouchDisabled) {
+ cancelHeightAnimator();
+ if (mTracking) {
+ onTrackingStopped(true /* expanded */);
+ }
+ notifyExpandingFinished();
+ }
+ }
+
+ public void startExpandLatencyTracking() {
+ if (mLatencyTracker.isEnabled()) {
+ mLatencyTracker.onActionStart(LatencyTracker.ACTION_EXPAND_PANEL);
+ mExpandLatencyTracking = true;
+ }
+ }
+
+ private void startOpening(MotionEvent event) {
+ runPeekAnimation(INITIAL_OPENING_PEEK_DURATION, getOpeningHeight(),
+ false /* collapseWhenFinished */);
+ notifyBarPanelExpansionChanged();
+ maybeVibrateOnOpening();
+
+ //TODO: keyguard opens QS a different way; log that too?
+
+ // Log the position of the swipe that opened the panel
+ float width = mStatusBar.getDisplayWidth();
+ float height = mStatusBar.getDisplayHeight();
+ int rot = mStatusBar.getRotation();
+
+ mLockscreenGestureLogger.writeAtFractionalPosition(MetricsEvent.ACTION_PANEL_VIEW_EXPAND,
+ (int) (event.getX() / width * 100), (int) (event.getY() / height * 100), rot);
+ }
+
+ protected void maybeVibrateOnOpening() {
+ if (mVibrateOnOpening) {
+ mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
+ }
+ }
+
+ protected abstract float getOpeningHeight();
+
+ /**
+ * @return whether the swiping direction is upwards and above a 45 degree angle compared to the
+ * horizontal direction
+ */
+ private boolean isDirectionUpwards(float x, float y) {
+ float xDiff = x - mInitialTouchX;
+ float yDiff = y - mInitialTouchY;
+ if (yDiff >= 0) {
+ return false;
+ }
+ return Math.abs(yDiff) >= Math.abs(xDiff);
+ }
+
+ protected void startExpandingFromPeek() {
+ mStatusBar.handlePeekToExpandTransistion();
+ }
+
+ protected void startExpandMotion(float newX, float newY, boolean startTracking,
+ float expandedHeight) {
+ mInitialOffsetOnTouch = expandedHeight;
+ mInitialTouchY = newY;
+ mInitialTouchX = newX;
+ if (startTracking) {
+ mTouchSlopExceeded = true;
+ setExpandedHeight(mInitialOffsetOnTouch);
+ onTrackingStarted();
+ }
+ }
+
+ private void endMotionEvent(MotionEvent event, float x, float y, boolean forceCancel) {
+ mTrackingPointer = -1;
+ if ((mTracking && mTouchSlopExceeded) || Math.abs(x - mInitialTouchX) > mTouchSlop
+ || Math.abs(y - mInitialTouchY) > mTouchSlop
+ || event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel) {
+ mVelocityTracker.computeCurrentVelocity(1000);
+ float vel = mVelocityTracker.getYVelocity();
+ float vectorVel = (float) Math.hypot(
+ mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
+
+ boolean expand = flingExpands(vel, vectorVel, x, y)
+ || event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel;
+ mDozeLog.traceFling(expand, mTouchAboveFalsingThreshold,
+ mStatusBar.isFalsingThresholdNeeded(), mStatusBar.isWakeUpComingFromTouch());
+ // Log collapse gesture if on lock screen.
+ if (!expand && mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
+ float displayDensity = mStatusBar.getDisplayDensity();
+ int heightDp = (int) Math.abs((y - mInitialTouchY) / displayDensity);
+ int velocityDp = (int) Math.abs(vel / displayDensity);
+ mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_UNLOCK, heightDp, velocityDp);
+ }
+ fling(vel, expand, isFalseTouch(x, y));
+ onTrackingStopped(expand);
+ mUpdateFlingOnLayout = expand && mPanelClosedOnDown && !mHasLayoutedSinceDown;
+ if (mUpdateFlingOnLayout) {
+ mUpdateFlingVelocity = vel;
+ }
+ } else if (mPanelClosedOnDown && !mHeadsUpManager.hasPinnedHeadsUp() && !mTracking
+ && !mStatusBar.isBouncerShowing()
+ && !mKeyguardStateController.isKeyguardFadingAway()) {
+ long timePassed = SystemClock.uptimeMillis() - mDownTime;
+ if (timePassed < ViewConfiguration.getLongPressTimeout()) {
+ // Lets show the user that he can actually expand the panel
+ runPeekAnimation(
+ PEEK_ANIMATION_DURATION, getPeekHeight(), true /* collapseWhenFinished */);
+ } else {
+ // We need to collapse the panel since we peeked to the small height.
+ mView.postOnAnimation(mPostCollapseRunnable);
+ }
+ } else if (!mStatusBar.isBouncerShowing()) {
+ boolean expands = onEmptySpaceClick(mInitialTouchX);
+ onTrackingStopped(expands);
+ }
+
+ mVelocityTracker.clear();
+ mPeekTouching = false;
+ }
+
+ protected float getCurrentExpandVelocity() {
+ mVelocityTracker.computeCurrentVelocity(1000);
+ return mVelocityTracker.getYVelocity();
+ }
+
+ private int getFalsingThreshold() {
+ float factor = mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
+ return (int) (mUnlockFalsingThreshold * factor);
+ }
+
+ protected abstract boolean shouldGestureWaitForTouchSlop();
+
+ protected abstract boolean shouldGestureIgnoreXTouchSlop(float x, float y);
+
+ protected void onTrackingStopped(boolean expand) {
+ mTracking = false;
+ mBar.onTrackingStopped(expand);
+ notifyBarPanelExpansionChanged();
+ }
+
+ protected void onTrackingStarted() {
+ endClosing();
+ mTracking = true;
+ mBar.onTrackingStarted();
+ notifyExpandingStarted();
+ notifyBarPanelExpansionChanged();
+ }
+
+ /**
+ * @return Whether a pair of coordinates are inside the visible view content bounds.
+ */
+ protected abstract boolean isInContentBounds(float x, float y);
+
+ protected void cancelHeightAnimator() {
+ if (mHeightAnimator != null) {
+ if (mHeightAnimator.isRunning()) {
+ mPanelUpdateWhenAnimatorEnds = false;
+ }
+ mHeightAnimator.cancel();
+ }
+ endClosing();
+ }
+
+ private void endClosing() {
+ if (mClosing) {
+ mClosing = false;
+ onClosingFinished();
+ }
+ }
+
+ protected boolean isScrolledToBottom() {
+ return true;
+ }
+
+ protected float getContentHeight() {
+ return mExpandedHeight;
+ }
+
+ /**
+ * @param vel the current vertical velocity of the motion
+ * @param vectorVel the length of the vectorial velocity
+ * @return whether a fling should expands the panel; contracts otherwise
+ */
+ protected boolean flingExpands(float vel, float vectorVel, float x, float y) {
+ if (mFalsingManager.isUnlockingDisabled()) {
+ return true;
+ }
+
+ if (isFalseTouch(x, y)) {
+ return true;
+ }
+ if (Math.abs(vectorVel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
+ return shouldExpandWhenNotFlinging();
+ } else {
+ return vel > 0;
+ }
+ }
+
+ protected boolean shouldExpandWhenNotFlinging() {
+ return getExpandedFraction() > 0.5f;
+ }
+
+ /**
+ * @param x the final x-coordinate when the finger was lifted
+ * @param y the final y-coordinate when the finger was lifted
+ * @return whether this motion should be regarded as a false touch
+ */
+ private boolean isFalseTouch(float x, float y) {
+ if (!mStatusBar.isFalsingThresholdNeeded()) {
+ return false;
+ }
+ if (mFalsingManager.isClassifierEnabled()) {
+ return mFalsingManager.isFalseTouch();
+ }
+ if (!mTouchAboveFalsingThreshold) {
+ return true;
+ }
+ if (mUpwardsWhenThresholdReached) {
+ return false;
+ }
+ return !isDirectionUpwards(x, y);
+ }
+
+ protected void fling(float vel, boolean expand) {
+ fling(vel, expand, 1.0f /* collapseSpeedUpFactor */, false);
+ }
+
+ protected void fling(float vel, boolean expand, boolean expandBecauseOfFalsing) {
+ fling(vel, expand, 1.0f /* collapseSpeedUpFactor */, expandBecauseOfFalsing);
+ }
+
+ protected void fling(float vel, boolean expand, float collapseSpeedUpFactor,
+ boolean expandBecauseOfFalsing) {
+ cancelPeek();
+ float target = expand ? getMaxPanelHeight() : 0;
+ if (!expand) {
+ mClosing = true;
+ }
+ flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing);
+ }
+
+ protected void flingToHeight(float vel, boolean expand, float target,
+ float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) {
+ // Hack to make the expand transition look nice when clear all button is visible - we make
+ // the animation only to the last notification, and then jump to the maximum panel height so
+ // clear all just fades in and the decelerating motion is towards the last notification.
+ final boolean
+ clearAllExpandHack =
+ expand && fullyExpandedClearAllVisible()
+ && mExpandedHeight < getMaxPanelHeight() - getClearAllHeight()
+ && !isClearAllVisible();
+ if (clearAllExpandHack) {
+ target = getMaxPanelHeight() - getClearAllHeight();
+ }
+ if (target == mExpandedHeight || getOverExpansionAmount() > 0f && expand) {
+ notifyExpandingFinished();
+ return;
+ }
+ mOverExpandedBeforeFling = getOverExpansionAmount() > 0f;
+ ValueAnimator animator = createHeightAnimator(target);
+ if (expand) {
+ if (expandBecauseOfFalsing && vel < 0) {
+ vel = 0;
+ }
+ mFlingAnimationUtils.apply(animator, mExpandedHeight, target, vel, mView.getHeight());
+ if (vel == 0) {
+ animator.setDuration(350);
+ }
+ } else {
+ if (shouldUseDismissingAnimation()) {
+ if (vel == 0) {
+ animator.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED);
+ long duration = (long) (200 + mExpandedHeight / mView.getHeight() * 100);
+ animator.setDuration(duration);
+ } else {
+ mFlingAnimationUtilsDismissing.apply(animator, mExpandedHeight, target, vel,
+ mView.getHeight());
+ }
+ } else {
+ mFlingAnimationUtilsClosing.apply(
+ animator, mExpandedHeight, target, vel, mView.getHeight());
+ }
+
+ // Make it shorter if we run a canned animation
+ if (vel == 0) {
+ animator.setDuration((long) (animator.getDuration() / collapseSpeedUpFactor));
+ }
+ if (mFixedDuration != NO_FIXED_DURATION) {
+ animator.setDuration(mFixedDuration);
+ }
+ }
+ animator.addListener(new AnimatorListenerAdapter() {
+ private boolean mCancelled;
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCancelled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (clearAllExpandHack && !mCancelled) {
+ setExpandedHeightInternal(getMaxPanelHeight());
+ }
+ setAnimator(null);
+ if (!mCancelled) {
+ notifyExpandingFinished();
+ }
+ notifyBarPanelExpansionChanged();
+ }
+ });
+ setAnimator(animator);
+ animator.start();
+ }
+
+ protected abstract boolean shouldUseDismissingAnimation();
+
+ public String getName() {
+ return mViewName;
+ }
+
+ public void setExpandedHeight(float height) {
+ if (DEBUG) logf("setExpandedHeight(%.1f)", height);
+ setExpandedHeightInternal(height + getOverExpansionPixels());
+ }
+
+ protected void requestPanelHeightUpdate() {
+ float currentMaxPanelHeight = getMaxPanelHeight();
+
+ if (isFullyCollapsed()) {
+ return;
+ }
+
+ if (currentMaxPanelHeight == mExpandedHeight) {
+ return;
+ }
+
+ if (mPeekAnimator != null || mPeekTouching) {
+ return;
+ }
+
+ if (mTracking && !isTrackingBlocked()) {
+ return;
+ }
+
+ if (mHeightAnimator != null) {
+ mPanelUpdateWhenAnimatorEnds = true;
+ return;
+ }
+
+ setExpandedHeight(currentMaxPanelHeight);
+ }
+
+ public void setExpandedHeightInternal(float h) {
+ if (mExpandLatencyTracking && h != 0f) {
+ DejankUtils.postAfterTraversal(
+ () -> mLatencyTracker.onActionEnd(LatencyTracker.ACTION_EXPAND_PANEL));
+ mExpandLatencyTracking = false;
+ }
+ float fhWithoutOverExpansion = getMaxPanelHeight() - getOverExpansionAmount();
+ if (mHeightAnimator == null) {
+ float overExpansionPixels = Math.max(0, h - fhWithoutOverExpansion);
+ if (getOverExpansionPixels() != overExpansionPixels && mTracking) {
+ setOverExpansion(overExpansionPixels, true /* isPixels */);
+ }
+ mExpandedHeight = Math.min(h, fhWithoutOverExpansion) + getOverExpansionAmount();
+ } else {
+ mExpandedHeight = h;
+ if (mOverExpandedBeforeFling) {
+ setOverExpansion(Math.max(0, h - fhWithoutOverExpansion), false /* isPixels */);
+ }
+ }
+
+ // If we are closing the panel and we are almost there due to a slow decelerating
+ // interpolator, abort the animation.
+ if (mExpandedHeight < 1f && mExpandedHeight != 0f && mClosing) {
+ mExpandedHeight = 0f;
+ if (mHeightAnimator != null) {
+ mHeightAnimator.end();
+ }
+ }
+ mExpandedFraction = Math.min(1f,
+ fhWithoutOverExpansion == 0 ? 0 : mExpandedHeight / fhWithoutOverExpansion);
+ onHeightUpdated(mExpandedHeight);
+ notifyBarPanelExpansionChanged();
+ }
+
+ /**
+ * @return true if the panel tracking should be temporarily blocked; this is used when a
+ * conflicting gesture (opening QS) is happening
+ */
+ protected abstract boolean isTrackingBlocked();
+
+ protected abstract void setOverExpansion(float overExpansion, boolean isPixels);
+
+ protected abstract void onHeightUpdated(float expandedHeight);
+
+ protected abstract float getOverExpansionAmount();
+
+ protected abstract float getOverExpansionPixels();
+
+ /**
+ * This returns the maximum height of the panel. Children should override this if their
+ * desired height is not the full height.
+ *
+ * @return the default implementation simply returns the maximum height.
+ */
+ protected abstract int getMaxPanelHeight();
+
+ public void setExpandedFraction(float frac) {
+ setExpandedHeight(getMaxPanelHeight() * frac);
+ }
+
+ public float getExpandedHeight() {
+ return mExpandedHeight;
+ }
+
+ public float getExpandedFraction() {
+ return mExpandedFraction;
+ }
+
+ public boolean isFullyExpanded() {
+ return mExpandedHeight >= getMaxPanelHeight();
+ }
+
+ public boolean isFullyCollapsed() {
+ return mExpandedFraction <= 0.0f;
+ }
+
+ public boolean isCollapsing() {
+ return mClosing || mLaunchingNotification;
+ }
+
+ public boolean isTracking() {
+ return mTracking;
+ }
+
+ public void setBar(PanelBar panelBar) {
+ mBar = panelBar;
+ }
+
+ public void collapse(boolean delayed, float speedUpFactor) {
+ if (DEBUG) logf("collapse: " + this);
+ if (canPanelBeCollapsed()) {
+ cancelHeightAnimator();
+ notifyExpandingStarted();
+
+ // Set after notifyExpandingStarted, as notifyExpandingStarted resets the closing state.
+ mClosing = true;
+ if (delayed) {
+ mNextCollapseSpeedUpFactor = speedUpFactor;
+ mView.postDelayed(mFlingCollapseRunnable, 120);
+ } else {
+ fling(0, false /* expand */, speedUpFactor, false /* expandBecauseOfFalsing */);
+ }
+ }
+ }
+
+ public boolean canPanelBeCollapsed() {
+ return !isFullyCollapsed() && !mTracking && !mClosing;
+ }
+
+ private final Runnable mFlingCollapseRunnable = new Runnable() {
+ @Override
+ public void run() {
+ fling(0, false /* expand */, mNextCollapseSpeedUpFactor,
+ false /* expandBecauseOfFalsing */);
+ }
+ };
+
+ public void cancelPeek() {
+ boolean cancelled = false;
+ if (mPeekAnimator != null) {
+ cancelled = true;
+ mPeekAnimator.cancel();
+ }
+
+ if (cancelled) {
+ // When peeking, we already tell mBar that we expanded ourselves. Make sure that we also
+ // notify mBar that we might have closed ourselves.
+ notifyBarPanelExpansionChanged();
+ }
+ }
+
+ public void expand(final boolean animate) {
+ if (!isFullyCollapsed() && !isCollapsing()) {
+ return;
+ }
+
+ mInstantExpanding = true;
+ mAnimateAfterExpanding = animate;
+ mUpdateFlingOnLayout = false;
+ abortAnimations();
+ cancelPeek();
+ if (mTracking) {
+ onTrackingStopped(true /* expands */); // The panel is expanded after this call.
+ }
+ if (mExpanding) {
+ notifyExpandingFinished();
+ }
+ notifyBarPanelExpansionChanged();
+
+ // Wait for window manager to pickup the change, so we know the maximum height of the panel
+ // then.
+ mView.getViewTreeObserver().addOnGlobalLayoutListener(
+ new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ if (!mInstantExpanding) {
+ mView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ return;
+ }
+ if (mStatusBar.getStatusBarWindow().getHeight()
+ != mStatusBar.getStatusBarHeight()) {
+ mView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ if (mAnimateAfterExpanding) {
+ notifyExpandingStarted();
+ fling(0, true /* expand */);
+ } else {
+ setExpandedFraction(1f);
+ }
+ mInstantExpanding = false;
+ }
+ }
+ });
+
+ // Make sure a layout really happens.
+ mView.requestLayout();
+ }
+
+ public void instantCollapse() {
+ abortAnimations();
+ setExpandedFraction(0f);
+ if (mExpanding) {
+ notifyExpandingFinished();
+ }
+ if (mInstantExpanding) {
+ mInstantExpanding = false;
+ notifyBarPanelExpansionChanged();
+ }
+ }
+
+ private void abortAnimations() {
+ cancelPeek();
+ cancelHeightAnimator();
+ mView.removeCallbacks(mPostCollapseRunnable);
+ mView.removeCallbacks(mFlingCollapseRunnable);
+ }
+
+ protected void onClosingFinished() {
+ mBar.onClosingFinished();
+ }
+
+
+ protected void startUnlockHintAnimation() {
+
+ // We don't need to hint the user if an animation is already running or the user is changing
+ // the expansion.
+ if (mHeightAnimator != null || mTracking) {
+ return;
+ }
+ cancelPeek();
+ notifyExpandingStarted();
+ startUnlockHintAnimationPhase1(() -> {
+ notifyExpandingFinished();
+ onUnlockHintFinished();
+ mHintAnimationRunning = false;
+ });
+ onUnlockHintStarted();
+ mHintAnimationRunning = true;
+ }
+
+ protected void onUnlockHintFinished() {
+ mStatusBar.onHintFinished();
+ }
+
+ protected void onUnlockHintStarted() {
+ mStatusBar.onUnlockHintStarted();
+ }
+
+ public boolean isUnlockHintRunning() {
+ return mHintAnimationRunning;
+ }
+
+ /**
+ * Phase 1: Move everything upwards.
+ */
+ private void startUnlockHintAnimationPhase1(final Runnable onAnimationFinished) {
+ float target = Math.max(0, getMaxPanelHeight() - mHintDistance);
+ ValueAnimator animator = createHeightAnimator(target);
+ animator.setDuration(250);
+ animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ animator.addListener(new AnimatorListenerAdapter() {
+ private boolean mCancelled;
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCancelled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mCancelled) {
+ setAnimator(null);
+ onAnimationFinished.run();
+ } else {
+ startUnlockHintAnimationPhase2(onAnimationFinished);
+ }
+ }
+ });
+ animator.start();
+ setAnimator(animator);
+
+ View[] viewsToAnimate = {
+ mKeyguardBottomArea.getIndicationArea(),
+ mStatusBar.getAmbientIndicationContainer()};
+ for (View v : viewsToAnimate) {
+ if (v == null) {
+ continue;
+ }
+ v.animate().translationY(-mHintDistance).setDuration(250).setInterpolator(
+ Interpolators.FAST_OUT_SLOW_IN).withEndAction(() -> v.animate().translationY(
+ 0).setDuration(450).setInterpolator(mBounceInterpolator).start()).start();
+ }
+ }
+
+ private void setAnimator(ValueAnimator animator) {
+ mHeightAnimator = animator;
+ if (animator == null && mPanelUpdateWhenAnimatorEnds) {
+ mPanelUpdateWhenAnimatorEnds = false;
+ requestPanelHeightUpdate();
+ }
+ }
+
+ /**
+ * Phase 2: Bounce down.
+ */
+ private void startUnlockHintAnimationPhase2(final Runnable onAnimationFinished) {
+ ValueAnimator animator = createHeightAnimator(getMaxPanelHeight());
+ animator.setDuration(450);
+ animator.setInterpolator(mBounceInterpolator);
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ setAnimator(null);
+ onAnimationFinished.run();
+ notifyBarPanelExpansionChanged();
+ }
+ });
+ animator.start();
+ setAnimator(animator);
+ }
+
+ private ValueAnimator createHeightAnimator(float targetHeight) {
+ ValueAnimator animator = ValueAnimator.ofFloat(mExpandedHeight, targetHeight);
+ animator.addUpdateListener(
+ animation -> setExpandedHeightInternal((float) animation.getAnimatedValue()));
+ return animator;
+ }
+
+ protected void notifyBarPanelExpansionChanged() {
+ if (mBar != null) {
+ mBar.panelExpansionChanged(
+ mExpandedFraction,
+ mExpandedFraction > 0f || mPeekAnimator != null || mInstantExpanding
+ || isPanelVisibleBecauseOfHeadsUp() || mTracking
+ || mHeightAnimator != null);
+ }
+ for (int i = 0; i < mExpansionListeners.size(); i++) {
+ mExpansionListeners.get(i).onPanelExpansionChanged(mExpandedFraction, mTracking);
+ }
+ }
+
+ public void addExpansionListener(PanelExpansionListener panelExpansionListener) {
+ mExpansionListeners.add(panelExpansionListener);
+ }
+
+ protected abstract boolean isPanelVisibleBecauseOfHeadsUp();
+
+ /**
+ * Gets called when the user performs a click anywhere in the empty area of the panel.
+ *
+ * @return whether the panel will be expanded after the action performed by this method
+ */
+ protected boolean onEmptySpaceClick(float x) {
+ if (mHintAnimationRunning) {
+ return true;
+ }
+ return onMiddleClicked();
+ }
+
+ protected final Runnable mPostCollapseRunnable = new Runnable() {
+ @Override
+ public void run() {
+ collapse(false /* delayed */, 1.0f /* speedUpFactor */);
+ }
+ };
+
+ protected abstract boolean onMiddleClicked();
+
+ protected abstract boolean isDozing();
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println(String.format("[PanelView(%s): expandedHeight=%f maxPanelHeight=%d closing=%s"
+ + " tracking=%s justPeeked=%s peekAnim=%s%s timeAnim=%s%s "
+ + "touchDisabled=%s" + "]",
+ this.getClass().getSimpleName(), getExpandedHeight(), getMaxPanelHeight(),
+ mClosing ? "T" : "f", mTracking ? "T" : "f", mJustPeeked ? "T" : "f", mPeekAnimator,
+ ((mPeekAnimator != null && mPeekAnimator.isStarted()) ? " (started)" : ""),
+ mHeightAnimator,
+ ((mHeightAnimator != null && mHeightAnimator.isStarted()) ? " (started)" : ""),
+ mTouchDisabled ? "T" : "f"));
+ }
+
+ public abstract void resetViews(boolean animate);
+
+ protected abstract float getPeekHeight();
+
+ /**
+ * @return whether "Clear all" button will be visible when the panel is fully expanded
+ */
+ protected abstract boolean fullyExpandedClearAllVisible();
+
+ protected abstract boolean isClearAllVisible();
+
+ /**
+ * @return the height of the clear all button, in pixels
+ */
+ protected abstract int getClearAllHeight();
+
+ public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
+ mHeadsUpManager = headsUpManager;
+ }
+
+ public void setLaunchingNotification(boolean launchingNotification) {
+ mLaunchingNotification = launchingNotification;
+ }
+
+ public void collapseWithDuration(int animationDuration) {
+ mFixedDuration = animationDuration;
+ collapse(false /* delayed */, 1.0f /* speedUpFactor */);
+ mFixedDuration = NO_FIXED_DURATION;
+ }
+
+ public ViewGroup getView() {
+ // TODO: remove this method, or at least reduce references to it.
+ return mView;
+ }
+
+ public boolean isEnabled() {
+ return mView.isEnabled();
+ }
+
+ public OnLayoutChangeListener createLayoutChangeListener() {
+ return new OnLayoutChangeListener();
+ }
+
+ protected TouchHandler createTouchHandler() {
+ return new TouchHandler();
+ }
+
+ protected OnConfigurationChangedListener createOnConfigurationChangedListener() {
+ return new OnConfigurationChangedListener();
+ }
+
+ public class TouchHandler implements View.OnTouchListener {
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ if (mInstantExpanding || !mNotificationsDragEnabled || mTouchDisabled || (mMotionAborted
+ && event.getActionMasked() != MotionEvent.ACTION_DOWN)) {
+ return false;
+ }
+
+ /*
+ * If the user drags anywhere inside the panel we intercept it if the movement is
+ * upwards. This allows closing the shade from anywhere inside the panel.
+ *
+ * We only do this if the current content is scrolled to the bottom,
+ * i.e isScrolledToBottom() is true and therefore there is no conflicting scrolling
+ * gesture
+ * possible.
+ */
+ int pointerIndex = event.findPointerIndex(mTrackingPointer);
+ if (pointerIndex < 0) {
+ pointerIndex = 0;
+ mTrackingPointer = event.getPointerId(pointerIndex);
+ }
+ final float x = event.getX(pointerIndex);
+ final float y = event.getY(pointerIndex);
+ boolean scrolledToBottom = isScrolledToBottom();
+
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ mStatusBar.userActivity();
+ mAnimatingOnDown = mHeightAnimator != null;
+ mMinExpandHeight = 0.0f;
+ mDownTime = SystemClock.uptimeMillis();
+ if (mAnimatingOnDown && mClosing && !mHintAnimationRunning
+ || mPeekAnimator != null) {
+ cancelHeightAnimator();
+ cancelPeek();
+ mTouchSlopExceeded = true;
+ return true;
+ }
+ mInitialTouchY = y;
+ mInitialTouchX = x;
+ mTouchStartedInEmptyArea = !isInContentBounds(x, y);
+ mTouchSlopExceeded = mTouchSlopExceededBeforeDown;
+ mJustPeeked = false;
+ mMotionAborted = false;
+ mPanelClosedOnDown = isFullyCollapsed();
+ mCollapsedAndHeadsUpOnDown = false;
+ mHasLayoutedSinceDown = false;
+ mUpdateFlingOnLayout = false;
+ mTouchAboveFalsingThreshold = false;
+ addMovement(event);
+ break;
+ case MotionEvent.ACTION_POINTER_UP:
+ final int upPointer = event.getPointerId(event.getActionIndex());
+ if (mTrackingPointer == upPointer) {
+ // gesture is ongoing, find a new pointer to track
+ final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
+ mTrackingPointer = event.getPointerId(newIndex);
+ mInitialTouchX = event.getX(newIndex);
+ mInitialTouchY = event.getY(newIndex);
+ }
+ break;
+ case MotionEvent.ACTION_POINTER_DOWN:
+ if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
+ mMotionAborted = true;
+ mVelocityTracker.clear();
+ }
+ break;
+ case MotionEvent.ACTION_MOVE:
+ final float h = y - mInitialTouchY;
+ addMovement(event);
+ if (scrolledToBottom || mTouchStartedInEmptyArea || mAnimatingOnDown) {
+ float hAbs = Math.abs(h);
+ if ((h < -mTouchSlop || (mAnimatingOnDown && hAbs > mTouchSlop))
+ && hAbs > Math.abs(x - mInitialTouchX)) {
+ cancelHeightAnimator();
+ startExpandMotion(x, y, true /* startTracking */, mExpandedHeight);
+ return true;
+ }
+ }
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ mVelocityTracker.clear();
+ break;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (mInstantExpanding || (mTouchDisabled
+ && event.getActionMasked() != MotionEvent.ACTION_CANCEL) || (mMotionAborted
+ && event.getActionMasked() != MotionEvent.ACTION_DOWN)) {
+ return false;
+ }
+
+ // If dragging should not expand the notifications shade, then return false.
+ if (!mNotificationsDragEnabled) {
+ if (mTracking) {
+ // Turn off tracking if it's on or the shade can get stuck in the down position.
+ onTrackingStopped(true /* expand */);
+ }
+ return false;
+ }
+
+ // On expanding, single mouse click expands the panel instead of dragging.
+ if (isFullyCollapsed() && event.isFromSource(InputDevice.SOURCE_MOUSE)) {
+ if (event.getAction() == MotionEvent.ACTION_UP) {
+ expand(true);
+ }
+ return true;
+ }
+
+ /*
+ * We capture touch events here and update the expand height here in case according to
+ * the users fingers. This also handles multi-touch.
+ *
+ * If the user just clicks shortly, we show a quick peek of the shade.
+ *
+ * Flinging is also enabled in order to open or close the shade.
+ */
+
+ int pointerIndex = event.findPointerIndex(mTrackingPointer);
+ if (pointerIndex < 0) {
+ pointerIndex = 0;
+ mTrackingPointer = event.getPointerId(pointerIndex);
+ }
+ final float x = event.getX(pointerIndex);
+ final float y = event.getY(pointerIndex);
+
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ mGestureWaitForTouchSlop = shouldGestureWaitForTouchSlop();
+ mIgnoreXTouchSlop = isFullyCollapsed() || shouldGestureIgnoreXTouchSlop(x, y);
+ }
+
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
+ mJustPeeked = false;
+ mMinExpandHeight = 0.0f;
+ mPanelClosedOnDown = isFullyCollapsed();
+ mHasLayoutedSinceDown = false;
+ mUpdateFlingOnLayout = false;
+ mMotionAborted = false;
+ mPeekTouching = mPanelClosedOnDown;
+ mDownTime = SystemClock.uptimeMillis();
+ mTouchAboveFalsingThreshold = false;
+ mCollapsedAndHeadsUpOnDown =
+ isFullyCollapsed() && mHeadsUpManager.hasPinnedHeadsUp();
+ addMovement(event);
+ if (!mGestureWaitForTouchSlop || (mHeightAnimator != null
+ && !mHintAnimationRunning) || mPeekAnimator != null) {
+ mTouchSlopExceeded =
+ (mHeightAnimator != null && !mHintAnimationRunning)
+ || mPeekAnimator != null || mTouchSlopExceededBeforeDown;
+ cancelHeightAnimator();
+ cancelPeek();
+ onTrackingStarted();
+ }
+ if (isFullyCollapsed() && !mHeadsUpManager.hasPinnedHeadsUp()
+ && !mStatusBar.isBouncerShowing()) {
+ startOpening(event);
+ }
+ break;
+
+ case MotionEvent.ACTION_POINTER_UP:
+ final int upPointer = event.getPointerId(event.getActionIndex());
+ if (mTrackingPointer == upPointer) {
+ // gesture is ongoing, find a new pointer to track
+ final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
+ final float newY = event.getY(newIndex);
+ final float newX = event.getX(newIndex);
+ mTrackingPointer = event.getPointerId(newIndex);
+ startExpandMotion(newX, newY, true /* startTracking */, mExpandedHeight);
+ }
+ break;
+ case MotionEvent.ACTION_POINTER_DOWN:
+ if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
+ mMotionAborted = true;
+ endMotionEvent(event, x, y, true /* forceCancel */);
+ return false;
+ }
+ break;
+ case MotionEvent.ACTION_MOVE:
+ addMovement(event);
+ float h = y - mInitialTouchY;
+
+ // If the panel was collapsed when touching, we only need to check for the
+ // y-component of the gesture, as we have no conflicting horizontal gesture.
+ if (Math.abs(h) > mTouchSlop && (Math.abs(h) > Math.abs(x - mInitialTouchX)
+ || mIgnoreXTouchSlop)) {
+ mTouchSlopExceeded = true;
+ if (mGestureWaitForTouchSlop && !mTracking && !mCollapsedAndHeadsUpOnDown) {
+ if (!mJustPeeked && mInitialOffsetOnTouch != 0f) {
+ startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
+ h = 0;
+ }
+ cancelHeightAnimator();
+ onTrackingStarted();
+ }
+ }
+ float newHeight = Math.max(0, h + mInitialOffsetOnTouch);
+ if (newHeight > mPeekHeight) {
+ if (mPeekAnimator != null) {
+ mPeekAnimator.cancel();
+ }
+ mJustPeeked = false;
+ } else if (mPeekAnimator == null && mJustPeeked) {
+ // The initial peek has finished, but we haven't dragged as far yet, lets
+ // speed it up by starting at the peek height.
+ mInitialOffsetOnTouch = mExpandedHeight;
+ mInitialTouchY = y;
+ mMinExpandHeight = mExpandedHeight;
+ mJustPeeked = false;
+ }
+ newHeight = Math.max(newHeight, mMinExpandHeight);
+ if (-h >= getFalsingThreshold()) {
+ mTouchAboveFalsingThreshold = true;
+ mUpwardsWhenThresholdReached = isDirectionUpwards(x, y);
+ }
+ if (!mJustPeeked && (!mGestureWaitForTouchSlop || mTracking)
+ && !isTrackingBlocked()) {
+ setExpandedHeightInternal(newHeight);
+ }
+ break;
+
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ addMovement(event);
+ endMotionEvent(event, x, y, false /* forceCancel */);
+ break;
+ }
+ return !mGestureWaitForTouchSlop || mTracking;
+ }
+ }
+
+ public class OnLayoutChangeListener implements View.OnLayoutChangeListener {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
+ int oldTop, int oldRight, int oldBottom) {
+ mStatusBar.onPanelLaidOut();
+ requestPanelHeightUpdate();
+ mHasLayoutedSinceDown = true;
+ if (mUpdateFlingOnLayout) {
+ abortAnimations();
+ fling(mUpdateFlingVelocity, true /* expands */);
+ mUpdateFlingOnLayout = false;
+ }
+ }
+ }
+
+ public class OnConfigurationChangedListener implements
+ PanelView.OnConfigurationChangedListener {
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ loadDimens();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 312ca26..45f3bf9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -236,7 +236,7 @@
public void onPanelFullyOpened() {
super.onPanelFullyOpened();
if (!mIsFullyOpenedPanel) {
- mPanel.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ mPanel.getView().sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
}
mIsFullyOpenedPanel = true;
maybeShowDivider(!mBar.mPanelExpanded);
@@ -420,7 +420,8 @@
void maybeShowDivider(boolean showDivider) {
int state =
- showDivider && NotificationPanelView.isQsSplitEnabled() ? View.VISIBLE : View.GONE;
+ showDivider && NotificationPanelViewController.isQsSplitEnabled()
+ ? View.VISIBLE : View.GONE;
mDividerContainer.setVisibility(state);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
index 57e7014..866dc2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
@@ -79,7 +79,7 @@
public void instantExpandNotificationsPanel() {
// Make our window larger and the panel expanded.
getStatusBar().makeExpandedVisible(true /* force */);
- getNotificationPanelView().expand(false /* animate */);
+ getNotificationPanelViewController().expand(false /* animate */);
mCommandQueue.recomputeDisableFlags(mDisplayId, false /* animate */);
}
@@ -123,8 +123,9 @@
// TODO(b/62444020): remove when this bug is fixed
Log.v(TAG, "mStatusBarWindow: " + getStatusBarWindowView() + " canPanelBeCollapsed(): "
- + getNotificationPanelView().canPanelBeCollapsed());
- if (getStatusBarWindowView() != null && getNotificationPanelView().canPanelBeCollapsed()) {
+ + getNotificationPanelViewController().canPanelBeCollapsed());
+ if (getStatusBarWindowView() != null
+ && getNotificationPanelViewController().canPanelBeCollapsed()) {
// release focus immediately to kick off focus change transition
mStatusBarWindowController.setStatusBarFocusable(false);
@@ -138,7 +139,7 @@
@Override
public boolean closeShadeIfOpen() {
- if (!getNotificationPanelView().isFullyCollapsed()) {
+ if (!getNotificationPanelViewController().isFullyCollapsed()) {
mCommandQueue.animateCollapsePanels(
CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
getStatusBar().visibilityChanged(false);
@@ -149,15 +150,14 @@
@Override
public void postOnShadeExpanded(Runnable executable) {
- getNotificationPanelView().getViewTreeObserver().addOnGlobalLayoutListener(
+ getNotificationPanelViewController().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (getStatusBar().getStatusBarWindow().getHeight()
!= getStatusBar().getStatusBarHeight()) {
- getNotificationPanelView().getViewTreeObserver()
- .removeOnGlobalLayoutListener(this);
- getNotificationPanelView().post(executable);
+ getNotificationPanelViewController().removeOnGlobalLayoutListener(this);
+ getNotificationPanelViewController().getView().post(executable);
}
}
});
@@ -187,7 +187,7 @@
@Override
public boolean collapsePanel() {
- if (!getNotificationPanelView().isFullyCollapsed()) {
+ if (!getNotificationPanelViewController().isFullyCollapsed()) {
// close the shade if it was open
animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
true /* force */, true /* delayed */);
@@ -230,7 +230,7 @@
return (PhoneStatusBarView) getStatusBar().getStatusBarView();
}
- private NotificationPanelView getNotificationPanelView() {
- return getStatusBar().getPanel();
+ private NotificationPanelViewController getNotificationPanelViewController() {
+ return getStatusBar().getPanelController();
}
}
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 a6a734a..30825ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -131,7 +131,6 @@
import com.android.systemui.Dumpable;
import com.android.systemui.EventLogTags;
import com.android.systemui.InitController;
-import com.android.systemui.Interpolators;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
@@ -199,7 +198,6 @@
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationListController;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
-import com.android.systemui.statusbar.notification.ViewGroupFadeHelper;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
@@ -207,7 +205,9 @@
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
@@ -392,7 +392,8 @@
private final DismissCallbackRegistry mDismissCallbackRegistry;
// expanded notifications
- protected NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window
+ // the sliding/resizing panel within the notification window
+ protected NotificationPanelViewController mNotificationPanelViewController;
// settings
private QSPanel mQSPanel;
@@ -413,6 +414,7 @@
private final NotificationGutsManager mGutsManager;
private final NotificationLogger mNotificationLogger;
private final NotificationEntryManager mEntryManager;
+ private final NotificationRowContentBinder mRowContentBinder;
private NotificationListController mNotificationListController;
private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
private final NotificationViewHierarchyManager mViewHierarchyManager;
@@ -461,8 +463,8 @@
mUserSetup = userSetup;
if (!mUserSetup && mStatusBarView != null)
animateCollapseQuickSettings();
- if (mNotificationPanel != null) {
- mNotificationPanel.setUserSetupComplete(mUserSetup);
+ if (mNotificationPanelViewController != null) {
+ mNotificationPanelViewController.setUserSetupComplete(mUserSetup);
}
updateQsExpansionEnabled();
}
@@ -634,6 +636,7 @@
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
NotificationEntryManager notificationEntryManager,
+ NotificationRowContentBinder notificationRowContentBinder,
NotificationInterruptionStateProvider notificationInterruptionStateProvider,
NotificationViewHierarchyManager notificationViewHierarchyManager,
KeyguardViewMediator keyguardViewMediator,
@@ -715,6 +718,7 @@
mGutsManager = notificationGutsManager;
mNotificationLogger = notificationLogger;
mEntryManager = notificationEntryManager;
+ mRowContentBinder = notificationRowContentBinder;
mNotificationInterruptionStateProvider = notificationInterruptionStateProvider;
mViewHierarchyManager = notificationViewHierarchyManager;
mKeyguardViewMediator = keyguardViewMediator;
@@ -913,9 +917,8 @@
mKeyguardUpdateMonitor.registerCallback(mUpdateCallback);
mDozeServiceHost.initialize(this, mNotificationIconAreaController,
- mStatusBarKeyguardViewManager,
- mStatusBarWindowViewController,
- mNotificationPanel, mAmbientIndicationContainer);
+ mStatusBarKeyguardViewManager, mStatusBarWindowViewController,
+ mNotificationPanelViewController, mAmbientIndicationContainer);
mConfigurationController.addCallback(this);
@@ -985,8 +988,6 @@
// TODO: Deal with the ugliness that comes from having some of the statusbar broken out
// into fragments, but the rest here, it leaves some awkward lifecycle and whatnot.
- mNotificationPanel = mSuperStatusBarViewFactory.getNotificationPanelView();
-
mStackScroller = mStatusBarWindow.findViewById(R.id.notification_stack_scroller);
NotificationListContainer notifListContainer = (NotificationListContainer) mStackScroller;
mNotificationLogger.setUpWithContainer(notifListContainer);
@@ -1000,8 +1001,9 @@
mWakeUpCoordinator.setIconAreaController(mNotificationIconAreaController);
inflateShelf();
mNotificationIconAreaController.setupShelf(mNotificationShelf);
- mNotificationPanel.setOnReinflationListener(mNotificationIconAreaController::initAodIcons);
- mNotificationPanel.addExpansionListener(mWakeUpCoordinator);
+ mNotificationPanelViewController.setOnReinflationListener(
+ mNotificationIconAreaController::initAodIcons);
+ mNotificationPanelViewController.addExpansionListener(mWakeUpCoordinator);
mDarkIconDispatcher.addDarkReceiver(mNotificationIconAreaController);
// Allow plugins to reference DarkIconDispatcher and StatusBarStateController
@@ -1015,7 +1017,7 @@
PhoneStatusBarView oldStatusBarView = mStatusBarView;
mStatusBarView = (PhoneStatusBarView) fragment.getView();
mStatusBarView.setBar(this);
- mStatusBarView.setPanel(mNotificationPanel);
+ mStatusBarView.setPanel(mNotificationPanelViewController);
mStatusBarView.setScrimController(mScrimController);
// CollapsedStatusBarFragment re-inflated PhoneStatusBarView and both of
@@ -1026,7 +1028,7 @@
// it needs to notify PhoneStatusBarView's new instance to update the correct
// status by calling mNotificationPanel.notifyBarPanelExpansionChanged().
if (mHeadsUpManager.hasPinnedHeadsUp()) {
- mNotificationPanel.notifyBarPanelExpansionChanged();
+ mNotificationPanelViewController.notifyBarPanelExpansionChanged();
}
mStatusBarView.setBouncerShowing(mBouncerShowing);
if (oldStatusBarView != null) {
@@ -1040,10 +1042,12 @@
// This view is being recreated, let's destroy the old one
mHeadsUpAppearanceController.destroy();
}
+ // TODO: this should probably be scoped to the StatusBarComponent
mHeadsUpAppearanceController = new HeadsUpAppearanceController(
mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow,
mStatusBarStateController, mKeyguardBypassController,
- mKeyguardStateController, mWakeUpCoordinator, mCommandQueue);
+ mKeyguardStateController, mWakeUpCoordinator, mCommandQueue,
+ mNotificationPanelViewController);
mHeadsUpAppearanceController.readFrom(oldController);
mLightsOutNotifController.setLightsOutNotifView(
@@ -1059,11 +1063,11 @@
mHeadsUpManager.setUp(mStatusBarWindow, mGroupManager, this, mVisualStabilityManager);
mConfigurationController.addCallback(mHeadsUpManager);
mHeadsUpManager.addListener(this);
- mHeadsUpManager.addListener(mNotificationPanel);
+ mHeadsUpManager.addListener(mNotificationPanelViewController.getOnHeadsUpChangedListener());
mHeadsUpManager.addListener(mGroupManager);
mHeadsUpManager.addListener(mGroupAlertTransferHelper);
mHeadsUpManager.addListener(mVisualStabilityManager);
- mNotificationPanel.setHeadsUpManager(mHeadsUpManager);
+ mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
mGroupManager.setHeadsUpManager(mHeadsUpManager);
mGroupAlertTransferHelper.setHeadsUpManager(mHeadsUpManager);
mNotificationLogger.setHeadsUpManager(mHeadsUpManager);
@@ -1078,7 +1082,8 @@
SystemUIFactory.getInstance().createKeyguardIndicationController(mContext,
mStatusBarWindow.findViewById(R.id.keyguard_indication_area),
mStatusBarWindow.findViewById(R.id.lock_icon));
- mNotificationPanel.setKeyguardIndicationController(mKeyguardIndicationController);
+ mNotificationPanelViewController.setKeyguardIndicationController(
+ mKeyguardIndicationController);
mAmbientIndicationContainer = mStatusBarWindow.findViewById(
R.id.ambient_indication_container);
@@ -1113,19 +1118,19 @@
});
mScrimController.attachViews(scrimBehind, scrimInFront, scrimForBubble);
- mNotificationPanel.initDependencies(this, mGroupManager, mNotificationShelf,
- mHeadsUpManager, mNotificationIconAreaController, mScrimController);
+ mNotificationPanelViewController.initDependencies(this, mGroupManager, mNotificationShelf,
+ mNotificationIconAreaController, mScrimController);
BackDropView backdrop = mStatusBarWindow.findViewById(R.id.backdrop);
mMediaManager.setup(backdrop, backdrop.findViewById(R.id.backdrop_front),
backdrop.findViewById(R.id.backdrop_back), mScrimController, mLockscreenWallpaper);
- mNotificationPanel.setUserSetupComplete(mUserSetup);
+ mNotificationPanelViewController.setUserSetupComplete(mUserSetup);
if (UserManager.get(mContext).isUserSwitcherEnabled()) {
createUserSwitcher();
}
- mNotificationPanel.setLaunchAffordanceListener(
+ mNotificationPanelViewController.setLaunchAffordanceListener(
mLockscreenLockIconController::onShowingLaunchAffordanceChanged);
// Set up the quick settings tile panel
@@ -1139,6 +1144,7 @@
.withDefault(this::createDefaultQSFragment)
.build());
mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow,
+ mNotificationPanelViewController,
(visible) -> {
mBrightnessMirrorVisible = visible;
updateScrimController();
@@ -1232,19 +1238,20 @@
private void setUpPresenter() {
// Set up the initial notification state.
mActivityLaunchAnimator = new ActivityLaunchAnimator(
- mStatusBarWindowViewController, this, mNotificationPanel,
+ mStatusBarWindowViewController, this, mNotificationPanelViewController,
(NotificationListContainer) mStackScroller);
final NotificationRowBinderImpl rowBinder =
new NotificationRowBinderImpl(
mContext,
+ mRowContentBinder,
mAllowNotificationLongPress,
mKeyguardBypassController,
mStatusBarStateController,
mNotificationLogger);
// TODO: inject this.
- mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanel,
+ mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanelViewController,
mHeadsUpManager, mStatusBarWindow, mStackScroller, mDozeScrimController,
mScrimController, mActivityLaunchAnimator, mDynamicPrivacyController,
mNotificationAlertingManager, rowBinder, mKeyguardStateController,
@@ -1265,11 +1272,16 @@
.setStatusBar(this)
.setActivityLaunchAnimator(mActivityLaunchAnimator)
.setNotificationPresenter(mPresenter)
+ .setNotificationPanelViewController(mNotificationPanelViewController)
.build();
mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
- mEntryManager.setRowBinder(rowBinder);
+ if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
+ mEntryManager.setRowBinder(rowBinder);
+ rowBinder.setInflationCallback(mEntryManager);
+ }
+
mRemoteInputUriController.attach(mEntryManager);
rowBinder.setNotificationClicker(new NotificationClicker(
@@ -1279,7 +1291,7 @@
mNotificationListController.bind();
if (mFeatureFlags.isNewNotifPipelineEnabled()) {
- mNewNotifPipeline.get().initialize(mNotificationListener);
+ mNewNotifPipeline.get().initialize(mNotificationListener, rowBinder);
}
mEntryManager.attach(mNotificationListener);
}
@@ -1374,7 +1386,7 @@
}
// We need the new R.id.keyguard_indication_area before recreating
// mKeyguardIndicationController
- mNotificationPanel.onThemeChanged();
+ mNotificationPanelViewController.onThemeChanged();
onThemeChanged();
}
@@ -1389,7 +1401,7 @@
mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext,
mStatusBarWindow.findViewById(R.id.keyguard_user_switcher),
mStatusBarWindow.findViewById(R.id.keyguard_header),
- mNotificationPanel);
+ mNotificationPanelViewController);
}
private void inflateStatusBarWindow() {
@@ -1398,16 +1410,17 @@
.statusBarWindowView(mStatusBarWindow).build();
mStatusBarWindowViewController = statusBarComponent.getStatusBarWindowViewController();
mStatusBarWindowViewController.setupExpandedStatusBar();
+ mNotificationPanelViewController = statusBarComponent.getNotificationPanelViewController();
}
protected void startKeyguard() {
Trace.beginSection("StatusBar#startKeyguard");
mBiometricUnlockController = mBiometricUnlockControllerLazy.get();
mStatusBarKeyguardViewManager.registerStatusBar(
- /* statusBar= */ this, getBouncerContainer(), mNotificationPanel,
- mBiometricUnlockController, mDismissCallbackRegistry,
- mStatusBarWindow.findViewById(R.id.lock_icon_container), mStackScroller,
- mKeyguardBypassController, mFalsingManager);
+ /* statusBar= */ this, getBouncerContainer(),
+ mNotificationPanelViewController, mBiometricUnlockController,
+ mDismissCallbackRegistry, mStatusBarWindow.findViewById(R.id.lock_icon_container),
+ mStackScroller, mKeyguardBypassController, mFalsingManager);
mKeyguardIndicationController
.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
mBiometricUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
@@ -1484,7 +1497,7 @@
&& ((mDisabled2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) == 0)
&& !mDozing
&& !ONLY_CORE_APPS;
- mNotificationPanel.setQsExpansionEnabled(expandEnabled);
+ mNotificationPanelViewController.setQsExpansionEnabled(expandEnabled);
Log.d(TAG, "updateQsExpansionEnabled - QS Expand enabled: " + expandEnabled);
}
@@ -1644,7 +1657,7 @@
public void setQsExpanded(boolean expanded) {
mStatusBarWindowController.setQsExpanded(expanded);
- mNotificationPanel.setStatusAccessibilityImportance(expanded
+ mNotificationPanelViewController.setStatusAccessibilityImportance(expanded
? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
: View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
if (getNavigationBarView() != null) {
@@ -1678,22 +1691,22 @@
if (inPinnedMode) {
mStatusBarWindowController.setHeadsUpShowing(true);
mStatusBarWindowController.setForceStatusBarVisible(true);
- if (mNotificationPanel.isFullyCollapsed()) {
+ if (mNotificationPanelViewController.isFullyCollapsed()) {
// We need to ensure that the touchable region is updated before the window will be
// resized, in order to not catch any touches. A layout will ensure that
// onComputeInternalInsets will be called and after that we can resize the layout. Let's
// make sure that the window stays small for one frame until the touchableRegion is set.
- mNotificationPanel.requestLayout();
+ mNotificationPanelViewController.getView().requestLayout();
mStatusBarWindowController.setForceWindowCollapsed(true);
- mNotificationPanel.post(() -> {
+ mNotificationPanelViewController.getView().post(() -> {
mStatusBarWindowController.setForceWindowCollapsed(false);
});
}
} else {
boolean bypassKeyguard = mKeyguardBypassController.getBypassEnabled()
&& mState == StatusBarState.KEYGUARD;
- if (!mNotificationPanel.isFullyCollapsed() || mNotificationPanel.isTracking()
- || bypassKeyguard) {
+ if (!mNotificationPanelViewController.isFullyCollapsed()
+ || mNotificationPanelViewController.isTracking() || bypassKeyguard) {
// We are currently tracking or is open and the shade doesn't need to be kept
// open artificially.
mStatusBarWindowController.setHeadsUpShowing(false);
@@ -1704,7 +1717,7 @@
// we need to keep the panel open artificially, let's wait until the animation
// is finished.
mHeadsUpManager.setHeadsUpGoingAway(true);
- mNotificationPanel.runAfterAnimationFinished(() -> {
+ mNotificationPanelViewController.runAfterAnimationFinished(() -> {
if (!mHeadsUpManager.hasPinnedHeadsUp()) {
mStatusBarWindowController.setHeadsUpShowing(false);
mHeadsUpManager.setHeadsUpGoingAway(false);
@@ -1757,7 +1770,7 @@
}
public boolean hideStatusBarIconsWhenExpanded() {
- return mNotificationPanel.hideStatusBarIconsWhenExpanded();
+ return mNotificationPanelViewController.hideStatusBarIconsWhenExpanded();
}
@Override
@@ -1947,19 +1960,21 @@
if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP == key) {
mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_UP);
- mNotificationPanel.collapse(false /* delayed */, 1.0f /* speedUpFactor */);
+ mNotificationPanelViewController.collapse(
+ false /* delayed */, 1.0f /* speedUpFactor */);
} else if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN == key) {
mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_DOWN);
- if (mNotificationPanel.isFullyCollapsed()) {
+ if (mNotificationPanelViewController.isFullyCollapsed()) {
if (mVibrateOnOpening) {
mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
}
- mNotificationPanel.expand(true /* animate */);
+ mNotificationPanelViewController.expand(true /* animate */);
((NotificationListContainer) mStackScroller).setWillExpand(true);
mHeadsUpManager.unpinAll(true /* userUnpinned */);
mMetricsLogger.count(NotificationPanelView.COUNTER_PANEL_OPEN, 1);
- } else if (!mNotificationPanel.isInSettings() && !mNotificationPanel.isExpanding()){
- mNotificationPanel.flingSettings(0 /* velocity */,
+ } else if (!mNotificationPanelViewController.isInSettings()
+ && !mNotificationPanelViewController.isExpanding()) {
+ mNotificationPanelViewController.flingSettings(0 /* velocity */,
NotificationPanelView.FLING_EXPAND);
mMetricsLogger.count(NotificationPanelView.COUNTER_PANEL_OPEN_QS, 1);
}
@@ -2054,9 +2069,9 @@
}
if (start) {
- mNotificationPanel.startWaitingForOpenPanelGesture();
+ mNotificationPanelViewController.startWaitingForOpenPanelGesture();
} else {
- mNotificationPanel.stopWaitingForOpenPanelGesture(velocity);
+ mNotificationPanelViewController.stopWaitingForOpenPanelGesture(velocity);
}
}
@@ -2067,7 +2082,7 @@
return ;
}
- mNotificationPanel.expandWithoutQs();
+ mNotificationPanelViewController.expandWithoutQs();
if (false) postStartTracing();
}
@@ -2085,7 +2100,7 @@
if (subPanel != null) {
mQSPanel.openDetails(subPanel);
}
- mNotificationPanel.expandWithQs();
+ mNotificationPanelViewController.expandWithQs();
if (false) postStartTracing();
}
@@ -2108,7 +2123,7 @@
mStatusBarView.collapsePanel(/*animate=*/ false, false /* delayed*/,
1.0f /* speedUpFactor */);
- mNotificationPanel.closeQs();
+ mNotificationPanelViewController.closeQs();
mExpandedVisible = false;
visibilityChanged(false);
@@ -2129,7 +2144,8 @@
Log.d(TAG, "Not showing bouncer due to activity showing over lockscreen");
}
mCommandQueue.recomputeDisableFlags(
- mDisplayId, mNotificationPanel.hideStatusBarIconsWhenExpanded() /* animate */);
+ mDisplayId,
+ mNotificationPanelViewController.hideStatusBarIconsWhenExpanded() /* animate */);
// Trimming will happen later if Keyguard is showing - doing it here might cause a jank in
// the bouncer appear animation.
@@ -2310,12 +2326,12 @@
batteryLevel, new WirelessChargingAnimation.Callback() {
@Override
public void onAnimationStarting() {
- CrossFadeHelper.fadeOut(mNotificationPanel, 1);
+ CrossFadeHelper.fadeOut(mNotificationPanelViewController.getView(), 1);
}
@Override
public void onAnimationEnded() {
- CrossFadeHelper.fadeIn(mNotificationPanel);
+ CrossFadeHelper.fadeIn(mNotificationPanelViewController.getView());
}
}, mDozing).show();
} else {
@@ -2344,7 +2360,7 @@
// Called by NavigationBarFragment
void setQsScrimEnabled(boolean scrimEnabled) {
- mNotificationPanel.setQsScrimEnabled(scrimEnabled);
+ mNotificationPanelViewController.setQsScrimEnabled(scrimEnabled);
}
void checkBarMode(@TransitionMode int mode, @WindowVisibleState int windowState,
@@ -2436,11 +2452,12 @@
}
pw.println(" Panels: ");
- if (mNotificationPanel != null) {
- pw.println(" mNotificationPanel=" +
- mNotificationPanel + " params=" + mNotificationPanel.getLayoutParams().debug(""));
+ if (mNotificationPanelViewController != null) {
+ pw.println(" mNotificationPanel="
+ + mNotificationPanelViewController.getView() + " params="
+ + mNotificationPanelViewController.getView().getLayoutParams().debug(""));
pw.print (" ");
- mNotificationPanel.dump(fd, pw, args);
+ mNotificationPanelViewController.dump(fd, pw, args);
}
pw.println(" mStackScroller: ");
if (mStackScroller instanceof Dumpable) {
@@ -2653,7 +2670,8 @@
// Do it after DismissAction has been processed to conserve the needed ordering.
mHandler.post(mShadeController::runPostCollapseRunnables);
}
- } else if (isInLaunchTransition() && mNotificationPanel.isLaunchTransitionFinished()) {
+ } else if (isInLaunchTransition()
+ && mNotificationPanelViewController.isLaunchTransitionFinished()) {
// We are not dismissing the shade, but the launch transition is already finished,
// so nobody will call readyForKeyguardDone anymore. Post it such that
@@ -2810,8 +2828,8 @@
if (mStatusBarView != null) {
mStatusBarView.updateResources();
}
- if (mNotificationPanel != null) {
- mNotificationPanel.updateResources();
+ if (mNotificationPanelViewController != null) {
+ mNotificationPanelViewController.updateResources();
}
if (mBrightnessMirrorController != null) {
mBrightnessMirrorController.updateResources();
@@ -3103,7 +3121,7 @@
public void showKeyguardImpl() {
mIsKeyguard = true;
if (mKeyguardStateController.isLaunchTransitionFadingAway()) {
- mNotificationPanel.animate().cancel();
+ mNotificationPanelViewController.cancelAnimation();
onLaunchTransitionFadingEnded();
}
mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
@@ -3130,8 +3148,8 @@
}
private void onLaunchTransitionFadingEnded() {
- mNotificationPanel.setAlpha(1.0f);
- mNotificationPanel.onAffordanceLaunchEnded();
+ mNotificationPanelViewController.setAlpha(1.0f);
+ mNotificationPanelViewController.onAffordanceLaunchEnded();
releaseGestureWakeLock();
runLaunchTransitionEndRunnable();
mKeyguardStateController.setLaunchTransitionFadingAway(false);
@@ -3139,8 +3157,8 @@
}
public boolean isInLaunchTransition() {
- return mNotificationPanel.isLaunchTransitionRunning()
- || mNotificationPanel.isLaunchTransitionFinished();
+ return mNotificationPanelViewController.isLaunchTransitionRunning()
+ || mNotificationPanelViewController.isLaunchTransitionFinished();
}
/**
@@ -3161,18 +3179,15 @@
}
updateScrimController();
mPresenter.updateMediaMetaData(false, true);
- mNotificationPanel.setAlpha(1);
- mNotificationPanel.animate()
- .alpha(0)
- .setStartDelay(FADE_KEYGUARD_START_DELAY)
- .setDuration(FADE_KEYGUARD_DURATION)
- .withLayer()
- .withEndAction(this::onLaunchTransitionFadingEnded);
+ mNotificationPanelViewController.setAlpha(1);
+ mNotificationPanelViewController.fadeOut(
+ FADE_KEYGUARD_START_DELAY, FADE_KEYGUARD_DURATION,
+ this::onLaunchTransitionFadingEnded);
mCommandQueue.appTransitionStarting(mDisplayId, SystemClock.uptimeMillis(),
LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true);
};
- if (mNotificationPanel.isLaunchTransitionRunning()) {
- mNotificationPanel.setLaunchTransitionEndRunnable(hideRunnable);
+ if (mNotificationPanelViewController.isLaunchTransitionRunning()) {
+ mNotificationPanelViewController.setLaunchTransitionEndRunnable(hideRunnable);
} else {
hideRunnable.run();
}
@@ -3183,22 +3198,18 @@
* fading.
*/
public void fadeKeyguardWhilePulsing() {
- mNotificationPanel.animate()
- .alpha(0f)
- .setStartDelay(0)
- .setDuration(FADE_KEYGUARD_DURATION_PULSING)
- .setInterpolator(Interpolators.ALPHA_OUT)
- .withEndAction(()-> {
- hideKeyguard();
- mStatusBarKeyguardViewManager.onKeyguardFadedAway();
- }).start();
+ mNotificationPanelViewController.fadeOut(0, FADE_KEYGUARD_DURATION_PULSING,
+ ()-> {
+ hideKeyguard();
+ mStatusBarKeyguardViewManager.onKeyguardFadedAway();
+ }).start();
}
/**
* Plays the animation when an activity that was occluding Keyguard goes away.
*/
public void animateKeyguardUnoccluding() {
- mNotificationPanel.setExpandedFraction(0f);
+ mNotificationPanelViewController.setExpandedFraction(0f);
animateExpandNotificationsPanel();
}
@@ -3214,9 +3225,9 @@
private void onLaunchTransitionTimeout() {
Log.w(TAG, "Launch transition: Timeout!");
- mNotificationPanel.onAffordanceLaunchEnded();
+ mNotificationPanelViewController.onAffordanceLaunchEnded();
releaseGestureWakeLock();
- mNotificationPanel.resetViews(false /* animate */);
+ mNotificationPanelViewController.resetViews(false /* animate */);
}
private void runLaunchTransitionEndRunnable() {
@@ -3249,7 +3260,7 @@
mStatusBarStateController.setLeaveOpenOnKeyguardHide(false);
}
long delay = mKeyguardStateController.calculateGoingToFullShadeDelay();
- mNotificationPanel.animateToFullShade(delay);
+ mNotificationPanelViewController.animateToFullShade(delay);
if (mDraggedDownEntry != null) {
mDraggedDownEntry.setUserLocked(false);
mDraggedDownEntry = null;
@@ -3258,7 +3269,7 @@
// Disable layout transitions in navbar for this transition because the load is just
// too heavy for the CPU and GPU on any device.
mNavigationBarController.disableAnimationsDuringHide(mDisplayId, delay);
- } else if (!mNotificationPanel.isCollapsing()) {
+ } else if (!mNotificationPanelViewController.isCollapsing()) {
instantCollapseNotificationPanel();
}
@@ -3269,10 +3280,10 @@
}
mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
releaseGestureWakeLock();
- mNotificationPanel.onAffordanceLaunchEnded();
- mNotificationPanel.animate().cancel();
- mNotificationPanel.setAlpha(1f);
- ViewGroupFadeHelper.reset(mNotificationPanel);
+ mNotificationPanelViewController.onAffordanceLaunchEnded();
+ mNotificationPanelViewController.cancelAnimation();
+ mNotificationPanelViewController.setAlpha(1f);
+ mNotificationPanelViewController.resetViewGroupFade();
updateScrimController();
Trace.endSection();
return staying;
@@ -3347,7 +3358,7 @@
boolean animate = (!mDozing && mDozeServiceHost.shouldAnimateWakeup() && !wakeAndUnlock)
|| (mDozing && mDozeServiceHost.shouldAnimateScreenOff() && sleepingFromKeyguard);
- mNotificationPanel.setDozing(mDozing, animate, mWakeUpTouchLocation);
+ mNotificationPanelViewController.setDozing(mDozing, animate, mWakeUpTouchLocation);
updateQsExpansionEnabled();
Trace.endSection();
}
@@ -3379,27 +3390,27 @@
public void endAffordanceLaunch() {
releaseGestureWakeLock();
- mNotificationPanel.onAffordanceLaunchEnded();
+ mNotificationPanelViewController.onAffordanceLaunchEnded();
}
public boolean onBackPressed() {
boolean isScrimmedBouncer = mScrimController.getState() == ScrimState.BOUNCER_SCRIMMED;
if (mStatusBarKeyguardViewManager.onBackPressed(isScrimmedBouncer /* hideImmediately */)) {
if (!isScrimmedBouncer) {
- mNotificationPanel.expandWithoutQs();
+ mNotificationPanelViewController.expandWithoutQs();
}
return true;
}
- if (mNotificationPanel.isQsExpanded()) {
- if (mNotificationPanel.isQsDetailShowing()) {
- mNotificationPanel.closeQsDetail();
+ if (mNotificationPanelViewController.isQsExpanded()) {
+ if (mNotificationPanelViewController.isQsDetailShowing()) {
+ mNotificationPanelViewController.closeQsDetail();
} else {
- mNotificationPanel.animateCloseQs(false /* animateAway */);
+ mNotificationPanelViewController.animateCloseQs(false /* animateAway */);
}
return true;
}
if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) {
- if (mNotificationPanel.canPanelBeCollapsed()) {
+ if (mNotificationPanelViewController.canPanelBeCollapsed()) {
mShadeController.animateCollapsePanels();
} else {
mBubbleController.performBackPressIfNeeded();
@@ -3429,7 +3440,7 @@
}
void instantCollapseNotificationPanel() {
- mNotificationPanel.instantCollapse();
+ mNotificationPanelViewController.instantCollapse();
mShadeController.runPostCollapseRunnables();
}
@@ -3491,12 +3502,11 @@
public void onDozingChanged(boolean isDozing) {
Trace.beginSection("StatusBar#updateDozing");
mDozing = isDozing;
- mDozeServiceHost.setDozing(mDozing);
// Collapse the notification panel if open
boolean dozingAnimated = mDozeServiceHost.getDozingRequested()
&& mDozeParameters.shouldControlScreenOff();
- mNotificationPanel.resetViews(dozingAnimated);
+ mNotificationPanelViewController.resetViews(dozingAnimated);
updateQsExpansionEnabled();
mKeyguardViewMediator.setDozing(mDozing);
@@ -3570,7 +3580,7 @@
* @return bottom area view
*/
public KeyguardBottomAreaView getKeyguardBottomAreaView() {
- return mNotificationPanel.getKeyguardBottomAreaView();
+ return mNotificationPanelViewController.getKeyguardBottomAreaView();
}
/**
@@ -3609,7 +3619,7 @@
mDraggedDownEntry = entry;
mPendingRemoteInputView = null;
} else {
- mNotificationPanel.animateToFullShade(0 /* delay */);
+ mNotificationPanelViewController.animateToFullShade(0 /* delay */);
mStatusBarStateController.setState(StatusBarState.SHADE_LOCKED);
}
}
@@ -3635,7 +3645,7 @@
* Collapses the notification shade if it is tracking or expanded.
*/
public void collapseShade() {
- if (mNotificationPanel.isTracking()) {
+ if (mNotificationPanelViewController.isTracking()) {
mStatusBarWindowViewController.cancelCurrentTouch();
}
if (mPanelExpanded && mState == StatusBarState.SHADE) {
@@ -3647,7 +3657,7 @@
final WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() {
@Override
public void onFinishedGoingToSleep() {
- mNotificationPanel.onAffordanceLaunchEnded();
+ mNotificationPanelViewController.onAffordanceLaunchEnded();
releaseGestureWakeLock();
mLaunchCameraWhenFinishedWaking = false;
mDeviceInteractive = false;
@@ -3708,7 +3718,8 @@
mBypassHeadsUpNotifier.setFullyAwake(true);
mWakeUpCoordinator.setWakingUp(false);
if (mLaunchCameraWhenFinishedWaking) {
- mNotificationPanel.launchCamera(false /* animate */, mLastCameraLaunchSource);
+ mNotificationPanelViewController.launchCamera(
+ false /* animate */, mLastCameraLaunchSource);
mLaunchCameraWhenFinishedWaking = false;
}
updateScrimController();
@@ -3725,7 +3736,7 @@
&& !mDozeParameters.shouldControlScreenOff();
boolean disabled = (!mDeviceInteractive && !mDozeServiceHost.isPulsing())
|| goingToSleepWithoutAnimation;
- mNotificationPanel.setTouchAndAnimationDisabled(disabled);
+ mNotificationPanelViewController.setTouchAndAnimationDisabled(disabled);
mNotificationIconAreaController.setAnimationsEnabled(!disabled);
}
@@ -3733,7 +3744,7 @@
@Override
public void onScreenTurningOn() {
mFalsingManager.onScreenTurningOn();
- mNotificationPanel.onScreenTurningOn();
+ mNotificationPanelViewController.onScreenTurningOn();
}
@Override
@@ -3802,7 +3813,7 @@
mLaunchCameraOnFinishedGoingToSleep = true;
return;
}
- if (!mNotificationPanel.canCameraGestureBeLaunched()) {
+ if (!mNotificationPanelViewController.canCameraGestureBeLaunched()) {
if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Can't launch camera right now");
return;
}
@@ -3832,7 +3843,8 @@
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
mStatusBarKeyguardViewManager.reset(true /* hide */);
}
- mNotificationPanel.launchCamera(mDeviceInteractive /* animate */, source);
+ mNotificationPanelViewController.launchCamera(
+ mDeviceInteractive /* animate */, source);
updateScrimController();
} else {
// We need to defer the camera launch until the screen comes on, since otherwise
@@ -3891,7 +3903,7 @@
!mBiometricUnlockController.isBiometricUnlock());
boolean launchingAffordanceWithPreview =
- mNotificationPanel.isLaunchingAffordanceWithPreview();
+ mNotificationPanelViewController.isLaunchingAffordanceWithPreview();
mScrimController.setLaunchingAffordanceWithPreview(launchingAffordanceWithPreview);
if (mBouncerShowing) {
@@ -4240,7 +4252,7 @@
* When {@link KeyguardBouncer} starts to be dismissed, playing its animation.
*/
public void onBouncerPreHideAnimation() {
- mNotificationPanel.onBouncerPreHideAnimation();
+ mNotificationPanelViewController.onBouncerPreHideAnimation();
mLockscreenLockIconController.onBouncerPreHideAnimation();
}
@@ -4283,8 +4295,8 @@
mAssistManagerLazy.get().showDisclosure();
}
- public NotificationPanelView getPanel() {
- return mNotificationPanel;
+ public NotificationPanelViewController getPanelController() {
+ return mNotificationPanelViewController;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index f51174b..407d256 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -133,7 +133,7 @@
protected LockPatternUtils mLockPatternUtils;
protected ViewMediatorCallback mViewMediatorCallback;
protected StatusBar mStatusBar;
- private NotificationPanelView mNotificationPanelView;
+ private NotificationPanelViewController mNotificationPanelViewController;
private BiometricUnlockController mBiometricUnlockController;
private ViewGroup mContainer;
@@ -224,7 +224,7 @@
public void registerStatusBar(StatusBar statusBar,
ViewGroup container,
- NotificationPanelView notificationPanelView,
+ NotificationPanelViewController notificationPanelViewController,
BiometricUnlockController biometricUnlockController,
DismissCallbackRegistry dismissCallbackRegistry,
ViewGroup lockIconContainer, View notificationContainer,
@@ -239,8 +239,8 @@
mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext,
mViewMediatorCallback, mLockPatternUtils, container, dismissCallbackRegistry,
mExpansionCallback, mKeyguardStateController, falsingManager, bypassController);
- mNotificationPanelView = notificationPanelView;
- notificationPanelView.addExpansionListener(this);
+ mNotificationPanelViewController = notificationPanelViewController;
+ notificationPanelViewController.addExpansionListener(this);
mBypassController = bypassController;
mNotificationContainer = notificationContainer;
}
@@ -253,7 +253,7 @@
// • The user quickly taps on the display and we show "swipe up to unlock."
// • Keyguard will be dismissed by an action. a.k.a: FLAG_DISMISS_KEYGUARD_ACTIVITY
// • Full-screen user switcher is displayed.
- if (mNotificationPanelView.isUnlockHintRunning()) {
+ if (mNotificationPanelViewController.isUnlockHintRunning()) {
mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
} else if (bouncerNeedsScrimming()) {
mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
@@ -284,7 +284,7 @@
return;
}
boolean keyguardWithoutQs = mStatusBarStateController.getState() == StatusBarState.KEYGUARD
- && !mNotificationPanelView.isQsExpanded();
+ && !mNotificationPanelViewController.isQsExpanded();
boolean lockVisible = (mBouncer.isShowing() || keyguardWithoutQs)
&& !mBouncer.isAnimatingAway() && !mKeyguardStateController.isKeyguardFadingAway();
@@ -555,7 +555,7 @@
} else if (finishRunnable != null) {
finishRunnable.run();
}
- mNotificationPanelView.blockExpansionForCurrentTouch();
+ mNotificationPanelViewController.blockExpansionForCurrentTouch();
updateLockIcon();
}
@@ -609,7 +609,8 @@
hideBouncer(true /* destroyView */);
if (wakeUnlockPulsing) {
if (needsFading) {
- ViewGroupFadeHelper.fadeOutAllChildrenExcept(mNotificationPanelView,
+ ViewGroupFadeHelper.fadeOutAllChildrenExcept(
+ mNotificationPanelViewController.getView(),
mNotificationContainer,
fadeoutDuration,
() -> {
@@ -625,7 +626,8 @@
if (!staying) {
mStatusBarWindowController.setKeyguardFadingAway(true);
if (needsFading) {
- ViewGroupFadeHelper.fadeOutAllChildrenExcept(mNotificationPanelView,
+ ViewGroupFadeHelper.fadeOutAllChildrenExcept(
+ mNotificationPanelViewController.getView(),
mNotificationContainer,
fadeoutDuration,
() -> {
@@ -684,7 +686,7 @@
public void onKeyguardFadedAway() {
mContainer.postDelayed(() -> mStatusBarWindowController.setKeyguardFadingAway(false),
100);
- ViewGroupFadeHelper.reset(mNotificationPanelView);
+ ViewGroupFadeHelper.reset(mNotificationPanelViewController.getView());
mStatusBar.finishKeyguardFadingAway();
mBiometricUnlockController.finishKeyguardFadingAway();
WindowManagerGlobal.getInstance().trimMemory(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
index 12033de..df74107 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
@@ -69,6 +69,8 @@
import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder;
+import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -124,6 +126,7 @@
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
NotificationEntryManager notificationEntryManager,
+ NotificationRowContentBinder notificationRowContentBinder,
NotificationInterruptionStateProvider notificationInterruptionStateProvider,
NotificationViewHierarchyManager notificationViewHierarchyManager,
KeyguardViewMediator keyguardViewMediator,
@@ -206,6 +209,7 @@
notificationGutsManager,
notificationLogger,
notificationEntryManager,
+ notificationRowContentBinder,
notificationInterruptionStateProvider,
notificationViewHierarchyManager,
keyguardViewMediator,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 661a7b1..0f3b5db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -61,7 +61,6 @@
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
@@ -102,7 +101,7 @@
private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
private final MetricsLogger mMetricsLogger;
private final Context mContext;
- private final NotificationPanelView mNotificationPanel;
+ private final NotificationPanelViewController mNotificationPanel;
private final NotificationPresenter mPresenter;
private final LockPatternUtils mLockPatternUtils;
private final HeadsUpManagerPhone mHeadsUpManager;
@@ -121,7 +120,7 @@
private boolean mIsCollapsingToShowActivityOverLockscreen;
private StatusBarNotificationActivityStarter(Context context, CommandQueue commandQueue,
- Lazy<AssistManager> assistManagerLazy, NotificationPanelView panel,
+ Lazy<AssistManager> assistManagerLazy, NotificationPanelViewController panel,
NotificationPresenter presenter, NotificationEntryManager entryManager,
HeadsUpManagerPhone headsUpManager, ActivityStarter activityStarter,
ActivityLaunchAnimator activityLaunchAnimator, IStatusBarService statusBarService,
@@ -519,7 +518,6 @@
private final NotificationGroupManager mGroupManager;
private final NotificationLockscreenUserManager mLockscreenUserManager;
private final KeyguardStateController mKeyguardStateController;
- private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
private final MetricsLogger mMetricsLogger;
private final LockPatternUtils mLockPatternUtils;
private final Handler mMainThreadHandler;
@@ -527,7 +525,8 @@
private final Executor mUiBgExecutor;
private final ActivityIntentHelper mActivityIntentHelper;
private final BubbleController mBubbleController;
- private final SuperStatusBarViewFactory mSuperStatusBarViewFactory;
+ private NotificationPanelViewController mNotificationPanelViewController;
+ private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
private final ShadeController mShadeController;
private NotificationPresenter mNotificationPresenter;
private ActivityLaunchAnimator mActivityLaunchAnimator;
@@ -558,8 +557,7 @@
@UiBackground Executor uiBgExecutor,
ActivityIntentHelper activityIntentHelper,
BubbleController bubbleController,
- ShadeController shadeController,
- SuperStatusBarViewFactory superStatusBarViewFactory) {
+ ShadeController shadeController) {
mContext = context;
mCommandQueue = commandQueue;
mAssistManagerLazy = assistManagerLazy;
@@ -585,7 +583,6 @@
mActivityIntentHelper = activityIntentHelper;
mBubbleController = bubbleController;
mShadeController = shadeController;
- mSuperStatusBarViewFactory = superStatusBarViewFactory;
}
/** Sets the status bar to use as {@link StatusBar}. */
@@ -604,10 +601,19 @@
return this;
}
+ /** Set the NotificationPanelViewController */
+ public Builder setNotificationPanelViewController(
+ NotificationPanelViewController notificationPanelViewController) {
+ mNotificationPanelViewController = notificationPanelViewController;
+ return this;
+ }
+
+
+
public StatusBarNotificationActivityStarter build() {
return new StatusBarNotificationActivityStarter(mContext,
mCommandQueue, mAssistManagerLazy,
- mSuperStatusBarViewFactory.getNotificationPanelView(),
+ mNotificationPanelViewController,
mNotificationPresenter,
mEntryManager,
mHeadsUpManager,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index beb4579..12a6516 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -107,7 +107,7 @@
private final NotificationGutsManager mGutsManager =
Dependency.get(NotificationGutsManager.class);
- private final NotificationPanelView mNotificationPanel;
+ private final NotificationPanelViewController mNotificationPanel;
private final HeadsUpManagerPhone mHeadsUpManager;
private final AboveShelfObserver mAboveShelfObserver;
private final DozeScrimController mDozeScrimController;
@@ -132,7 +132,7 @@
private int mMaxKeyguardNotifications;
public StatusBarNotificationPresenter(Context context,
- NotificationPanelView panel,
+ NotificationPanelViewController panel,
HeadsUpManagerPhone headsUp,
StatusBarWindowView statusBarWindow,
ViewGroup stackScroller,
@@ -172,7 +172,7 @@
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
if (MULTIUSER_DEBUG) {
- mNotificationPanelDebugText = mNotificationPanel.findViewById(R.id.header_debug_info);
+ mNotificationPanelDebugText = mNotificationPanel.getHeaderDebugInfo();
mNotificationPanelDebugText.setVisibility(View.VISIBLE);
}
@@ -217,7 +217,7 @@
mEntryManager.addNotificationLifetimeExtenders(
remoteInputManager.getLifetimeExtenders());
notificationRowBinder.setUpWithPresenter(this, notifListContainer, mHeadsUpManager,
- mEntryManager, this);
+ this);
mNotificationInterruptionStateProvider.setUpWithPresenter(
this, mHeadsUpManager, this::canHeadsUp);
mLockscreenUserManager.setUpWithPresenter(this);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 6b51391..1e3c5d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -75,6 +75,10 @@
setMotionEventSplittingEnabled(false);
}
+ public NotificationPanelView getNotificationPanelView() {
+ return findViewById(R.id.notification_panel);
+ }
+
@Override
public WindowInsets onApplyWindowInsets(WindowInsets windowInsets) {
final Insets insets = windowInsets.getMaxInsets(WindowInsets.Type.systemBars());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java
index eb86bcc..4935f0e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java
@@ -26,11 +26,9 @@
import android.view.GestureDetector;
import android.view.InputDevice;
import android.view.KeyEvent;
-import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewStub;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.ExpandHelper;
@@ -93,6 +91,7 @@
private boolean mSingleTapEnabled;
private boolean mExpandingBelowNotch;
private final DockManager mDockManager;
+ private final NotificationPanelViewController mNotificationPanelViewController;
@Inject
public StatusBarWindowViewController(
@@ -113,7 +112,8 @@
CommandQueue commandQueue,
ShadeController shadeController,
DockManager dockManager,
- StatusBarWindowView statusBarWindowView) {
+ StatusBarWindowView statusBarWindowView,
+ NotificationPanelViewController notificationPanelViewController) {
mInjectionInflationController = injectionInflationController;
mCoordinator = coordinator;
mPulseExpansionHandler = pulseExpansionHandler;
@@ -132,6 +132,7 @@
mView = statusBarWindowView;
mShadeController = shadeController;
mDockManager = dockManager;
+ mNotificationPanelViewController = notificationPanelViewController;
// This view is not part of the newly inflated expanded status bar.
mBrightnessMirror = mView.findViewById(R.id.brightness_mirror);
@@ -139,39 +140,6 @@
/** Inflates the {@link R.layout#status_bar_expanded} layout and sets it up. */
public void setupExpandedStatusBar() {
- // TODO: create controller for NotificationPanelView
- NotificationPanelView notificationPanelView = new NotificationPanelView(
- mView.getContext(),
- null,
- mInjectionInflationController,
- mCoordinator,
- mPulseExpansionHandler,
- mDynamicPrivacyController,
- mBypassController,
- mFalsingManager,
- mPluginManager,
- mShadeController,
- mNotificationLockscreenUserManager,
- mNotificationEntryManager,
- mKeyguardStateController,
- mStatusBarStateController,
- mDozeLog,
- mDozeParameters,
- mCommandQueue);
- ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
- notificationPanelView.setVisibility(View.INVISIBLE);
- notificationPanelView.setId(R.id.notification_panel);
- LayoutInflater li = mInjectionInflationController.injectable(
- LayoutInflater.from(mView.getContext()));
-
- li.inflate(R.layout.status_bar_expanded, notificationPanelView);
- notificationPanelView.onChildrenAttached();
-
- ViewStub statusBarExpanded = mView.findViewById(R.id.status_bar_expanded);
- mView.addView(notificationPanelView, mView.indexOfChild(statusBarExpanded), lp);
- mView.removeView(statusBarExpanded);
-
mStackScrollLayout = mView.findViewById(R.id.notification_stack_scroller);
TunerService.Tunable tunable = (key, newValue) -> {
@@ -233,8 +201,8 @@
if (!isCancel && mService.shouldIgnoreTouch()) {
return false;
}
- if (isDown && notificationPanelView.isFullyCollapsed()) {
- notificationPanelView.startExpandLatencyTracking();
+ if (isDown && mNotificationPanelViewController.isFullyCollapsed()) {
+ mNotificationPanelViewController.startExpandLatencyTracking();
}
if (isDown) {
setTouchActive(true);
@@ -287,7 +255,7 @@
return true;
}
boolean intercept = false;
- if (notificationPanelView.isFullyExpanded()
+ if (mNotificationPanelViewController.isFullyExpanded()
&& mDragDownHelper.isDragDownEnabled()
&& !mService.isBouncerShowing()
&& !mStatusBarStateController.isDozing()) {
@@ -303,7 +271,7 @@
MotionEvent cancellation = MotionEvent.obtain(ev);
cancellation.setAction(MotionEvent.ACTION_CANCEL);
mStackScrollLayout.onInterceptTouchEvent(cancellation);
- notificationPanelView.onInterceptTouchEvent(cancellation);
+ mNotificationPanelViewController.getView().onInterceptTouchEvent(cancellation);
cancellation.recycle();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
similarity index 74%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarComponent.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
index f3c843c..21d0bb8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
@@ -14,10 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.phone;
+package com.android.systemui.statusbar.phone.dagger;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import com.android.systemui.statusbar.phone.NotificationPanelViewController;
+import com.android.systemui.statusbar.phone.StatusBarWindowView;
+import com.android.systemui.statusbar.phone.StatusBarWindowViewController;
+
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
@@ -29,7 +33,8 @@
/**
* Dagger subcomponent tied to the lifecycle of StatusBar views.
*/
-@Subcomponent
+@Subcomponent(modules = {StatusBarViewModule.class})
+@StatusBarComponent.StatusBarScope
public interface StatusBarComponent {
/**
* Builder for {@link StatusBarComponent}.
@@ -54,4 +59,10 @@
@StatusBarScope
StatusBarWindowViewController getStatusBarWindowViewController();
+ /**
+ * Creates a NotificationPanelViewController.
+ */
+ @StatusBarScope
+ NotificationPanelViewController getNotificationPanelViewController();
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
new file mode 100644
index 0000000..20bd51d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone.dagger;
+
+import com.android.systemui.statusbar.phone.NotificationPanelView;
+import com.android.systemui.statusbar.phone.StatusBarWindowView;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+public abstract class StatusBarViewModule {
+ /** */
+ @Provides
+ @StatusBarComponent.StatusBarScope
+ public static NotificationPanelView getNotificationPanelView(
+ StatusBarWindowView statusBarWindowView) {
+ return statusBarWindowView.getNotificationPanelView();
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
index 88edf8e..625d884 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
@@ -24,7 +24,7 @@
import android.widget.FrameLayout;
import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.NotificationPanelView;
+import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.StatusBarWindowView;
import java.util.Objects;
@@ -38,16 +38,17 @@
private final StatusBarWindowView mStatusBarWindow;
private final Consumer<Boolean> mVisibilityCallback;
- private final NotificationPanelView mNotificationPanel;
+ private final NotificationPanelViewController mNotificationPanel;
private final ArraySet<BrightnessMirrorListener> mBrightnessMirrorListeners = new ArraySet<>();
private final int[] mInt2Cache = new int[2];
private View mBrightnessMirror;
public BrightnessMirrorController(StatusBarWindowView statusBarWindow,
+ NotificationPanelViewController notificationPanelViewController,
@NonNull Consumer<Boolean> visibilityCallback) {
mStatusBarWindow = statusBarWindow;
mBrightnessMirror = statusBarWindow.findViewById(R.id.brightness_mirror);
- mNotificationPanel = statusBarWindow.findViewById(R.id.notification_panel);
+ mNotificationPanel = notificationPanelViewController;
mNotificationPanel.setPanelAlphaEndAction(() -> {
mBrightnessMirror.setVisibility(View.INVISIBLE);
});
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
index 4b283fed..d28a6699 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
@@ -35,7 +35,7 @@
import com.android.systemui.R;
import com.android.systemui.qs.tiles.UserDetailItemView;
import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
-import com.android.systemui.statusbar.phone.NotificationPanelView;
+import com.android.systemui.statusbar.phone.NotificationPanelViewController;
/**
* Manages the user switcher on the Keyguard.
@@ -57,7 +57,8 @@
private boolean mAnimating;
public KeyguardUserSwitcher(Context context, ViewStub userSwitcher,
- KeyguardStatusBarView statusBarView, NotificationPanelView panelView) {
+ KeyguardStatusBarView statusBarView,
+ NotificationPanelViewController panelViewController) {
boolean keyguardUserSwitcherEnabled =
context.getResources().getBoolean(R.bool.config_keyguardUserSwitcher) || ALWAYS_ON;
UserSwitcherController userSwitcherController = Dependency.get(UserSwitcherController.class);
@@ -67,7 +68,7 @@
reinflateViews();
mStatusBarView = statusBarView;
mStatusBarView.setKeyguardUserSwitcher(this);
- panelView.setKeyguardUserSwitcher(this);
+ panelViewController.setKeyguardUserSwitcher(this);
mAdapter = new Adapter(context, userSwitcherController, this);
mAdapter.registerDataSetObserver(mDataSetObserver);
mUserSwitcherController = userSwitcherController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 6759020..6b842d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -398,7 +398,7 @@
intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false),
intent.getStringExtra(TelephonyIntents.EXTRA_PLMN));
notifyListenersIfNecessary();
- } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
+ } else if (action.equals(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
updateDataSim();
notifyListenersIfNecessary();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index f20a47b..f640d03 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -318,9 +318,9 @@
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
- filter.addAction(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
- filter.addAction(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
- filter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
+ filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
+ filter.addAction(TelephonyManager.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
+ filter.addAction(Intent.ACTION_SERVICE_STATE);
filter.addAction(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION);
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
@@ -512,11 +512,11 @@
refreshLocale();
updateAirplaneMode(false);
break;
- case TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED:
+ case TelephonyManager.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED:
// We are using different subs now, we might be able to make calls.
recalculateEmergency();
break;
- case TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED:
+ case TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED:
// Notify every MobileSignalController so they can know whether they are the
// data sim or not.
for (int i = 0; i < mMobileSignalControllers.size(); i++) {
@@ -534,7 +534,7 @@
// Might have different subscriptions now.
updateMobileControllers();
break;
- case TelephonyIntents.ACTION_SERVICE_STATE_CHANGED:
+ case Intent.ACTION_SERVICE_STATE:
mLastServiceState = ServiceState.newFromBundle(intent.getExtras());
if (mMobileSignalControllers.size() == 0) {
// If none of the subscriptions are active, we might need to recalculate
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index e7d1c95..718522c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -85,7 +85,8 @@
boolean visibleWhenEnabled = mContext.getResources().getBoolean(
R.bool.config_showWifiIndicatorWhenEnabled);
boolean wifiVisible = mCurrentState.enabled
- && (mCurrentState.connected || !mHasMobileData || visibleWhenEnabled);
+ && ((mCurrentState.connected && mCurrentState.inetCondition == 1)
+ || !mHasMobileData || visibleWhenEnabled);
String wifiDesc = wifiVisible ? mCurrentState.ssid : null;
boolean ssidPresent = wifiVisible && mCurrentState.ssid != null;
String contentDescription = getStringIfExists(getContentDescription());
diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
index 6976649..56aae17 100644
--- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
+++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
@@ -35,7 +35,6 @@
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.phone.LockIcon;
-import com.android.systemui.statusbar.phone.NotificationPanelView;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -129,11 +128,6 @@
NotificationStackScrollLayout createNotificationStackScrollLayout();
/**
- * Creates the NotificationPanelView.
- */
- NotificationPanelView createPanelView();
-
- /**
* Creates the Shelf.
*/
NotificationShelf creatNotificationShelf();
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
new file mode 100644
index 0000000..d413308
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.wm;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.content.res.Configuration;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.IDisplayWindowInsetsController;
+import android.view.InsetsSource;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
+import android.view.Surface;
+import android.view.SurfaceControl;
+import android.view.WindowInsets;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+
+import com.android.systemui.dagger.qualifiers.Main;
+
+import java.util.ArrayList;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Manages IME control at the display-level. This occurs when IME comes up in multi-window mode.
+ */
+@Singleton
+public class DisplayImeController implements DisplayWindowController.DisplayWindowListener {
+ private static final String TAG = "DisplayImeController";
+
+ static final int ANIMATION_DURATION_SHOW_MS = 275;
+ static final int ANIMATION_DURATION_HIDE_MS = 340;
+ static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
+ private static final int DIRECTION_NONE = 0;
+ private static final int DIRECTION_SHOW = 1;
+ private static final int DIRECTION_HIDE = 2;
+
+ SystemWindows mSystemWindows;
+ final Handler mHandler;
+
+ final SparseArray<PerDisplay> mImePerDisplay = new SparseArray<>();
+
+ final ArrayList<ImePositionProcessor> mPositionProcessors = new ArrayList<>();
+
+ @Inject
+ DisplayImeController(SystemWindows syswin, DisplayWindowController displayController,
+ @Main Handler mainHandler) {
+ mHandler = mainHandler;
+ mSystemWindows = syswin;
+ displayController.addDisplayWindowListener(this);
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ // Add's a system-ui window-manager specifically for ime. This type is special because
+ // WM will defer IME inset handling to it in multi-window scenarious.
+ PerDisplay pd = new PerDisplay(displayId,
+ mSystemWindows.mDisplayController.getDisplayLayout(displayId).rotation());
+ try {
+ mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, pd);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to set insets controller on display " + displayId);
+ }
+ mImePerDisplay.put(displayId, pd);
+ }
+
+ @Override
+ public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
+ PerDisplay pd = mImePerDisplay.get(displayId);
+ if (pd == null) {
+ return;
+ }
+ if (mSystemWindows.mDisplayController.getDisplayLayout(displayId).rotation()
+ != pd.mRotation && isImeShowing(displayId)) {
+ pd.startAnimation(true);
+ }
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ try {
+ mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, null);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to remove insets controller on display " + displayId);
+ }
+ mImePerDisplay.remove(displayId);
+ }
+
+ private boolean isImeShowing(int displayId) {
+ PerDisplay pd = mImePerDisplay.get(displayId);
+ if (pd == null) {
+ return false;
+ }
+ final InsetsSource imeSource = pd.mInsetsState.getSource(InsetsState.ITYPE_IME);
+ return imeSource != null && pd.mImeSourceControl != null && imeSource.isVisible();
+ }
+
+ private void dispatchPositionChanged(int displayId, int imeTop,
+ SurfaceControl.Transaction t) {
+ synchronized (mPositionProcessors) {
+ for (ImePositionProcessor pp : mPositionProcessors) {
+ pp.onImePositionChanged(displayId, imeTop, t);
+ }
+ }
+ }
+
+ private void dispatchStartPositioning(int displayId, int imeTop, int finalImeTop,
+ boolean show, SurfaceControl.Transaction t) {
+ synchronized (mPositionProcessors) {
+ for (ImePositionProcessor pp : mPositionProcessors) {
+ pp.onImeStartPositioning(displayId, imeTop, finalImeTop, show, t);
+ }
+ }
+ }
+
+ private void dispatchEndPositioning(int displayId, int imeTop, boolean show,
+ SurfaceControl.Transaction t) {
+ synchronized (mPositionProcessors) {
+ for (ImePositionProcessor pp : mPositionProcessors) {
+ pp.onImeEndPositioning(displayId, imeTop, show, t);
+ }
+ }
+ }
+
+ /**
+ * Adds an {@link ImePositionProcessor} to be called during ime position updates.
+ */
+ public void addPositionProcessor(ImePositionProcessor processor) {
+ synchronized (mPositionProcessors) {
+ if (mPositionProcessors.contains(processor)) {
+ return;
+ }
+ mPositionProcessors.add(processor);
+ }
+ }
+
+ /**
+ * Removes an {@link ImePositionProcessor} to be called during ime position updates.
+ */
+ public void removePositionProcessor(ImePositionProcessor processor) {
+ synchronized (mPositionProcessors) {
+ mPositionProcessors.remove(processor);
+ }
+ }
+
+ class PerDisplay extends IDisplayWindowInsetsController.Stub {
+ final int mDisplayId;
+ final InsetsState mInsetsState = new InsetsState();
+ InsetsSourceControl mImeSourceControl = null;
+ int mAnimationDirection = DIRECTION_NONE;
+ ValueAnimator mAnimation = null;
+ int mRotation = Surface.ROTATION_0;
+
+ PerDisplay(int displayId, int initialRotation) {
+ mDisplayId = displayId;
+ mRotation = initialRotation;
+ }
+
+ @Override
+ public void insetsChanged(InsetsState insetsState) {
+ if (mInsetsState.equals(insetsState)) {
+ return;
+ }
+ mInsetsState.set(insetsState, true /* copySources */);
+ }
+
+ @Override
+ public void insetsControlChanged(InsetsState insetsState,
+ InsetsSourceControl[] activeControls) {
+ insetsChanged(insetsState);
+ if (activeControls != null) {
+ for (InsetsSourceControl activeControl : activeControls) {
+ if (activeControl == null) {
+ continue;
+ }
+ if (activeControl.getType() == InsetsState.ITYPE_IME) {
+ mImeSourceControl = activeControl;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void showInsets(int types, boolean fromIme) {
+ if ((types & WindowInsets.Type.ime()) == 0) {
+ return;
+ }
+ startAnimation(true /* show */);
+ }
+
+ @Override
+ public void hideInsets(int types, boolean fromIme) {
+ if ((types & WindowInsets.Type.ime()) == 0) {
+ return;
+ }
+ startAnimation(false /* show */);
+ }
+
+ /**
+ * Sends the local visibility state back to window manager. Needed for legacy adjustForIme.
+ */
+ private void setVisibleDirectly(boolean visible) {
+ mInsetsState.getSource(InsetsState.ITYPE_IME).setVisible(visible);
+ try {
+ mSystemWindows.mWmService.modifyDisplayWindowInsets(mDisplayId, mInsetsState);
+ } catch (RemoteException e) {
+ }
+ }
+
+ private int imeTop(InsetsSource imeSource, float surfaceOffset) {
+ return imeSource.getFrame().top + (int) surfaceOffset;
+ }
+
+ private void startAnimation(final boolean show) {
+ final InsetsSource imeSource = mInsetsState.getSource(InsetsState.ITYPE_IME);
+ if (imeSource == null || mImeSourceControl == null) {
+ return;
+ }
+ if ((mAnimationDirection == DIRECTION_SHOW && show)
+ || (mAnimationDirection == DIRECTION_HIDE && !show)) {
+ return;
+ }
+ if (mAnimationDirection != DIRECTION_NONE) {
+ mAnimation.end();
+ mAnimationDirection = DIRECTION_NONE;
+ }
+ mAnimationDirection = show ? DIRECTION_SHOW : DIRECTION_HIDE;
+ mHandler.post(() -> {
+ final float defaultY = mImeSourceControl.getSurfacePosition().y;
+ final float x = mImeSourceControl.getSurfacePosition().x;
+ final float startY = show ? defaultY + imeSource.getFrame().height() : defaultY;
+ final float endY = show ? defaultY : defaultY + imeSource.getFrame().height();
+ mAnimation = ValueAnimator.ofFloat(startY, endY);
+ mAnimation.setDuration(
+ show ? ANIMATION_DURATION_SHOW_MS : ANIMATION_DURATION_HIDE_MS);
+
+ mAnimation.addUpdateListener(animation -> {
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ float value = (float) animation.getAnimatedValue();
+ t.setPosition(mImeSourceControl.getLeash(), x, value);
+ dispatchPositionChanged(mDisplayId, imeTop(imeSource, value), t);
+ t.apply();
+ t.close();
+ });
+ mAnimation.setInterpolator(INTERPOLATOR);
+ mAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ t.setPosition(mImeSourceControl.getLeash(), x, startY);
+ dispatchStartPositioning(mDisplayId, imeTop(imeSource, startY),
+ imeTop(imeSource, endY), mAnimationDirection == DIRECTION_SHOW,
+ t);
+ if (mAnimationDirection == DIRECTION_SHOW) {
+ t.show(mImeSourceControl.getLeash());
+ }
+ t.apply();
+ t.close();
+ }
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ t.setPosition(mImeSourceControl.getLeash(), x, endY);
+ dispatchEndPositioning(mDisplayId, imeTop(imeSource, endY),
+ mAnimationDirection == DIRECTION_SHOW, t);
+ if (mAnimationDirection == DIRECTION_HIDE) {
+ t.hide(mImeSourceControl.getLeash());
+ }
+ t.apply();
+ t.close();
+
+ mAnimationDirection = DIRECTION_NONE;
+ mAnimation = null;
+ }
+ });
+ if (!show) {
+ // When going away, queue up insets change first, otherwise any bounds changes
+ // can have a "flicker" of ime-provided insets.
+ setVisibleDirectly(false /* visible */);
+ }
+ mAnimation.start();
+ if (show) {
+ // When showing away, queue up insets change last, otherwise any bounds changes
+ // can have a "flicker" of ime-provided insets.
+ setVisibleDirectly(true /* visible */);
+ }
+ });
+ }
+ }
+
+ /**
+ * Allows other things to synchronize with the ime position
+ */
+ public interface ImePositionProcessor {
+ /**
+ * Called when the IME position is starting to animate.
+ */
+ void onImeStartPositioning(int displayId, int imeTop, int finalImeTop, boolean showing,
+ SurfaceControl.Transaction t);
+
+ /**
+ * Called when the ime position changed. This is expected to be a synchronous call on the
+ * animation thread. Operations can be added to the transaction to be applied in sync.
+ */
+ void onImePositionChanged(int displayId, int imeTop, SurfaceControl.Transaction t);
+
+ /**
+ * Called when the IME position is done animating.
+ */
+ void onImeEndPositioning(int displayId, int imeTop, boolean showing,
+ SurfaceControl.Transaction t);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
index b70fdbd..eccf096 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
@@ -19,7 +19,7 @@
import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE;
import static android.telephony.SubscriptionManager.DATA_ROAMING_ENABLE;
-import static android.telephony.SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE;
+import static android.telephony.SubscriptionManager.NAME_SOURCE_DEFAULT;
import static junit.framework.Assert.assertTrue;
import static junit.framework.TestCase.assertFalse;
@@ -83,14 +83,14 @@
private static final String TEST_CARRIER_2 = "TEST_CARRIER_2";
private static final int TEST_CARRIER_ID = 1;
private static final SubscriptionInfo TEST_SUBSCRIPTION = new SubscriptionInfo(0, "", 0,
- TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "",
+ TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT, 0xFFFFFF, "",
DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", false, null,
TEST_CARRIER_ID, 0);
private static final SubscriptionInfo TEST_SUBSCRIPTION_NULL = new SubscriptionInfo(0, "", 0,
- TEST_CARRIER, null, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "", DATA_ROAMING_DISABLE,
+ TEST_CARRIER, null, NAME_SOURCE_DEFAULT, 0xFFFFFF, "", DATA_ROAMING_DISABLE,
null, null, null, null, false, null, "");
private static final SubscriptionInfo TEST_SUBSCRIPTION_ROAMING = new SubscriptionInfo(0, "", 0,
- TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "",
+ TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT, 0xFFFFFF, "",
DATA_ROAMING_ENABLE, null, null, null, null, false, null, "");
@Mock
private WifiManager mWifiManager;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 4bf1e1c..0c22aae 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -17,7 +17,7 @@
package com.android.keyguard;
import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE;
-import static android.telephony.SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE;
+import static android.telephony.SubscriptionManager.NAME_SOURCE_DEFAULT;
import static com.google.common.truth.Truth.assertThat;
@@ -84,11 +84,11 @@
private static final int TEST_CARRIER_ID = 1;
private static final String TEST_GROUP_UUID = "59b5c870-fc4c-47a4-a99e-9db826b48b24";
private static final SubscriptionInfo TEST_SUBSCRIPTION = new SubscriptionInfo(1, "", 0,
- TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "",
+ TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT, 0xFFFFFF, "",
DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", false, TEST_GROUP_UUID,
TEST_CARRIER_ID, 0);
private static final SubscriptionInfo TEST_SUBSCRIPTION_2 = new SubscriptionInfo(2, "", 0,
- TEST_CARRIER, TEST_CARRIER_2, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "",
+ TEST_CARRIER, TEST_CARRIER_2, NAME_SOURCE_DEFAULT, 0xFFFFFF, "",
DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", true, TEST_GROUP_UUID,
TEST_CARRIER_ID, 0);
@Mock
@@ -204,7 +204,7 @@
@Test
public void testTelephonyCapable_SimInvalid_ServiceState_InService() {
// SERVICE_STATE - IN_SERVICE, but SIM_STATE is invalid TelephonyCapable should be False
- Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
+ Intent intent = new Intent(Intent.ACTION_SERVICE_STATE);
Bundle data = new Bundle();
ServiceState state = new ServiceState();
state.setState(ServiceState.STATE_IN_SERVICE);
@@ -219,7 +219,7 @@
public void testTelephonyCapable_SimValid_ServiceState_PowerOff() {
// Simulate AirplaneMode case, SERVICE_STATE - POWER_OFF, check TelephonyCapable False
// Only receive ServiceState callback IN_SERVICE -> OUT_OF_SERVICE -> POWER_OFF
- Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
+ Intent intent = new Intent(Intent.ACTION_SERVICE_STATE);
intent.putExtra(Intent.EXTRA_SIM_STATE
, Intent.SIM_STATE_LOADED);
Bundle data = new Bundle();
@@ -241,7 +241,7 @@
*/
@Test
public void testTelephonyCapable_BootInitState_ServiceState_OutOfService() {
- Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
+ Intent intent = new Intent(Intent.ACTION_SERVICE_STATE);
Bundle data = new Bundle();
ServiceState state = new ServiceState();
state.setState(ServiceState.STATE_OUT_OF_SERVICE);
@@ -285,7 +285,7 @@
@Test
public void testTelephonyCapable_BootInitState_ServiceState_PowerOff() {
- Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
+ Intent intent = new Intent(Intent.ACTION_SERVICE_STATE);
Bundle data = new Bundle();
ServiceState state = new ServiceState();
state.setState(ServiceState.STATE_POWER_OFF);
@@ -298,7 +298,7 @@
@Test
public void testTelephonyCapable_SimValid_ServiceState_InService() {
- Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
+ Intent intent = new Intent(Intent.ACTION_SERVICE_STATE);
Bundle data = new Bundle();
ServiceState state = new ServiceState();
state.setState(ServiceState.STATE_IN_SERVICE);
@@ -321,10 +321,10 @@
mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
, putPhoneInfo(intentSimState, data, true));
mTestableLooper.processAllMessages();
- // Even SimState Loaded, still need ACTION_SERVICE_STATE_CHANGED turn on mTelephonyCapable
+ // Even SimState Loaded, still need ACTION_SERVICE_STATE turn on mTelephonyCapable
assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isFalse();
- Intent intentServiceState = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
+ Intent intentServiceState = new Intent(Intent.ACTION_SERVICE_STATE);
intentSimState.putExtra(Intent.EXTRA_SIM_STATE
, Intent.SIM_STATE_LOADED);
mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 48169ea..0a3bc6d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -154,12 +154,14 @@
@Test
public void onAlignmentStateChanged_showsSlowChargingIndication() {
- createController();
- verify(mDockManager).addAlignmentStateListener(mAlignmentListener.capture());
- mController.setVisible(true);
+ mInstrumentation.runOnMainSync(() -> {
+ createController();
+ verify(mDockManager).addAlignmentStateListener(mAlignmentListener.capture());
+ mController.setVisible(true);
- mAlignmentListener.getValue().onAlignmentStateChanged(
- DockManager.ALIGN_STATE_POOR);
+ mAlignmentListener.getValue().onAlignmentStateChanged(DockManager.ALIGN_STATE_POOR);
+ });
+ mInstrumentation.waitForIdleSync();
assertThat(mTextView.getText()).isEqualTo(
mContext.getResources().getString(R.string.dock_alignment_slow_charging));
@@ -169,11 +171,14 @@
@Test
public void onAlignmentStateChanged_showsNotChargingIndication() {
- createController();
- verify(mDockManager).addAlignmentStateListener(mAlignmentListener.capture());
- mController.setVisible(true);
+ mInstrumentation.runOnMainSync(() -> {
+ createController();
+ verify(mDockManager).addAlignmentStateListener(mAlignmentListener.capture());
+ mController.setVisible(true);
- mAlignmentListener.getValue().onAlignmentStateChanged(DockManager.ALIGN_STATE_TERRIBLE);
+ mAlignmentListener.getValue().onAlignmentStateChanged(DockManager.ALIGN_STATE_TERRIBLE);
+ });
+ mInstrumentation.waitForIdleSync();
assertThat(mTextView.getText()).isEqualTo(
mContext.getResources().getString(R.string.dock_alignment_not_charging));
@@ -183,13 +188,15 @@
@Test
public void onAlignmentStateChanged_whileDozing_showsSlowChargingIndication() {
- createController();
- verify(mDockManager).addAlignmentStateListener(mAlignmentListener.capture());
- mController.setVisible(true);
- mController.setDozing(true);
+ mInstrumentation.runOnMainSync(() -> {
+ createController();
+ verify(mDockManager).addAlignmentStateListener(mAlignmentListener.capture());
+ mController.setVisible(true);
+ mController.setDozing(true);
- mAlignmentListener.getValue().onAlignmentStateChanged(
- DockManager.ALIGN_STATE_POOR);
+ mAlignmentListener.getValue().onAlignmentStateChanged(DockManager.ALIGN_STATE_POOR);
+ });
+ mInstrumentation.waitForIdleSync();
assertThat(mTextView.getText()).isEqualTo(
mContext.getResources().getString(R.string.dock_alignment_slow_charging));
@@ -199,12 +206,15 @@
@Test
public void onAlignmentStateChanged_whileDozing_showsNotChargingIndication() {
- createController();
- verify(mDockManager).addAlignmentStateListener(mAlignmentListener.capture());
- mController.setVisible(true);
- mController.setDozing(true);
+ mInstrumentation.runOnMainSync(() -> {
+ createController();
+ verify(mDockManager).addAlignmentStateListener(mAlignmentListener.capture());
+ mController.setVisible(true);
+ mController.setDozing(true);
- mAlignmentListener.getValue().onAlignmentStateChanged(DockManager.ALIGN_STATE_TERRIBLE);
+ mAlignmentListener.getValue().onAlignmentStateChanged(DockManager.ALIGN_STATE_TERRIBLE);
+ });
+ mInstrumentation.waitForIdleSync();
assertThat(mTextView.getText()).isEqualTo(
mContext.getResources().getString(R.string.dock_alignment_not_charging));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index 40b0ba9..3fdbd3e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -47,6 +47,9 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.ExpansionLogger;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener;
+import com.android.systemui.statusbar.notification.row.NotifRemoteViewCache;
import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
@@ -72,6 +75,7 @@
public static final UserHandle USER_HANDLE = UserHandle.of(ActivityManager.getCurrentUser());
private static final String GROUP_KEY = "gruKey";
+ private static final String APP_NAME = "appName";
private final Context mContext;
private int mId;
@@ -303,9 +307,6 @@
null /* root */,
false /* attachToRoot */);
ExpandableNotificationRow row = mRow;
- row.setGroupManager(mGroupManager);
- row.setHeadsUpManager(mHeadsUpManager);
- row.setAboveShelfChangedListener(aboveShelf -> {});
final NotificationChannel channel =
new NotificationChannel(
@@ -329,6 +330,23 @@
entry.setRow(row);
entry.createIcons(mContext, entry.getSbn());
row.setEntry(entry);
+
+ NotificationContentInflater contentBinder = new NotificationContentInflater(
+ mock(NotifRemoteViewCache.class),
+ mock(NotificationRemoteInputManager.class));
+ contentBinder.setInflateSynchronously(true);
+
+ row.initialize(
+ APP_NAME,
+ entry.getKey(),
+ mock(ExpansionLogger.class),
+ mock(KeyguardBypassController.class),
+ mGroupManager,
+ mHeadsUpManager,
+ contentBinder,
+ mock(OnExpandClickListener.class));
+ row.setAboveShelfChangedListener(aboveShelf -> { });
+
row.setInflationFlags(extraInflationFlags);
inflateAndWait(row);
@@ -341,11 +359,10 @@
private static void inflateAndWait(ExpandableNotificationRow row) throws Exception {
CountDownLatch countDownLatch = new CountDownLatch(1);
- row.getNotificationInflater().setInflateSynchronously(true);
NotificationContentInflater.InflationCallback callback =
new NotificationContentInflater.InflationCallback() {
@Override
- public void handleInflationException(StatusBarNotification notification,
+ public void handleInflationException(NotificationEntry entry,
Exception e) {
countDownLatch.countDown();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
index 145a25c..a54f733 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
@@ -33,7 +33,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.phone.NotificationPanelView;
+import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.StatusBarWindowView;
import com.android.systemui.statusbar.phone.StatusBarWindowViewController;
@@ -64,7 +64,7 @@
mLaunchAnimator = new ActivityLaunchAnimator(
mStatusBarWindowViewController,
mCallback,
- mock(NotificationPanelView.class),
+ mock(NotificationPanelViewController.class),
mNotificationContainer);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index f55ea4f..1c29453 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -65,6 +65,7 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLifetimeExtender;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -86,7 +87,10 @@
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotifRemoteViewCache;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder;
import com.android.systemui.statusbar.notification.row.RowInflaterTask;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -143,6 +147,7 @@
@Mock private RowInflaterTask mAsyncInflationTask;
@Mock private NotificationRowBinder mMockedRowBinder;
@Mock private NotifLog mNotifLog;
+ @Mock private FeatureFlags mFeatureFlags;
private int mId;
private NotificationEntry mEntry;
@@ -219,6 +224,8 @@
mEntry.expandedIcon = mock(StatusBarIconView.class);
+ when(mFeatureFlags.isNewNotifPipelineEnabled()).thenReturn(false);
+ when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
mEntryManager = new TestableNotificationEntryManager(
mNotifLog,
mGroupManager,
@@ -230,19 +237,27 @@
mNotifLog,
mock(NotificationSectionsFeatureManager.class),
mock(PeopleNotificationIdentifier.class)),
- mEnvironment
+ mEnvironment,
+ mFeatureFlags
);
mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mHeadsUpManager);
mEntryManager.addNotificationEntryListener(mEntryListener);
mEntryManager.setNotificationRemoveInterceptor(mRemoveInterceptor);
+ NotificationRowContentBinder contentBinder = new NotificationContentInflater(
+ mock(NotifRemoteViewCache.class),
+ mRemoteInputManager);
+
NotificationRowBinderImpl notificationRowBinder =
- new NotificationRowBinderImpl(mContext, true, /* allowLongPress */
+ new NotificationRowBinderImpl(mContext,
+ contentBinder,
+ true, /* allowLongPress */
mock(KeyguardBypassController.class),
mock(StatusBarStateController.class),
mock(NotificationLogger.class));
notificationRowBinder.setUpWithPresenter(
- mPresenter, mListContainer, mHeadsUpManager, mEntryManager, mBindCallback);
+ mPresenter, mListContainer, mHeadsUpManager, mBindCallback);
+ notificationRowBinder.setInflationCallback(mEntryManager);
notificationRowBinder.setNotificationClicker(mock(NotificationClicker.class));
mEntryManager.setRowBinder(notificationRowBinder);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt
index 34beefe..1afee12 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification
+import com.android.systemui.statusbar.FeatureFlags
import com.android.systemui.statusbar.NotificationPresenter
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager
@@ -33,8 +34,9 @@
log: NotifLog,
gm: NotificationGroupManager,
rm: NotificationRankingManager,
- ke: KeyguardEnvironment
-) : NotificationEntryManager(log, gm, rm, ke) {
+ ke: KeyguardEnvironment,
+ ff: FeatureFlags
+) : NotificationEntryManager(log, gm, rm, ke, ff) {
public var countDownLatch: CountDownLatch = CountDownLatch(1)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index 0837a42..28feaca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -24,6 +24,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -261,9 +262,13 @@
.setRank(5)
.setExplanation("baz buzz")
.build();
+
+ // WHEN entry3's ranking update includes an update to its overrideGroupKey
+ final String newOverrideGroupKey = "newOverrideGroupKey";
Ranking newRanking3 = new RankingBuilder(notif3.ranking)
.setRank(6)
.setExplanation("Penguin pizza")
+ .setOverrideGroupKey(newOverrideGroupKey)
.build();
mNoMan.setRanking(notif1.sbn.getKey(), newRanking1);
@@ -275,6 +280,10 @@
assertEquals(newRanking1, entry1.getRanking());
assertEquals(newRanking2, entry2.getRanking());
assertEquals(newRanking3, entry3.getRanking());
+
+ // THEN the entry3's overrideGroupKey is updated along with its groupKey
+ assertEquals(newOverrideGroupKey, entry3.getSbn().getOverrideGroupKey());
+ assertNotNull(entry3.getSbn().getGroupKey());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt
index 0764d0c..867a9b9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt
@@ -178,9 +178,10 @@
private fun fakePersonModel(
id: String,
name: CharSequence,
- clickIntent: PendingIntent
+ clickIntent: PendingIntent,
+ userId: Int = 0
): PersonModel =
- PersonModel(id, name, mock(Drawable::class.java), clickIntent)
+ PersonModel(id, name, mock(Drawable::class.java), clickIntent, userId)
private fun fakePersonViewModel(name: CharSequence): PersonViewModel =
PersonViewModel(name, mock(Drawable::class.java), mock({}.javaClass))
@@ -207,4 +208,4 @@
override fun onDataChanged(data: T) {
lastSeen = Maybe.Just(data)
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImplTest.java
new file mode 100644
index 0000000..d7214f3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImplTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+import android.widget.RemoteViews;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class NotifRemoteViewCacheImplTest extends SysuiTestCase {
+
+ private NotifRemoteViewCacheImpl mNotifRemoteViewCache;
+ private NotificationEntry mEntry;
+ private NotificationEntryListener mEntryListener;
+ @Mock private RemoteViews mRemoteViews;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mEntry = new NotificationEntryBuilder().build();
+
+ NotificationEntryManager entryManager = mock(NotificationEntryManager.class);
+ mNotifRemoteViewCache = new NotifRemoteViewCacheImpl(entryManager);
+ ArgumentCaptor<NotificationEntryListener> entryListenerCaptor =
+ ArgumentCaptor.forClass(NotificationEntryListener.class);
+ verify(entryManager).addNotificationEntryListener(entryListenerCaptor.capture());
+ mEntryListener = entryListenerCaptor.getValue();
+ }
+
+ @Test
+ public void testPutCachedView() {
+ // GIVEN an initialized cache for an entry.
+ mEntryListener.onPendingEntryAdded(mEntry);
+
+ // WHEN a notification's cached remote views is put in.
+ mNotifRemoteViewCache.putCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED, mRemoteViews);
+
+ // THEN the remote view is cached.
+ assertTrue(mNotifRemoteViewCache.hasCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED));
+ assertEquals(
+ "Cached remote view is not the one we put in.",
+ mRemoteViews,
+ mNotifRemoteViewCache.getCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED));
+ }
+
+ @Test
+ public void testRemoveCachedView() {
+ // GIVEN a cache with a cached view.
+ mEntryListener.onPendingEntryAdded(mEntry);
+ mNotifRemoteViewCache.putCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED, mRemoteViews);
+
+ // WHEN we remove the cached view.
+ mNotifRemoteViewCache.removeCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED);
+
+ // THEN the remote view is not cached.
+ assertFalse(mNotifRemoteViewCache.hasCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED));
+ }
+
+ @Test
+ public void testClearCache() {
+ // GIVEN a non-empty cache.
+ mEntryListener.onPendingEntryAdded(mEntry);
+ mNotifRemoteViewCache.putCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED, mRemoteViews);
+ mNotifRemoteViewCache.putCachedView(mEntry, FLAG_CONTENT_VIEW_EXPANDED, mRemoteViews);
+
+ // WHEN we clear the cache.
+ mNotifRemoteViewCache.clearCache(mEntry);
+
+ // THEN the cache is empty.
+ assertFalse(mNotifRemoteViewCache.hasCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED));
+ assertFalse(mNotifRemoteViewCache.hasCachedView(mEntry, FLAG_CONTENT_VIEW_EXPANDED));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index dc4e498..cb9da6a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -22,30 +22,35 @@
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.Notification;
import android.content.Context;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.Looper;
-import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
-import android.util.ArrayMap;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RemoteViews;
+import android.widget.TextView;
import androidx.test.filters.SmallTest;
import androidx.test.filters.Suppress;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.InflationTask;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams;
@@ -58,6 +63,8 @@
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.util.HashMap;
import java.util.concurrent.CountDownLatch;
@@ -74,8 +81,11 @@
private Notification.Builder mBuilder;
private ExpandableNotificationRow mRow;
+ @Mock private NotifRemoteViewCache mCache;
+
@Before
public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
mBuilder = new Notification.Builder(mContext).setSmallIcon(
R.drawable.ic_person)
.setContentTitle("Title")
@@ -84,7 +94,9 @@
ExpandableNotificationRow row = new NotificationTestHelper(mContext, mDependency).createRow(
mBuilder.build());
mRow = spy(row);
- mNotificationInflater = new NotificationContentInflater();
+ mNotificationInflater = new NotificationContentInflater(
+ mCache,
+ mock(NotificationRemoteInputManager.class));
}
@Test
@@ -175,11 +187,13 @@
result,
FLAG_CONTENT_VIEW_EXPANDED,
0,
- new ArrayMap() /* cachedContentViews */, mRow,
+ mock(NotifRemoteViewCache.class),
+ mRow.getEntry(),
+ mRow,
true /* isNewView */, (v, p, r) -> true,
new InflationCallback() {
@Override
- public void handleInflationException(StatusBarNotification notification,
+ public void handleInflationException(NotificationEntry entry,
Exception e) {
countDownLatch.countDown();
throw new RuntimeException("No Exception expected");
@@ -245,6 +259,71 @@
NotificationContentInflater.canReapplyRemoteView(mediaView, decoratedMediaView));
}
+ @Test
+ public void testUsesSameViewWhenCachedPossibleToReuse() throws Exception {
+ // GIVEN a cached view.
+ RemoteViews contractedRemoteView = mBuilder.createContentView();
+ when(mCache.hasCachedView(mRow.getEntry(), FLAG_CONTENT_VIEW_CONTRACTED))
+ .thenReturn(true);
+ when(mCache.getCachedView(mRow.getEntry(), FLAG_CONTENT_VIEW_CONTRACTED))
+ .thenReturn(contractedRemoteView);
+
+ // GIVEN existing bound view with same layout id.
+ View view = contractedRemoteView.apply(mContext, null /* parent */);
+ mRow.getPrivateLayout().setContractedChild(view);
+
+ // WHEN inflater inflates
+ inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, mRow);
+
+ // THEN the view should be re-used
+ assertEquals("Binder inflated a new view even though the old one was cached and usable.",
+ view, mRow.getPrivateLayout().getContractedChild());
+ }
+
+ @Test
+ public void testInflatesNewViewWhenCachedNotPossibleToReuse() throws Exception {
+ // GIVEN a cached remote view.
+ RemoteViews contractedRemoteView = mBuilder.createHeadsUpContentView();
+ when(mCache.hasCachedView(mRow.getEntry(), FLAG_CONTENT_VIEW_CONTRACTED))
+ .thenReturn(true);
+ when(mCache.getCachedView(mRow.getEntry(), FLAG_CONTENT_VIEW_CONTRACTED))
+ .thenReturn(contractedRemoteView);
+
+ // GIVEN existing bound view with different layout id.
+ View view = new TextView(mContext);
+ mRow.getPrivateLayout().setContractedChild(view);
+
+ // WHEN inflater inflates
+ inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, mRow);
+
+ // THEN the view should be a new view
+ assertNotEquals("Binder (somehow) used the same view when inflating.",
+ view, mRow.getPrivateLayout().getContractedChild());
+ }
+
+ @Test
+ public void testInflationCachesCreatedRemoteView() throws Exception {
+ // WHEN inflater inflates
+ inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, mRow);
+
+ // THEN inflater informs cache of the new remote view
+ verify(mCache).putCachedView(
+ eq(mRow.getEntry()),
+ eq(FLAG_CONTENT_VIEW_CONTRACTED),
+ any());
+ }
+
+ @Test
+ public void testUnbindRemovesCachedRemoteView() {
+ // WHEN inflated unbinds content
+ mNotificationInflater.unbindContent(mRow.getEntry(), mRow, FLAG_CONTENT_VIEW_HEADS_UP);
+
+ // THEN inflated informs cache to remove remote view
+ verify(mCache).removeCachedView(
+ eq(mRow.getEntry()),
+ eq(FLAG_CONTENT_VIEW_HEADS_UP));
+ }
+
private static void inflateAndWait(NotificationContentInflater inflater,
@InflationFlag int contentToInflate,
ExpandableNotificationRow row)
@@ -261,7 +340,7 @@
inflater.setInflateSynchronously(true);
InflationCallback callback = new InflationCallback() {
@Override
- public void handleInflationException(StatusBarNotification notification,
+ public void handleInflationException(NotificationEntry entry,
Exception e) {
if (!expectingException) {
exceptionHolder.setException(e);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 77a6a26..39f037c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -53,6 +53,7 @@
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.EmptyShadeView;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationPresenter;
@@ -166,7 +167,8 @@
mock(NotificationSectionsFeatureManager.class),
mock(PeopleNotificationIdentifier.class)
),
- mock(NotificationEntryManager.KeyguardEnvironment.class));
+ mock(NotificationEntryManager.KeyguardEnvironment.class),
+ mock(FeatureFlags.class));
mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
mEntryManager.setUpForTest(mock(NotificationPresenter.class), null, mHeadsUpManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index 8decae3..ae87eef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -64,7 +64,8 @@
mMockNotificiationAreaController = mock(NotificationIconAreaController.class);
mNotificationAreaInner = mock(View.class);
mCenteredNotificationAreaView = mock(View.class);
- when(statusBar.getPanel()).thenReturn(mock(NotificationPanelView.class));
+ when(statusBar.getPanelController()).thenReturn(
+ mock(NotificationPanelViewController.class));
when(mNotificationAreaInner.animate()).thenReturn(mock(ViewPropertyAnimator.class));
when(mMockNotificiationAreaController.getNotificationInnerAreaView()).thenReturn(
mNotificationAreaInner);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
index 46f6cfe..d31f175 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
@@ -86,7 +86,7 @@
@Mock private NotificationIconAreaController mNotificationIconAreaController;
@Mock private StatusBarWindowViewController mStatusBarWindowViewController;
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
- @Mock private NotificationPanelView mNotificationPanel;
+ @Mock private NotificationPanelViewController mNotificationPanel;
@Mock private View mAmbientIndicationContainer;
@Mock private BiometricUnlockController mBiometricUnlockController;
@Mock private LockscreenLockIconController mLockscreenLockIconController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index 0260269..7448dbd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -53,7 +53,8 @@
private final NotificationStackScrollLayout mStackScroller =
mock(NotificationStackScrollLayout.class);
- private final NotificationPanelView mPanelView = mock(NotificationPanelView.class);
+ private final NotificationPanelViewController mPanelView =
+ mock(NotificationPanelViewController.class);
private final DarkIconDispatcher mDarkIconDispatcher = mock(DarkIconDispatcher.class);
private HeadsUpAppearanceController mHeadsUpAppearanceController;
private ExpandableNotificationRow mFirst;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index c165e56..1f37ad8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -16,9 +16,13 @@
package com.android.systemui.statusbar.phone;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -26,37 +30,44 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.ActivityManager;
import android.app.StatusBarManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
import android.hardware.biometrics.BiometricSourceType;
+import android.os.PowerManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
import androidx.test.filters.SmallTest;
-import com.android.keyguard.KeyguardStatusView;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.util.LatencyTracker;
+import com.android.keyguard.KeyguardClockSwitch;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.SystemUIFactory;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.KeyguardAffordanceView;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
-import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
-import com.android.systemui.statusbar.notification.logging.NotifLog;
import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -70,6 +81,7 @@
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
import java.util.function.Consumer;
@@ -85,8 +97,6 @@
@Mock
private NotificationStackScrollLayout mNotificationStackScrollLayout;
@Mock
- private KeyguardStatusView mKeyguardStatusView;
- @Mock
private KeyguardBottomAreaView mKeyguardBottomArea;
@Mock
private KeyguardBottomAreaView mQsFrame;
@@ -109,27 +119,89 @@
@Mock
private PanelBar mPanelBar;
@Mock
- private KeyguardAffordanceHelper mAffordanceHelper;
- @Mock
private KeyguardUpdateMonitor mUpdateMonitor;
@Mock
private FalsingManager mFalsingManager;
@Mock
private KeyguardBypassController mKeyguardBypassController;
- @Mock private DozeParameters mDozeParameters;
- private NotificationPanelView mNotificationPanelView;
+ @Mock
+ private DozeParameters mDozeParameters;
+ @Mock
+ private NotificationPanelView mView;
+ @Mock
+ private InjectionInflationController mInjectionInflationController;
+ @Mock
+ private DynamicPrivacyController mDynamicPrivacyController;
+ @Mock
+ private PluginManager mPluginManager;
+ @Mock
+ private ShadeController mShadeController;
+ @Mock
+ private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
+ @Mock
+ private NotificationEntryManager mNotificationEntryManager;
+ @Mock
+ private KeyguardStateController mKeyguardStateController;
+ @Mock
+ private DozeLog mDozeLog;
+ @Mock
+ private CommandQueue mCommandQueue;
+ @Mock
+ private VibratorHelper mVibratorHelper;
+ @Mock
+ private LatencyTracker mLatencyTracker;
+ @Mock
+ private PowerManager mPowerManager;
+ @Mock
+ private AccessibilityManager mAccessibilityManager;
+ @Mock
+ private MetricsLogger mMetricsLogger;
+ @Mock
+ private ActivityManager mActivityManager;
+ @Mock
+ private Resources mResources;
+ @Mock
+ private Configuration mConfiguration;
+ private DisplayMetrics mDisplayMetrics = new DisplayMetrics();
+ @Mock
+ private KeyguardClockSwitch mKeyguardClockSwitch;
+ private PanelViewController.TouchHandler mTouchHandler;
+ @Mock
+ private ZenModeController mZenModeController;
+ @Mock
+ private ConfigurationController mConfigurationController;
+ private FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder;
+
+ private NotificationPanelViewController mNotificationPanelViewController;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
+ when(mHeadsUpCallback.getContext()).thenReturn(mContext);
+ when(mView.getResources()).thenReturn(mResources);
+ when(mResources.getConfiguration()).thenReturn(mConfiguration);
+ mConfiguration.orientation = ORIENTATION_PORTRAIT;
+ when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics);
+ mDisplayMetrics.density = 100;
+ when(mResources.getBoolean(R.bool.config_enableNotificationShadeDrag)).thenReturn(true);
+ when(mView.getContext()).thenReturn(getContext());
+ when(mView.findViewById(R.id.keyguard_clock_container)).thenReturn(mKeyguardClockSwitch);
+ when(mView.findViewById(R.id.notification_stack_scroller))
+ .thenReturn(mNotificationStackScrollLayout);
when(mNotificationStackScrollLayout.getHeight()).thenReturn(1000);
when(mNotificationStackScrollLayout.getHeadsUpCallback()).thenReturn(mHeadsUpCallback);
- when(mHeadsUpCallback.getContext()).thenReturn(mContext);
- mDependency.injectTestDependency(StatusBarStateController.class,
- mStatusBarStateController);
- mDependency.injectTestDependency(KeyguardUpdateMonitor.class, mUpdateMonitor);
- mDependency.injectMockDependency(ConfigurationController.class);
- mDependency.injectMockDependency(ZenModeController.class);
+ when(mView.findViewById(R.id.keyguard_bottom_area)).thenReturn(mKeyguardBottomArea);
+ when(mKeyguardBottomArea.getLeftView()).thenReturn(mock(KeyguardAffordanceView.class));
+ when(mKeyguardBottomArea.getRightView()).thenReturn(mock(KeyguardAffordanceView.class));
+ when(mView.findViewById(R.id.big_clock_container)).thenReturn(mBigClockContainer);
+ when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame);
+ mFlingAnimationUtilsBuilder = new FlingAnimationUtils.Builder(mDisplayMetrics);
+
+ doAnswer((Answer<Void>) invocation -> {
+ mTouchHandler = invocation.getArgument(0);
+ return null;
+ }).when(mView).setOnTouchListener(any(PanelViewController.TouchHandler.class));
+
NotificationWakeUpCoordinator coordinator =
new NotificationWakeUpCoordinator(
mock(HeadsUpManagerPhone.class),
@@ -143,18 +215,26 @@
mock(NotificationRoundnessManager.class),
mStatusBarStateController,
new FalsingManagerFake());
- mNotificationPanelView = new TestableNotificationPanelView(coordinator, expansionHandler,
- mKeyguardBypassController, mStatusBarStateController);
- mNotificationPanelView.setHeadsUpManager(mHeadsUpManager);
- mNotificationPanelView.setBar(mPanelBar);
-
- when(mKeyguardBottomArea.getLeftView()).thenReturn(mock(KeyguardAffordanceView.class));
- when(mKeyguardBottomArea.getRightView()).thenReturn(mock(KeyguardAffordanceView.class));
+ mNotificationPanelViewController = new NotificationPanelViewController(mView,
+ mInjectionInflationController,
+ coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController,
+ mFalsingManager, mPluginManager, mShadeController,
+ mNotificationLockscreenUserManager, mNotificationEntryManager,
+ mKeyguardStateController, mStatusBarStateController, mDozeLog,
+ mDozeParameters, mCommandQueue, mVibratorHelper,
+ mLatencyTracker, mPowerManager, mAccessibilityManager, 0, mUpdateMonitor,
+ mMetricsLogger, mActivityManager, mZenModeController, mConfigurationController,
+ mFlingAnimationUtilsBuilder);
+ mNotificationPanelViewController.initDependencies(mStatusBar, mGroupManager,
+ mNotificationShelf, mNotificationAreaController, mScrimController);
+ mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
+ mNotificationPanelViewController.setBar(mPanelBar);
}
@Test
public void testSetDozing_notifiesNsslAndStateController() {
- mNotificationPanelView.setDozing(true /* dozing */, true /* animate */, null /* touch */);
+ mNotificationPanelViewController.setDozing(true /* dozing */, true /* animate */,
+ null /* touch */);
InOrder inOrder = inOrder(mNotificationStackScrollLayout, mStatusBarStateController);
inOrder.verify(mNotificationStackScrollLayout).setDozing(eq(true), eq(true), eq(null));
inOrder.verify(mStatusBarStateController).setDozeAmount(eq(1f), eq(true));
@@ -162,103 +242,63 @@
@Test
public void testSetExpandedHeight() {
- mNotificationPanelView.setExpandedHeight(200);
- assertThat((int) mNotificationPanelView.getExpandedHeight()).isEqualTo(200);
+ mNotificationPanelViewController.setExpandedHeight(200);
+ assertThat((int) mNotificationPanelViewController.getExpandedHeight()).isEqualTo(200);
}
@Test
public void testAffordanceLaunchingListener() {
Consumer<Boolean> listener = spy((showing) -> { });
- mNotificationPanelView.setExpandedFraction(1f);
- mNotificationPanelView.setLaunchAffordanceListener(listener);
- mNotificationPanelView.launchCamera(false /* animate */,
+ mNotificationPanelViewController.setExpandedFraction(1f);
+ mNotificationPanelViewController.setLaunchAffordanceListener(listener);
+ mNotificationPanelViewController.launchCamera(false /* animate */,
StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP);
verify(listener).accept(eq(true));
- mNotificationPanelView.onAffordanceLaunchEnded();
+ mNotificationPanelViewController.onAffordanceLaunchEnded();
verify(listener).accept(eq(false));
}
@Test
public void testOnTouchEvent_expansionCanBeBlocked() {
- mNotificationPanelView.onTouchEvent(MotionEvent.obtain(0L /* downTime */,
+ onTouchEvent(MotionEvent.obtain(0L /* downTime */,
0L /* eventTime */, MotionEvent.ACTION_DOWN, 0f /* x */, 0f /* y */,
0 /* metaState */));
- mNotificationPanelView.onTouchEvent(MotionEvent.obtain(0L /* downTime */,
+ onTouchEvent(MotionEvent.obtain(0L /* downTime */,
0L /* eventTime */, MotionEvent.ACTION_MOVE, 0f /* x */, 200f /* y */,
0 /* metaState */));
- assertThat((int) mNotificationPanelView.getExpandedHeight()).isEqualTo(200);
- assertThat(mNotificationPanelView.isTrackingBlocked()).isFalse();
+ assertThat((int) mNotificationPanelViewController.getExpandedHeight()).isEqualTo(200);
+ assertThat(mNotificationPanelViewController.isTrackingBlocked()).isFalse();
- mNotificationPanelView.blockExpansionForCurrentTouch();
- mNotificationPanelView.onTouchEvent(MotionEvent.obtain(0L /* downTime */,
+ mNotificationPanelViewController.blockExpansionForCurrentTouch();
+ onTouchEvent(MotionEvent.obtain(0L /* downTime */,
0L /* eventTime */, MotionEvent.ACTION_MOVE, 0f /* x */, 300f /* y */,
0 /* metaState */));
// Expansion should not have changed because it was blocked
- assertThat((int) mNotificationPanelView.getExpandedHeight()).isEqualTo(200);
- assertThat(mNotificationPanelView.isTrackingBlocked()).isTrue();
+ assertThat((int) mNotificationPanelViewController.getExpandedHeight()).isEqualTo(200);
+ assertThat(mNotificationPanelViewController.isTrackingBlocked()).isTrue();
- mNotificationPanelView.onTouchEvent(MotionEvent.obtain(0L /* downTime */,
+ onTouchEvent(MotionEvent.obtain(0L /* downTime */,
0L /* eventTime */, MotionEvent.ACTION_UP, 0f /* x */, 300f /* y */,
0 /* metaState */));
- assertThat(mNotificationPanelView.isTrackingBlocked()).isFalse();
+ assertThat(mNotificationPanelViewController.isTrackingBlocked()).isFalse();
}
@Test
public void testKeyguardStatusBarVisibility_hiddenForBypass() {
when(mUpdateMonitor.shouldListenForFace()).thenReturn(true);
- mNotificationPanelView.mKeyguardUpdateCallback.onBiometricRunningStateChanged(true,
- BiometricSourceType.FACE);
+ mNotificationPanelViewController.mKeyguardUpdateCallback.onBiometricRunningStateChanged(
+ true, BiometricSourceType.FACE);
verify(mKeyguardStatusBar, never()).setVisibility(View.VISIBLE);
when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true);
- mNotificationPanelView.mKeyguardUpdateCallback.onFinishedGoingToSleep(0);
- mNotificationPanelView.mKeyguardUpdateCallback.onBiometricRunningStateChanged(true,
- BiometricSourceType.FACE);
+ mNotificationPanelViewController.mKeyguardUpdateCallback.onFinishedGoingToSleep(0);
+ mNotificationPanelViewController.mKeyguardUpdateCallback.onBiometricRunningStateChanged(
+ true, BiometricSourceType.FACE);
verify(mKeyguardStatusBar, never()).setVisibility(View.VISIBLE);
}
- private class TestableNotificationPanelView extends NotificationPanelView {
- TestableNotificationPanelView(NotificationWakeUpCoordinator coordinator,
- PulseExpansionHandler expansionHandler,
- KeyguardBypassController bypassController,
- SysuiStatusBarStateController statusBarStateController) {
- super(
- NotificationPanelViewTest.this.mContext,
- null,
- new InjectionInflationController(
- SystemUIFactory.getInstance().getRootComponent()),
- coordinator,
- expansionHandler,
- mock(DynamicPrivacyController.class),
- bypassController,
- mFalsingManager,
- mock(PluginManager.class),
- mock(ShadeController.class),
- mock(NotificationLockscreenUserManager.class),
- new NotificationEntryManager(
- mock(NotifLog.class),
- mock(NotificationGroupManager.class),
- mock(NotificationRankingManager.class),
- mock(NotificationEntryManager.KeyguardEnvironment.class)),
- mock(KeyguardStateController.class),
- statusBarStateController,
- mock(DozeLog.class),
- mDozeParameters,
- new CommandQueue(NotificationPanelViewTest.this.mContext));
- mNotificationStackScroller = mNotificationStackScrollLayout;
- mKeyguardStatusView = NotificationPanelViewTest.this.mKeyguardStatusView;
- mKeyguardStatusBar = NotificationPanelViewTest.this.mKeyguardStatusBar;
- mKeyguardBottomArea = NotificationPanelViewTest.this.mKeyguardBottomArea;
- mBigClockContainer = NotificationPanelViewTest.this.mBigClockContainer;
- mQsFrame = NotificationPanelViewTest.this.mQsFrame;
- mAffordanceHelper = NotificationPanelViewTest.this.mAffordanceHelper;
- initDependencies(NotificationPanelViewTest.this.mStatusBar,
- NotificationPanelViewTest.this.mGroupManager,
- NotificationPanelViewTest.this.mNotificationShelf,
- NotificationPanelViewTest.this.mHeadsUpManager,
- NotificationPanelViewTest.this.mNotificationAreaController,
- NotificationPanelViewTest.this.mScrimController);
- }
+ private void onTouchEvent(MotionEvent ev) {
+ mTouchHandler.onTouch(mView, ev);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index b27e84a..5b5eaad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -74,7 +74,7 @@
@Mock
private ViewGroup mContainer;
@Mock
- private NotificationPanelView mNotificationPanelView;
+ private NotificationPanelViewController mNotificationPanelView;
@Mock
private BiometricUnlockController mBiometrucUnlockController;
@Mock
@@ -281,12 +281,12 @@
@Override
public void registerStatusBar(StatusBar statusBar, ViewGroup container,
- NotificationPanelView notificationPanelView,
+ NotificationPanelViewController notificationPanelViewController,
BiometricUnlockController fingerprintUnlockController,
DismissCallbackRegistry dismissCallbackRegistry,
ViewGroup lockIconContainer, View notificationContainer,
KeyguardBypassController bypassController, FalsingManager falsingManager) {
- super.registerStatusBar(statusBar, container, notificationPanelView,
+ super.registerStatusBar(statusBar, container, notificationPanelViewController,
fingerprintUnlockController, dismissCallbackRegistry, lockIconContainer,
notificationContainer, bypassController, falsingManager);
mBouncer = StatusBarKeyguardViewManagerTest.this.mBouncer;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 86b2a44..fea4b8b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -64,7 +64,6 @@
import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -124,10 +123,6 @@
private Intent mContentIntentInner;
@Mock
private NotificationActivityStarter mNotificationActivityStarter;
- @Mock
- private SuperStatusBarViewFactory mSuperStatusBarViewFactory;
- @Mock
- private NotificationPanelView mNotificationPanelView;
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
private NotificationTestHelper mNotificationTestHelper;
@@ -167,8 +162,6 @@
mActiveNotifications.add(mBubbleNotificationRow.getEntry());
when(mEntryManager.getVisibleNotifications()).thenReturn(mActiveNotifications);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
- when(mSuperStatusBarViewFactory.getNotificationPanelView())
- .thenReturn(mNotificationPanelView);
mNotificationActivityStarter = (new StatusBarNotificationActivityStarter.Builder(
getContext(), mock(CommandQueue.class), () -> mAssistManager,
@@ -182,9 +175,9 @@
mKeyguardStateController,
mock(NotificationInterruptionStateProvider.class), mock(MetricsLogger.class),
mock(LockPatternUtils.class), mHandler, mHandler, mUiBgExecutor,
- mActivityIntentHelper, mBubbleController, mShadeController,
- mSuperStatusBarViewFactory))
+ mActivityIntentHelper, mBubbleController, mShadeController))
.setStatusBar(mStatusBar)
+ .setNotificationPanelViewController(mock(NotificationPanelViewController.class))
.setNotificationPresenter(mock(NotificationPresenter.class))
.setActivityLaunchAnimator(mock(ActivityLaunchAnimator.class))
.build();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index 1296a97..5ac7bfb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -108,7 +108,7 @@
StatusBarWindowView statusBarWindowView = mock(StatusBarWindowView.class);
when(statusBarWindowView.getResources()).thenReturn(mContext.getResources());
mStatusBarNotificationPresenter = new StatusBarNotificationPresenter(mContext,
- mock(NotificationPanelView.class), mock(HeadsUpManagerPhone.class),
+ mock(NotificationPanelViewController.class), mock(HeadsUpManagerPhone.class),
statusBarWindowView, mock(NotificationListContainerViewGroup.class),
mock(DozeScrimController.class), mock(ScrimController.class),
mock(ActivityLaunchAnimator.class), mock(DynamicPrivacyController.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 1cdba47..3da87ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -122,8 +122,10 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -172,6 +174,7 @@
@Mock private KeyguardIndicationController mKeyguardIndicationController;
@Mock private NotificationStackScrollLayout mStackScroller;
@Mock private HeadsUpManagerPhone mHeadsUpManager;
+ @Mock private NotificationPanelViewController mNotificationPanelViewController;
@Mock private NotificationPanelView mNotificationPanelView;
@Mock private IStatusBarService mBarService;
@Mock private IDreamManager mDreamManager;
@@ -245,6 +248,7 @@
@Mock private DismissCallbackRegistry mDismissCallbackRegistry;
@Mock private ScreenPinningRequest mScreenPinningRequest;
@Mock private NotificationEntryManager mEntryManager;
+ @Mock private NotificationContentInflater mNotificationContentInflater;
@Mock private LockscreenLockIconController mLockscreenLockIconController;
@Mock private StatusBarNotificationActivityStarter.Builder
mStatusBarNotificationActivityStarterBuilder;
@@ -285,6 +289,7 @@
mContext.setTheme(R.style.Theme_SystemUI_Light);
when(mStackScroller.generateLayoutParams(any())).thenReturn(new LayoutParams(0, 0));
+ when(mNotificationPanelViewController.getView()).thenReturn(mNotificationPanelView);
when(mNotificationPanelView.getLayoutParams()).thenReturn(new LayoutParams(0, 0));
when(powerManagerService.isInteractive()).thenReturn(true);
when(mStackScroller.getActivatedChild()).thenReturn(null);
@@ -353,6 +358,7 @@
mNotificationGutsManager,
notificationLogger,
mEntryManager,
+ mNotificationContentInflater,
mNotificationInterruptionStateProvider,
mNotificationViewHierarchyManager,
mKeyguardViewMediator,
@@ -416,7 +422,7 @@
mLockIconContainer);
when(mKeyguardViewMediator.registerStatusBar(any(StatusBar.class), any(ViewGroup.class),
- any(NotificationPanelView.class), any(BiometricUnlockController.class),
+ any(NotificationPanelViewController.class), any(BiometricUnlockController.class),
any(ViewGroup.class), any(ViewGroup.class), any(KeyguardBypassController.class)))
.thenReturn(mStatusBarKeyguardViewManager);
@@ -426,7 +432,7 @@
// TODO: we should be able to call mStatusBar.start() and have all the below values
// initialized automatically.
mStatusBar.mStatusBarWindow = mStatusBarWindowView;
- mStatusBar.mNotificationPanel = mNotificationPanelView;
+ mStatusBar.mNotificationPanelViewController = mNotificationPanelViewController;
mStatusBar.mDozeScrimController = mDozeScrimController;
mStatusBar.mNotificationIconAreaController = mNotificationIconAreaController;
mStatusBar.mPresenter = mNotificationPresenter;
@@ -731,20 +737,20 @@
when(mCommandQueue.panelsEnabled()).thenReturn(false);
mStatusBar.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false);
- verify(mNotificationPanelView).setQsExpansionEnabled(false);
+ verify(mNotificationPanelViewController).setQsExpansionEnabled(false);
mStatusBar.animateExpandNotificationsPanel();
- verify(mNotificationPanelView, never()).expand(anyBoolean());
+ verify(mNotificationPanelViewController, never()).expand(anyBoolean());
mStatusBar.animateExpandSettingsPanel(null);
- verify(mNotificationPanelView, never()).expand(anyBoolean());
+ verify(mNotificationPanelViewController, never()).expand(anyBoolean());
when(mCommandQueue.panelsEnabled()).thenReturn(true);
mStatusBar.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
StatusBarManager.DISABLE2_NONE, false);
- verify(mNotificationPanelView).setQsExpansionEnabled(true);
+ verify(mNotificationPanelViewController).setQsExpansionEnabled(true);
mStatusBar.animateExpandNotificationsPanel();
- verify(mNotificationPanelView).expandWithoutQs();
+ verify(mNotificationPanelViewController).expandWithoutQs();
mStatusBar.animateExpandSettingsPanel(null);
- verify(mNotificationPanelView).expandWithQs();
+ verify(mNotificationPanelViewController).expandWithQs();
}
@Test
@@ -834,12 +840,12 @@
when(mDozeServiceHost.getDozingRequested()).thenReturn(true);
mStatusBar.updateIsKeyguard();
// TODO: mNotificationPanelView.expand(false) gets called twice. Should be once.
- verify(mNotificationPanelView, times(2)).expand(eq(false));
- clearInvocations(mNotificationPanelView);
+ verify(mNotificationPanelViewController, times(2)).expand(eq(false));
+ clearInvocations(mNotificationPanelViewController);
mStatusBar.mWakefulnessObserver.onStartedWakingUp();
verify(mDozeServiceHost).stopDozing();
- verify(mNotificationPanelView).expand(eq(false));
+ verify(mNotificationPanelViewController).expand(eq(false));
}
@Test
@@ -848,11 +854,11 @@
when(mStatusBarStateController.isKeyguardRequested()).thenReturn(true);
when(mDozeServiceHost.getDozingRequested()).thenReturn(true);
mStatusBar.updateIsKeyguard();
- clearInvocations(mNotificationPanelView);
+ clearInvocations(mNotificationPanelViewController);
mStatusBar.setBouncerShowing(true);
mStatusBar.mWakefulnessObserver.onStartedWakingUp();
- verify(mNotificationPanelView, never()).expand(anyBoolean());
+ verify(mNotificationPanelViewController, never()).expand(anyBoolean());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
index 9f899ee..f9848f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -26,6 +27,7 @@
import androidx.test.filters.SmallTest;
+import com.android.systemui.R;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
@@ -40,6 +42,7 @@
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.InjectionInflationController;
@@ -74,12 +77,16 @@
@Mock private DozeLog mDozeLog;
@Mock private DozeParameters mDozeParameters;
@Mock private DockManager mDockManager;
+ @Mock private NotificationPanelViewController mNotificationPanelViewController;
+ @Mock private NotificationStackScrollLayout mNotificationStackScrollLayout;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mView = new StatusBarWindowView(getContext(), null);
+ mView = spy(new StatusBarWindowView(getContext(), null));
+ when(mView.findViewById(R.id.notification_stack_scroller))
+ .thenReturn(mNotificationStackScrollLayout);
when(mStatusBarStateController.isDozing()).thenReturn(false);
mDependency.injectTestDependency(ShadeController.class, mShadeController);
@@ -104,7 +111,8 @@
new CommandQueue(mContext),
mShadeController,
mDockManager,
- mView);
+ mView,
+ mNotificationPanelViewController);
mController.setupExpandedStatusBar();
mController.setService(mStatusBar);
mController.setDragDownHelper(mDragDownHelper);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index 3451183..32da4c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -39,7 +39,8 @@
setWifiState(true, testSsid);
setWifiLevel(0);
- verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[0][0]);
+ // Connected, but still not validated - does not show
+ verifyLastWifiIcon(false, WifiIcons.WIFI_SIGNAL_STRENGTH[0][0]);
for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) {
setWifiLevel(testLevel);
@@ -47,7 +48,8 @@
setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]);
setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, false, true);
- verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[0][testLevel]);
+ // Icon does not show if not validated
+ verifyLastWifiIcon(false, WifiIcons.WIFI_SIGNAL_STRENGTH[0][testLevel]);
}
}
@@ -70,7 +72,7 @@
testSsid);
setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, false, true);
verifyLastQsWifiIcon(true, true, WifiIcons.QS_WIFI_SIGNAL_STRENGTH[0][testLevel],
- testSsid);
+ null);
}
}
@@ -132,7 +134,7 @@
verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]);
setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, false, true);
- verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[0][testLevel]);
+ verifyLastWifiIcon(false, WifiIcons.WIFI_SIGNAL_STRENGTH[0][testLevel]);
}
@Test
diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp
index 08552cb..0be853a 100644
--- a/packages/Tethering/Android.bp
+++ b/packages/Tethering/Android.bp
@@ -32,6 +32,7 @@
],
libs: [
"framework-tethering",
+ "unsupportedappusage",
],
manifest: "AndroidManifestBase.xml",
@@ -45,9 +46,9 @@
// Due to b/143733063, APK can't access a jni lib that is in APEX (but not in the APK).
cc_library {
- name: "libtetheroffloadjni",
+ name: "libtetherutilsjni",
srcs: [
- "jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp",
+ "jni/android_net_util_TetheringUtils.cpp",
],
shared_libs: [
"libcgrouprc",
@@ -87,7 +88,7 @@
"libcgrouprc",
"libnativehelper_compat_libc++",
"libvndksupport",
- "libtetheroffloadjni",
+ "libtetherutilsjni",
],
resource_dirs: [
"res",
diff --git a/packages/Tethering/jarjar-rules.txt b/packages/Tethering/jarjar-rules.txt
index dd9eab7..d93531b 100644
--- a/packages/Tethering/jarjar-rules.txt
+++ b/packages/Tethering/jarjar-rules.txt
@@ -13,3 +13,5 @@
rule com.android.internal.util.StateMachine* com.android.networkstack.tethering.util.StateMachine@1
rule android.net.LocalLog* com.android.networkstack.tethering.LocalLog@1
+
+rule android.net.shared.Inet4AddressUtils* com.android.networkstack.tethering.shared.Inet4AddressUtils@1
diff --git a/packages/Tethering/jni/android_net_util_TetheringUtils.cpp b/packages/Tethering/jni/android_net_util_TetheringUtils.cpp
new file mode 100644
index 0000000..1cf8f98
--- /dev/null
+++ b/packages/Tethering/jni/android_net_util_TetheringUtils.cpp
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <error.h>
+#include <hidl/HidlSupport.h>
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedUtfChars.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netlink.h>
+#include <net/if.h>
+#include <netinet/icmp6.h>
+#include <sys/socket.h>
+#include <android-base/unique_fd.h>
+#include <android/hardware/tetheroffload/config/1.0/IOffloadConfig.h>
+
+#define LOG_TAG "TetheringUtils"
+#include <utils/Log.h>
+
+namespace android {
+
+using hardware::hidl_handle;
+using hardware::hidl_string;
+using hardware::tetheroffload::config::V1_0::IOffloadConfig;
+
+namespace {
+
+inline const sockaddr * asSockaddr(const sockaddr_nl *nladdr) {
+ return reinterpret_cast<const sockaddr *>(nladdr);
+}
+
+int conntrackSocket(unsigned groups) {
+ base::unique_fd s(socket(AF_NETLINK, SOCK_DGRAM, NETLINK_NETFILTER));
+ if (s.get() < 0) return -errno;
+
+ const struct sockaddr_nl bind_addr = {
+ .nl_family = AF_NETLINK,
+ .nl_pad = 0,
+ .nl_pid = 0,
+ .nl_groups = groups,
+ };
+ if (bind(s.get(), asSockaddr(&bind_addr), sizeof(bind_addr)) != 0) {
+ return -errno;
+ }
+
+ const struct sockaddr_nl kernel_addr = {
+ .nl_family = AF_NETLINK,
+ .nl_pad = 0,
+ .nl_pid = 0,
+ .nl_groups = groups,
+ };
+ if (connect(s.get(), asSockaddr(&kernel_addr), sizeof(kernel_addr)) != 0) {
+ return -errno;
+ }
+
+ return s.release();
+}
+
+// Return a hidl_handle that owns the file descriptor owned by fd, and will
+// auto-close it (otherwise there would be double-close problems).
+//
+// Rely upon the compiler to eliminate the constexprs used for clarity.
+hidl_handle handleFromFileDescriptor(base::unique_fd fd) {
+ hidl_handle h;
+
+ static constexpr int kNumFds = 1;
+ static constexpr int kNumInts = 0;
+ native_handle_t *nh = native_handle_create(kNumFds, kNumInts);
+ nh->data[0] = fd.release();
+
+ static constexpr bool kTakeOwnership = true;
+ h.setTo(nh, kTakeOwnership);
+
+ return h;
+}
+
+} // namespace
+
+static jboolean android_net_util_configOffload(
+ JNIEnv* /* env */) {
+ sp<IOffloadConfig> configInterface = IOffloadConfig::getService();
+ if (configInterface.get() == nullptr) {
+ ALOGD("Could not find IOffloadConfig service.");
+ return false;
+ }
+
+ // Per the IConfigOffload definition:
+ //
+ // fd1 A file descriptor bound to the following netlink groups
+ // (NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY).
+ //
+ // fd2 A file descriptor bound to the following netlink groups
+ // (NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY).
+ base::unique_fd
+ fd1(conntrackSocket(NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY)),
+ fd2(conntrackSocket(NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY));
+ if (fd1.get() < 0 || fd2.get() < 0) {
+ ALOGE("Unable to create conntrack handles: %d/%s", errno, strerror(errno));
+ return false;
+ }
+
+ hidl_handle h1(handleFromFileDescriptor(std::move(fd1))),
+ h2(handleFromFileDescriptor(std::move(fd2)));
+
+ bool rval(false);
+ hidl_string msg;
+ const auto status = configInterface->setHandles(h1, h2,
+ [&rval, &msg](bool success, const hidl_string& errMsg) {
+ rval = success;
+ msg = errMsg;
+ });
+ if (!status.isOk() || !rval) {
+ ALOGE("IOffloadConfig::setHandles() error: '%s' / '%s'",
+ status.description().c_str(), msg.c_str());
+ // If status is somehow not ok, make sure rval captures this too.
+ rval = false;
+ }
+
+ return rval;
+}
+
+static void android_net_util_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd,
+ jint ifIndex)
+{
+ static const int kLinkLocalHopLimit = 255;
+
+ int fd = jniGetFDFromFileDescriptor(env, javaFd);
+
+ // Set an ICMPv6 filter that only passes Router Solicitations.
+ struct icmp6_filter rs_only;
+ ICMP6_FILTER_SETBLOCKALL(&rs_only);
+ ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &rs_only);
+ socklen_t len = sizeof(rs_only);
+ if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &rs_only, len) != 0) {
+ jniThrowExceptionFmt(env, "java/net/SocketException",
+ "setsockopt(ICMP6_FILTER): %s", strerror(errno));
+ return;
+ }
+
+ // Most/all of the rest of these options can be set via Java code, but
+ // because we're here on account of setting an icmp6_filter go ahead
+ // and do it all natively for now.
+
+ // Set the multicast hoplimit to 255 (link-local only).
+ int hops = kLinkLocalHopLimit;
+ len = sizeof(hops);
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, len) != 0) {
+ jniThrowExceptionFmt(env, "java/net/SocketException",
+ "setsockopt(IPV6_MULTICAST_HOPS): %s", strerror(errno));
+ return;
+ }
+
+ // Set the unicast hoplimit to 255 (link-local only).
+ hops = kLinkLocalHopLimit;
+ len = sizeof(hops);
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hops, len) != 0) {
+ jniThrowExceptionFmt(env, "java/net/SocketException",
+ "setsockopt(IPV6_UNICAST_HOPS): %s", strerror(errno));
+ return;
+ }
+
+ // Explicitly disable multicast loopback.
+ int off = 0;
+ len = sizeof(off);
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, len) != 0) {
+ jniThrowExceptionFmt(env, "java/net/SocketException",
+ "setsockopt(IPV6_MULTICAST_LOOP): %s", strerror(errno));
+ return;
+ }
+
+ // Specify the IPv6 interface to use for outbound multicast.
+ len = sizeof(ifIndex);
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifIndex, len) != 0) {
+ jniThrowExceptionFmt(env, "java/net/SocketException",
+ "setsockopt(IPV6_MULTICAST_IF): %s", strerror(errno));
+ return;
+ }
+
+ // Additional options to be considered:
+ // - IPV6_TCLASS
+ // - IPV6_RECVPKTINFO
+ // - IPV6_RECVHOPLIMIT
+
+ // Bind to [::].
+ const struct sockaddr_in6 sin6 = {
+ .sin6_family = AF_INET6,
+ .sin6_port = 0,
+ .sin6_flowinfo = 0,
+ .sin6_addr = IN6ADDR_ANY_INIT,
+ .sin6_scope_id = 0,
+ };
+ auto sa = reinterpret_cast<const struct sockaddr *>(&sin6);
+ len = sizeof(sin6);
+ if (bind(fd, sa, len) != 0) {
+ jniThrowExceptionFmt(env, "java/net/SocketException",
+ "bind(IN6ADDR_ANY): %s", strerror(errno));
+ return;
+ }
+
+ // Join the all-routers multicast group, ff02::2%index.
+ struct ipv6_mreq all_rtrs = {
+ .ipv6mr_multiaddr = {{{0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2}}},
+ .ipv6mr_interface = ifIndex,
+ };
+ len = sizeof(all_rtrs);
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &all_rtrs, len) != 0) {
+ jniThrowExceptionFmt(env, "java/net/SocketException",
+ "setsockopt(IPV6_JOIN_GROUP): %s", strerror(errno));
+ return;
+ }
+}
+
+/*
+ * JNI registration.
+ */
+static const JNINativeMethod gMethods[] = {
+ /* name, signature, funcPtr */
+ { "configOffload", "()Z", (void*) android_net_util_configOffload },
+ { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_util_setupRaSocket },
+};
+
+int register_android_net_util_TetheringUtils(JNIEnv* env) {
+ return jniRegisterNativeMethods(env,
+ "android/net/util/TetheringUtils",
+ gMethods, NELEM(gMethods));
+}
+
+extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
+ JNIEnv *env;
+ if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+ ALOGE("ERROR: GetEnv failed");
+ return JNI_ERR;
+ }
+
+ if (register_android_net_util_TetheringUtils(env) < 0) {
+ return JNI_ERR;
+ }
+
+ return JNI_VERSION_1_6;
+}
+
+}; // namespace android
diff --git a/packages/Tethering/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp b/packages/Tethering/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp
deleted file mode 100644
index 663154a..0000000
--- a/packages/Tethering/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <errno.h>
-#include <error.h>
-#include <hidl/HidlSupport.h>
-#include <jni.h>
-#include <nativehelper/JNIHelp.h>
-#include <linux/netfilter/nfnetlink.h>
-#include <linux/netlink.h>
-#include <sys/socket.h>
-#include <android-base/unique_fd.h>
-#include <android/hardware/tetheroffload/config/1.0/IOffloadConfig.h>
-
-#define LOG_TAG "OffloadHardwareInterface"
-#include <utils/Log.h>
-
-namespace android {
-
-using hardware::hidl_handle;
-using hardware::hidl_string;
-using hardware::tetheroffload::config::V1_0::IOffloadConfig;
-
-namespace {
-
-inline const sockaddr * asSockaddr(const sockaddr_nl *nladdr) {
- return reinterpret_cast<const sockaddr *>(nladdr);
-}
-
-int conntrackSocket(unsigned groups) {
- base::unique_fd s(socket(AF_NETLINK, SOCK_DGRAM, NETLINK_NETFILTER));
- if (s.get() < 0) return -errno;
-
- const struct sockaddr_nl bind_addr = {
- .nl_family = AF_NETLINK,
- .nl_pad = 0,
- .nl_pid = 0,
- .nl_groups = groups,
- };
- if (bind(s.get(), asSockaddr(&bind_addr), sizeof(bind_addr)) != 0) {
- return -errno;
- }
-
- const struct sockaddr_nl kernel_addr = {
- .nl_family = AF_NETLINK,
- .nl_pad = 0,
- .nl_pid = 0,
- .nl_groups = groups,
- };
- if (connect(s.get(), asSockaddr(&kernel_addr), sizeof(kernel_addr)) != 0) {
- return -errno;
- }
-
- return s.release();
-}
-
-// Return a hidl_handle that owns the file descriptor owned by fd, and will
-// auto-close it (otherwise there would be double-close problems).
-//
-// Rely upon the compiler to eliminate the constexprs used for clarity.
-hidl_handle handleFromFileDescriptor(base::unique_fd fd) {
- hidl_handle h;
-
- static constexpr int kNumFds = 1;
- static constexpr int kNumInts = 0;
- native_handle_t *nh = native_handle_create(kNumFds, kNumInts);
- nh->data[0] = fd.release();
-
- static constexpr bool kTakeOwnership = true;
- h.setTo(nh, kTakeOwnership);
-
- return h;
-}
-
-} // namespace
-
-static jboolean android_server_connectivity_tethering_OffloadHardwareInterface_configOffload(
- JNIEnv* /* env */) {
- sp<IOffloadConfig> configInterface = IOffloadConfig::getService();
- if (configInterface.get() == nullptr) {
- ALOGD("Could not find IOffloadConfig service.");
- return false;
- }
-
- // Per the IConfigOffload definition:
- //
- // fd1 A file descriptor bound to the following netlink groups
- // (NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY).
- //
- // fd2 A file descriptor bound to the following netlink groups
- // (NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY).
- base::unique_fd
- fd1(conntrackSocket(NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY)),
- fd2(conntrackSocket(NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY));
- if (fd1.get() < 0 || fd2.get() < 0) {
- ALOGE("Unable to create conntrack handles: %d/%s", errno, strerror(errno));
- return false;
- }
-
- hidl_handle h1(handleFromFileDescriptor(std::move(fd1))),
- h2(handleFromFileDescriptor(std::move(fd2)));
-
- bool rval(false);
- hidl_string msg;
- const auto status = configInterface->setHandles(h1, h2,
- [&rval, &msg](bool success, const hidl_string& errMsg) {
- rval = success;
- msg = errMsg;
- });
- if (!status.isOk() || !rval) {
- ALOGE("IOffloadConfig::setHandles() error: '%s' / '%s'",
- status.description().c_str(), msg.c_str());
- // If status is somehow not ok, make sure rval captures this too.
- rval = false;
- }
-
- return rval;
-}
-
-/*
- * JNI registration.
- */
-static const JNINativeMethod gMethods[] = {
- /* name, signature, funcPtr */
- { "configOffload", "()Z",
- (void*) android_server_connectivity_tethering_OffloadHardwareInterface_configOffload },
-};
-
-int register_android_server_connectivity_tethering_OffloadHardwareInterface(JNIEnv* env) {
- return jniRegisterNativeMethods(env,
- "com/android/server/connectivity/tethering/OffloadHardwareInterface",
- gMethods, NELEM(gMethods));
-}
-
-extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
- JNIEnv *env;
- if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
- ALOGE("ERROR: GetEnv failed");
- return JNI_ERR;
- }
-
- if (register_android_server_connectivity_tethering_OffloadHardwareInterface(env) < 0) {
- return JNI_ERR;
- }
-
- return JNI_VERSION_1_6;
-}
-
-}; // namespace android
diff --git a/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java b/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java
index 1fe2328..d6bc063 100644
--- a/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java
+++ b/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java
@@ -20,11 +20,11 @@
import android.annotation.NonNull;
import android.net.LinkAddress;
-
-import com.google.android.collect.Sets;
+import android.util.ArraySet;
import java.net.Inet4Address;
import java.util.Collection;
+import java.util.Collections;
import java.util.Set;
/**
@@ -68,7 +68,7 @@
* but it must always be set explicitly.
*/
public DhcpServingParamsParcelExt setDefaultRouters(@NonNull Inet4Address... defaultRouters) {
- return setDefaultRouters(Sets.newArraySet(defaultRouters));
+ return setDefaultRouters(newArraySet(defaultRouters));
}
/**
@@ -96,7 +96,7 @@
* <p>This may be an empty list of servers, but it must always be set explicitly.
*/
public DhcpServingParamsParcelExt setDnsServers(@NonNull Inet4Address... dnsServers) {
- return setDnsServers(Sets.newArraySet(dnsServers));
+ return setDnsServers(newArraySet(dnsServers));
}
/**
@@ -126,7 +126,7 @@
* and do not need to be set here.
*/
public DhcpServingParamsParcelExt setExcludedAddrs(@NonNull Inet4Address... excludedAddrs) {
- return setExcludedAddrs(Sets.newArraySet(excludedAddrs));
+ return setExcludedAddrs(newArraySet(excludedAddrs));
}
/**
@@ -169,4 +169,10 @@
}
return res;
}
+
+ private static ArraySet<Inet4Address> newArraySet(Inet4Address... addrs) {
+ ArraySet<Inet4Address> addrSet = new ArraySet<>(addrs.length);
+ Collections.addAll(addrSet, addrs);
+ return addrSet;
+ }
}
diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java
index 8fde520..abfb33c 100644
--- a/packages/Tethering/src/android/net/ip/IpServer.java
+++ b/packages/Tethering/src/android/net/ip/IpServer.java
@@ -17,10 +17,12 @@
package android.net.ip;
import static android.net.InetAddresses.parseNumericAddress;
+import static android.net.RouteInfo.RTN_UNICAST;
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
import static android.net.util.NetworkConstants.FF;
import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
import static android.net.util.NetworkConstants.asByte;
+import static android.net.util.TetheringMessageBase.BASE_IPSERVER;
import android.net.ConnectivityManager;
import android.net.INetd;
@@ -46,11 +48,9 @@
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.util.Log;
-import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.util.MessageUtils;
-import com.android.internal.util.Protocol;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
@@ -153,27 +153,26 @@
DhcpServerCallbacks cb);
}
- private static final int BASE_IFACE = Protocol.BASE_TETHERING + 100;
// request from the user that it wants to tether
- public static final int CMD_TETHER_REQUESTED = BASE_IFACE + 2;
+ public static final int CMD_TETHER_REQUESTED = BASE_IPSERVER + 1;
// request from the user that it wants to untether
- public static final int CMD_TETHER_UNREQUESTED = BASE_IFACE + 3;
+ public static final int CMD_TETHER_UNREQUESTED = BASE_IPSERVER + 2;
// notification that this interface is down
- public static final int CMD_INTERFACE_DOWN = BASE_IFACE + 4;
+ public static final int CMD_INTERFACE_DOWN = BASE_IPSERVER + 3;
// notification from the master SM that it had trouble enabling IP Forwarding
- public static final int CMD_IP_FORWARDING_ENABLE_ERROR = BASE_IFACE + 7;
+ public static final int CMD_IP_FORWARDING_ENABLE_ERROR = BASE_IPSERVER + 4;
// notification from the master SM that it had trouble disabling IP Forwarding
- public static final int CMD_IP_FORWARDING_DISABLE_ERROR = BASE_IFACE + 8;
+ public static final int CMD_IP_FORWARDING_DISABLE_ERROR = BASE_IPSERVER + 5;
// notification from the master SM that it had trouble starting tethering
- public static final int CMD_START_TETHERING_ERROR = BASE_IFACE + 9;
+ public static final int CMD_START_TETHERING_ERROR = BASE_IPSERVER + 6;
// notification from the master SM that it had trouble stopping tethering
- public static final int CMD_STOP_TETHERING_ERROR = BASE_IFACE + 10;
+ public static final int CMD_STOP_TETHERING_ERROR = BASE_IPSERVER + 7;
// notification from the master SM that it had trouble setting the DNS forwarders
- public static final int CMD_SET_DNS_FORWARDERS_ERROR = BASE_IFACE + 11;
+ public static final int CMD_SET_DNS_FORWARDERS_ERROR = BASE_IPSERVER + 8;
// the upstream connection has changed
- public static final int CMD_TETHER_CONNECTION_CHANGED = BASE_IFACE + 12;
+ public static final int CMD_TETHER_CONNECTION_CHANGED = BASE_IPSERVER + 9;
// new IPv6 tethering parameters need to be processed
- public static final int CMD_IPV6_TETHER_UPDATE = BASE_IFACE + 13;
+ public static final int CMD_IPV6_TETHER_UPDATE = BASE_IPSERVER + 10;
private final State mInitialState;
private final State mLocalHotspotState;
@@ -486,7 +485,9 @@
}
// Directly-connected route.
- final RouteInfo route = new RouteInfo(linkAddr);
+ final IpPrefix ipv4Prefix = new IpPrefix(linkAddr.getAddress(),
+ linkAddr.getPrefixLength());
+ final RouteInfo route = new RouteInfo(ipv4Prefix, null, null, RTN_UNICAST);
if (enabled) {
mLinkProperties.addLinkAddress(linkAddr);
mLinkProperties.addRoute(route);
@@ -1007,7 +1008,7 @@
String ifname, HashSet<IpPrefix> prefixes) {
final ArrayList<RouteInfo> localRoutes = new ArrayList<RouteInfo>();
for (IpPrefix ipp : prefixes) {
- localRoutes.add(new RouteInfo(ipp, null, ifname));
+ localRoutes.add(new RouteInfo(ipp, null, ifname, RTN_UNICAST));
}
return localRoutes;
}
@@ -1019,7 +1020,7 @@
try {
return Inet6Address.getByAddress(null, dnsBytes, 0);
} catch (UnknownHostException e) {
- Slog.wtf(TAG, "Failed to construct Inet6Address from: " + localPrefix);
+ Log.wtf(TAG, "Failed to construct Inet6Address from: " + localPrefix);
return null;
}
}
diff --git a/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java b/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
index 4147413..bba61d7 100644
--- a/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
+++ b/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
@@ -22,14 +22,14 @@
import static android.system.OsConstants.IPPROTO_ICMPV6;
import static android.system.OsConstants.SOCK_RAW;
import static android.system.OsConstants.SOL_SOCKET;
-import static android.system.OsConstants.SO_BINDTODEVICE;
import static android.system.OsConstants.SO_SNDTIMEO;
import android.net.IpPrefix;
import android.net.LinkAddress;
-import android.net.NetworkUtils;
import android.net.TrafficStats;
import android.net.util.InterfaceParams;
+import android.net.util.SocketUtils;
+import android.net.util.TetheringUtils;
import android.system.ErrnoException;
import android.system.Os;
import android.system.StructTimeval;
@@ -38,8 +38,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.TrafficStatsConstants;
-import libcore.io.IoBridge;
-
import java.io.FileDescriptor;
import java.io.IOException;
import java.net.Inet6Address;
@@ -611,9 +609,8 @@
// Setting SNDTIMEO is purely for defensive purposes.
Os.setsockoptTimeval(
mSocket, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(send_timout_ms));
- Os.setsockoptIfreq(mSocket, SOL_SOCKET, SO_BINDTODEVICE, mInterface.name);
- NetworkUtils.protectFromVpn(mSocket);
- NetworkUtils.setupRaSocket(mSocket, mInterface.index);
+ SocketUtils.bindSocketToInterface(mSocket, mInterface.name);
+ TetheringUtils.setupRaSocket(mSocket, mInterface.index);
} catch (ErrnoException | IOException e) {
Log.e(TAG, "Failed to create RA daemon socket: " + e);
return false;
@@ -627,7 +624,7 @@
private void closeSocket() {
if (mSocket != null) {
try {
- IoBridge.closeAndSignalBlockedThreads(mSocket);
+ SocketUtils.closeSocket(mSocket);
} catch (IOException ignored) { }
}
mSocket = null;
diff --git a/packages/Tethering/src/android/net/util/TetheringMessageBase.java b/packages/Tethering/src/android/net/util/TetheringMessageBase.java
new file mode 100644
index 0000000..1b763ce
--- /dev/null
+++ b/packages/Tethering/src/android/net/util/TetheringMessageBase.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net.util;
+
+/**
+ * This class defines Message.what base addresses for various state machine.
+ */
+public class TetheringMessageBase {
+ public static final int BASE_MASTER = 0;
+ public static final int BASE_IPSERVER = 100;
+
+}
diff --git a/packages/Tethering/src/android/net/util/TetheringUtils.java b/packages/Tethering/src/android/net/util/TetheringUtils.java
new file mode 100644
index 0000000..fa543bd
--- /dev/null
+++ b/packages/Tethering/src/android/net/util/TetheringUtils.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net.util;
+
+import java.io.FileDescriptor;
+import java.net.SocketException;
+
+/**
+ * Native methods for tethering utilization.
+ *
+ * {@hide}
+ */
+public class TetheringUtils {
+
+ /**
+ * Offload management process need to know conntrack rules to support NAT, but it may not have
+ * permission to create netlink netfilter sockets. Create two netlink netfilter sockets and
+ * share them with offload management process.
+ */
+ public static native boolean configOffload();
+
+ /**
+ * Configures a socket for receiving ICMPv6 router solicitations and sending advertisements.
+ * @param fd the socket's {@link FileDescriptor}.
+ * @param ifIndex the interface index.
+ */
+ public static native void setupRaSocket(FileDescriptor fd, int ifIndex)
+ throws SocketException;
+
+ /**
+ * Read s as an unsigned 16-bit integer.
+ */
+ public static int uint16(short s) {
+ return s & 0xffff;
+ }
+}
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
index ba5d08d..7e685fb 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
@@ -38,7 +38,6 @@
import android.content.IntentFilter;
import android.content.res.Resources;
import android.net.util.SharedLog;
-import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -48,7 +47,6 @@
import android.os.ResultReceiver;
import android.os.SystemClock;
import android.os.SystemProperties;
-import android.os.UserHandle;
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
import android.util.ArraySet;
@@ -196,9 +194,9 @@
// till upstream change to cellular.
if (mUsingCellularAsUpstream) {
if (showProvisioningUi) {
- runUiTetherProvisioning(type, config.subId);
+ runUiTetherProvisioning(type, config.activeDataSubId);
} else {
- runSilentTetherProvisioning(type, config.subId);
+ runSilentTetherProvisioning(type, config.activeDataSubId);
}
mNeedReRunProvisioningUi = false;
} else {
@@ -270,9 +268,9 @@
if (mCellularPermitted.indexOfKey(downstream) < 0) {
if (mNeedReRunProvisioningUi) {
mNeedReRunProvisioningUi = false;
- runUiTetherProvisioning(downstream, config.subId);
+ runUiTetherProvisioning(downstream, config.activeDataSubId);
} else {
- runSilentTetherProvisioning(downstream, config.subId);
+ runSilentTetherProvisioning(downstream, config.activeDataSubId);
}
}
}
@@ -336,7 +334,8 @@
.getSystemService(Context.CARRIER_CONFIG_SERVICE);
if (configManager == null) return null;
- final PersistableBundle carrierConfig = configManager.getConfigForSubId(config.subId);
+ final PersistableBundle carrierConfig = configManager.getConfigForSubId(
+ config.activeDataSubId);
if (CarrierConfigManager.isConfigForIdentifiedCarrier(carrierConfig)) {
return carrierConfig;
@@ -379,12 +378,9 @@
intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
intent.putExtra(EXTRA_SUBID, subId);
intent.setComponent(TETHER_SERVICE);
- final long ident = Binder.clearCallingIdentity();
- try {
- mContext.startServiceAsUser(intent, UserHandle.CURRENT);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
+ // Only admin user can change tethering and SilentTetherProvisioning don't need to
+ // show UI, it is fine to always start setting's background service as system user.
+ mContext.startService(intent);
}
private void runUiTetherProvisioning(int type, int subId) {
@@ -407,12 +403,9 @@
intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
intent.putExtra(EXTRA_SUBID, subId);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- final long ident = Binder.clearCallingIdentity();
- try {
- mContext.startActivityAsUser(intent, UserHandle.CURRENT);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
+ // Only launch entitlement UI for system user. Entitlement UI should not appear for other
+ // user because only admin user is allowed to change tethering.
+ mContext.startActivity(intent);
}
// Not needed to check if this don't run on the handler thread because it's private.
@@ -671,7 +664,7 @@
receiver.send(cacheValue, null);
} else {
ResultReceiver proxy = buildProxyReceiver(downstream, false/* notifyFail */, receiver);
- runUiTetherProvisioning(downstream, config.subId, proxy);
+ runUiTetherProvisioning(downstream, config.activeDataSubId, proxy);
}
}
}
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java b/packages/Tethering/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
index 9305414..66b9ade8 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
@@ -29,6 +29,7 @@
import java.net.Inet6Address;
import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
@@ -257,7 +258,7 @@
final LinkProperties lp = new LinkProperties();
final IpPrefix local48 = makeUniqueLocalPrefix(ulp, (short) 0, 48);
- lp.addRoute(new RouteInfo(local48, null, null));
+ lp.addRoute(new RouteInfo(local48, null, null, RouteInfo.RTN_UNICAST));
final IpPrefix local64 = makeUniqueLocalPrefix(ulp, subnetId, 64);
// Because this is a locally-generated ULA, we don't have an upstream
@@ -273,7 +274,13 @@
final byte[] bytes = Arrays.copyOf(in6addr, in6addr.length);
bytes[7] = (byte) (subnetId >> 8);
bytes[8] = (byte) subnetId;
- return new IpPrefix(bytes, prefixlen);
+ final InetAddress addr;
+ try {
+ addr = InetAddress.getByAddress(bytes);
+ } catch (UnknownHostException e) {
+ throw new IllegalStateException("Invalid address length: " + bytes.length, e);
+ }
+ return new IpPrefix(addr, prefixlen);
}
// Generates a Unique Locally-assigned Prefix:
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
index 16734d8..38fa91e 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
@@ -25,6 +25,7 @@
import android.content.ContentResolver;
import android.net.ITetheringStatsProvider;
+import android.net.InetAddresses;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
@@ -33,7 +34,6 @@
import android.net.netlink.ConntrackMessage;
import android.net.netlink.NetlinkConstants;
import android.net.netlink.NetlinkSocket;
-import android.net.util.IpUtils;
import android.net.util.SharedLog;
import android.os.Handler;
import android.os.INetworkManagementService;
@@ -477,9 +477,10 @@
if (!ri.hasGateway()) continue;
final String gateway = ri.getGateway().getHostAddress();
- if (ri.isIPv4Default()) {
+ final InetAddress address = ri.getDestination().getAddress();
+ if (ri.isDefaultRoute() && address instanceof Inet4Address) {
v4gateway = gateway;
- } else if (ri.isIPv6Default()) {
+ } else if (ri.isDefaultRoute() && address instanceof Inet6Address) {
v6gateways.add(gateway);
}
}
@@ -547,7 +548,10 @@
private static boolean shouldIgnoreDownstreamRoute(RouteInfo route) {
// Ignore any link-local routes.
- if (!route.getDestinationLinkAddress().isGlobalPreferred()) return true;
+ final IpPrefix destination = route.getDestination();
+ final LinkAddress linkAddr = new LinkAddress(destination.getAddress(),
+ destination.getPrefixLength());
+ if (!linkAddr.isGlobalPreferred()) return true;
return false;
}
@@ -588,7 +592,7 @@
return;
}
- if (!IpUtils.isValidUdpOrTcpPort(srcPort)) {
+ if (!isValidUdpOrTcpPort(srcPort)) {
mLog.e("Invalid src port: " + srcPort);
return;
}
@@ -599,7 +603,7 @@
return;
}
- if (!IpUtils.isValidUdpOrTcpPort(dstPort)) {
+ if (!isValidUdpOrTcpPort(dstPort)) {
mLog.e("Invalid dst port: " + dstPort);
return;
}
@@ -628,7 +632,7 @@
private static Inet4Address parseIPv4Address(String addrString) {
try {
- final InetAddress ip = InetAddress.parseNumericAddress(addrString);
+ final InetAddress ip = InetAddresses.parseNumericAddress(addrString);
// TODO: Consider other sanitization steps here, including perhaps:
// not eql to 0.0.0.0
// not within 169.254.0.0/16
@@ -668,4 +672,8 @@
return 180;
}
}
+
+ private static boolean isValidUdpOrTcpPort(int port) {
+ return port > 0 && port < 65536;
+ }
}
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
index 00a6773..4a8ef1f 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
@@ -16,7 +16,7 @@
package com.android.server.connectivity.tethering;
-import static com.android.internal.util.BitUtils.uint16;
+import static android.net.util.TetheringUtils.uint16;
import android.hardware.tetheroffload.control.V1_0.IOffloadControl;
import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback;
@@ -24,6 +24,7 @@
import android.hardware.tetheroffload.control.V1_0.NetworkProtocol;
import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent;
import android.net.util.SharedLog;
+import android.net.util.TetheringUtils;
import android.os.Handler;
import android.os.RemoteException;
import android.system.OsConstants;
@@ -47,8 +48,6 @@
private static final String NO_IPV4_ADDRESS = "";
private static final String NO_IPV4_GATEWAY = "";
- private static native boolean configOffload();
-
private final Handler mHandler;
private final SharedLog mLog;
private IOffloadControl mOffloadControl;
@@ -107,8 +106,6 @@
public OffloadHardwareInterface(Handler h, SharedLog log) {
mHandler = h;
mLog = log.forSubComponent(TAG);
-
- System.loadLibrary("tetheroffloadjni");
}
/** Get default value indicating whether offload is supported. */
@@ -118,7 +115,7 @@
/** Configure offload management process. */
public boolean initOffloadConfig() {
- return configOffload();
+ return TetheringUtils.configOffload();
}
/** Initialize the tethering offload HAL. */
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
index 3d414ee..5b26704 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
@@ -37,6 +37,7 @@
import static android.net.ConnectivityManager.TETHER_ERROR_SERVICE_UNAVAIL;
import static android.net.ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
+import static android.net.util.TetheringMessageBase.BASE_MASTER;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
@@ -106,7 +107,6 @@
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.MessageUtils;
-import com.android.internal.util.Protocol;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.networkstack.tethering.R;
@@ -120,6 +120,8 @@
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
/**
*
@@ -185,10 +187,10 @@
private final TetheringDependencies mDeps;
private final EntitlementManager mEntitlementMgr;
private final Handler mHandler;
- private final PhoneStateListener mPhoneStateListener;
private final INetd mNetd;
private final NetdCallback mNetdCallback;
private final UserRestrictionActionListener mTetheringRestriction;
+ private final ActiveDataSubIdListener mActiveDataSubIdListener;
private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID;
// All the usage of mTetheringEventCallback should run in the same thread.
private ITetheringEventCallback mTetheringEventCallback = null;
@@ -252,26 +254,6 @@
mEntitlementMgr.reevaluateSimCardProvisioning(mConfig);
});
- mPhoneStateListener = new PhoneStateListener(mLooper) {
- @Override
- public void onActiveDataSubscriptionIdChanged(int subId) {
- mLog.log("OBSERVED active data subscription change, from " + mActiveDataSubId
- + " to " + subId);
- if (subId == mActiveDataSubId) return;
-
- mActiveDataSubId = subId;
- updateConfiguration();
- // To avoid launching unexpected provisioning checks, ignore re-provisioning when
- // no CarrierConfig loaded yet. Assume reevaluateSimCardProvisioning() will be
- // triggered again when CarrierConfig is loaded.
- if (mEntitlementMgr.getCarrierConfig(mConfig) != null) {
- mEntitlementMgr.reevaluateSimCardProvisioning(mConfig);
- } else {
- mLog.log("IGNORED reevaluate provisioning due to no carrier config loaded");
- }
- }
- };
-
mStateReceiver = new StateReceiver();
mNetdCallback = new NetdCallback();
@@ -284,6 +266,8 @@
final UserManager userManager = (UserManager) mContext.getSystemService(
Context.USER_SERVICE);
mTetheringRestriction = new UserRestrictionActionListener(userManager, this);
+ final TetheringThreadExecutor executor = new TetheringThreadExecutor(mHandler);
+ mActiveDataSubIdListener = new ActiveDataSubIdListener(executor);
// Load tethering configuration.
updateConfiguration();
@@ -294,8 +278,8 @@
private void startStateMachineUpdaters(Handler handler) {
mCarrierConfigChange.startListening();
- mContext.getSystemService(TelephonyManager.class).listen(
- mPhoneStateListener, PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);
+ mContext.getSystemService(TelephonyManager.class).listen(mActiveDataSubIdListener,
+ PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);
IntentFilter filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_STATE);
@@ -314,6 +298,43 @@
}
+ private class TetheringThreadExecutor implements Executor {
+ private final Handler mTetherHandler;
+ TetheringThreadExecutor(Handler handler) {
+ mTetherHandler = handler;
+ }
+ @Override
+ public void execute(Runnable command) {
+ if (!mTetherHandler.post(command)) {
+ throw new RejectedExecutionException(mTetherHandler + " is shutting down");
+ }
+ }
+ }
+
+ private class ActiveDataSubIdListener extends PhoneStateListener {
+ ActiveDataSubIdListener(Executor executor) {
+ super(executor);
+ }
+
+ @Override
+ public void onActiveDataSubscriptionIdChanged(int subId) {
+ mLog.log("OBSERVED active data subscription change, from " + mActiveDataSubId
+ + " to " + subId);
+ if (subId == mActiveDataSubId) return;
+
+ mActiveDataSubId = subId;
+ updateConfiguration();
+ // To avoid launching unexpected provisioning checks, ignore re-provisioning
+ // when no CarrierConfig loaded yet. Assume reevaluateSimCardProvisioning()
+ // ill be triggered again when CarrierConfig is loaded.
+ if (mEntitlementMgr.getCarrierConfig(mConfig) != null) {
+ mEntitlementMgr.reevaluateSimCardProvisioning(mConfig);
+ } else {
+ mLog.log("IGNORED reevaluate provisioning, no carrier config loaded");
+ }
+ }
+ }
+
private WifiManager getWifiManager() {
return (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
}
@@ -326,8 +347,7 @@
}
private void maybeDunSettingChanged() {
- final boolean isDunRequired = TetheringConfiguration.checkDunRequired(
- mContext, mActiveDataSubId);
+ final boolean isDunRequired = TetheringConfiguration.checkDunRequired(mContext);
if (isDunRequired == mConfig.isDunRequired) return;
updateConfiguration();
}
@@ -633,8 +653,7 @@
reportTetherStateChanged(mTetherStatesParcel);
final Intent bcast = new Intent(ACTION_TETHER_STATE_CHANGED);
- bcast.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
- | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ bcast.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
bcast.putStringArrayListExtra(EXTRA_AVAILABLE_TETHER, availableList);
bcast.putStringArrayListExtra(EXTRA_ACTIVE_LOCAL_ONLY, localOnlyList);
bcast.putStringArrayListExtra(EXTRA_ACTIVE_TETHER, tetherList);
@@ -1163,7 +1182,6 @@
}
class TetherMasterSM extends StateMachine {
- private static final int BASE_MASTER = Protocol.BASE_TETHERING;
// an interface SM has requested Tethering/Local Hotspot
static final int EVENT_IFACE_SERVING_STATE_ACTIVE = BASE_MASTER + 1;
// an interface SM has unrequested Tethering/Local Hotspot
@@ -1180,7 +1198,6 @@
static final int EVENT_IFACE_UPDATE_LINKPROPERTIES = BASE_MASTER + 7;
// Events from EntitlementManager to choose upstream again.
static final int EVENT_UPSTREAM_PERMISSION_CHANGED = BASE_MASTER + 8;
-
private final State mInitialState;
private final State mTetherModeAliveState;
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
index 0ab4d63..490614b 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -100,13 +100,13 @@
public final String provisioningAppNoUi;
public final int provisioningCheckPeriod;
- public final int subId;
+ public final int activeDataSubId;
public TetheringConfiguration(Context ctx, SharedLog log, int id) {
final SharedLog configLog = log.forSubComponent("config");
- subId = id;
- Resources res = getResources(ctx, subId);
+ activeDataSubId = id;
+ Resources res = getResources(ctx, activeDataSubId);
tetherableUsbRegexs = getResourceStringArray(res, config_tether_usb_regexs);
// TODO: Evaluate deleting this altogether now that Wi-Fi always passes
@@ -116,7 +116,7 @@
tetherableWifiP2pRegexs = getResourceStringArray(res, config_tether_wifi_p2p_regexs);
tetherableBluetoothRegexs = getResourceStringArray(res, config_tether_bluetooth_regexs);
- isDunRequired = checkDunRequired(ctx, subId);
+ isDunRequired = checkDunRequired(ctx);
chooseUpstreamAutomatically = getResourceBoolean(res, config_tether_upstream_automatic);
preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(res, isDunRequired);
@@ -166,8 +166,8 @@
/** Does the dumping.*/
public void dump(PrintWriter pw) {
- pw.print("subId: ");
- pw.println(subId);
+ pw.print("activeDataSubId: ");
+ pw.println(activeDataSubId);
dumpStringArray(pw, "tetherableUsbRegexs", tetherableUsbRegexs);
dumpStringArray(pw, "tetherableWifiRegexs", tetherableWifiRegexs);
@@ -196,7 +196,7 @@
/** Returns the string representation of this object.*/
public String toString() {
final StringJoiner sj = new StringJoiner(" ");
- sj.add(String.format("subId:%d", subId));
+ sj.add(String.format("activeDataSubId:%d", activeDataSubId));
sj.add(String.format("tetherableUsbRegexs:%s", makeString(tetherableUsbRegexs)));
sj.add(String.format("tetherableWifiRegexs:%s", makeString(tetherableWifiRegexs)));
sj.add(String.format("tetherableWifiP2pRegexs:%s", makeString(tetherableWifiP2pRegexs)));
@@ -250,9 +250,11 @@
}
/** Check whether dun is required. */
- public static boolean checkDunRequired(Context ctx, int id) {
+ public static boolean checkDunRequired(Context ctx) {
final TelephonyManager tm = (TelephonyManager) ctx.getSystemService(TELEPHONY_SERVICE);
- return (tm != null) ? tm.isTetheringApnRequired(id) : false;
+ // TelephonyManager would uses the active data subscription, which should be the one used
+ // by tethering.
+ return (tm != null) ? tm.isTetheringApnRequired() : false;
}
private static Collection<Integer> getUpstreamIfaceTypes(Resources res, boolean dunRequired) {
@@ -391,7 +393,7 @@
*/
public TetheringConfigurationParcel toStableParcelable() {
final TetheringConfigurationParcel parcel = new TetheringConfigurationParcel();
- parcel.subId = subId;
+ parcel.subId = activeDataSubId;
parcel.tetherableUsbRegexs = tetherableUsbRegexs;
parcel.tetherableWifiRegexs = tetherableWifiRegexs;
parcel.tetherableBluetoothRegexs = tetherableBluetoothRegexs;
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
index ba30845..775484e 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
@@ -21,6 +21,7 @@
import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION;
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_UNSUPPORTED;
+import static android.net.dhcp.IDhcpServer.STATUS_UNKNOWN_ERROR;
import android.app.Service;
import android.content.Context;
@@ -84,6 +85,7 @@
*/
@VisibleForTesting
public Tethering makeTethering(TetheringDependencies deps) {
+ System.loadLibrary("tetherutilsjni");
return new Tethering(deps);
}
@@ -339,7 +341,10 @@
service.makeDhcpServer(ifName, params, cb);
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ Log.e(TAG, "Fail to make dhcp server");
+ try {
+ cb.onDhcpServerCreated(STATUS_UNKNOWN_ERROR, null);
+ } catch (RemoteException re) { }
}
}
};
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
index dc38c49a..22150f6 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -328,13 +328,6 @@
network, newNc));
}
- // Log changes in upstream network signal strength, if available.
- if (network.equals(mTetheringUpstreamNetwork) && newNc.hasSignalStrength()) {
- final int newSignal = newNc.getSignalStrength();
- final String prevSignal = getSignalStrength(prev.networkCapabilities);
- mLog.logf("upstream network signal strength: %s -> %s", prevSignal, newSignal);
- }
-
mNetworkMap.put(network, new UpstreamNetworkState(
prev.linkProperties, newNc, network));
// TODO: If sufficient information is available to select a more
@@ -557,11 +550,6 @@
return prefixSet;
}
- private static String getSignalStrength(NetworkCapabilities nc) {
- if (nc == null || !nc.hasSignalStrength()) return "unknown";
- return Integer.toString(nc.getSignalStrength());
- }
-
private static boolean isCellular(UpstreamNetworkState ns) {
return (ns != null) && isCellular(ns.networkCapabilities);
}
diff --git a/packages/Tethering/tests/unit/Android.bp b/packages/Tethering/tests/unit/Android.bp
index 81a0548..53782fed 100644
--- a/packages/Tethering/tests/unit/Android.bp
+++ b/packages/Tethering/tests/unit/Android.bp
@@ -20,7 +20,11 @@
srcs: [
"src/**/*.java",
],
- test_suites: ["device-tests"],
+ test_suites: [
+ "device-tests",
+ "mts",
+ ],
+ compile_multilib: "both",
static_libs: [
"androidx.test.rules",
"frameworks-base-testutils",
diff --git a/packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java b/packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java
index e01ac7f..e8add98 100644
--- a/packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java
+++ b/packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java
@@ -18,8 +18,6 @@
import static android.net.InetAddresses.parseNumericAddress;
-import static com.google.android.collect.Sets.newHashSet;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -34,6 +32,8 @@
import org.junit.runner.RunWith;
import java.net.Inet4Address;
+import java.util.Arrays;
+import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@@ -47,9 +47,10 @@
private static final int TEST_LEASE_TIME_SECS = 120;
private static final int TEST_MTU = 1000;
private static final Set<Inet4Address> TEST_ADDRESS_SET =
- newHashSet(inet4Addr("192.168.1.123"), inet4Addr("192.168.1.124"));
+ new HashSet<Inet4Address>(Arrays.asList(
+ new Inet4Address[] {inet4Addr("192.168.1.123"), inet4Addr("192.168.1.124")}));
private static final Set<Integer> TEST_ADDRESS_SET_PARCELED =
- newHashSet(0xc0a8017b, 0xc0a8017c);
+ new HashSet<Integer>(Arrays.asList(new Integer[] {0xc0a8017b, 0xc0a8017c}));
private DhcpServingParamsParcelExt mParcel;
diff --git a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index 4358cd6..fd2f708 100644
--- a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -510,8 +510,10 @@
}
assertNotNull("missing IPv4 address", addr4);
+ final IpPrefix destination = new IpPrefix(addr4.getAddress(), addr4.getPrefixLength());
// Assert the presence of the associated directly connected route.
- final RouteInfo directlyConnected = new RouteInfo(addr4, null, lp.getInterfaceName());
+ final RouteInfo directlyConnected = new RouteInfo(destination, null, lp.getInterfaceName(),
+ RouteInfo.RTN_UNICAST);
assertTrue("missing directly connected route: '" + directlyConnected.toString() + "'",
lp.getRoutes().contains(directlyConnected));
}
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java
index 8574f54..7886ca6 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java
@@ -21,6 +21,7 @@
import static android.net.NetworkStats.STATS_PER_UID;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
+import static android.net.RouteInfo.RTN_UNICAST;
import static android.net.TrafficStats.UID_TETHERING;
import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;
@@ -269,7 +270,7 @@
final String ipv4Addr = "192.0.2.5";
final String linkAddr = ipv4Addr + "/24";
lp.addLinkAddress(new LinkAddress(linkAddr));
- lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24")));
+ lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"), null, null, RTN_UNICAST));
offload.setUpstreamLinkProperties(lp);
// IPv4 prefixes and addresses on the upstream are simply left as whole
// prefixes (already passed in from UpstreamNetworkMonitor code). If a
@@ -285,7 +286,7 @@
inOrder.verifyNoMoreInteractions();
final String ipv4Gateway = "192.0.2.1";
- lp.addRoute(new RouteInfo(InetAddress.getByName(ipv4Gateway)));
+ lp.addRoute(new RouteInfo(null, InetAddress.getByName(ipv4Gateway), null, RTN_UNICAST));
offload.setUpstreamLinkProperties(lp);
// No change in local addresses means no call to setLocalPrefixes().
inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture());
@@ -296,7 +297,7 @@
inOrder.verifyNoMoreInteractions();
final String ipv6Gw1 = "fe80::cafe";
- lp.addRoute(new RouteInfo(InetAddress.getByName(ipv6Gw1)));
+ lp.addRoute(new RouteInfo(null, InetAddress.getByName(ipv6Gw1), null, RTN_UNICAST));
offload.setUpstreamLinkProperties(lp);
// No change in local addresses means no call to setLocalPrefixes().
inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture());
@@ -310,7 +311,7 @@
inOrder.verifyNoMoreInteractions();
final String ipv6Gw2 = "fe80::d00d";
- lp.addRoute(new RouteInfo(InetAddress.getByName(ipv6Gw2)));
+ lp.addRoute(new RouteInfo(null, InetAddress.getByName(ipv6Gw2), null, RTN_UNICAST));
offload.setUpstreamLinkProperties(lp);
// No change in local addresses means no call to setLocalPrefixes().
inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture());
@@ -327,8 +328,10 @@
final LinkProperties stacked = new LinkProperties();
stacked.setInterfaceName("stacked");
stacked.addLinkAddress(new LinkAddress("192.0.2.129/25"));
- stacked.addRoute(new RouteInfo(InetAddress.getByName("192.0.2.254")));
- stacked.addRoute(new RouteInfo(InetAddress.getByName("fe80::bad:f00")));
+ stacked.addRoute(new RouteInfo(null, InetAddress.getByName("192.0.2.254"), null,
+ RTN_UNICAST));
+ stacked.addRoute(new RouteInfo(null, InetAddress.getByName("fe80::bad:f00"), null,
+ RTN_UNICAST));
assertTrue(lp.addStackedLink(stacked));
offload.setUpstreamLinkProperties(lp);
// No change in local addresses means no call to setLocalPrefixes().
@@ -348,7 +351,7 @@
// removed from "local prefixes" and /128s added for the upstream IPv6
// addresses. This is not yet implemented, and for now we simply
// expect to see these /128s.
- lp.addRoute(new RouteInfo(new IpPrefix("2001:db8::/64")));
+ lp.addRoute(new RouteInfo(new IpPrefix("2001:db8::/64"), null, null, RTN_UNICAST));
// "2001:db8::/64" plus "assigned" ASCII in hex
lp.addLinkAddress(new LinkAddress("2001:db8::6173:7369:676e:6564/64"));
// "2001:db8::/64" plus "random" ASCII in hex
@@ -574,13 +577,15 @@
final LinkProperties usbLinkProperties = new LinkProperties();
usbLinkProperties.setInterfaceName(RNDIS0);
usbLinkProperties.addLinkAddress(new LinkAddress("192.168.42.1/24"));
- usbLinkProperties.addRoute(new RouteInfo(new IpPrefix(USB_PREFIX)));
+ usbLinkProperties.addRoute(
+ new RouteInfo(new IpPrefix(USB_PREFIX), null, null, RTN_UNICAST));
offload.notifyDownstreamLinkProperties(usbLinkProperties);
inOrder.verify(mHardware, times(1)).addDownstreamPrefix(RNDIS0, USB_PREFIX);
inOrder.verifyNoMoreInteractions();
// [2] Routes for IPv6 link-local prefixes should never be added.
- usbLinkProperties.addRoute(new RouteInfo(new IpPrefix(IPV6_LINKLOCAL)));
+ usbLinkProperties.addRoute(
+ new RouteInfo(new IpPrefix(IPV6_LINKLOCAL), null, null, RTN_UNICAST));
offload.notifyDownstreamLinkProperties(usbLinkProperties);
inOrder.verify(mHardware, never()).addDownstreamPrefix(eq(RNDIS0), anyString());
inOrder.verifyNoMoreInteractions();
@@ -588,7 +593,8 @@
// [3] Add an IPv6 prefix for good measure. Only new offload-able
// prefixes should be passed to the HAL.
usbLinkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64"));
- usbLinkProperties.addRoute(new RouteInfo(new IpPrefix(IPV6_DOC_PREFIX)));
+ usbLinkProperties.addRoute(
+ new RouteInfo(new IpPrefix(IPV6_DOC_PREFIX), null, null, RTN_UNICAST));
offload.notifyDownstreamLinkProperties(usbLinkProperties);
inOrder.verify(mHardware, times(1)).addDownstreamPrefix(RNDIS0, IPV6_DOC_PREFIX);
inOrder.verifyNoMoreInteractions();
@@ -601,8 +607,10 @@
// [5] Differences in local routes are converted into addDownstream()
// and removeDownstream() invocations accordingly.
- usbLinkProperties.removeRoute(new RouteInfo(new IpPrefix(IPV6_DOC_PREFIX), null, RNDIS0));
- usbLinkProperties.addRoute(new RouteInfo(new IpPrefix(IPV6_DISCARD_PREFIX)));
+ usbLinkProperties.removeRoute(
+ new RouteInfo(new IpPrefix(IPV6_DOC_PREFIX), null, RNDIS0, RTN_UNICAST));
+ usbLinkProperties.addRoute(
+ new RouteInfo(new IpPrefix(IPV6_DISCARD_PREFIX), null, null, RTN_UNICAST));
offload.notifyDownstreamLinkProperties(usbLinkProperties);
inOrder.verify(mHardware, times(1)).removeDownstreamPrefix(RNDIS0, IPV6_DOC_PREFIX);
inOrder.verify(mHardware, times(1)).addDownstreamPrefix(RNDIS0, IPV6_DISCARD_PREFIX);
@@ -680,19 +688,23 @@
final LinkProperties usbLinkProperties = new LinkProperties();
usbLinkProperties.setInterfaceName(RNDIS0);
usbLinkProperties.addLinkAddress(new LinkAddress("192.168.42.1/24"));
- usbLinkProperties.addRoute(new RouteInfo(new IpPrefix(USB_PREFIX)));
+ usbLinkProperties.addRoute(
+ new RouteInfo(new IpPrefix(USB_PREFIX), null, null, RTN_UNICAST));
offload.notifyDownstreamLinkProperties(usbLinkProperties);
final LinkProperties wifiLinkProperties = new LinkProperties();
wifiLinkProperties.setInterfaceName(WLAN0);
wifiLinkProperties.addLinkAddress(new LinkAddress("192.168.43.1/24"));
- wifiLinkProperties.addRoute(new RouteInfo(new IpPrefix(WIFI_PREFIX)));
- wifiLinkProperties.addRoute(new RouteInfo(new IpPrefix(IPV6_LINKLOCAL)));
+ wifiLinkProperties.addRoute(
+ new RouteInfo(new IpPrefix(WIFI_PREFIX), null, null, RTN_UNICAST));
+ wifiLinkProperties.addRoute(
+ new RouteInfo(new IpPrefix(IPV6_LINKLOCAL), null, null, RTN_UNICAST));
// Use a benchmark prefix (RFC 5180 + erratum), since the documentation
// prefix is included in the excluded prefix list.
wifiLinkProperties.addLinkAddress(new LinkAddress("2001:2::1/64"));
wifiLinkProperties.addLinkAddress(new LinkAddress("2001:2::2/64"));
- wifiLinkProperties.addRoute(new RouteInfo(new IpPrefix("2001:2::/64")));
+ wifiLinkProperties.addRoute(
+ new RouteInfo(new IpPrefix("2001:2::/64"), null, null, RTN_UNICAST));
offload.notifyDownstreamLinkProperties(wifiLinkProperties);
offload.removeDownstreamInterface(RNDIS0);
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
index 30bff35..7799da4 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
@@ -34,7 +34,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.when;
import android.content.ContentResolver;
@@ -145,7 +144,7 @@
@Test
public void testDunFromTelephonyManagerMeansDun() {
- when(mTelephonyManager.isTetheringApnRequired(anyInt())).thenReturn(true);
+ when(mTelephonyManager.isTetheringApnRequired()).thenReturn(true);
final TetheringConfiguration cfgWifi = getTetheringConfiguration(TYPE_WIFI);
final TetheringConfiguration cfgMobileWifiHipri = getTetheringConfiguration(
@@ -169,7 +168,7 @@
@Test
public void testDunNotRequiredFromTelephonyManagerMeansNoDun() {
- when(mTelephonyManager.isTetheringApnRequired(anyInt())).thenReturn(false);
+ when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false);
final TetheringConfiguration cfgWifi = getTetheringConfiguration(TYPE_WIFI);
final TetheringConfiguration cfgMobileWifiHipri = getTetheringConfiguration(
@@ -212,7 +211,7 @@
@Test
public void testNoDefinedUpstreamTypesAddsEthernet() {
when(mResources.getIntArray(config_tether_upstream_types)).thenReturn(new int[]{});
- when(mTelephonyManager.isTetheringApnRequired(anyInt())).thenReturn(false);
+ when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false);
final TetheringConfiguration cfg = new TetheringConfiguration(
mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
@@ -235,7 +234,7 @@
public void testDefinedUpstreamTypesSansEthernetAddsEthernet() {
when(mResources.getIntArray(config_tether_upstream_types)).thenReturn(
new int[]{TYPE_WIFI, TYPE_MOBILE_HIPRI});
- when(mTelephonyManager.isTetheringApnRequired(anyInt())).thenReturn(false);
+ when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false);
final TetheringConfiguration cfg = new TetheringConfiguration(
mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
@@ -253,7 +252,7 @@
public void testDefinedUpstreamTypesWithEthernetDoesNotAddEthernet() {
when(mResources.getIntArray(config_tether_upstream_types))
.thenReturn(new int[]{TYPE_WIFI, TYPE_ETHERNET, TYPE_MOBILE_HIPRI});
- when(mTelephonyManager.isTetheringApnRequired(anyInt())).thenReturn(false);
+ when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false);
final TetheringConfiguration cfg = new TetheringConfiguration(
mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
index 9d9ad10..04b2eb4 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
@@ -28,6 +28,7 @@
import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
+import static android.net.RouteInfo.RTN_UNICAST;
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
@@ -72,6 +73,7 @@
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
import android.net.ITetheringEventCallback;
+import android.net.InetAddresses;
import android.net.InterfaceConfiguration;
import android.net.IpPrefix;
import android.net.LinkAddress;
@@ -81,7 +83,6 @@
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkRequest;
-import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.net.TetherStatesParcel;
import android.net.TetheringConfigurationParcel;
@@ -365,23 +366,26 @@
if (withIPv4) {
prop.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0),
- NetworkUtils.numericToInetAddress("10.0.0.1"), TEST_MOBILE_IFNAME));
+ InetAddresses.parseNumericAddress("10.0.0.1"),
+ TEST_MOBILE_IFNAME, RTN_UNICAST));
}
if (withIPv6) {
- prop.addDnsServer(NetworkUtils.numericToInetAddress("2001:db8::2"));
+ prop.addDnsServer(InetAddresses.parseNumericAddress("2001:db8::2"));
prop.addLinkAddress(
- new LinkAddress(NetworkUtils.numericToInetAddress("2001:db8::"),
+ new LinkAddress(InetAddresses.parseNumericAddress("2001:db8::"),
NetworkConstants.RFC7421_PREFIX_LENGTH));
prop.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0),
- NetworkUtils.numericToInetAddress("2001:db8::1"), TEST_MOBILE_IFNAME));
+ InetAddresses.parseNumericAddress("2001:db8::1"),
+ TEST_MOBILE_IFNAME, RTN_UNICAST));
}
if (with464xlat) {
final LinkProperties stackedLink = new LinkProperties();
stackedLink.setInterfaceName(TEST_XLAT_MOBILE_IFNAME);
stackedLink.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0),
- NetworkUtils.numericToInetAddress("192.0.0.1"), TEST_XLAT_MOBILE_IFNAME));
+ InetAddresses.parseNumericAddress("192.0.0.1"),
+ TEST_XLAT_MOBILE_IFNAME, RTN_UNICAST));
prop.addStackedLink(stackedLink);
}
@@ -1210,12 +1214,12 @@
@Test
public void testMultiSimAware() throws Exception {
final TetheringConfiguration initailConfig = mTethering.getTetheringConfiguration();
- assertEquals(INVALID_SUBSCRIPTION_ID, initailConfig.subId);
+ assertEquals(INVALID_SUBSCRIPTION_ID, initailConfig.activeDataSubId);
final int fakeSubId = 1234;
mPhoneStateListener.onActiveDataSubscriptionIdChanged(fakeSubId);
final TetheringConfiguration newConfig = mTethering.getTetheringConfiguration();
- assertEquals(fakeSubId, newConfig.subId);
+ assertEquals(fakeSubId, newConfig.activeDataSubId);
}
private void workingWifiP2pGroupOwner(
diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java
index f4c2777..5b79d51 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -447,44 +447,33 @@
validate();
return rsnAllocationCreateTyped(mContext, type, mip, usage, pointer);
}
- native long rsnAllocationCreateFromBitmap(long con, long type, int mip, long bitmapHandle,
+
+ native long rsnAllocationCreateFromBitmap(long con, long type, int mip, Bitmap bmp,
int usage);
synchronized long nAllocationCreateFromBitmap(long type, int mip, Bitmap bmp, int usage) {
validate();
- return rsnAllocationCreateFromBitmap(mContext, type, mip, bmp.getNativeInstance(), usage);
+ return rsnAllocationCreateFromBitmap(mContext, type, mip, bmp, usage);
}
- native long rsnAllocationCreateBitmapBackedAllocation(long con, long type, int mip, long bitmapHandle,
+ native long rsnAllocationCreateBitmapBackedAllocation(long con, long type, int mip, Bitmap bmp,
int usage);
synchronized long nAllocationCreateBitmapBackedAllocation(long type, int mip, Bitmap bmp,
int usage) {
validate();
- return rsnAllocationCreateBitmapBackedAllocation(mContext, type, mip, bmp.getNativeInstance(),
- usage);
+ return rsnAllocationCreateBitmapBackedAllocation(mContext, type, mip, bmp, usage);
}
- native long rsnAllocationCubeCreateFromBitmap(long con, long type, int mip, long bitmapHandle,
+ native long rsnAllocationCubeCreateFromBitmap(long con, long type, int mip, Bitmap bmp,
int usage);
synchronized long nAllocationCubeCreateFromBitmap(long type, int mip, Bitmap bmp, int usage) {
validate();
- return rsnAllocationCubeCreateFromBitmap(mContext, type, mip, bmp.getNativeInstance(),
- usage);
- }
- native long rsnAllocationCreateBitmapRef(long con, long type, long bitmapHandle);
- synchronized long nAllocationCreateBitmapRef(long type, Bitmap bmp) {
- validate();
- return rsnAllocationCreateBitmapRef(mContext, type, bmp.getNativeInstance());
- }
- native long rsnAllocationCreateFromAssetStream(long con, int mips, int assetStream, int usage);
- synchronized long nAllocationCreateFromAssetStream(int mips, int assetStream, int usage) {
- validate();
- return rsnAllocationCreateFromAssetStream(mContext, mips, assetStream, usage);
+ return rsnAllocationCubeCreateFromBitmap(mContext, type, mip, bmp, usage);
}
- native void rsnAllocationCopyToBitmap(long con, long alloc, long bitmapHandle);
+ native void rsnAllocationCopyToBitmap(long con, long alloc, Bitmap bmp);
synchronized void nAllocationCopyToBitmap(long alloc, Bitmap bmp) {
validate();
- rsnAllocationCopyToBitmap(mContext, alloc, bmp.getNativeInstance());
+ rsnAllocationCopyToBitmap(mContext, alloc, bmp);
}
native void rsnAllocationSyncAll(long con, long alloc, int src);
@@ -537,10 +526,10 @@
validate();
rsnAllocationGenerateMipmaps(mContext, alloc);
}
- native void rsnAllocationCopyFromBitmap(long con, long alloc, long bitmapHandle);
+ native void rsnAllocationCopyFromBitmap(long con, long alloc, Bitmap bmp);
synchronized void nAllocationCopyFromBitmap(long alloc, Bitmap bmp) {
validate();
- rsnAllocationCopyFromBitmap(mContext, alloc, bmp.getNativeInstance());
+ rsnAllocationCopyFromBitmap(mContext, alloc, bmp);
}
diff --git a/rs/jni/Android.mk b/rs/jni/Android.mk
index 0854b95..f9ef0b7 100644
--- a/rs/jni/Android.mk
+++ b/rs/jni/Android.mk
@@ -12,7 +12,6 @@
libRS \
libcutils \
liblog \
- libhwui \
libutils \
libui \
libgui \
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index dfee961..5ae895d 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -32,10 +32,10 @@
#include "jni.h"
#include <nativehelper/JNIHelp.h>
+#include <android/graphics/bitmap.h>
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/android_view_Surface.h"
#include "android_runtime/android_util_AssetManager.h"
-#include "android/graphics/GraphicsJNI.h"
#include "android/native_window.h"
#include "android/native_window_jni.h"
@@ -1319,27 +1319,28 @@
rsAllocationGenerateMipmaps((RsContext)con, (RsAllocation)alloc);
}
+static size_t computeByteSize(const android::graphics::Bitmap& bitmap) {
+ AndroidBitmapInfo info = bitmap.getInfo();
+ return info.height * info.stride;
+}
+
static jlong
nAllocationCreateFromBitmap(JNIEnv *_env, jobject _this, jlong con, jlong type, jint mip,
- jlong bitmapPtr, jint usage)
+ jobject jbitmap, jint usage)
{
- SkBitmap bitmap;
- bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap);
-
+ android::graphics::Bitmap bitmap(_env, jbitmap);
const void* ptr = bitmap.getPixels();
jlong id = (jlong)(uintptr_t)rsAllocationCreateFromBitmap((RsContext)con,
(RsType)type, (RsAllocationMipmapControl)mip,
- ptr, bitmap.computeByteSize(), usage);
+ ptr, computeByteSize(bitmap), usage);
return id;
}
static jlong
nAllocationCreateBitmapBackedAllocation(JNIEnv *_env, jobject _this, jlong con, jlong type,
- jint mip, jlong bitmapPtr, jint usage)
+ jint mip, jobject jbitmap, jint usage)
{
- SkBitmap bitmap;
- bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap);
-
+ android::graphics::Bitmap bitmap(_env, jbitmap);
const void* ptr = bitmap.getPixels();
jlong id = (jlong)(uintptr_t)rsAllocationCreateTyped((RsContext)con,
(RsType)type, (RsAllocationMipmapControl)mip,
@@ -1349,40 +1350,35 @@
static jlong
nAllocationCubeCreateFromBitmap(JNIEnv *_env, jobject _this, jlong con, jlong type, jint mip,
- jlong bitmapPtr, jint usage)
+ jobject jbitmap, jint usage)
{
- SkBitmap bitmap;
- bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap);
-
+ android::graphics::Bitmap bitmap(_env, jbitmap);
const void* ptr = bitmap.getPixels();
jlong id = (jlong)(uintptr_t)rsAllocationCubeCreateFromBitmap((RsContext)con,
(RsType)type, (RsAllocationMipmapControl)mip,
- ptr, bitmap.computeByteSize(), usage);
+ ptr, computeByteSize(bitmap), usage);
return id;
}
static void
-nAllocationCopyFromBitmap(JNIEnv *_env, jobject _this, jlong con, jlong alloc, jlong bitmapPtr)
+nAllocationCopyFromBitmap(JNIEnv *_env, jobject _this, jlong con, jlong alloc, jobject jbitmap)
{
- SkBitmap bitmap;
- bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap);
- int w = bitmap.width();
- int h = bitmap.height();
+ android::graphics::Bitmap bitmap(_env, jbitmap);
+ int w = bitmap.getInfo().width;
+ int h = bitmap.getInfo().height;
const void* ptr = bitmap.getPixels();
rsAllocation2DData((RsContext)con, (RsAllocation)alloc, 0, 0,
0, RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X,
- w, h, ptr, bitmap.computeByteSize(), 0);
+ w, h, ptr, computeByteSize(bitmap), 0);
}
static void
-nAllocationCopyToBitmap(JNIEnv *_env, jobject _this, jlong con, jlong alloc, jlong bitmapPtr)
+nAllocationCopyToBitmap(JNIEnv *_env, jobject _this, jlong con, jlong alloc, jobject jbitmap)
{
- SkBitmap bitmap;
- bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap);
-
+ android::graphics::Bitmap bitmap(_env, jbitmap);
void* ptr = bitmap.getPixels();
- rsAllocationCopyToBitmap((RsContext)con, (RsAllocation)alloc, ptr, bitmap.computeByteSize());
+ rsAllocationCopyToBitmap((RsContext)con, (RsAllocation)alloc, ptr, computeByteSize(bitmap));
bitmap.notifyPixelsChanged();
}
@@ -2867,12 +2863,12 @@
{"rsnTypeGetNativeData", "(JJ[J)V", (void*)nTypeGetNativeData },
{"rsnAllocationCreateTyped", "(JJIIJ)J", (void*)nAllocationCreateTyped },
-{"rsnAllocationCreateFromBitmap", "(JJIJI)J", (void*)nAllocationCreateFromBitmap },
-{"rsnAllocationCreateBitmapBackedAllocation", "(JJIJI)J", (void*)nAllocationCreateBitmapBackedAllocation },
-{"rsnAllocationCubeCreateFromBitmap","(JJIJI)J", (void*)nAllocationCubeCreateFromBitmap },
+{"rsnAllocationCreateFromBitmap", "(JJILandroid/graphics/Bitmap;I)J", (void*)nAllocationCreateFromBitmap },
+{"rsnAllocationCreateBitmapBackedAllocation", "(JJILandroid/graphics/Bitmap;I)J", (void*)nAllocationCreateBitmapBackedAllocation },
+{"rsnAllocationCubeCreateFromBitmap","(JJILandroid/graphics/Bitmap;I)J", (void*)nAllocationCubeCreateFromBitmap },
-{"rsnAllocationCopyFromBitmap", "(JJJ)V", (void*)nAllocationCopyFromBitmap },
-{"rsnAllocationCopyToBitmap", "(JJJ)V", (void*)nAllocationCopyToBitmap },
+{"rsnAllocationCopyFromBitmap", "(JJLandroid/graphics/Bitmap;)V", (void*)nAllocationCopyFromBitmap },
+{"rsnAllocationCopyToBitmap", "(JJLandroid/graphics/Bitmap;)V", (void*)nAllocationCopyToBitmap },
{"rsnAllocationSyncAll", "(JJI)V", (void*)nAllocationSyncAll },
{"rsnAllocationSetupBufferQueue", "(JJI)V", (void*)nAllocationSetupBufferQueue },
diff --git a/services/Android.bp b/services/Android.bp
index ede4c3e..5afed6c 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -61,6 +61,7 @@
"services.devicepolicy",
"services.midi",
"services.net",
+ "services.people",
"services.print",
"services.restrictions",
"services.startop",
@@ -112,7 +113,7 @@
srcs: [":services-sources"],
installable: false,
// TODO: remove the --hide options below
- args: " --show-single-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES,process=android.annotation.SystemApi.Process.SYSTEM_SERVER\\)" +
+ args: " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES,process=android.annotation.SystemApi.Process.SYSTEM_SERVER\\)" +
" --hide-annotation android.annotation.Hide" +
" --hide-package com.google.android.startop.iorap" +
" --hide ReferencesHidden" +
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 2c8bc22..95cd8fc 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -42,8 +42,6 @@
import android.app.assist.AssistStructure;
import android.app.assist.AssistStructure.AutofillOverlay;
import android.app.assist.AssistStructure.ViewNode;
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -72,6 +70,7 @@
import android.service.autofill.FillContext;
import android.service.autofill.FillRequest;
import android.service.autofill.FillResponse;
+import android.service.autofill.InlinePresentation;
import android.service.autofill.InternalSanitizer;
import android.service.autofill.InternalValidator;
import android.service.autofill.SaveInfo;
@@ -83,7 +82,6 @@
import android.util.ArraySet;
import android.util.LocalLog;
import android.util.Log;
-import android.util.Size;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
@@ -646,8 +644,10 @@
@Override
public void onInlineSuggestionsUnsupported() throws RemoteException {
- Log.i(TAG, "inline suggestions request unsupported, "
- + "falling back to regular autofill");
+ if (sDebug) {
+ Log.d(TAG, "inline suggestions request unsupported, "
+ + "falling back to regular autofill");
+ }
final Session session = mSession.get();
if (session != null) {
synchronized (session.mLock) {
@@ -660,8 +660,9 @@
@Override
public void onInlineSuggestionsRequest(InlineSuggestionsRequest request,
IInlineSuggestionsResponseCallback callback) throws RemoteException {
- Log.i(TAG, "onInlineSuggestionsRequest() received: "
- + request);
+ if (sDebug) {
+ Log.d(TAG, "onInlineSuggestionsRequest() received: " + request);
+ }
final Session session = mSession.get();
if (session != null) {
synchronized (session.mLock) {
@@ -2634,9 +2635,8 @@
return;
}
- final List<Slice> inlineSuggestionSlices = response.getInlineSuggestionSlices();
- if (inlineSuggestionSlices != null) {
- if (requestShowInlineSuggestions(inlineSuggestionSlices, response)) {
+ if (response.supportsInlineSuggestions()) {
+ if (requestShowInlineSuggestions(response)) {
//TODO(b/137800469): Add logging instead of bypassing below logic.
return;
}
@@ -2677,10 +2677,22 @@
/**
* Returns whether we made a request to show inline suggestions.
*/
- private boolean requestShowInlineSuggestions(List<Slice> inlineSuggestionSlices,
- FillResponse response) {
- final IInlineSuggestionsResponseCallback inlineContentCallback =
- getInlineSuggestionsResponseCallback();
+ private boolean requestShowInlineSuggestions(FillResponse response) {
+ IInlineSuggestionsResponseCallback inlineContentCallback = null;
+ synchronized (mLock) {
+ if (mInlineSuggestionsResponseCallbackFuture != null) {
+ try {
+ inlineContentCallback = mInlineSuggestionsResponseCallbackFuture.get(
+ INLINE_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (TimeoutException e) {
+ Log.w(TAG, "Exception getting inline suggestions callback in time: " + e);
+ } catch (CancellationException e) {
+ Log.w(TAG, "Inline suggestions callback cancelled");
+ } catch (InterruptedException | ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
if (inlineContentCallback == null) {
Log.w(TAG, "Session input method callback is not set yet");
@@ -2693,54 +2705,20 @@
return false;
}
+ final int size = datasets.size();
final ArrayList<InlineSuggestion> inlineSuggestions = new ArrayList<>();
- final int slicesSize = inlineSuggestionSlices.size();
- if (datasets.size() < slicesSize) {
- Log.w(TAG, "Too many slices provided, not enough corresponding datasets");
- return false;
- }
- for (int sliceIndex = 0; sliceIndex < slicesSize; sliceIndex++) {
- Log.i(TAG, "Reading slice-" + sliceIndex + " at requestshowinlinesuggestions");
- final Slice inlineSuggestionSlice = inlineSuggestionSlices.get(sliceIndex);
- final List<SliceItem> sliceItems = inlineSuggestionSlice.getItems();
-
- final int itemsSize = sliceItems.size();
- int minWidth = -1;
- int maxWidth = -1;
- int minHeight = -1;
- int maxHeight = -1;
- for (int itemIndex = 0; itemIndex < itemsSize; itemIndex++) {
- final SliceItem item = sliceItems.get(itemIndex);
- final String subtype = item.getSubType();
- switch (item.getSubType()) {
- case "SUBTYPE_MIN_WIDTH":
- minWidth = item.getInt();
- break;
- case "SUBTYPE_MAX_WIDTH":
- maxWidth = item.getInt();
- break;
- case "SUBTYPE_MIN_HEIGHT":
- minHeight = item.getInt();
- break;
- case "SUBTYPE_MAX_HEIGHT":
- maxHeight = item.getInt();
- break;
- default:
- Log.i(TAG, "unrecognized inline suggestions subtype: " + subtype);
- }
+ for (int index = 0; index < size; index++) {
+ final Dataset dataset = datasets.get(index);
+ //TODO(b/146453536): Use the proper presentation/spec for currently focused view.
+ final InlinePresentation inlinePresentation = dataset.getFieldInlinePresentation(0);
+ if (inlinePresentation == null) {
+ if (sDebug) Log.d(TAG, "Missing InlinePresentation on dataset=" + dataset);
+ continue;
}
-
- if (minWidth < 0 || maxWidth < 0 || minHeight < 0 || maxHeight < 0) {
- Log.w(TAG, "missing inline suggestion requirements");
- return false;
- }
-
- final InlinePresentationSpec spec = new InlinePresentationSpec.Builder(
- new Size(minWidth, minHeight), new Size(maxWidth, maxHeight)).build();
+ final InlinePresentationSpec spec = inlinePresentation.getInlinePresentationSpec();
final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
spec, InlineSuggestionInfo.SOURCE_AUTOFILL, new String[] { "" });
- final Dataset dataset = datasets.get(sliceIndex);
inlineSuggestions.add(new InlineSuggestion(inlineSuggestionInfo,
new IInlineContentProvider.Stub() {
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 3651a41..3bce322 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -16,8 +16,6 @@
package com.android.server.backup;
-import static com.android.internal.util.Preconditions.checkNotNull;
-
import static java.util.Collections.emptySet;
import android.Manifest;
@@ -68,6 +66,7 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
/**
@@ -124,7 +123,7 @@
static BackupManagerService sInstance;
static BackupManagerService getInstance() {
- return checkNotNull(sInstance);
+ return Objects.requireNonNull(sInstance);
}
private final Context mContext;
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
index 92c2ee4..c9b09e3 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
@@ -21,7 +21,6 @@
import android.os.SELinux;
import android.util.Slog;
-import com.android.internal.util.Preconditions;
import com.android.server.backup.fullbackup.AppMetadataBackupWriter;
import com.android.server.backup.remote.ServiceBackupCallback;
import com.android.server.backup.utils.FullBackupUtils;
@@ -33,6 +32,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
+import java.util.Objects;
/**
* Used by BackupManagerService to perform adb backup for key-value packages. At the moment this
@@ -87,7 +87,7 @@
pkg + BACKUP_KEY_VALUE_NEW_STATE_FILENAME_SUFFIX);
mManifestFile = new File(mDataDir, BACKUP_MANIFEST_FILENAME);
- mAgentTimeoutParameters = Preconditions.checkNotNull(
+ mAgentTimeoutParameters = Objects.requireNonNull(
backupManagerService.getAgentTimeoutParameters(),
"Timeout parameters cannot be null");
}
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 2799f12..064cd06 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -18,7 +18,6 @@
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND;
-import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
@@ -159,6 +158,7 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Queue;
import java.util.Random;
import java.util.Set;
@@ -518,7 +518,7 @@
File dataDir,
TransportManager transportManager) {
mUserId = userId;
- mContext = checkNotNull(context, "context cannot be null");
+ mContext = Objects.requireNonNull(context, "context cannot be null");
mPackageManager = context.getPackageManager();
mPackageManagerBinder = AppGlobals.getPackageManager();
mActivityManager = ActivityManager.getService();
@@ -528,14 +528,14 @@
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mStorageManager = IStorageManager.Stub.asInterface(ServiceManager.getService("mount"));
- checkNotNull(parent, "parent cannot be null");
+ Objects.requireNonNull(parent, "parent cannot be null");
mBackupManagerBinder = BackupManagerService.asInterface(parent.asBinder());
mAgentTimeoutParameters = new
BackupAgentTimeoutParameters(Handler.getMain(), mContext.getContentResolver());
mAgentTimeoutParameters.start();
- checkNotNull(userBackupThread, "userBackupThread cannot be null");
+ Objects.requireNonNull(userBackupThread, "userBackupThread cannot be null");
mBackupHandler = new BackupHandler(this, userBackupThread);
// Set up our bookkeeping
@@ -551,7 +551,7 @@
mSetupObserver,
mUserId);
- mBaseStateDir = checkNotNull(baseStateDir, "baseStateDir cannot be null");
+ mBaseStateDir = Objects.requireNonNull(baseStateDir, "baseStateDir cannot be null");
// TODO (b/120424138): Remove once the system user is migrated to use the per-user CE
// directory. Per-user CE directories are managed by vold.
if (userId == UserHandle.USER_SYSTEM) {
@@ -563,7 +563,7 @@
// TODO (b/120424138): The system user currently uses the cache which is managed by init.rc
// Initialization and restorecon is managed by vold for per-user CE directories.
- mDataDir = checkNotNull(dataDir, "dataDir cannot be null");
+ mDataDir = Objects.requireNonNull(dataDir, "dataDir cannot be null");
mBackupPasswordManager = new BackupPasswordManager(mContext, mBaseStateDir, mRng);
// Receivers for scheduled backups and transport initialization operations.
@@ -625,7 +625,7 @@
addPackageParticipantsLocked(null);
}
- mTransportManager = checkNotNull(transportManager, "transportManager cannot be null");
+ mTransportManager = Objects.requireNonNull(transportManager, "transportManager cannot be null");
mTransportManager.setOnTransportRegisteredListener(this::onTransportRegistered);
mRegisterTransportsRequestedTime = SystemClock.elapsedRealtime();
mBackupHandler.postDelayed(
@@ -3047,9 +3047,9 @@
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BACKUP, "updateTransportAttributes");
- Preconditions.checkNotNull(transportComponent, "transportComponent can't be null");
- Preconditions.checkNotNull(name, "name can't be null");
- Preconditions.checkNotNull(
+ Objects.requireNonNull(transportComponent, "transportComponent can't be null");
+ Objects.requireNonNull(name, "name can't be null");
+ Objects.requireNonNull(
currentDestinationString, "currentDestinationString can't be null");
Preconditions.checkArgument(
(dataManagementIntent == null) == (dataManagementLabel == null),
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
index 7ea1892..846c6a2 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
@@ -36,7 +36,6 @@
import android.os.RemoteException;
import android.util.Slog;
-import com.android.internal.util.Preconditions;
import com.android.server.AppWidgetBackupBridge;
import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupRestoreTask;
@@ -47,6 +46,7 @@
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
+import java.util.Objects;
/**
* Core logic for performing one package's full backup, gathering the tarball from the application
@@ -201,7 +201,7 @@
mOpToken = opToken;
mTransportFlags = transportFlags;
mAgentTimeoutParameters =
- Preconditions.checkNotNull(
+ Objects.requireNonNull(
backupManagerService.getAgentTimeoutParameters(),
"Timeout parameters cannot be null");
}
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
index e142537..aaf1f0a 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
@@ -32,13 +32,13 @@
import android.util.Slog;
import com.android.internal.backup.IObbBackupService;
-import com.android.internal.util.Preconditions;
import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.utils.FullBackupUtils;
import java.io.IOException;
import java.io.OutputStream;
+import java.util.Objects;
/**
* Full backup/restore to a file/socket.
@@ -52,7 +52,7 @@
public FullBackupObbConnection(UserBackupManagerService backupManagerService) {
this.backupManagerService = backupManagerService;
mService = null;
- mAgentTimeoutParameters = Preconditions.checkNotNull(
+ mAgentTimeoutParameters = Objects.requireNonNull(
backupManagerService.getAgentTimeoutParameters(),
"Timeout parameters cannot be null");
}
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index 18c38dc..f7f0138 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -42,7 +42,6 @@
import android.util.Slog;
import com.android.internal.backup.IBackupTransport;
-import com.android.internal.util.Preconditions;
import com.android.server.EventLogTags;
import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupRestoreTask;
@@ -62,6 +61,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
@@ -171,7 +171,7 @@
mUserInitiated = userInitiated;
mCurrentOpToken = backupManagerService.generateRandomIntegerToken();
mBackupRunnerOpToken = backupManagerService.generateRandomIntegerToken();
- mAgentTimeoutParameters = Preconditions.checkNotNull(
+ mAgentTimeoutParameters = Objects.requireNonNull(
backupManagerService.getAgentTimeoutParameters(),
"Timeout parameters cannot be null");
mUserId = backupManagerService.getUserId();
diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
index 8c48b84..eb62620 100644
--- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java
+++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
@@ -31,8 +31,8 @@
import android.util.Pair;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.backup.IBackupTransport;
-import com.android.internal.util.Preconditions;
import com.android.server.EventLogTags;
import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupRestoreTask;
@@ -58,6 +58,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
/**
* Asynchronous backup/restore handler thread.
@@ -91,14 +92,16 @@
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
private final HandlerThread mBackupThread;
- private volatile boolean mIsStopping = false;
+
+ @VisibleForTesting
+ volatile boolean mIsStopping = false;
public BackupHandler(
UserBackupManagerService backupManagerService, HandlerThread backupThread) {
super(backupThread.getLooper());
mBackupThread = backupThread;
this.backupManagerService = backupManagerService;
- mAgentTimeoutParameters = Preconditions.checkNotNull(
+ mAgentTimeoutParameters = Objects.requireNonNull(
backupManagerService.getAgentTimeoutParameters(),
"Timeout parameters cannot be null");
}
@@ -113,6 +116,24 @@
sendMessage(obtainMessage(BackupHandler.MSG_STOP));
}
+ @Override
+ public void dispatchMessage(Message message) {
+ try {
+ dispatchMessageInternal(message);
+ } catch (Exception e) {
+ // If the backup service is stopping, we'll suppress all exceptions to avoid crashes
+ // caused by code still running after the current user has become unavailable.
+ if (!mIsStopping) {
+ throw e;
+ }
+ }
+ }
+
+ @VisibleForTesting
+ void dispatchMessageInternal(Message message) {
+ super.dispatchMessage(message);
+ }
+
public void handleMessage(Message msg) {
if (msg.what == MSG_STOP) {
Slog.v(TAG, "Stopping backup handler");
@@ -414,7 +435,7 @@
try {
params.observer.onTimeout();
} catch (RemoteException e) {
- /* don't care if the app has gone away */
+ /* don't care if the app has gone away */
}
}
} else {
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
index 9492118..bda0e3b 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
@@ -310,7 +310,7 @@
mUserInitiated = userInitiated;
mNonIncremental = nonIncremental;
mAgentTimeoutParameters =
- Preconditions.checkNotNull(
+ Objects.requireNonNull(
backupManagerService.getAgentTimeoutParameters(),
"Timeout parameters cannot be null");
mStateDirectory = new File(backupManagerService.getBaseStateDir(), transportDirName);
diff --git a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
index e4890e0..376b618 100644
--- a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
+++ b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
@@ -21,11 +21,11 @@
import android.util.Slog;
-import com.android.internal.util.Preconditions;
import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.UserBackupManagerService;
+import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -45,7 +45,7 @@
this.backupManagerService = backupManagerService;
mLatch = new CountDownLatch(1);
mCurrentOpToken = currentOpToken;
- mAgentTimeoutParameters = Preconditions.checkNotNull(
+ mAgentTimeoutParameters = Objects.requireNonNull(
backupManagerService.getAgentTimeoutParameters(),
"Timeout parameters cannot be null");
}
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index eba9e4a..82bed3b 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -42,7 +42,6 @@
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupRestoreTask;
@@ -62,6 +61,7 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Objects;
/**
* Full restore engine, used by both adb restore and transport-based full restore.
@@ -142,7 +142,7 @@
mOnlyPackage = onlyPackage;
mAllowApks = allowApks;
mBuffer = new byte[32 * 1024];
- mAgentTimeoutParameters = Preconditions.checkNotNull(
+ mAgentTimeoutParameters = Objects.requireNonNull(
backupManagerService.getAgentTimeoutParameters(),
"Timeout parameters cannot be null");
mIsAdbRestore = isAdbRestore;
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index de6a080..16484df9f 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -54,7 +54,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.backup.IBackupTransport;
-import com.android.internal.util.Preconditions;
import com.android.server.AppWidgetBackupBridge;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
@@ -79,6 +78,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
public class PerformUnifiedRestoreTask implements BackupRestoreTask {
@@ -208,7 +208,7 @@
mFinished = false;
mDidLaunch = false;
mListener = listener;
- mAgentTimeoutParameters = Preconditions.checkNotNull(
+ mAgentTimeoutParameters = Objects.requireNonNull(
backupManagerService.getAgentTimeoutParameters(),
"Timeout parameters cannot be null");
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 5b98f06..a1f57cb 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -94,8 +94,8 @@
"android.hardware.light-V2.0-java",
"android.hardware.power-V1.0-java",
"android.hardware.tv.cec-V1.0-java",
+ "android.hardware.vibrator-java",
"app-compat-annotations",
- "vintf-vibrator-java",
"framework-tethering",
],
@@ -117,6 +117,7 @@
"android.hardware.oemlock-V1.0-java",
"android.hardware.configstore-V1.0-java",
"android.hardware.contexthub-V1.0-java",
+ "android.hardware.rebootescrow-java",
"android.hardware.soundtrigger-V2.3-java",
"android.hidl.manager-V1.2-java",
"dnsresolver_aidl_interface-V2-java",
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 40a7dfc..312dd46 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -319,6 +319,12 @@
int deviceOwnerUserId, String deviceOwner, SparseArray<String> profileOwners);
/**
+ * Called by DevicePolicyManagerService to set the package names protected by the device
+ * owner.
+ */
+ public abstract void setDeviceOwnerProtectedPackages(List<String> packageNames);
+
+ /**
* Returns {@code true} if a given package can't be wiped. Otherwise, returns {@code false}.
*/
public abstract boolean isPackageDataProtected(int userId, String packageName);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index deac1e3..4515be8 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2023,6 +2023,15 @@
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
}
+ private void enforceAirplaneModePermission() {
+ enforceAnyPermissionOf(
+ android.Manifest.permission.NETWORK_AIRPLANE_MODE,
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD,
+ android.Manifest.permission.NETWORK_STACK,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+ }
+
private boolean checkNetworkStackPermission() {
return checkAnyPermissionOf(
android.Manifest.permission.NETWORK_STACK,
@@ -3020,25 +3029,9 @@
if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
if (VDBG) log("NetworkFactory connected");
// Finish setting up the full connection
- mNetworkFactoryInfos.get(msg.replyTo).asyncChannel.sendMessage(
- AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
- // A network factory has connected. Send it all current NetworkRequests.
- for (NetworkRequestInfo nri : mNetworkRequests.values()) {
- if (nri.request.isListen()) continue;
- ensureRunningOnConnectivityServiceThread();
- NetworkAgentInfo nai = nri.mSatisfier;
- final int score;
- final int serial;
- if (nai != null) {
- score = nai.getCurrentScore();
- serial = nai.factorySerialNumber;
- } else {
- score = 0;
- serial = NetworkFactory.SerialNumber.NONE;
- }
- ac.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score, serial,
- nri.request);
- }
+ NetworkFactoryInfo nfi = mNetworkFactoryInfos.get(msg.replyTo);
+ nfi.completeConnection();
+ sendAllRequestsToFactory(nfi);
} else {
loge("Error connecting NetworkFactory");
mNetworkFactoryInfos.remove(msg.obj);
@@ -3421,8 +3414,7 @@
}
for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
- nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_CANCEL_REQUEST,
- nri.request);
+ nfi.cancelRequest(nri.request);
}
} else {
// listens don't have a singular affectedNetwork. Check all networks to see
@@ -4734,7 +4726,7 @@
@Override
public void setAirplaneMode(boolean enable) {
- enforceNetworkStackSettingsOrSetup();
+ enforceAirplaneModePermission();
final long ident = Binder.clearCallingIdentity();
try {
final ContentResolver cr = mContext.getContentResolver();
@@ -4918,16 +4910,33 @@
private static class NetworkFactoryInfo {
public final String name;
public final Messenger messenger;
- public final AsyncChannel asyncChannel;
+ private final AsyncChannel mAsyncChannel;
public final int factorySerialNumber;
NetworkFactoryInfo(String name, Messenger messenger, AsyncChannel asyncChannel,
int factorySerialNumber) {
this.name = name;
this.messenger = messenger;
- this.asyncChannel = asyncChannel;
+ this.mAsyncChannel = asyncChannel;
this.factorySerialNumber = factorySerialNumber;
}
+
+ void requestNetwork(NetworkRequest request, int score, int servingSerialNumber) {
+ mAsyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score,
+ servingSerialNumber, request);
+ }
+
+ void cancelRequest(NetworkRequest request) {
+ mAsyncChannel.sendMessage(android.net.NetworkFactory.CMD_CANCEL_REQUEST, request);
+ }
+
+ void connect(Context context, Handler handler) {
+ mAsyncChannel.connect(context, handler, messenger);
+ }
+
+ void completeConnection() {
+ mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
+ }
}
private void ensureNetworkRequestHasType(NetworkRequest request) {
@@ -5316,7 +5325,7 @@
private void handleRegisterNetworkFactory(NetworkFactoryInfo nfi) {
if (DBG) log("Got NetworkFactory Messenger for " + nfi.name);
mNetworkFactoryInfos.put(nfi.messenger, nfi);
- nfi.asyncChannel.connect(mContext, mTrackerHandler, nfi.messenger);
+ nfi.connect(mContext, mTrackerHandler);
}
@Override
@@ -5952,8 +5961,26 @@
log("sending new Min Network Score(" + score + "): " + networkRequest.toString());
}
for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
- nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score,
- serial, networkRequest);
+ nfi.requestNetwork(networkRequest, score, serial);
+ }
+ }
+
+ /** Sends all current NetworkRequests to the specified factory. */
+ private void sendAllRequestsToFactory(NetworkFactoryInfo nfi) {
+ ensureRunningOnConnectivityServiceThread();
+ for (NetworkRequestInfo nri : mNetworkRequests.values()) {
+ if (nri.request.isListen()) continue;
+ NetworkAgentInfo nai = nri.mSatisfier;
+ final int score;
+ final int serial;
+ if (nai != null) {
+ score = nai.getCurrentScore();
+ serial = nai.factorySerialNumber;
+ } else {
+ score = 0;
+ serial = NetworkFactory.SerialNumber.NONE;
+ }
+ nfi.requestNetwork(nri.request, score, serial);
}
}
diff --git a/services/core/java/com/android/server/CountryDetectorService.java b/services/core/java/com/android/server/CountryDetectorService.java
index 861c731..b0132d3 100644
--- a/services/core/java/com/android/server/CountryDetectorService.java
+++ b/services/core/java/com/android/server/CountryDetectorService.java
@@ -24,21 +24,29 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
+import android.text.TextUtils;
import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.util.Slog;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.server.location.ComprehensiveCountryDetector;
+import com.android.server.location.CountryDetectorBase;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
/**
- * This class detects the country that the user is in through {@link ComprehensiveCountryDetector}.
+ * This class detects the country that the user is in. The default country detection is made through
+ * {@link com.android.server.location.ComprehensiveCountryDetector}. It is possible to overlay the
+ * detection algorithm by overlaying the attribute R.string.config_customCountryDetector with the
+ * custom class name to use instead. The custom class must extend
+ * {@link com.android.server.location.CountryDetectorBase}
*
* @hide
*/
@@ -88,7 +96,7 @@
private final HashMap<IBinder, Receiver> mReceivers;
private final Context mContext;
- private ComprehensiveCountryDetector mCountryDetector;
+ private CountryDetectorBase mCountryDetector;
private boolean mSystemReady;
private Handler mHandler;
private CountryListener mLocationBasedDetectorListener;
@@ -184,8 +192,17 @@
});
}
- private void initialize() {
- mCountryDetector = new ComprehensiveCountryDetector(mContext);
+ @VisibleForTesting
+ void initialize() {
+ final String customCountryClass = mContext.getString(R.string.config_customCountryDetector);
+ if (!TextUtils.isEmpty(customCountryClass)) {
+ mCountryDetector = loadCustomCountryDetectorIfAvailable(customCountryClass);
+ }
+
+ if (mCountryDetector == null) {
+ Slog.d(TAG, "Using default country detector");
+ mCountryDetector = new ComprehensiveCountryDetector(mContext);
+ }
mLocationBasedDetectorListener = country -> mHandler.post(() -> notifyReceivers(country));
}
@@ -194,10 +211,32 @@
}
@VisibleForTesting
+ CountryDetectorBase getCountryDetector() {
+ return mCountryDetector;
+ }
+
+ @VisibleForTesting
boolean isSystemReady() {
return mSystemReady;
}
+ private CountryDetectorBase loadCustomCountryDetectorIfAvailable(
+ final String customCountryClass) {
+ CountryDetectorBase customCountryDetector = null;
+
+ Slog.d(TAG, "Using custom country detector class: " + customCountryClass);
+ try {
+ customCountryDetector = Class.forName(customCountryClass).asSubclass(
+ CountryDetectorBase.class).getConstructor(Context.class).newInstance(
+ mContext);
+ } catch (ClassNotFoundException | InstantiationException | IllegalAccessException
+ | NoSuchMethodException | InvocationTargetException e) {
+ Slog.e(TAG, "Could not instantiate the custom country detector class");
+ }
+
+ return customCountryDetector;
+ }
+
@SuppressWarnings("unused")
@Override
protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
@@ -206,9 +245,10 @@
try {
final Printer p = new PrintWriterPrinter(fout);
p.println("CountryDetectorService state:");
+ p.println("Country detector class=" + mCountryDetector.getClass().getName());
p.println(" Number of listeners=" + mReceivers.keySet().size());
if (mCountryDetector == null) {
- p.println(" ComprehensiveCountryDetector not initialized");
+ p.println(" CountryDetector not initialized");
} else {
p.println(" " + mCountryDetector.toString());
}
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index c5f1923..0fc5340 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -3192,6 +3192,8 @@
}
ipw.decreaseIndent();
+ mRequestStatistics.history.dump(ipw);
+
ipw.println("Last Known Locations:");
ipw.increaseIndent();
for (Map.Entry<String, Location> entry : mLastLocation.entrySet()) {
diff --git a/services/core/java/com/android/server/MmsServiceBroker.java b/services/core/java/com/android/server/MmsServiceBroker.java
index 92d1da5..9b1326b 100644
--- a/services/core/java/com/android/server/MmsServiceBroker.java
+++ b/services/core/java/com/android/server/MmsServiceBroker.java
@@ -140,11 +140,6 @@
}
@Override
- public Bundle getCarrierConfigValues(int subId) throws RemoteException {
- return null;
- }
-
- @Override
public Uri importTextMessage(String callingPkg, String address, int type, String text,
long timestampMillis, boolean seen, boolean read) throws RemoteException {
return null;
@@ -373,12 +368,6 @@
}
@Override
- public Bundle getCarrierConfigValues(int subId) throws RemoteException {
- Slog.d(TAG, "getCarrierConfigValues() by " + getCallingPackageName());
- return getServiceGuarded().getCarrierConfigValues(subId);
- }
-
- @Override
public Uri importTextMessage(String callingPkg, String address, int type, String text,
long timestampMillis, boolean seen, boolean read) throws RemoteException {
if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
diff --git a/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java b/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java
index 39be311..d20936c 100644
--- a/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java
@@ -18,6 +18,8 @@
import android.app.AlarmManager;
import android.app.PendingIntent;
+import android.app.timedetector.NetworkTimeSuggestion;
+import android.app.timedetector.TimeDetector;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -34,8 +36,8 @@
import android.os.Message;
import android.os.PowerManager;
import android.os.SystemClock;
+import android.os.TimestampedValue;
import android.provider.Settings;
-import android.telephony.TelephonyManager;
import android.util.Log;
import android.util.NtpTrustedTime;
import android.util.TimeUtils;
@@ -46,21 +48,19 @@
import java.io.PrintWriter;
/**
- * Monitors the network time and updates the system time if it is out of sync
- * and there hasn't been any NITZ update from the carrier recently.
- * If looking up the network time fails for some reason, it tries a few times with a short
- * interval and then resets to checking on longer intervals.
- * <p>
- * If the user enables AUTO_TIME, it will check immediately for the network time, if NITZ wasn't
- * available.
- * </p>
+ * Monitors the network time. If looking up the network time fails for some reason, it tries a few
+ * times with a short interval and then resets to checking on longer intervals.
+ *
+ * <p>When available, the time is always suggested to the {@link
+ * com.android.server.timedetector.TimeDetectorService} where it may be used to set the device
+ * system clock, depending on user settings and what other signals are available.
*/
public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeUpdateService {
private static final String TAG = "NetworkTimeUpdateService";
private static final boolean DBG = false;
- private static final int EVENT_AUTO_TIME_CHANGED = 1;
+ private static final int EVENT_AUTO_TIME_ENABLED = 1;
private static final int EVENT_POLL_NETWORK_TIME = 2;
private static final int EVENT_NETWORK_CHANGED = 3;
@@ -69,20 +69,19 @@
private static final int POLL_REQUEST = 0;
- private static final long NOT_SET = -1;
- private long mNitzTimeSetTime = NOT_SET;
private Network mDefaultNetwork = null;
private final Context mContext;
private final NtpTrustedTime mTime;
private final AlarmManager mAlarmManager;
+ private final TimeDetector mTimeDetector;
private final ConnectivityManager mCM;
private final PendingIntent mPendingPollIntent;
private final PowerManager.WakeLock mWakeLock;
// NTP lookup is done on this thread and handler
private Handler mHandler;
- private SettingsObserver mSettingsObserver;
+ private AutoTimeSettingObserver mAutoTimeSettingObserver;
private NetworkTimeUpdateCallback mNetworkTimeUpdateCallback;
// Normal polling frequency
@@ -91,8 +90,6 @@
private final long mPollingIntervalShorterMs;
// Number of times to try again
private final int mTryAgainTimesMax;
- // If the time difference is greater than this threshold, then update the time.
- private final int mTimeErrorThresholdMs;
// Keeps track of how many quick attempts were made to fetch NTP time.
// During bootup, the network may not have been up yet, or it's taking time for the
// connection to happen.
@@ -102,6 +99,7 @@
mContext = context;
mTime = NtpTrustedTime.getInstance(context);
mAlarmManager = mContext.getSystemService(AlarmManager.class);
+ mTimeDetector = mContext.getSystemService(TimeDetector.class);
mCM = mContext.getSystemService(ConnectivityManager.class);
Intent pollIntent = new Intent(ACTION_POLL, null);
@@ -113,8 +111,6 @@
com.android.internal.R.integer.config_ntpPollingIntervalShorter);
mTryAgainTimesMax = mContext.getResources().getInteger(
com.android.internal.R.integer.config_ntpRetry);
- mTimeErrorThresholdMs = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_ntpThreshold);
mWakeLock = context.getSystemService(PowerManager.class).newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, TAG);
@@ -122,7 +118,6 @@
@Override
public void systemRunning() {
- registerForTelephonyIntents();
registerForAlarms();
HandlerThread thread = new HandlerThread(TAG);
@@ -131,14 +126,9 @@
mNetworkTimeUpdateCallback = new NetworkTimeUpdateCallback();
mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler);
- mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
- mSettingsObserver.observe(mContext);
- }
-
- private void registerForTelephonyIntents() {
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(TelephonyManager.ACTION_NETWORK_SET_TIME);
- mContext.registerReceiver(mNitzReceiver, intentFilter);
+ mAutoTimeSettingObserver = new AutoTimeSettingObserver(mContext, mHandler,
+ EVENT_AUTO_TIME_ENABLED);
+ mAutoTimeSettingObserver.observe();
}
private void registerForAlarms() {
@@ -152,8 +142,7 @@
}
private void onPollNetworkTime(int event) {
- // If Automatic time is not set, don't bother. Similarly, if we don't
- // have any default network, don't bother.
+ // If we don't have any default network, don't bother.
if (mDefaultNetwork == null) return;
mWakeLock.acquire();
try {
@@ -173,10 +162,12 @@
if (mTime.getCacheAge() < mPollingIntervalMs) {
// Obtained fresh fix; schedule next normal update
resetAlarm(mPollingIntervalMs);
- if (isAutomaticTimeRequested()) {
- updateSystemClock(event);
- }
+ // Suggest the time to the time detector. It may choose use it to set the system clock.
+ TimestampedValue<Long> timeSignal = mTime.getCachedNtpTimeSignal();
+ NetworkTimeSuggestion timeSuggestion = new NetworkTimeSuggestion(timeSignal);
+ timeSuggestion.addDebugInfo("Origin: NetworkTimeUpdateServiceImpl. event=" + event);
+ mTimeDetector.suggestNetworkTime(timeSuggestion);
} else {
// No fresh fix; schedule retry
mTryAgainCounter++;
@@ -190,36 +181,6 @@
}
}
- private long getNitzAge() {
- if (mNitzTimeSetTime == NOT_SET) {
- return Long.MAX_VALUE;
- } else {
- return SystemClock.elapsedRealtime() - mNitzTimeSetTime;
- }
- }
-
- /**
- * Consider updating system clock based on current NTP fix, if requested by
- * user, significant enough delta, and we don't have a recent NITZ.
- */
- private void updateSystemClock(int event) {
- final boolean forceUpdate = (event == EVENT_AUTO_TIME_CHANGED);
- if (!forceUpdate) {
- if (getNitzAge() < mPollingIntervalMs) {
- if (DBG) Log.d(TAG, "Ignoring NTP update due to recent NITZ");
- return;
- }
-
- final long skew = Math.abs(mTime.currentTimeMillis() - System.currentTimeMillis());
- if (skew < mTimeErrorThresholdMs) {
- if (DBG) Log.d(TAG, "Ignoring NTP update due to low skew");
- return;
- }
- }
-
- SystemClock.setCurrentTimeMillis(mTime.currentTimeMillis());
- }
-
/**
* Cancel old alarm and starts a new one for the specified interval.
*
@@ -232,27 +193,6 @@
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
}
- /**
- * Checks if the user prefers to automatically set the time.
- */
- private boolean isAutomaticTimeRequested() {
- return Settings.Global.getInt(
- mContext.getContentResolver(), Settings.Global.AUTO_TIME, 0) != 0;
- }
-
- /** Receiver for Nitz time events */
- private BroadcastReceiver mNitzReceiver = new BroadcastReceiver() {
-
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (DBG) Log.d(TAG, "Received " + action);
- if (TelephonyManager.ACTION_NETWORK_SET_TIME.equals(action)) {
- mNitzTimeSetTime = SystemClock.elapsedRealtime();
- }
- }
- };
-
/** Handler to do the network accesses on */
private class MyHandler extends Handler {
@@ -263,7 +203,7 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case EVENT_AUTO_TIME_CHANGED:
+ case EVENT_AUTO_TIME_ENABLED:
case EVENT_POLL_NETWORK_TIME:
case EVENT_NETWORK_CHANGED:
onPollNetworkTime(msg.what);
@@ -287,27 +227,42 @@
}
}
- /** Observer to watch for changes to the AUTO_TIME setting */
- private static class SettingsObserver extends ContentObserver {
+ /**
+ * Observer to watch for changes to the AUTO_TIME setting. It only triggers when the setting
+ * is enabled.
+ */
+ private static class AutoTimeSettingObserver extends ContentObserver {
- private int mMsg;
- private Handler mHandler;
+ private final Context mContext;
+ private final int mMsg;
+ private final Handler mHandler;
- SettingsObserver(Handler handler, int msg) {
+ AutoTimeSettingObserver(Context context, Handler handler, int msg) {
super(handler);
+ mContext = context;
mHandler = handler;
mMsg = msg;
}
- void observe(Context context) {
- ContentResolver resolver = context.getContentResolver();
+ void observe() {
+ ContentResolver resolver = mContext.getContentResolver();
resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME),
false, this);
}
@Override
public void onChange(boolean selfChange) {
- mHandler.obtainMessage(mMsg).sendToTarget();
+ if (isAutomaticTimeEnabled()) {
+ mHandler.obtainMessage(mMsg).sendToTarget();
+ }
+ }
+
+ /**
+ * Checks if the user prefers to automatically set the time.
+ */
+ private boolean isAutomaticTimeEnabled() {
+ ContentResolver resolver = mContext.getContentResolver();
+ return Settings.Global.getInt(resolver, Settings.Global.AUTO_TIME, 0) != 0;
}
}
@@ -319,8 +274,6 @@
pw.print("\nPollingIntervalShorterMs: ");
TimeUtils.formatDuration(mPollingIntervalShorterMs, pw);
pw.println("\nTryAgainTimesMax: " + mTryAgainTimesMax);
- pw.print("TimeErrorThresholdMs: ");
- TimeUtils.formatDuration(mTimeErrorThresholdMs, pw);
pw.println("\nTryAgainCounter: " + mTryAgainCounter);
pw.println("NTP cache age: " + mTime.getCacheAge());
pw.println("NTP cache certainty: " + mTime.getCacheCertainty());
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index c474f47..3d455ee 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -61,6 +61,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.IPackageMoveObserver;
import android.content.pm.PackageManager;
@@ -74,8 +75,6 @@
import android.os.Binder;
import android.os.DropBoxManager;
import android.os.Environment;
-import android.os.Environment.UserEnvironment;
-import android.os.FileUtils;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -183,6 +182,8 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
@@ -379,6 +380,14 @@
@GuardedBy("mAppFuseLock")
private AppFuseBridge mAppFuseBridge = null;
+ /** Matches known application dir paths. The first group contains the generic part of the path,
+ * the second group contains the user id (or null if it's a public volume without users), the
+ * third group contains the package name, and the fourth group the remainder of the path.
+ */
+ public static final Pattern KNOWN_APP_DIR_PATHS = Pattern.compile(
+ "(?i)(^/storage/[^/]+/(?:([0-9]+)/)?Android/(?:data|media|obb|sandbox)/)([^/]+)(/.*)?");
+
+
private VolumeInfo findVolumeByIdOrThrow(String id) {
synchronized (mLock) {
final VolumeInfo vol = mVolumes.get(id);
@@ -1110,7 +1119,7 @@
// Push down current secure keyguard status so that we ignore malicious
// USB devices while locked.
mSecureKeyguardShowing = isShowing
- && mContext.getSystemService(KeyguardManager.class).isDeviceSecure();
+ && mContext.getSystemService(KeyguardManager.class).isDeviceSecure(mCurrentUserId);
try {
mVold.onSecureKeyguardStateChanged(mSecureKeyguardShowing);
} catch (Exception e) {
@@ -3135,7 +3144,6 @@
public void mkdirs(String callingPkg, String appPath) {
final int callingUid = Binder.getCallingUid();
final int userId = UserHandle.getUserId(callingUid);
- final UserEnvironment userEnv = new UserEnvironment(userId);
final String propertyName = "sys.user." + userId + ".ce_available";
// Ignore requests to create directories while storage is locked
@@ -3161,25 +3169,36 @@
throw new IllegalStateException("Failed to resolve " + appPath + ": " + e);
}
- // Try translating the app path into a vold path, but require that it
- // belong to the calling package.
- if (FileUtils.contains(userEnv.buildExternalStorageAppDataDirs(callingPkg), appFile) ||
- FileUtils.contains(userEnv.buildExternalStorageAppObbDirs(callingPkg), appFile) ||
- FileUtils.contains(userEnv.buildExternalStorageAppMediaDirs(callingPkg), appFile)) {
- appPath = appFile.getAbsolutePath();
- if (!appPath.endsWith("/")) {
- appPath = appPath + "/";
+ appPath = appFile.getAbsolutePath();
+ if (!appPath.endsWith("/")) {
+ appPath = appPath + "/";
+ }
+ // Ensure that the path we're asked to create is a known application directory
+ // path.
+ final Matcher matcher = KNOWN_APP_DIR_PATHS.matcher(appPath);
+ if (matcher.matches()) {
+ // And that the package dir matches the calling package
+ if (!matcher.group(3).equals(callingPkg)) {
+ throw new SecurityException("Invalid mkdirs path: " + appFile
+ + " does not contain calling package " + callingPkg);
}
-
+ // And that the user id part of the path (if any) matches the calling user id,
+ // or if for a public volume (no user id), the user matches the current user
+ if ((matcher.group(2) != null && !matcher.group(2).equals(Integer.toString(userId)))
+ || (matcher.group(2) == null && userId != mCurrentUserId)) {
+ throw new SecurityException("Invalid mkdirs path: " + appFile
+ + " does not match calling user id " + userId);
+ }
try {
- mVold.mkdirs(appPath);
- return;
- } catch (Exception e) {
+ mVold.setupAppDir(appPath, matcher.group(1), callingUid);
+ } catch (RemoteException e) {
throw new IllegalStateException("Failed to prepare " + appPath + ": " + e);
}
- }
- throw new SecurityException("Invalid mkdirs path: " + appFile);
+ return;
+ }
+ throw new SecurityException("Invalid mkdirs path: " + appFile
+ + " is not a known app path.");
}
@Override
@@ -3898,8 +3917,22 @@
// Otherwise we're willing to give out sandboxed or non-sandboxed if
// they hold the runtime permission
- final boolean hasLegacy = mIAppOpsService.checkOperation(OP_LEGACY_STORAGE,
+ boolean hasLegacy = mIAppOpsService.checkOperation(OP_LEGACY_STORAGE,
uid, packageName) == MODE_ALLOWED;
+
+ // Hack(b/147137425): we have to honor hasRequestedLegacyExternalStorage for a short
+ // while to enable 2 cases.
+ // 1) Apps that want to be in scoped storage in R, but want to opt out in Q devices,
+ // because they want to use raw file paths, would fail until fuse is enabled by default.
+ // 2) Test apps that target current sdk will fail. They would fail even after fuse is
+ // enabled, but we are fixing it with b/142395442. We are not planning to enable
+ // fuse by default until b/142395442 is fixed.
+ if (!hasLegacy && !mIsFuseEnabled) {
+ ApplicationInfo ai = mIPackageManager.getApplicationInfo(packageName,
+ 0, UserHandle.getUserId(uid));
+ hasLegacy = ai.hasRequestedLegacyExternalStorage();
+ }
+
if (hasLegacy && hasWrite) {
return Zygote.MOUNT_EXTERNAL_WRITE;
} else if (hasLegacy && hasRead) {
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 783715c..3e6ccb5 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -23,6 +23,7 @@
import static java.util.Arrays.copyOf;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AppOpsManager;
@@ -41,6 +42,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.telephony.Annotation;
+import android.telephony.Annotation.ApnType;
import android.telephony.Annotation.DataFailureCause;
import android.telephony.Annotation.RadioPowerState;
import android.telephony.Annotation.SrvccState;
@@ -49,6 +51,13 @@
import android.telephony.CellIdentity;
import android.telephony.CellInfo;
import android.telephony.CellLocation;
+import android.telephony.CellSignalStrength;
+import android.telephony.CellSignalStrengthCdma;
+import android.telephony.CellSignalStrengthGsm;
+import android.telephony.CellSignalStrengthLte;
+import android.telephony.CellSignalStrengthNr;
+import android.telephony.CellSignalStrengthTdscdma;
+import android.telephony.CellSignalStrengthWcdma;
import android.telephony.DataFailCause;
import android.telephony.DisconnectCause;
import android.telephony.LocationAccessPolicy;
@@ -74,7 +83,6 @@
import com.android.internal.telephony.IOnSubscriptionsChangedListener;
import com.android.internal.telephony.IPhoneStateListener;
import com.android.internal.telephony.ITelephonyRegistry;
-import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.TelephonyPermissions;
import com.android.internal.util.ArrayUtils;
@@ -261,8 +269,8 @@
private final LocalLog mListenLog = new LocalLog(100);
// Per-phoneMap of APN Type to DataConnectionState
- private List<Map<String, PreciseDataConnectionState>> mPreciseDataConnectionStates =
- new ArrayList<Map<String, PreciseDataConnectionState>>();
+ private List<Map<Integer, PreciseDataConnectionState>> mPreciseDataConnectionStates =
+ new ArrayList<Map<Integer, PreciseDataConnectionState>>();
// Nothing here yet, but putting it here in case we want to add more in the future.
static final int ENFORCE_COARSE_LOCATION_PERMISSION_MASK = 0;
@@ -274,11 +282,12 @@
static final int ENFORCE_PHONE_STATE_PERMISSION_MASK =
PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR
| PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
- | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST;
+ | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST
+ | PhoneStateListener.LISTEN_REGISTRATION_FAILURE;
static final int PRECISE_PHONE_STATE_PERMISSION_MASK =
- PhoneStateListener.LISTEN_PRECISE_CALL_STATE |
- PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE;
+ PhoneStateListener.LISTEN_PRECISE_CALL_STATE
+ | PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE;
static final int READ_ACTIVE_EMERGENCY_SESSION_PERMISSION_MASK =
PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL
@@ -402,7 +411,11 @@
mVoiceActivationState = copyOf(mVoiceActivationState, mNumPhones);
mDataActivationState = copyOf(mDataActivationState, mNumPhones);
mUserMobileDataState = copyOf(mUserMobileDataState, mNumPhones);
- mSignalStrength = copyOf(mSignalStrength, mNumPhones);
+ if (mSignalStrength != null) {
+ mSignalStrength = copyOf(mSignalStrength, mNumPhones);
+ } else {
+ mSignalStrength = new SignalStrength[mNumPhones];
+ }
mMessageWaiting = copyOf(mMessageWaiting, mNumPhones);
mCallForwarding = copyOf(mCallForwarding, mNumPhones);
mCellIdentity = copyOf(mCellIdentity, mNumPhones);
@@ -436,7 +449,7 @@
mDataActivationState[i] = TelephonyManager.SIM_ACTIVATION_STATE_UNKNOWN;
mCallIncomingNumber[i] = "";
mServiceState[i] = new ServiceState();
- mSignalStrength[i] = new SignalStrength();
+ mSignalStrength[i] = null;
mUserMobileDataState[i] = false;
mMessageWaiting[i] = false;
mCallForwarding[i] = false;
@@ -454,7 +467,7 @@
mRingingCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
mForegroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
mBackgroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
- mPreciseDataConnectionStates.add(new HashMap<String, PreciseDataConnectionState>());
+ mPreciseDataConnectionStates.add(new HashMap<Integer, PreciseDataConnectionState>());
}
}
@@ -520,7 +533,7 @@
mDataActivationState[i] = TelephonyManager.SIM_ACTIVATION_STATE_UNKNOWN;
mCallIncomingNumber[i] = "";
mServiceState[i] = new ServiceState();
- mSignalStrength[i] = new SignalStrength();
+ mSignalStrength[i] = null;
mUserMobileDataState[i] = false;
mMessageWaiting[i] = false;
mCallForwarding[i] = false;
@@ -538,7 +551,7 @@
mRingingCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
mForegroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
mBackgroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
- mPreciseDataConnectionStates.add(new HashMap<String, PreciseDataConnectionState>());
+ mPreciseDataConnectionStates.add(new HashMap<Integer, PreciseDataConnectionState>());
}
mAppOps = mContext.getSystemService(AppOpsManager.class);
@@ -797,10 +810,12 @@
}
if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) {
try {
- int gsmSignalStrength = mSignalStrength[phoneId]
- .getGsmSignalStrength();
- r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1
- : gsmSignalStrength));
+ if (mSignalStrength[phoneId] != null) {
+ int gsmSignalStrength = mSignalStrength[phoneId]
+ .getGsmSignalStrength();
+ r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1
+ : gsmSignalStrength));
+ }
} catch (RemoteException ex) {
remove(r.binder);
}
@@ -857,7 +872,9 @@
}
if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) {
try {
- r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]);
+ if (mSignalStrength[phoneId] != null) {
+ r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]);
+ }
} catch (RemoteException ex) {
remove(r.binder);
}
@@ -1290,7 +1307,7 @@
public void notifyCarrierNetworkChange(boolean active) {
// only CarrierService with carrier privilege rule should have the permission
int[] subIds = Arrays.stream(SubscriptionManager.from(mContext)
- .getActiveSubscriptionIdList(false))
+ .getActiveAndHiddenSubscriptionIdList())
.filter(i -> TelephonyPermissions.checkCarrierPrivilegeForSubId(mContext,
i)).toArray();
if (ArrayUtils.isEmpty(subIds)) {
@@ -1479,11 +1496,12 @@
*
* @param phoneId the phoneId carrying the data connection
* @param subId the subscriptionId for the data connection
- * @param apnType the APN type that triggered a change in the data connection
+ * @param apnType the apn type bitmask, defined with {@code ApnSetting#TYPE_*} flags.
* @param preciseState a PreciseDataConnectionState that has info about the data connection
*/
+ @Override
public void notifyDataConnectionForSubscriber(
- int phoneId, int subId, String apnType, PreciseDataConnectionState preciseState) {
+ int phoneId, int subId, @ApnType int apnType, PreciseDataConnectionState preciseState) {
if (!checkNotifyPermission("notifyDataConnection()" )) {
return;
}
@@ -1509,7 +1527,7 @@
synchronized (mRecords) {
if (validatePhoneId(phoneId)) {
// We only call the callback when the change is for default APN type.
- if (PhoneConstants.APN_TYPE_DEFAULT.equals(apnType)
+ if ((ApnSetting.TYPE_DEFAULT & apnType) != 0
&& (mDataConnectionState[phoneId] != state
|| mDataConnectionNetworkType[phoneId] != networkType)) {
String str = "onDataConnectionStateChanged("
@@ -1578,7 +1596,7 @@
loge("This function should not be invoked");
}
- private void notifyDataConnectionFailedForSubscriber(int phoneId, int subId, String apnType) {
+ private void notifyDataConnectionFailedForSubscriber(int phoneId, int subId, int apnType) {
if (!checkNotifyPermission("notifyDataConnectionFailed()")) {
return;
}
@@ -1593,7 +1611,7 @@
new PreciseDataConnectionState(
TelephonyManager.DATA_UNKNOWN,
TelephonyManager.NETWORK_TYPE_UNKNOWN,
- ApnSetting.getApnTypesBitmaskFromString(apnType), null, null,
+ apnType, null, null,
DataFailCause.NONE, null));
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
@@ -1762,7 +1780,8 @@
}
}
- public void notifyPreciseDataConnectionFailed(int phoneId, int subId, String apnType,
+ @Override
+ public void notifyPreciseDataConnectionFailed(int phoneId, int subId, @ApnType int apnType,
String apn, @DataFailureCause int failCause) {
if (!checkNotifyPermission("notifyPreciseDataConnectionFailed()")) {
return;
@@ -1778,7 +1797,7 @@
new PreciseDataConnectionState(
TelephonyManager.DATA_UNKNOWN,
TelephonyManager.NETWORK_TYPE_UNKNOWN,
- ApnSetting.getApnTypesBitmaskFromString(apnType), null, null,
+ apnType, null, null,
failCause, null));
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
@@ -2051,6 +2070,40 @@
}
@Override
+ public void notifyRegistrationFailed(int phoneId, int subId, @NonNull CellIdentity cellIdentity,
+ @NonNull String chosenPlmn, int domain, int causeCode, int additionalCauseCode) {
+ if (!checkNotifyPermission("notifyRegistrationFailed()")) {
+ return;
+ }
+
+ // In case callers don't have fine location access, pre-construct a location-free version
+ // of the CellIdentity. This will still have the PLMN ID, which should be sufficient for
+ // most purposes.
+ final CellIdentity noLocationCi = cellIdentity.sanitizeLocationInfo();
+
+ synchronized (mRecords) {
+ if (validatePhoneId(phoneId)) {
+ for (Record r : mRecords) {
+ if (r.matchPhoneStateListenerEvent(
+ PhoneStateListener.LISTEN_REGISTRATION_FAILURE)
+ && idMatch(r.subId, subId, phoneId)) {
+ try {
+ r.callback.onRegistrationFailed(
+ checkFineLocationAccess(r, Build.VERSION_CODES.R)
+ ? cellIdentity : noLocationCi,
+ chosenPlmn, domain, causeCode,
+ additionalCauseCode);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ }
+ }
+ handleRemoveListLocked();
+ }
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
@@ -2126,10 +2179,19 @@
/** Fired when a subscription's phone state changes. */
private static final String ACTION_SUBSCRIPTION_PHONE_STATE_CHANGED =
"android.intent.action.SUBSCRIPTION_PHONE_STATE";
+ /**
+ * Broadcast Action: The data connection state has changed for any one of the
+ * phone's mobile data connections (eg, default, MMS or GPS specific connection).
+ */
+ private static final String ACTION_ANY_DATA_CONNECTION_STATE_CHANGED =
+ "android.intent.action.ANY_DATA_STATE";
// Legacy intent extra keys, copied from PhoneConstants.
// Used in legacy intents sent here, for backward compatibility.
+ private static final String PHONE_CONSTANTS_DATA_APN_TYPE_KEY = "apnType";
+ private static final String PHONE_CONSTANTS_DATA_APN_KEY = "apn";
private static final String PHONE_CONSTANTS_SLOT_KEY = "slot";
+ private static final String PHONE_CONSTANTS_STATE_KEY = "state";
private static final String PHONE_CONSTANTS_SUBSCRIPTION_KEY = "subscription";
private void broadcastServiceStateChanged(ServiceState state, int phoneId, int subId) {
@@ -2142,7 +2204,7 @@
Binder.restoreCallingIdentity(ident);
}
- Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
+ Intent intent = new Intent(Intent.ACTION_SERVICE_STATE);
intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
Bundle data = new Bundle();
state.fillInNotifierBundle(data);
@@ -2168,13 +2230,32 @@
Intent intent = new Intent(TelephonyIntents.ACTION_SIGNAL_STRENGTH_CHANGED);
Bundle data = new Bundle();
- signalStrength.fillInNotifierBundle(data);
+ fillInSignalStrengthNotifierBundle(signalStrength, data);
intent.putExtras(data);
intent.putExtra(PHONE_CONSTANTS_SUBSCRIPTION_KEY, subId);
intent.putExtra(PHONE_CONSTANTS_SLOT_KEY, phoneId);
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
+ private void fillInSignalStrengthNotifierBundle(SignalStrength signalStrength, Bundle bundle) {
+ List<CellSignalStrength> cellSignalStrengths = signalStrength.getCellSignalStrengths();
+ for (CellSignalStrength cellSignalStrength : cellSignalStrengths) {
+ if (cellSignalStrength instanceof CellSignalStrengthLte) {
+ bundle.putParcelable("Lte", (CellSignalStrengthLte) cellSignalStrength);
+ } else if (cellSignalStrength instanceof CellSignalStrengthCdma) {
+ bundle.putParcelable("Cdma", (CellSignalStrengthCdma) cellSignalStrength);
+ } else if (cellSignalStrength instanceof CellSignalStrengthGsm) {
+ bundle.putParcelable("Gsm", (CellSignalStrengthGsm) cellSignalStrength);
+ } else if (cellSignalStrength instanceof CellSignalStrengthWcdma) {
+ bundle.putParcelable("Wcdma", (CellSignalStrengthWcdma) cellSignalStrength);
+ } else if (cellSignalStrength instanceof CellSignalStrengthTdscdma) {
+ bundle.putParcelable("Tdscdma", (CellSignalStrengthTdscdma) cellSignalStrength);
+ } else if (cellSignalStrength instanceof CellSignalStrengthNr) {
+ bundle.putParcelable("Nr", (CellSignalStrengthNr) cellSignalStrength);
+ }
+ }
+ }
+
/**
* Broadcasts an intent notifying apps of a phone state change. {@code subId} can be
* a valid subId, in which case this function fires a subId-specific intent, or it
@@ -2248,19 +2329,50 @@
}
private void broadcastDataConnectionStateChanged(int state, String apn,
- String apnType, int subId) {
+ int apnType, int subId) {
// Note: not reporting to the battery stats service here, because the
// status bar takes care of that after taking into account all of the
// required info.
- Intent intent = new Intent(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
- intent.putExtra(TelephonyManager.EXTRA_STATE, dataStateToString(state));
-
- intent.putExtra(PhoneConstants.DATA_APN_KEY, apn);
- intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY, apnType);
+ Intent intent = new Intent(ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
+ intent.putExtra(PHONE_CONSTANTS_STATE_KEY, dataStateToString(state));
+ intent.putExtra(PHONE_CONSTANTS_DATA_APN_KEY, apn);
+ intent.putExtra(PHONE_CONSTANTS_DATA_APN_TYPE_KEY, getApnTypesStringFromBitmask(apnType));
intent.putExtra(PHONE_CONSTANTS_SUBSCRIPTION_KEY, subId);
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
+ private static final Map<Integer, String> APN_TYPE_INT_MAP;
+ static {
+ APN_TYPE_INT_MAP = new android.util.ArrayMap<Integer, String>();
+ APN_TYPE_INT_MAP.put(ApnSetting.TYPE_DEFAULT, "default");
+ APN_TYPE_INT_MAP.put(ApnSetting.TYPE_MMS, "mms");
+ APN_TYPE_INT_MAP.put(ApnSetting.TYPE_SUPL, "supl");
+ APN_TYPE_INT_MAP.put(ApnSetting.TYPE_DUN, "dun");
+ APN_TYPE_INT_MAP.put(ApnSetting.TYPE_HIPRI, "hipri");
+ APN_TYPE_INT_MAP.put(ApnSetting.TYPE_FOTA, "fota");
+ APN_TYPE_INT_MAP.put(ApnSetting.TYPE_IMS, "ims");
+ APN_TYPE_INT_MAP.put(ApnSetting.TYPE_CBS, "cbs");
+ APN_TYPE_INT_MAP.put(ApnSetting.TYPE_IA, "ia");
+ APN_TYPE_INT_MAP.put(ApnSetting.TYPE_EMERGENCY, "emergency");
+ APN_TYPE_INT_MAP.put(ApnSetting.TYPE_MCX, "mcx");
+ APN_TYPE_INT_MAP.put(ApnSetting.TYPE_XCAP, "xcap");
+ }
+
+ /**
+ * Copy of ApnSetting#getApnTypesStringFromBitmask for legacy broadcast.
+ * @param apnTypeBitmask bitmask of APN types.
+ * @return comma delimited list of APN types.
+ */
+ private static String getApnTypesStringFromBitmask(int apnTypeBitmask) {
+ List<String> types = new ArrayList<>();
+ for (Integer type : APN_TYPE_INT_MAP.keySet()) {
+ if ((apnTypeBitmask & type) == type) {
+ types.add(APN_TYPE_INT_MAP.get(type));
+ }
+ }
+ return android.text.TextUtils.join(",", types);
+ }
+
private void enforceNotifyPermissionOrCarrierPrivilege(String method) {
if (checkNotifyPermission()) {
return;
@@ -2483,11 +2595,14 @@
if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) {
try {
- SignalStrength signalStrength = mSignalStrength[phoneId];
- if (DBG) {
- log("checkPossibleMissNotify: onSignalStrengthsChanged SS=" + signalStrength);
+ if (mSignalStrength[phoneId] != null) {
+ SignalStrength signalStrength = mSignalStrength[phoneId];
+ if (DBG) {
+ log("checkPossibleMissNotify: onSignalStrengthsChanged SS="
+ + signalStrength);
+ }
+ r.callback.onSignalStrengthsChanged(new SignalStrength(signalStrength));
}
- r.callback.onSignalStrengthsChanged(new SignalStrength(signalStrength));
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
@@ -2495,14 +2610,16 @@
if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) {
try {
- int gsmSignalStrength = mSignalStrength[phoneId]
- .getGsmSignalStrength();
- if (DBG) {
- log("checkPossibleMissNotify: onSignalStrengthChanged SS=" +
- gsmSignalStrength);
+ if (mSignalStrength[phoneId] != null) {
+ int gsmSignalStrength = mSignalStrength[phoneId]
+ .getGsmSignalStrength();
+ if (DBG) {
+ log("checkPossibleMissNotify: onSignalStrengthChanged SS="
+ + gsmSignalStrength);
+ }
+ r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1
+ : gsmSignalStrength));
}
- r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1
- : gsmSignalStrength));
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
@@ -2651,8 +2768,14 @@
return "TD_SCDMA";
case TelephonyManager.NETWORK_TYPE_IWLAN:
return "IWLAN";
- case TelephonyManager.NETWORK_TYPE_LTE_CA:
- return "LTE_CA";
+
+ //TODO: This network type is marked as hidden because it is not a
+ // true network type and we are looking to remove it completely from the available list
+ // of network types. Since this method is only used for logging, in the event that this
+ // network type is selected, the log will read as "Unknown."
+ //case TelephonyManager.NETWORK_TYPE_LTE_CA:
+ // return "LTE_CA";
+
case TelephonyManager.NETWORK_TYPE_NR:
return "NR";
default:
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 76a8f92..27e0d52 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -32,7 +32,6 @@
import android.hardware.vibrator.IVibrator;
import android.hardware.vibrator.V1_0.EffectStrength;
import android.icu.text.DateFormat;
-import android.media.AudioAttributes;
import android.media.AudioManager;
import android.os.BatteryStats;
import android.os.Binder;
@@ -54,6 +53,7 @@
import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
+import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.os.WorkSource;
@@ -107,8 +107,9 @@
private static final int SCALE_VERY_LOW_MAX_AMPLITUDE = 168; // 2/3 * 255
private static final int SCALE_LOW_MAX_AMPLITUDE = 192; // 3/4 * 255
- // If a vibration is playing for longer than 5s, it's probably not haptic feedback.
- private static final long MAX_HAPTIC_FEEDBACK_DURATION = 5000;
+ // Default vibration attributes. Used when vibration is requested without attributes
+ private static final VibrationAttributes DEFAULT_ATTRIBUTES =
+ new VibrationAttributes.Builder().build();
// If HAL supports callbacks set the timeout to ASYNC_TIMEOUT_MULTIPLIER * duration.
private static final long ASYNC_TIMEOUT_MULTIPLIER = 2;
@@ -163,7 +164,7 @@
private int mHapticFeedbackIntensity;
private int mNotificationIntensity;
private int mRingIntensity;
- private SparseArray<Pair<VibrationEffect, AudioAttributes>> mAlwaysOnEffects =
+ private SparseArray<Pair<VibrationEffect, VibrationAttributes>> mAlwaysOnEffects =
new SparseArray<>();
static native boolean vibratorExists();
@@ -207,7 +208,7 @@
// with other system events, any duration calculations should be done use startTime so as
// not to be affected by discontinuities created by RTC adjustments.
public final long startTimeDebug;
- public final AudioAttributes attrs;
+ public final VibrationAttributes attrs;
public final int uid;
public final String opPkg;
public final String reason;
@@ -220,7 +221,7 @@
public VibrationEffect originalEffect;
private Vibration(IBinder token, VibrationEffect effect,
- AudioAttributes attrs, int uid, String opPkg, String reason) {
+ VibrationAttributes attrs, int uid, String opPkg, String reason) {
this.token = token;
this.effect = effect;
this.startTime = SystemClock.elapsedRealtime();
@@ -253,28 +254,7 @@
}
public boolean isHapticFeedback() {
- if (VibratorService.this.isHapticFeedback(attrs.getUsage())) {
- return true;
- }
- if (effect instanceof VibrationEffect.Prebaked) {
- VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect;
- switch (prebaked.getId()) {
- case VibrationEffect.EFFECT_CLICK:
- case VibrationEffect.EFFECT_DOUBLE_CLICK:
- case VibrationEffect.EFFECT_HEAVY_CLICK:
- case VibrationEffect.EFFECT_TEXTURE_TICK:
- case VibrationEffect.EFFECT_TICK:
- case VibrationEffect.EFFECT_POP:
- case VibrationEffect.EFFECT_THUD:
- return true;
- default:
- Slog.w(TAG, "Unknown prebaked vibration effect, "
- + "assuming it isn't haptic feedback.");
- return false;
- }
- }
- final long duration = effect.getDuration();
- return duration >= 0 && duration < MAX_HAPTIC_FEEDBACK_DURATION;
+ return VibratorService.this.isHapticFeedback(attrs.getUsage());
}
public boolean isNotification() {
@@ -303,13 +283,13 @@
private final long mStartTimeDebug;
private final VibrationEffect mEffect;
private final VibrationEffect mOriginalEffect;
- private final AudioAttributes mAttrs;
+ private final VibrationAttributes mAttrs;
private final int mUid;
private final String mOpPkg;
private final String mReason;
- public VibrationInfo(long startTimeDebug, VibrationEffect effect,
- VibrationEffect originalEffect, AudioAttributes attrs, int uid,
+ VibrationInfo(long startTimeDebug, VibrationEffect effect,
+ VibrationEffect originalEffect, VibrationAttributes attrs, int uid,
String opPkg, String reason) {
mStartTimeDebug = startTimeDebug;
mEffect = effect;
@@ -528,7 +508,7 @@
}
@Override // Binder call
- public boolean setAlwaysOnEffect(int id, VibrationEffect effect, AudioAttributes attrs) {
+ public boolean setAlwaysOnEffect(int id, VibrationEffect effect, VibrationAttributes attrs) {
if (!hasPermission(android.Manifest.permission.VIBRATE_ALWAYS_ON)) {
throw new SecurityException("Requires VIBRATE_ALWAYS_ON permission");
}
@@ -550,8 +530,7 @@
return false;
}
if (attrs == null) {
- attrs = new AudioAttributes.Builder()
- .setUsage(AudioAttributes.USAGE_UNKNOWN)
+ attrs = new VibrationAttributes.Builder()
.build();
}
synchronized (mLock) {
@@ -610,7 +589,7 @@
@Override // Binder call
public void vibrate(int uid, String opPkg, VibrationEffect effect,
- @Nullable AudioAttributes attrs, String reason, IBinder token) {
+ @Nullable VibrationAttributes attrs, String reason, IBinder token) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason = " + reason);
try {
if (!hasPermission(android.Manifest.permission.VIBRATE)) {
@@ -626,18 +605,16 @@
}
if (attrs == null) {
- attrs = new AudioAttributes.Builder()
- .setUsage(AudioAttributes.USAGE_UNKNOWN)
- .build();
+ attrs = DEFAULT_ATTRIBUTES;
}
if (shouldBypassDnd(attrs)) {
if (!(hasPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
|| hasPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
|| hasPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING))) {
- final int flags = attrs.getAllFlags()
- & ~AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
- attrs = new AudioAttributes.Builder(attrs).replaceFlags(flags).build();
+ final int flags = attrs.getFlags()
+ & ~VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
+ attrs = new VibrationAttributes.Builder(attrs).replaceFlags(flags).build();
}
}
@@ -868,18 +845,10 @@
return true;
}
- if (vib.attrs.getUsage() == AudioAttributes.USAGE_NOTIFICATION_RINGTONE) {
- return true;
- }
-
- if (vib.attrs.getUsage() == AudioAttributes.USAGE_ALARM
- || vib.attrs.getUsage() == AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY
- || vib.attrs.getUsage()
- == AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST) {
- return true;
- }
-
- return false;
+ int usage = vib.attrs.getUsage();
+ return usage == VibrationAttributes.USAGE_RINGTONE
+ || usage == VibrationAttributes.USAGE_ALARM
+ || usage == VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
}
private int getCurrentIntensityLocked(Vibration vib) {
@@ -968,13 +937,13 @@
}
}
- private static boolean shouldBypassDnd(AudioAttributes attrs) {
- return (attrs.getAllFlags() & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0;
+ private static boolean shouldBypassDnd(VibrationAttributes attrs) {
+ return attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY);
}
private int getAppOpMode(Vibration vib) {
int mode = mAppOps.checkAudioOpNoThrow(AppOpsManager.OP_VIBRATE,
- vib.attrs.getUsage(), vib.uid, vib.opPkg);
+ vib.attrs.getAudioAttributes().getUsage(), vib.uid, vib.opPkg);
if (mode == AppOpsManager.MODE_ALLOWED) {
mode = mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, vib.uid, vib.opPkg);
}
@@ -1100,7 +1069,7 @@
mVibrator.getDefaultRingVibrationIntensity(), UserHandle.USER_CURRENT);
}
- private void updateAlwaysOnLocked(int id, VibrationEffect effect, AudioAttributes attrs) {
+ private void updateAlwaysOnLocked(int id, VibrationEffect effect, VibrationAttributes attrs) {
// TODO: Check DND and LowPower settings
final Vibration vib = new Vibration(null, effect, attrs, 0, null, null);
final int intensity = getCurrentIntensityLocked(vib);
@@ -1116,7 +1085,7 @@
private void updateAlwaysOnLocked() {
for (int i = 0; i < mAlwaysOnEffects.size(); i++) {
int id = mAlwaysOnEffects.keyAt(i);
- Pair<VibrationEffect, AudioAttributes> pair = mAlwaysOnEffects.valueAt(i);
+ Pair<VibrationEffect, VibrationAttributes> pair = mAlwaysOnEffects.valueAt(i);
updateAlwaysOnLocked(id, pair.first, pair.second);
}
}
@@ -1148,7 +1117,7 @@
return vibratorExists();
}
- private void doVibratorOn(long millis, int amplitude, int uid, AudioAttributes attrs) {
+ private void doVibratorOn(long millis, int amplitude, int uid, VibrationAttributes attrs) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOn");
try {
synchronized (mInputDeviceVibrators) {
@@ -1163,7 +1132,7 @@
final int vibratorCount = mInputDeviceVibrators.size();
if (vibratorCount != 0) {
for (int i = 0; i < vibratorCount; i++) {
- mInputDeviceVibrators.get(i).vibrate(millis, attrs);
+ mInputDeviceVibrators.get(i).vibrate(millis, attrs.getAudioAttributes());
}
} else {
// Note: ordering is important here! Many haptic drivers will reset their
@@ -1272,28 +1241,19 @@
}
private static boolean isNotification(int usageHint) {
- switch (usageHint) {
- case AudioAttributes.USAGE_NOTIFICATION:
- case AudioAttributes.USAGE_NOTIFICATION_EVENT:
- case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
- case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
- case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
- return true;
- default:
- return false;
- }
+ return usageHint == VibrationAttributes.USAGE_NOTIFICATION;
}
private static boolean isRingtone(int usageHint) {
- return usageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
+ return usageHint == VibrationAttributes.USAGE_RINGTONE;
}
private static boolean isHapticFeedback(int usageHint) {
- return usageHint == AudioAttributes.USAGE_ASSISTANCE_SONIFICATION;
+ return usageHint == VibrationAttributes.USAGE_TOUCH;
}
private static boolean isAlarm(int usageHint) {
- return usageHint == AudioAttributes.USAGE_ALARM;
+ return usageHint == VibrationAttributes.USAGE_ALARM;
}
private void noteVibratorOnLocked(int uid, long millis) {
@@ -1332,11 +1292,11 @@
private class VibrateThread extends Thread {
private final VibrationEffect.Waveform mWaveform;
private final int mUid;
- private final AudioAttributes mAttrs;
+ private final VibrationAttributes mAttrs;
private boolean mForceStop;
- VibrateThread(VibrationEffect.Waveform waveform, int uid, AudioAttributes attrs) {
+ VibrateThread(VibrationEffect.Waveform waveform, int uid, VibrationAttributes attrs) {
mWaveform = waveform;
mUid = uid;
mAttrs = attrs;
@@ -1600,7 +1560,7 @@
Slog.e(TAG, "Playing external vibration: " + vib);
}
}
- final int usage = vib.getAudioAttributes().getUsage();
+ final int usage = vib.getVibrationAttributes().getUsage();
final int defaultIntensity;
final int currentIntensity;
if (isRingtone(usage)) {
@@ -1731,7 +1691,7 @@
VibrationEffect effect =
VibrationEffect.createOneShot(duration, VibrationEffect.DEFAULT_AMPLITUDE);
- AudioAttributes attrs = createAudioAttributes(commonOptions);
+ VibrationAttributes attrs = createVibrationAttributes(commonOptions);
vibrate(Binder.getCallingUid(), description, effect, attrs, "Shell Command",
mToken);
return 0;
@@ -1792,7 +1752,7 @@
amplitudesList.stream().mapToInt(Integer::intValue).toArray();
effect = VibrationEffect.createWaveform(timings, amplitudes, repeat);
}
- AudioAttributes attrs = createAudioAttributes(commonOptions);
+ VibrationAttributes attrs = createVibrationAttributes(commonOptions);
vibrate(Binder.getCallingUid(), description, effect, attrs, "Shell Command",
mToken);
return 0;
@@ -1824,7 +1784,7 @@
VibrationEffect effect =
VibrationEffect.get(id, false);
- AudioAttributes attrs = createAudioAttributes(commonOptions);
+ VibrationAttributes attrs = createVibrationAttributes(commonOptions);
vibrate(Binder.getCallingUid(), description, effect, attrs, "Shell Command",
mToken);
return 0;
@@ -1833,13 +1793,13 @@
}
}
- private AudioAttributes createAudioAttributes(CommonOptions commonOptions) {
+ private VibrationAttributes createVibrationAttributes(CommonOptions commonOptions) {
final int flags = commonOptions.force
- ? AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY
+ ? VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY
: 0;
- return new AudioAttributes.Builder()
- .setUsage(AudioAttributes.USAGE_UNKNOWN)
- .setFlags(flags)
+ return new VibrationAttributes.Builder()
+ .setUsage(VibrationAttributes.USAGE_UNKNOWN)
+ .replaceFlags(flags)
.build();
}
diff --git a/services/core/java/com/android/server/am/ActiveInstrumentation.java b/services/core/java/com/android/server/am/ActiveInstrumentation.java
index b2c82f0..db63638 100644
--- a/services/core/java/com/android/server/am/ActiveInstrumentation.java
+++ b/services/core/java/com/android/server/am/ActiveInstrumentation.java
@@ -67,6 +67,9 @@
// Set to true when we have told the watcher the instrumentation is finished.
boolean mFinished;
+ // The uid of the process who started this instrumentation.
+ int mSourceUid;
+
ActiveInstrumentation(ActivityManagerService service) {
mService = service;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index dd1f370..c21adb0 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -275,6 +275,7 @@
import android.provider.Settings;
import android.server.ServerProtoEnums;
import android.sysprop.VoldProperties;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.style.SuggestionSpan;
@@ -318,7 +319,6 @@
import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.os.TransferPipe;
import com.android.internal.os.Zygote;
-import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastPrintWriter;
@@ -2176,10 +2176,13 @@
@Override
public void dumpCritical(FileDescriptor fd, PrintWriter pw, String[] args,
boolean asProto) {
- if (asProto) return;
if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
"cpuinfo", pw)) return;
synchronized (mActivityManagerService.mProcessCpuTracker) {
+ if (asProto) {
+ mActivityManagerService.mProcessCpuTracker.dumpProto(fd);
+ return;
+ }
pw.print(mActivityManagerService.mProcessCpuTracker.printCurrentLoad());
pw.print(mActivityManagerService.mProcessCpuTracker.printCurrentState(
SystemClock.uptimeMillis()));
@@ -4338,6 +4341,18 @@
final boolean allUids = mAtmInternal.isGetTasksAllowed(
"getProcessMemoryInfo", callingPid, callingUid);
+ // Check if the caller is actually instrumented and from shell, if it's true, we may lift
+ // the throttle of PSS info sampling.
+ boolean isCallerInstrumentedFromShell = false;
+ synchronized (mPidsSelfLocked) {
+ ProcessRecord caller = mPidsSelfLocked.get(callingPid);
+ if (caller != null) {
+ final ActiveInstrumentation instr = caller.getActiveInstrumentation();
+ isCallerInstrumentedFromShell = instr != null
+ && (instr.mSourceUid == SHELL_UID || instr.mSourceUid == ROOT_UID);
+ }
+ }
+
Debug.MemoryInfo[] infos = new Debug.MemoryInfo[pids.length];
for (int i=pids.length-1; i>=0; i--) {
infos[i] = new Debug.MemoryInfo();
@@ -4361,7 +4376,8 @@
continue; // Not allowed to see other users.
}
}
- if (proc != null && proc.lastMemInfoTime >= lastNow && proc.lastMemInfo != null) {
+ if (proc != null && proc.lastMemInfoTime >= lastNow && proc.lastMemInfo != null
+ && !isCallerInstrumentedFromShell) {
// It hasn't been long enough that we want to take another sample; return
// the last one.
infos[i].set(proc.lastMemInfo);
@@ -12594,7 +12610,7 @@
ArrayList<ProcessRecord> procs, PrintWriter categoryPw) {
long uptime = SystemClock.uptimeMillis();
long realtime = SystemClock.elapsedRealtime();
- final long[] tmpLong = new long[1];
+ final long[] tmpLong = new long[3];
if (procs == null) {
// No Java processes. Maybe they want to print a native process.
@@ -12627,17 +12643,25 @@
for (int i = nativeProcs.size() - 1 ; i >= 0 ; i--) {
final ProcessCpuTracker.Stats r = nativeProcs.get(i);
final int pid = r.pid;
- if (!opts.isCheckinRequest && opts.dumpDetails) {
- pw.println("\n** MEMINFO in pid " + pid + " [" + r.baseName + "] **");
- }
if (mi == null) {
mi = new Debug.MemoryInfo();
}
if (opts.dumpDetails || (!brief && !opts.oomOnly)) {
- Debug.getMemoryInfo(pid, mi);
+ if (!Debug.getMemoryInfo(pid, mi)) {
+ continue;
+ }
} else {
- mi.dalvikPss = (int)Debug.getPss(pid, tmpLong, null);
- mi.dalvikPrivateDirty = (int)tmpLong[0];
+ long pss = Debug.getPss(pid, tmpLong, null);
+ if (pss == 0) {
+ continue;
+ }
+ mi.nativePss = (int) pss;
+ mi.nativePrivateDirty = (int) tmpLong[0];
+ mi.nativeRss = (int) tmpLong[2];
+ }
+ if (!opts.isCheckinRequest && opts.dumpDetails) {
+ pw.println("\n** MEMINFO in pid " + pid + " ["
+ + r.baseName + "] **");
}
ActivityThread.dumpMemInfoTable(pw, mi, opts.isCheckinRequest,
opts.dumpFullDetails, opts.dumpDalvik, opts.dumpSummaryOnly,
@@ -12715,9 +12739,6 @@
hasActivities = r.hasActivities();
}
if (thread != null) {
- if (!opts.isCheckinRequest && opts.dumpDetails) {
- pw.println("\n** MEMINFO in pid " + pid + " [" + r.processName + "] **");
- }
if (mi == null) {
mi = new Debug.MemoryInfo();
}
@@ -12727,17 +12748,26 @@
if (opts.dumpDetails || (!brief && !opts.oomOnly)) {
reportType = ProcessStats.ADD_PSS_EXTERNAL_SLOW;
startTime = SystemClock.currentThreadTimeMillis();
- Debug.getMemoryInfo(pid, mi);
+ if (!Debug.getMemoryInfo(pid, mi)) {
+ continue;
+ }
endTime = SystemClock.currentThreadTimeMillis();
hasSwapPss = mi.hasSwappedOutPss;
} else {
reportType = ProcessStats.ADD_PSS_EXTERNAL;
startTime = SystemClock.currentThreadTimeMillis();
- mi.dalvikPss = (int)Debug.getPss(pid, tmpLong, null);
+ long pss = Debug.getPss(pid, tmpLong, null);
+ if (pss == 0) {
+ continue;
+ }
+ mi.dalvikPss = (int) pss;
endTime = SystemClock.currentThreadTimeMillis();
- mi.dalvikPrivateDirty = (int)tmpLong[0];
+ mi.dalvikPrivateDirty = (int) tmpLong[0];
mi.dalvikRss = (int) tmpLong[2];
}
+ if (!opts.isCheckinRequest && opts.dumpDetails) {
+ pw.println("\n** MEMINFO in pid " + pid + " [" + r.processName + "] **");
+ }
if (opts.dumpDetails) {
if (opts.localOnly) {
ActivityThread.dumpMemInfoTable(pw, mi, opts.isCheckinRequest, opts.dumpFullDetails,
@@ -12867,10 +12897,17 @@
mi = new Debug.MemoryInfo();
}
if (!brief && !opts.oomOnly) {
- Debug.getMemoryInfo(st.pid, mi);
+ if (!Debug.getMemoryInfo(st.pid, mi)) {
+ continue;
+ }
} else {
- mi.nativePss = (int)Debug.getPss(st.pid, tmpLong, null);
- mi.nativePrivateDirty = (int)tmpLong[0];
+ long pss = Debug.getPss(st.pid, tmpLong, null);
+ if (pss == 0) {
+ continue;
+ }
+ mi.nativePss = (int) pss;
+ mi.nativePrivateDirty = (int) tmpLong[0];
+ mi.nativeRss = (int) tmpLong[2];
}
final long myTotalPss = mi.getTotalPss();
@@ -13174,7 +13211,7 @@
ArrayList<ProcessRecord> procs) {
final long uptimeMs = SystemClock.uptimeMillis();
final long realtimeMs = SystemClock.elapsedRealtime();
- final long[] tmpLong = new long[1];
+ final long[] tmpLong = new long[3];
if (procs == null) {
// No Java processes. Maybe they want to print a native process.
@@ -13209,20 +13246,29 @@
for (int i = nativeProcs.size() - 1 ; i >= 0 ; i--) {
final ProcessCpuTracker.Stats r = nativeProcs.get(i);
final int pid = r.pid;
- final long nToken = proto.start(MemInfoDumpProto.NATIVE_PROCESSES);
-
- proto.write(MemInfoDumpProto.ProcessMemory.PID, pid);
- proto.write(MemInfoDumpProto.ProcessMemory.PROCESS_NAME, r.baseName);
if (mi == null) {
mi = new Debug.MemoryInfo();
}
if (opts.dumpDetails || (!brief && !opts.oomOnly)) {
- Debug.getMemoryInfo(pid, mi);
+ if (!Debug.getMemoryInfo(pid, mi)) {
+ continue;
+ }
} else {
- mi.dalvikPss = (int)Debug.getPss(pid, tmpLong, null);
- mi.dalvikPrivateDirty = (int)tmpLong[0];
+ long pss = Debug.getPss(pid, tmpLong, null);
+ if (pss == 0) {
+ continue;
+ }
+ mi.nativePss = (int) pss;
+ mi.nativePrivateDirty = (int) tmpLong[0];
+ mi.nativeRss = (int) tmpLong[2];
}
+
+ final long nToken = proto.start(MemInfoDumpProto.NATIVE_PROCESSES);
+
+ proto.write(MemInfoDumpProto.ProcessMemory.PID, pid);
+ proto.write(MemInfoDumpProto.ProcessMemory.PROCESS_NAME, r.baseName);
+
ActivityThread.dumpMemInfoTable(proto, mi, opts.dumpDalvik,
opts.dumpSummaryOnly, 0, 0, 0, 0, 0, 0);
@@ -13313,13 +13359,19 @@
if (opts.dumpDetails || (!brief && !opts.oomOnly)) {
reportType = ProcessStats.ADD_PSS_EXTERNAL_SLOW;
startTime = SystemClock.currentThreadTimeMillis();
- Debug.getMemoryInfo(pid, mi);
+ if (!Debug.getMemoryInfo(pid, mi)) {
+ continue;
+ }
endTime = SystemClock.currentThreadTimeMillis();
hasSwapPss = mi.hasSwappedOutPss;
} else {
reportType = ProcessStats.ADD_PSS_EXTERNAL;
startTime = SystemClock.currentThreadTimeMillis();
- mi.dalvikPss = (int) Debug.getPss(pid, tmpLong, null);
+ long pss = Debug.getPss(pid, tmpLong, null);
+ if (pss == 0) {
+ continue;
+ }
+ mi.dalvikPss = (int) pss;
endTime = SystemClock.currentThreadTimeMillis();
mi.dalvikPrivateDirty = (int) tmpLong[0];
mi.dalvikRss = (int) tmpLong[2];
@@ -13447,10 +13499,17 @@
mi = new Debug.MemoryInfo();
}
if (!brief && !opts.oomOnly) {
- Debug.getMemoryInfo(st.pid, mi);
+ if (!Debug.getMemoryInfo(st.pid, mi)) {
+ continue;
+ }
} else {
- mi.nativePss = (int)Debug.getPss(st.pid, tmpLong, null);
- mi.nativePrivateDirty = (int)tmpLong[0];
+ long pss = Debug.getPss(st.pid, tmpLong, null);
+ if (pss == 0) {
+ continue;
+ }
+ mi.nativePss = (int) pss;
+ mi.nativePrivateDirty = (int) tmpLong[0];
+ mi.nativeRss = (int) tmpLong[2];
}
final long myTotalPss = mi.getTotalPss();
@@ -15098,7 +15157,7 @@
|| AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
|| AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)
|| LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action)
- || TelephonyIntents.ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE.equals(action)
+ || TelephonyManager.ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE.equals(action)
|| SuggestionSpan.ACTION_SUGGESTION_PICKED.equals(action)
|| AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION.equals(action)
|| AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION.equals(action)) {
@@ -16166,6 +16225,7 @@
disableTestApiChecks, mountExtStorageFull, abiOverride);
app.setActiveInstrumentation(activeInstr);
activeInstr.mFinished = false;
+ activeInstr.mSourceUid = callingUid;
activeInstr.mRunningProcesses.add(app);
if (!mActiveInstrumentation.contains(activeInstr)) {
mActiveInstrumentation.add(activeInstr);
@@ -18613,6 +18673,11 @@
}
@Override
+ public void monitor() {
+ ActivityManagerService.this.monitor();
+ }
+
+ @Override
public long inputDispatchingTimedOut(int pid, boolean aboveSystem, String reason) {
synchronized (ActivityManagerService.this) {
return ActivityManagerService.this.inputDispatchingTimedOut(
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index a4c44fa..d7ad1c2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2934,33 +2934,37 @@
}
ArraySet<Long> enabled = new ArraySet<>();
ArraySet<Long> disabled = new ArraySet<>();
- switch (toggleValue) {
- case "enable":
- enabled.add(changeId);
- pw.println("Enabled change " + changeId + " for " + packageName + ".");
- CompatibilityChangeConfig overrides =
- new CompatibilityChangeConfig(
- new Compatibility.ChangeConfig(enabled, disabled));
- platformCompat.setOverrides(overrides, packageName);
- return 0;
- case "disable":
- disabled.add(changeId);
- pw.println("Disabled change " + changeId + " for " + packageName + ".");
- overrides =
- new CompatibilityChangeConfig(
- new Compatibility.ChangeConfig(enabled, disabled));
- platformCompat.setOverrides(overrides, packageName);
- return 0;
- case "reset":
- if (platformCompat.clearOverride(changeId, packageName)) {
- pw.println("Reset change " + changeId + " for " + packageName
- + " to default value.");
- } else {
- pw.println("No override exists for changeId " + changeId + ".");
- }
- return 0;
- default:
- pw.println("Invalid toggle value: '" + toggleValue + "'.");
+ try {
+ switch (toggleValue) {
+ case "enable":
+ enabled.add(changeId);
+ CompatibilityChangeConfig overrides =
+ new CompatibilityChangeConfig(
+ new Compatibility.ChangeConfig(enabled, disabled));
+ platformCompat.setOverrides(overrides, packageName);
+ pw.println("Enabled change " + changeId + " for " + packageName + ".");
+ return 0;
+ case "disable":
+ disabled.add(changeId);
+ overrides =
+ new CompatibilityChangeConfig(
+ new Compatibility.ChangeConfig(enabled, disabled));
+ platformCompat.setOverrides(overrides, packageName);
+ pw.println("Disabled change " + changeId + " for " + packageName + ".");
+ return 0;
+ case "reset":
+ if (platformCompat.clearOverride(changeId, packageName)) {
+ pw.println("Reset change " + changeId + " for " + packageName
+ + " to default value.");
+ } else {
+ pw.println("No override exists for changeId " + changeId + ".");
+ }
+ return 0;
+ default:
+ pw.println("Invalid toggle value: '" + toggleValue + "'.");
+ }
+ } catch (SecurityException e) {
+ pw.println(e.getMessage());
}
return -1;
}
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index c380726..5e48dcf 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -88,6 +88,11 @@
private final ProcessMap<Long> mProcessCrashTimesPersistent = new ProcessMap<>();
/**
+ * The last time that various processes have crashed and shown an error dialog.
+ */
+ private final ProcessMap<Long> mProcessCrashShowDialogTimes = new ProcessMap<>();
+
+ /**
* Set of applications that we consider to be bad, and will reject
* incoming broadcasts from (which the user has no control over).
* Processes are added to this set when they have crashed twice within
@@ -820,6 +825,11 @@
}
return;
}
+ Long crashShowErrorTime = null;
+ if (!proc.isolated) {
+ crashShowErrorTime = mProcessCrashShowDialogTimes.get(proc.info.processName,
+ proc.uid);
+ }
final boolean showFirstCrash = Settings.Global.getInt(
mContext.getContentResolver(),
Settings.Global.SHOW_FIRST_CRASH_DIALOG, 0) != 0;
@@ -830,10 +840,16 @@
mService.mUserController.getCurrentUserId()) != 0;
final boolean crashSilenced = mAppsNotReportingCrashes != null &&
mAppsNotReportingCrashes.contains(proc.info.packageName);
+ final long now = SystemClock.uptimeMillis();
+ final boolean shouldThottle = crashShowErrorTime != null
+ && now < crashShowErrorTime + ProcessList.MIN_CRASH_INTERVAL;
if ((mService.mAtmInternal.canShowErrorDialogs() || showBackground)
- && !crashSilenced
+ && !crashSilenced && !shouldThottle
&& (showFirstCrash || showFirstCrashDevOption || data.repeating)) {
proc.getDialogController().showCrashDialogs(data);
+ if (!proc.isolated) {
+ mProcessCrashShowDialogTimes.put(proc.info.processName, proc.uid, now);
+ }
} else {
// The device is asleep, so just pretend that the user
// saw a crash dialog and hit "force quit".
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 12f4656..0fc885a 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1541,12 +1541,12 @@
trackedProcState = true;
}
} else if ((cr.flags & Context.BIND_NOT_PERCEPTIBLE) != 0
- && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ
- && adj > ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
+ && clientAdj <= ProcessList.PERCEPTIBLE_APP_ADJ
+ && adj >= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
newAdj = ProcessList.PERCEPTIBLE_LOW_APP_ADJ;
} else if ((cr.flags&Context.BIND_NOT_VISIBLE) != 0
&& clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ
- && adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
+ && adj >= ProcessList.PERCEPTIBLE_APP_ADJ) {
newAdj = ProcessList.PERCEPTIBLE_APP_ADJ;
} else if (clientAdj >= ProcessList.PERCEPTIBLE_APP_ADJ) {
newAdj = clientAdj;
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 5bbb517..a7593c7 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -17,6 +17,11 @@
package com.android.server.appop;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
+import static android.app.AppOpsManager.FILTER_BY_FEATURE_ID;
+import static android.app.AppOpsManager.FILTER_BY_OP_NAMES;
+import static android.app.AppOpsManager.FILTER_BY_PACKAGE_NAME;
+import static android.app.AppOpsManager.FILTER_BY_UID;
+import static android.app.AppOpsManager.HistoricalOpsRequestFilter;
import static android.app.AppOpsManager.NoteOpEvent;
import static android.app.AppOpsManager.OP_CAMERA;
import static android.app.AppOpsManager.OP_COARSE_LOCATION;
@@ -53,7 +58,6 @@
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.AppOpsManager.HistoricalOps;
-import android.app.AppOpsManager.HistoricalOpsRequest;
import android.app.AppOpsManager.Mode;
import android.app.AppOpsManager.OpEntry;
import android.app.AppOpsManager.OpFeatureEntry;
@@ -632,6 +636,7 @@
}
private final class FeatureOp {
+ public final @Nullable String featureId;
public final @NonNull Op parent;
/**
@@ -658,7 +663,8 @@
@GuardedBy("AppOpsService.this")
private @Nullable ArrayMap<IBinder, InProgressStartOpEvent> mInProgressEvents;
- FeatureOp(@NonNull Op parent) {
+ FeatureOp(@Nullable String featureId, @NonNull Op parent) {
+ this.featureId = featureId;
this.parent = parent;
}
@@ -676,6 +682,9 @@
@OpFlags int flags) {
accessed(System.currentTimeMillis(), -1, proxyUid, proxyPackageName,
proxyFeatureId, uidState, flags);
+
+ mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid, parent.packageName,
+ featureId, uidState, flags);
}
/**
@@ -720,6 +729,9 @@
*/
public void rejected(@AppOpsManager.UidState int uidState, @OpFlags int flags) {
rejected(System.currentTimeMillis(), uidState, flags);
+
+ mHistoricalRegistry.incrementOpRejected(parent.op, parent.uid, parent.packageName,
+ featureId, uidState, flags);
}
/**
@@ -780,7 +792,7 @@
// startOp events don't support proxy, hence use flags==SELF
mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid, parent.packageName,
- uidState, OP_FLAG_SELF);
+ featureId, uidState, OP_FLAG_SELF);
}
/**
@@ -820,8 +832,8 @@
mAccessEvents.put(makeKey(event.getUidState(), OP_FLAG_SELF), finishedEvent);
mHistoricalRegistry.increaseOpAccessDuration(parent.op, parent.uid,
- parent.packageName, event.getUidState(), AppOpsManager.OP_FLAG_SELF,
- finishedEvent.getDuration());
+ parent.packageName, featureId, event.getUidState(),
+ AppOpsManager.OP_FLAG_SELF, finishedEvent.getDuration());
mInProgressStartOpEventPool.release(event);
@@ -1031,7 +1043,7 @@
featureOp = mFeatures.get(featureId);
if (featureOp == null) {
- featureOp = new FeatureOp(parent);
+ featureOp = new FeatureOp(featureId, parent);
mFeatures.put(featureId, featureOp);
}
@@ -1697,18 +1709,47 @@
}
}
+ /**
+ * Verify that historical appop request arguments are valid.
+ */
+ private void ensureHistoricalOpRequestIsValid(int uid, String packageName, String featureId,
+ List<String> opNames, int filter, long beginTimeMillis, long endTimeMillis,
+ int flags) {
+ if ((filter & FILTER_BY_UID) != 0) {
+ Preconditions.checkArgument(uid != Process.INVALID_UID);
+ } else {
+ Preconditions.checkArgument(uid == Process.INVALID_UID);
+ }
+
+ if ((filter & FILTER_BY_PACKAGE_NAME) != 0) {
+ Objects.requireNonNull(packageName);
+ } else {
+ Preconditions.checkArgument(packageName == null);
+ }
+
+ if ((filter & FILTER_BY_FEATURE_ID) == 0) {
+ Preconditions.checkArgument(featureId == null);
+ }
+
+ if ((filter & FILTER_BY_OP_NAMES) != 0) {
+ Objects.requireNonNull(opNames);
+ } else {
+ Preconditions.checkArgument(opNames == null);
+ }
+
+ Preconditions.checkFlagsArgument(filter,
+ FILTER_BY_UID | FILTER_BY_PACKAGE_NAME | FILTER_BY_FEATURE_ID | FILTER_BY_OP_NAMES);
+ Preconditions.checkArgumentNonnegative(beginTimeMillis);
+ Preconditions.checkArgument(endTimeMillis > beginTimeMillis);
+ Preconditions.checkFlagsArgument(flags, OP_FLAGS_ALL);
+ }
+
@Override
- public void getHistoricalOps(int uid, @NonNull String packageName,
- @Nullable List<String> opNames, long beginTimeMillis, long endTimeMillis,
- @OpFlags int flags, @NonNull RemoteCallback callback) {
- // Use the builder to validate arguments.
- new HistoricalOpsRequest.Builder(
- beginTimeMillis, endTimeMillis)
- .setUid(uid)
- .setPackageName(packageName)
- .setOpNames(opNames)
- .setFlags(flags)
- .build();
+ public void getHistoricalOps(int uid, String packageName, String featureId,
+ List<String> opNames, int filter, long beginTimeMillis, long endTimeMillis,
+ int flags, RemoteCallback callback) {
+ ensureHistoricalOpRequestIsValid(uid, packageName, featureId, opNames, filter,
+ beginTimeMillis, endTimeMillis, flags);
Objects.requireNonNull(callback, "callback cannot be null");
mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
@@ -1718,22 +1759,16 @@
? opNames.toArray(new String[opNames.size()]) : null;
// Must not hold the appops lock
- mHistoricalRegistry.getHistoricalOps(uid, packageName, opNamesArray,
+ mHistoricalRegistry.getHistoricalOps(uid, packageName, featureId, opNamesArray, filter,
beginTimeMillis, endTimeMillis, flags, callback);
}
@Override
- public void getHistoricalOpsFromDiskRaw(int uid, @NonNull String packageName,
- @Nullable List<String> opNames, long beginTimeMillis, long endTimeMillis,
- @OpFlags int flags, @NonNull RemoteCallback callback) {
- // Use the builder to validate arguments.
- new HistoricalOpsRequest.Builder(
- beginTimeMillis, endTimeMillis)
- .setUid(uid)
- .setPackageName(packageName)
- .setOpNames(opNames)
- .setFlags(flags)
- .build();
+ public void getHistoricalOpsFromDiskRaw(int uid, String packageName, String featureId,
+ List<String> opNames, int filter, long beginTimeMillis, long endTimeMillis,
+ int flags, RemoteCallback callback) {
+ ensureHistoricalOpRequestIsValid(uid, packageName, featureId, opNames, filter,
+ beginTimeMillis, endTimeMillis, flags);
Objects.requireNonNull(callback, "callback cannot be null");
mContext.enforcePermission(Manifest.permission.MANAGE_APPOPS,
@@ -1743,8 +1778,8 @@
? opNames.toArray(new String[opNames.size()]) : null;
// Must not hold the appops lock
- mHistoricalRegistry.getHistoricalOpsFromDiskRaw(uid, packageName, opNamesArray,
- beginTimeMillis, endTimeMillis, flags, callback);
+ mHistoricalRegistry.getHistoricalOpsFromDiskRaw(uid, packageName, featureId, opNamesArray,
+ filter, beginTimeMillis, endTimeMillis, flags, callback);
}
@Override
@@ -2631,8 +2666,6 @@
+ switchCode + " (" + code + ") uid " + uid + " package "
+ packageName);
featureOp.rejected(uidState.state, flags);
- mHistoricalRegistry.incrementOpRejected(code, uid, packageName,
- uidState.state, flags);
scheduleOpNotedIfNeededLocked(code, uid, packageName, uidMode);
return uidMode;
}
@@ -2645,8 +2678,6 @@
+ switchCode + " (" + code + ") uid " + uid + " package "
+ packageName);
featureOp.rejected(uidState.state, flags);
- mHistoricalRegistry.incrementOpRejected(code, uid, packageName,
- uidState.state, flags);
scheduleOpNotedIfNeededLocked(code, uid, packageName, mode);
return mode;
}
@@ -2654,9 +2685,6 @@
if (DEBUG) Slog.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
+ " package " + packageName + (featureId == null ? "" : "." + featureId));
featureOp.accessed(proxyUid, proxyPackageName, proxyFeatureId, uidState.state, flags);
- // TODO moltmann: Add features to historical app-ops
- mHistoricalRegistry.incrementOpAccessedCount(op.op, uid, packageName,
- uidState.state, flags);
scheduleOpNotedIfNeededLocked(code, uid, packageName,
AppOpsManager.MODE_ALLOWED);
@@ -2938,8 +2966,6 @@
+ switchCode + " (" + code + ") uid " + uid + " package "
+ resolvedPackageName);
featureOp.rejected(uidState.state, AppOpsManager.OP_FLAG_SELF);
- mHistoricalRegistry.incrementOpRejected(opCode, uid, packageName,
- uidState.state, AppOpsManager.OP_FLAG_SELF);
return uidMode;
}
} else {
@@ -2952,8 +2978,6 @@
+ switchCode + " (" + code + ") uid " + uid + " package "
+ resolvedPackageName);
featureOp.rejected(uidState.state, AppOpsManager.OP_FLAG_SELF);
- mHistoricalRegistry.incrementOpRejected(opCode, uid, packageName,
- uidState.state, AppOpsManager.OP_FLAG_SELF);
return mode;
}
}
@@ -4437,16 +4461,24 @@
pw.println(" Limit output to data associated with the given app op mode.");
pw.println(" --package [PACKAGE]");
pw.println(" Limit output to data associated with the given package name.");
+ pw.println(" --featureId [featureId]");
+ pw.println(" Limit output to data associated with the given feature id.");
pw.println(" --watchers");
pw.println(" Only output the watcher sections.");
pw.println(" --history");
pw.println(" Output the historical data.");
}
- private void dumpStatesLocked(@NonNull PrintWriter pw, long nowElapsed, @NonNull Op op,
- long now, @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix) {
+ private void dumpStatesLocked(@NonNull PrintWriter pw, @Nullable String filterFeatureId,
+ @HistoricalOpsRequestFilter int filter, long nowElapsed, @NonNull Op op, long now,
+ @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix) {
final int numFeatures = op.mFeatures.size();
for (int i = 0; i < numFeatures; i++) {
+ if ((filter & FILTER_BY_FEATURE_ID) != 0 && !Objects.equals(op.mFeatures.keyAt(i),
+ filterFeatureId)) {
+ continue;
+ }
+
pw.print(prefix + op.mFeatures.keyAt(i) + "=[\n");
dumpStatesLocked(pw, nowElapsed, op, op.mFeatures.keyAt(i), now, sdf, date,
prefix + " ");
@@ -4563,10 +4595,12 @@
int dumpOp = OP_NONE;
String dumpPackage = null;
+ String dumpFeatureId = null;
int dumpUid = Process.INVALID_UID;
int dumpMode = -1;
boolean dumpWatchers = false;
boolean dumpHistory = false;
+ @HistoricalOpsRequestFilter int dumpFilter = 0;
if (args != null) {
for (int i=0; i<args.length; i++) {
@@ -4583,6 +4617,7 @@
return;
}
dumpOp = Shell.strOpToOp(args[i], pw);
+ dumpFilter |= FILTER_BY_OP_NAMES;
if (dumpOp < 0) {
return;
}
@@ -4593,6 +4628,7 @@
return;
}
dumpPackage = args[i];
+ dumpFilter |= FILTER_BY_PACKAGE_NAME;
try {
dumpUid = AppGlobals.getPackageManager().getPackageUid(dumpPackage,
PackageManager.MATCH_KNOWN_PACKAGES | PackageManager.MATCH_INSTANT,
@@ -4604,6 +4640,15 @@
return;
}
dumpUid = UserHandle.getAppId(dumpUid);
+ dumpFilter |= FILTER_BY_UID;
+ } else if ("--featureId".equals(arg)) {
+ i++;
+ if (i >= args.length) {
+ pw.println("No argument for --featureId option");
+ return;
+ }
+ dumpFeatureId = args[i];
+ dumpFilter |= FILTER_BY_FEATURE_ID;
} else if ("--mode".equals(arg)) {
i++;
if (i >= args.length) {
@@ -4640,8 +4685,8 @@
final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
final Date date = new Date();
boolean needSep = false;
- if (dumpOp < 0 && dumpMode < 0 && dumpPackage == null && mProfileOwners != null
- && !dumpWatchers && !dumpHistory) {
+ if (dumpFilter == 0 && dumpMode < 0 && mProfileOwners != null && !dumpWatchers
+ && !dumpHistory) {
pw.println(" Profile owners:");
for (int poi = 0; poi < mProfileOwners.size(); poi++) {
pw.print(" User #");
@@ -4944,7 +4989,8 @@
pw.print("="); pw.print(AppOpsManager.modeToName(mode));
}
pw.println("): ");
- dumpStatesLocked(pw, nowElapsed, op, now, sdf, date, " ");
+ dumpStatesLocked(pw, dumpFeatureId, dumpFilter, nowElapsed, op, now, sdf,
+ date, " ");
}
}
}
@@ -5043,7 +5089,8 @@
// Must not hold the appops lock
if (dumpHistory && !dumpWatchers) {
- mHistoricalRegistry.dump(" ", pw, dumpUid, dumpPackage, dumpOp);
+ mHistoricalRegistry.dump(" ", pw, dumpUid, dumpPackage, dumpFeatureId, dumpOp,
+ dumpFilter);
}
}
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index 2175ca0..cd450d4 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -15,12 +15,19 @@
*/
package com.android.server.appop;
+import static android.app.AppOpsManager.FILTER_BY_FEATURE_ID;
+import static android.app.AppOpsManager.FILTER_BY_OP_NAMES;
+import static android.app.AppOpsManager.FILTER_BY_PACKAGE_NAME;
+import static android.app.AppOpsManager.FILTER_BY_UID;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
+import android.app.AppOpsManager.HistoricalFeatureOps;
import android.app.AppOpsManager.HistoricalMode;
import android.app.AppOpsManager.HistoricalOp;
import android.app.AppOpsManager.HistoricalOps;
+import android.app.AppOpsManager.HistoricalOpsRequestFilter;
import android.app.AppOpsManager.HistoricalPackageOps;
import android.app.AppOpsManager.HistoricalUidOps;
import android.app.AppOpsManager.OpFlags;
@@ -72,6 +79,7 @@
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@@ -273,8 +281,9 @@
+ "=" + setting + " resetting!");
}
- void dump(String prefix, PrintWriter pw, int filterUid,
- String filterPackage, int filterOp) {
+ void dump(String prefix, PrintWriter pw, int filterUid, @Nullable String filterPackage,
+ @Nullable String filterFeatureId, int filterOp,
+ @HistoricalOpsRequestFilter int filter) {
if (!isApiEnabled()) {
return;
}
@@ -289,7 +298,7 @@
pw.println(AppOpsManager.historicalModeToString(mMode));
final StringDumpVisitor visitor = new StringDumpVisitor(prefix + " ",
- pw, filterUid, filterPackage, filterOp);
+ pw, filterUid, filterPackage, filterFeatureId, filterOp, filter);
final long nowMillis = System.currentTimeMillis();
// Dump in memory state first
@@ -329,7 +338,8 @@
}
void getHistoricalOpsFromDiskRaw(int uid, @NonNull String packageName,
- @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis,
+ @Nullable String featureId, @Nullable String[] opNames,
+ @HistoricalOpsRequestFilter int filter, long beginTimeMillis, long endTimeMillis,
@OpFlags int flags, @NonNull RemoteCallback callback) {
if (!isApiEnabled()) {
callback.sendResult(new Bundle());
@@ -344,8 +354,8 @@
return;
}
final HistoricalOps result = new HistoricalOps(beginTimeMillis, endTimeMillis);
- mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, opNames,
- beginTimeMillis, endTimeMillis, flags);
+ mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, featureId,
+ opNames, filter, beginTimeMillis, endTimeMillis, flags);
final Bundle payload = new Bundle();
payload.putParcelable(AppOpsManager.KEY_HISTORICAL_OPS, result);
callback.sendResult(payload);
@@ -353,9 +363,10 @@
}
}
- void getHistoricalOps(int uid, @NonNull String packageName,
- @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis,
- @OpFlags int flags, @NonNull RemoteCallback callback) {
+ void getHistoricalOps(int uid, @NonNull String packageName, @Nullable String featureId,
+ @Nullable String[] opNames, @HistoricalOpsRequestFilter int filter,
+ long beginTimeMillis, long endTimeMillis, @OpFlags int flags,
+ @NonNull RemoteCallback callback) {
if (!isApiEnabled()) {
callback.sendResult(new Bundle());
return;
@@ -390,8 +401,8 @@
|| inMemoryAdjEndTimeMillis <= currentOps.getBeginTimeMillis())) {
// Some of the current batch falls into the query, so extract that.
final HistoricalOps currentOpsCopy = new HistoricalOps(currentOps);
- currentOpsCopy.filter(uid, packageName, opNames, inMemoryAdjBeginTimeMillis,
- inMemoryAdjEndTimeMillis);
+ currentOpsCopy.filter(uid, packageName, featureId, opNames, filter,
+ inMemoryAdjBeginTimeMillis, inMemoryAdjEndTimeMillis);
result.merge(currentOpsCopy);
}
pendingWrites = new ArrayList<>(mPendingWrites);
@@ -410,8 +421,8 @@
- onDiskAndInMemoryOffsetMillis, 0);
final long onDiskAdjEndTimeMillis = Math.max(inMemoryAdjEndTimeMillis
- onDiskAndInMemoryOffsetMillis, 0);
- mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, opNames,
- onDiskAdjBeginTimeMillis, onDiskAdjEndTimeMillis, flags);
+ mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, featureId,
+ opNames, filter, onDiskAdjBeginTimeMillis, onDiskAdjEndTimeMillis, flags);
}
// Rebase the result time to be since epoch.
@@ -425,43 +436,47 @@
}
void incrementOpAccessedCount(int op, int uid, @NonNull String packageName,
- @UidState int uidState, @OpFlags int flags) {
+ @Nullable String featureId, @UidState int uidState, @OpFlags int flags) {
synchronized (mInMemoryLock) {
if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
if (!isPersistenceInitializedMLocked()) {
Slog.e(LOG_TAG, "Interaction before persistence initialized");
return;
}
- getUpdatedPendingHistoricalOpsMLocked(System.currentTimeMillis())
- .increaseAccessCount(op, uid, packageName, uidState, flags, 1);
+ getUpdatedPendingHistoricalOpsMLocked(
+ System.currentTimeMillis()).increaseAccessCount(op, uid, packageName,
+ featureId, uidState, flags, 1);
}
}
}
void incrementOpRejected(int op, int uid, @NonNull String packageName,
- @UidState int uidState, @OpFlags int flags) {
+ @Nullable String featureId, @UidState int uidState, @OpFlags int flags) {
synchronized (mInMemoryLock) {
if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
if (!isPersistenceInitializedMLocked()) {
Slog.e(LOG_TAG, "Interaction before persistence initialized");
return;
}
- getUpdatedPendingHistoricalOpsMLocked(System.currentTimeMillis())
- .increaseRejectCount(op, uid, packageName, uidState, flags, 1);
+ getUpdatedPendingHistoricalOpsMLocked(
+ System.currentTimeMillis()).increaseRejectCount(op, uid, packageName,
+ featureId, uidState, flags, 1);
}
}
}
void increaseOpAccessDuration(int op, int uid, @NonNull String packageName,
- @UidState int uidState, @OpFlags int flags, long increment) {
+ @Nullable String featureId, @UidState int uidState, @OpFlags int flags,
+ long increment) {
synchronized (mInMemoryLock) {
if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
if (!isPersistenceInitializedMLocked()) {
Slog.e(LOG_TAG, "Interaction before persistence initialized");
return;
}
- getUpdatedPendingHistoricalOpsMLocked(System.currentTimeMillis())
- .increaseAccessDuration(op, uid, packageName, uidState, flags, increment);
+ getUpdatedPendingHistoricalOpsMLocked(
+ System.currentTimeMillis()).increaseAccessDuration(op, uid, packageName,
+ featureId, uidState, flags, increment);
}
}
}
@@ -713,6 +728,7 @@
private static final String TAG_OPS = "ops";
private static final String TAG_UID = "uid";
private static final String TAG_PACKAGE = "pkg";
+ private static final String TAG_FEATURE = "ftr";
private static final String TAG_OP = "op";
private static final String TAG_STATE = "st";
@@ -791,8 +807,8 @@
@Nullable List<HistoricalOps> readHistoryRawDLocked() {
return collectHistoricalOpsBaseDLocked(Process.INVALID_UID /*filterUid*/,
- null /*filterPackageName*/, null /*filterOpNames*/,
- 0 /*filterBeginTimeMills*/, Long.MAX_VALUE /*filterEndTimeMills*/,
+ null /*filterPackageName*/, null /*filterFeatureId*/, null /*filterOpNames*/,
+ 0 /*filter*/, 0 /*filterBeginTimeMills*/, Long.MAX_VALUE /*filterEndTimeMills*/,
AppOpsManager.OP_FLAGS_ALL);
}
@@ -846,11 +862,12 @@
}
private void collectHistoricalOpsDLocked(@NonNull HistoricalOps currentOps,
- int filterUid, @NonNull String filterPackageName, @Nullable String[] filterOpNames,
+ int filterUid, @Nullable String filterPackageName, @Nullable String filterFeatureId,
+ @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter,
long filterBeingMillis, long filterEndMillis, @OpFlags int filterFlags) {
final List<HistoricalOps> readOps = collectHistoricalOpsBaseDLocked(filterUid,
- filterPackageName, filterOpNames, filterBeingMillis, filterEndMillis,
- filterFlags);
+ filterPackageName, filterFeatureId, filterOpNames, filter, filterBeingMillis,
+ filterEndMillis, filterFlags);
if (readOps != null) {
final int readCount = readOps.size();
for (int i = 0; i < readCount; i++) {
@@ -861,7 +878,8 @@
}
private @Nullable LinkedList<HistoricalOps> collectHistoricalOpsBaseDLocked(
- int filterUid, @NonNull String filterPackageName, @Nullable String[] filterOpNames,
+ int filterUid, @Nullable String filterPackageName, @Nullable String filterFeatureId,
+ @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter,
long filterBeginTimeMillis, long filterEndTimeMillis, @OpFlags int filterFlags) {
File baseDir = null;
try {
@@ -874,9 +892,9 @@
final Set<String> historyFiles = getHistoricalFileNames(baseDir);
final long[] globalContentOffsetMillis = {0};
final LinkedList<HistoricalOps> ops = collectHistoricalOpsRecursiveDLocked(
- baseDir, filterUid, filterPackageName, filterOpNames, filterBeginTimeMillis,
- filterEndTimeMillis, filterFlags, globalContentOffsetMillis,
- null /*outOps*/, 0 /*depth*/, historyFiles);
+ baseDir, filterUid, filterPackageName, filterFeatureId, filterOpNames,
+ filter, filterBeginTimeMillis, filterEndTimeMillis, filterFlags,
+ globalContentOffsetMillis, null /*outOps*/, 0 /*depth*/, historyFiles);
if (DEBUG) {
filesInvariant.stopTracking(baseDir);
}
@@ -890,8 +908,9 @@
}
private @Nullable LinkedList<HistoricalOps> collectHistoricalOpsRecursiveDLocked(
- @NonNull File baseDir, int filterUid, @NonNull String filterPackageName,
- @Nullable String[] filterOpNames, long filterBeginTimeMillis,
+ @NonNull File baseDir, int filterUid, @Nullable String filterPackageName,
+ @Nullable String filterFeatureId, @Nullable String[] filterOpNames,
+ @HistoricalOpsRequestFilter int filter, long filterBeginTimeMillis,
long filterEndTimeMillis, @OpFlags int filterFlags,
@NonNull long[] globalContentOffsetMillis,
@Nullable LinkedList<HistoricalOps> outOps, int depth,
@@ -908,17 +927,17 @@
// Read historical data at this level
final List<HistoricalOps> readOps = readHistoricalOpsLocked(baseDir,
previousIntervalEndMillis, currentIntervalEndMillis, filterUid,
- filterPackageName, filterOpNames, filterBeginTimeMillis, filterEndTimeMillis,
- filterFlags, globalContentOffsetMillis, depth, historyFiles);
-
+ filterPackageName, filterFeatureId, filterOpNames, filter,
+ filterBeginTimeMillis, filterEndTimeMillis, filterFlags,
+ globalContentOffsetMillis, depth, historyFiles);
// Empty is a special signal to stop diving
if (readOps != null && readOps.isEmpty()) {
return outOps;
}
// Collect older historical data from subsequent levels
- outOps = collectHistoricalOpsRecursiveDLocked(
- baseDir, filterUid, filterPackageName, filterOpNames, filterBeginTimeMillis,
+ outOps = collectHistoricalOpsRecursiveDLocked(baseDir, filterUid, filterPackageName,
+ filterFeatureId, filterOpNames, filter, filterBeginTimeMillis,
filterEndTimeMillis, filterFlags, globalContentOffsetMillis, outOps, depth + 1,
historyFiles);
@@ -987,10 +1006,10 @@
final List<HistoricalOps> existingOps = readHistoricalOpsLocked(oldBaseDir,
previousIntervalEndMillis, currentIntervalEndMillis,
Process.INVALID_UID /*filterUid*/, null /*filterPackageName*/,
- null /*filterOpNames*/, Long.MIN_VALUE /*filterBeginTimeMillis*/,
- Long.MAX_VALUE /*filterEndTimeMillis*/, AppOpsManager.OP_FLAGS_ALL,
- null, depth, null /*historyFiles*/);
-
+ null /*filterFeatureId*/, null /*filterOpNames*/, 0 /*filter*/,
+ Long.MIN_VALUE /*filterBeginTimeMillis*/,
+ Long.MAX_VALUE /*filterEndTimeMillis*/, AppOpsManager.OP_FLAGS_ALL, null, depth,
+ null /*historyFiles*/);
if (DEBUG) {
enforceOpsWellFormed(existingOps);
}
@@ -1100,8 +1119,9 @@
}
private @Nullable List<HistoricalOps> readHistoricalOpsLocked(File baseDir,
- long intervalBeginMillis, long intervalEndMillis,
- int filterUid, @Nullable String filterPackageName, @Nullable String[] filterOpNames,
+ long intervalBeginMillis, long intervalEndMillis, int filterUid,
+ @Nullable String filterPackageName, @Nullable String filterFeatureId,
+ @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter,
long filterBeginTimeMillis, long filterEndTimeMillis, @OpFlags int filterFlags,
@Nullable long[] cumulativeOverflowMillis, int depth,
@NonNull Set<String> historyFiles)
@@ -1127,13 +1147,14 @@
return null;
}
}
- return readHistoricalOpsLocked(file, filterUid, filterPackageName, filterOpNames,
- filterBeginTimeMillis, filterEndTimeMillis, filterFlags,
+ return readHistoricalOpsLocked(file, filterUid, filterPackageName, filterFeatureId,
+ filterOpNames, filter, filterBeginTimeMillis, filterEndTimeMillis, filterFlags,
cumulativeOverflowMillis);
}
private @Nullable List<HistoricalOps> readHistoricalOpsLocked(@NonNull File file,
- int filterUid, @Nullable String filterPackageName, @Nullable String[] filterOpNames,
+ int filterUid, @Nullable String filterPackageName, @Nullable String filterFeatureId,
+ @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter,
long filterBeginTimeMillis, long filterEndTimeMillis, @OpFlags int filterFlags,
@Nullable long[] cumulativeOverflowMillis)
throws IOException, XmlPullParserException {
@@ -1158,9 +1179,10 @@
final int depth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, depth)) {
if (TAG_OPS.equals(parser.getName())) {
- final HistoricalOps ops = readeHistoricalOpsDLocked(parser,
- filterUid, filterPackageName, filterOpNames, filterBeginTimeMillis,
- filterEndTimeMillis, filterFlags, cumulativeOverflowMillis);
+ final HistoricalOps ops = readeHistoricalOpsDLocked(parser, filterUid,
+ filterPackageName, filterFeatureId, filterOpNames, filter,
+ filterBeginTimeMillis, filterEndTimeMillis, filterFlags,
+ cumulativeOverflowMillis);
if (ops == null) {
continue;
}
@@ -1193,7 +1215,8 @@
private @Nullable HistoricalOps readeHistoricalOpsDLocked(
@NonNull XmlPullParser parser, int filterUid, @Nullable String filterPackageName,
- @Nullable String[] filterOpNames, long filterBeginTimeMillis,
+ @Nullable String filterFeatureId, @Nullable String[] filterOpNames,
+ @HistoricalOpsRequestFilter int filter, long filterBeginTimeMillis,
long filterEndTimeMillis, @OpFlags int filterFlags,
@Nullable long[] cumulativeOverflowMillis)
throws IOException, XmlPullParserException {
@@ -1222,7 +1245,8 @@
while (XmlUtils.nextElementWithin(parser, depth)) {
if (TAG_UID.equals(parser.getName())) {
final HistoricalOps returnedOps = readHistoricalUidOpsDLocked(ops, parser,
- filterUid, filterPackageName, filterOpNames, filterFlags, filterScale);
+ filterUid, filterPackageName, filterFeatureId, filterOpNames, filter,
+ filterFlags, filterScale);
if (ops == null) {
ops = returnedOps;
}
@@ -1236,11 +1260,12 @@
private @Nullable HistoricalOps readHistoricalUidOpsDLocked(
@Nullable HistoricalOps ops, @NonNull XmlPullParser parser, int filterUid,
- @Nullable String filterPackageName, @Nullable String[] filterOpNames,
+ @Nullable String filterPackageName, @Nullable String filterFeatureId,
+ @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter,
@OpFlags int filterFlags, double filterScale)
throws IOException, XmlPullParserException {
final int uid = XmlUtils.readIntAttribute(parser, ATTR_NAME);
- if (filterUid != Process.INVALID_UID && filterUid != uid) {
+ if ((filter & FILTER_BY_UID) != 0 && filterUid != uid) {
XmlUtils.skipCurrentTag(parser);
return null;
}
@@ -1248,8 +1273,8 @@
while (XmlUtils.nextElementWithin(parser, depth)) {
if (TAG_PACKAGE.equals(parser.getName())) {
final HistoricalOps returnedOps = readHistoricalPackageOpsDLocked(ops,
- uid, parser, filterPackageName, filterOpNames, filterFlags,
- filterScale);
+ uid, parser, filterPackageName, filterFeatureId, filterOpNames, filter,
+ filterFlags, filterScale);
if (ops == null) {
ops = returnedOps;
}
@@ -1260,19 +1285,46 @@
private @Nullable HistoricalOps readHistoricalPackageOpsDLocked(
@Nullable HistoricalOps ops, int uid, @NonNull XmlPullParser parser,
- @Nullable String filterPackageName, @Nullable String[] filterOpNames,
+ @Nullable String filterPackageName, @Nullable String filterFeatureId,
+ @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter,
@OpFlags int filterFlags, double filterScale)
throws IOException, XmlPullParserException {
final String packageName = XmlUtils.readStringAttribute(parser, ATTR_NAME);
- if (filterPackageName != null && !filterPackageName.equals(packageName)) {
+ if ((filter & FILTER_BY_PACKAGE_NAME) != 0 && !filterPackageName.equals(packageName)) {
+ XmlUtils.skipCurrentTag(parser);
+ return null;
+ }
+ final int depth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, depth)) {
+ if (TAG_FEATURE.equals(parser.getName())) {
+ final HistoricalOps returnedOps = readHistoricalFeatureOpsDLocked(ops, uid,
+ packageName, parser, filterFeatureId, filterOpNames, filter,
+ filterFlags, filterScale);
+ if (ops == null) {
+ ops = returnedOps;
+ }
+ }
+ }
+ return ops;
+ }
+
+ private @Nullable HistoricalOps readHistoricalFeatureOpsDLocked(@Nullable HistoricalOps ops,
+ int uid, String packageName, @NonNull XmlPullParser parser,
+ @Nullable String filterFeatureId, @Nullable String[] filterOpNames,
+ @HistoricalOpsRequestFilter int filter, @OpFlags int filterFlags,
+ double filterScale)
+ throws IOException, XmlPullParserException {
+ final String featureId = XmlUtils.readStringAttribute(parser, ATTR_NAME);
+ if ((filter & FILTER_BY_FEATURE_ID) != 0 && !Objects.equals(filterFeatureId,
+ featureId)) {
XmlUtils.skipCurrentTag(parser);
return null;
}
final int depth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, depth)) {
if (TAG_OP.equals(parser.getName())) {
- final HistoricalOps returnedOps = readHistoricalOpDLocked(ops, uid,
- packageName, parser, filterOpNames, filterFlags, filterScale);
+ final HistoricalOps returnedOps = readHistoricalOpDLocked(ops, uid, packageName,
+ featureId, parser, filterOpNames, filter, filterFlags, filterScale);
if (ops == null) {
ops = returnedOps;
}
@@ -1282,11 +1334,13 @@
}
private @Nullable HistoricalOps readHistoricalOpDLocked(@Nullable HistoricalOps ops,
- int uid, String packageName, @NonNull XmlPullParser parser,
- @Nullable String[] filterOpNames, @OpFlags int filterFlags, double filterScale)
+ int uid, @NonNull String packageName, @Nullable String featureId,
+ @NonNull XmlPullParser parser, @Nullable String[] filterOpNames,
+ @HistoricalOpsRequestFilter int filter, @OpFlags int filterFlags,
+ double filterScale)
throws IOException, XmlPullParserException {
final int op = XmlUtils.readIntAttribute(parser, ATTR_NAME);
- if (filterOpNames != null && !ArrayUtils.contains(filterOpNames,
+ if ((filter & FILTER_BY_OP_NAMES) != 0 && !ArrayUtils.contains(filterOpNames,
AppOpsManager.opToPublicName(op))) {
XmlUtils.skipCurrentTag(parser);
return null;
@@ -1295,7 +1349,7 @@
while (XmlUtils.nextElementWithin(parser, depth)) {
if (TAG_STATE.equals(parser.getName())) {
final HistoricalOps returnedOps = readStateDLocked(ops, uid,
- packageName, op, parser, filterFlags, filterScale);
+ packageName, featureId, op, parser, filterFlags, filterScale);
if (ops == null) {
ops = returnedOps;
}
@@ -1305,8 +1359,9 @@
}
private @Nullable HistoricalOps readStateDLocked(@Nullable HistoricalOps ops,
- int uid, String packageName, int op, @NonNull XmlPullParser parser,
- @OpFlags int filterFlags, double filterScale) throws IOException {
+ int uid, @NonNull String packageName, @Nullable String featureId, int op,
+ @NonNull XmlPullParser parser, @OpFlags int filterFlags, double filterScale)
+ throws IOException {
final long key = XmlUtils.readLongAttribute(parser, ATTR_NAME);
final int flags = AppOpsManager.extractFlagsFromKey(key) & filterFlags;
if (flags == 0) {
@@ -1322,7 +1377,8 @@
if (ops == null) {
ops = new HistoricalOps(0, 0);
}
- ops.increaseAccessCount(op, uid, packageName, uidState, flags, accessCount);
+ ops.increaseAccessCount(op, uid, packageName, featureId, uidState, flags,
+ accessCount);
}
long rejectCount = XmlUtils.readLongAttribute(parser, ATTR_REJECT_COUNT, 0);
if (rejectCount > 0) {
@@ -1333,7 +1389,8 @@
if (ops == null) {
ops = new HistoricalOps(0, 0);
}
- ops.increaseRejectCount(op, uid, packageName, uidState, flags, rejectCount);
+ ops.increaseRejectCount(op, uid, packageName, featureId, uidState, flags,
+ rejectCount);
}
long accessDuration = XmlUtils.readLongAttribute(parser, ATTR_ACCESS_DURATION, 0);
if (accessDuration > 0) {
@@ -1344,7 +1401,8 @@
if (ops == null) {
ops = new HistoricalOps(0, 0);
}
- ops.increaseAccessDuration(op, uid, packageName, uidState, flags, accessDuration);
+ ops.increaseAccessDuration(op, uid, packageName, featureId, uidState, flags,
+ accessDuration);
}
return ops;
}
@@ -1409,14 +1467,26 @@
@NonNull XmlSerializer serializer) throws IOException {
serializer.startTag(null, TAG_PACKAGE);
serializer.attribute(null, ATTR_NAME, packageOps.getPackageName());
- final int opCount = packageOps.getOpCount();
- for (int i = 0; i < opCount; i++) {
- final HistoricalOp op = packageOps.getOpAt(i);
- writeHistoricalOpDLocked(op, serializer);
+ final int numFeatures = packageOps.getFeatureCount();
+ for (int i = 0; i < numFeatures; i++) {
+ final HistoricalFeatureOps op = packageOps.getFeatureOpsAt(i);
+ writeHistoricalFeatureOpsDLocked(op, serializer);
}
serializer.endTag(null, TAG_PACKAGE);
}
+ private void writeHistoricalFeatureOpsDLocked(@NonNull HistoricalFeatureOps featureOps,
+ @NonNull XmlSerializer serializer) throws IOException {
+ serializer.startTag(null, TAG_FEATURE);
+ XmlUtils.writeStringAttribute(serializer, ATTR_NAME, featureOps.getFeatureId());
+ final int opCount = featureOps.getOpCount();
+ for (int i = 0; i < opCount; i++) {
+ final HistoricalOp op = featureOps.getOpAt(i);
+ writeHistoricalOpDLocked(op, serializer);
+ }
+ serializer.endTag(null, TAG_FEATURE);
+ }
+
private void writeHistoricalOpDLocked(@NonNull HistoricalOp op,
@NonNull XmlSerializer serializer) throws IOException {
final LongSparseArray keys = op.collectKeys();
@@ -1648,24 +1718,31 @@
private final @NonNull String mOpsPrefix;
private final @NonNull String mUidPrefix;
private final @NonNull String mPackagePrefix;
+ private final @NonNull String mFeaturePrefix;
private final @NonNull String mEntryPrefix;
private final @NonNull String mUidStatePrefix;
private final @NonNull PrintWriter mWriter;
private final int mFilterUid;
private final String mFilterPackage;
+ private final String mFilterFeatureId;
private final int mFilterOp;
+ private final @HistoricalOpsRequestFilter int mFilter;
- StringDumpVisitor(@NonNull String prefix, @NonNull PrintWriter writer,
- int filterUid, String filterPackage, int filterOp) {
+ StringDumpVisitor(@NonNull String prefix, @NonNull PrintWriter writer, int filterUid,
+ @Nullable String filterPackage, @Nullable String filterFeatureId, int filterOp,
+ @HistoricalOpsRequestFilter int filter) {
mOpsPrefix = prefix + " ";
mUidPrefix = mOpsPrefix + " ";
mPackagePrefix = mUidPrefix + " ";
- mEntryPrefix = mPackagePrefix + " ";
+ mFeaturePrefix = mPackagePrefix + " ";
+ mEntryPrefix = mFeaturePrefix + " ";
mUidStatePrefix = mEntryPrefix + " ";
mWriter = writer;
mFilterUid = filterUid;
mFilterPackage = filterPackage;
+ mFilterFeatureId = filterFeatureId;
mFilterOp = filterOp;
+ mFilter = filter;
}
@Override
@@ -1691,7 +1768,7 @@
@Override
public void visitHistoricalUidOps(HistoricalUidOps ops) {
- if (mFilterUid != Process.INVALID_UID && mFilterUid != ops.getUid()) {
+ if ((mFilter & FILTER_BY_UID) != 0 && mFilterUid != ops.getUid()) {
return;
}
mWriter.println();
@@ -1703,7 +1780,8 @@
@Override
public void visitHistoricalPackageOps(HistoricalPackageOps ops) {
- if (mFilterPackage != null && !mFilterPackage.equals(ops.getPackageName())) {
+ if ((mFilter & FILTER_BY_PACKAGE_NAME) != 0 && !mFilterPackage.equals(
+ ops.getPackageName())) {
return;
}
mWriter.print(mPackagePrefix);
@@ -1713,8 +1791,20 @@
}
@Override
+ public void visitHistoricalFeatureOps(HistoricalFeatureOps ops) {
+ if ((mFilter & FILTER_BY_FEATURE_ID) != 0 && !Objects.equals(mFilterPackage,
+ ops.getFeatureId())) {
+ return;
+ }
+ mWriter.print(mFeaturePrefix);
+ mWriter.print("Feature ");
+ mWriter.print(ops.getFeatureId());
+ mWriter.println(":");
+ }
+
+ @Override
public void visitHistoricalOp(HistoricalOp ops) {
- if (mFilterOp != AppOpsManager.OP_NONE && mFilterOp != ops.getOpCode()) {
+ if ((mFilter & FILTER_BY_OP_NAMES) != 0 && mFilterOp != ops.getOpCode()) {
return;
}
mWriter.print(mEntryPrefix);
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index cf83dd6..f15d999 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -17,8 +17,10 @@
package com.android.server.compat;
import android.compat.Compatibility.ChangeConfig;
+import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.Environment;
+import android.os.RemoteException;
import android.text.TextUtils;
import android.util.LongArray;
import android.util.LongSparseArray;
@@ -26,8 +28,11 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.compat.AndroidBuildClassifier;
import com.android.internal.compat.CompatibilityChangeConfig;
import com.android.internal.compat.CompatibilityChangeInfo;
+import com.android.internal.compat.IOverrideValidator;
+import com.android.internal.compat.OverrideAllowedState;
import com.android.server.compat.config.Change;
import com.android.server.compat.config.XmlParser;
@@ -54,22 +59,14 @@
private static final String TAG = "CompatConfig";
- private static final CompatConfig sInstance = new CompatConfig().initConfigFromLib(
- Environment.buildPath(
- Environment.getRootDirectory(), "etc", "compatconfig"));
-
@GuardedBy("mChanges")
private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>();
- @VisibleForTesting
- CompatConfig() {
- }
+ private IOverrideValidator mOverrideValidator;
- /**
- * @return The static instance of this class to be used within the system server.
- */
- static CompatConfig get() {
- return sInstance;
+ @VisibleForTesting
+ CompatConfig(AndroidBuildClassifier androidBuildClassifier, Context context) {
+ mOverrideValidator = new OverrideValidatorImpl(androidBuildClassifier, context, this);
}
/**
@@ -159,8 +156,12 @@
* @param enabled If the change should be enabled or disabled.
* @return {@code true} if the change existed before adding the override.
*/
- boolean addOverride(long changeId, String packageName, boolean enabled) {
+ boolean addOverride(long changeId, String packageName, boolean enabled)
+ throws RemoteException, SecurityException {
boolean alreadyKnown = true;
+ OverrideAllowedState allowedState =
+ mOverrideValidator.getOverrideAllowedState(changeId, packageName);
+ allowedState.enforce(changeId, packageName);
synchronized (mChanges) {
CompatChange c = mChanges.get(changeId);
if (c == null) {
@@ -186,6 +187,20 @@
}
/**
+ * Returns the minimum sdk version for which this change should be enabled (or 0 if it is not
+ * target sdk gated).
+ */
+ int minTargetSdkForChangeId(long changeId) {
+ synchronized (mChanges) {
+ CompatChange c = mChanges.get(changeId);
+ if (c == null) {
+ return 0;
+ }
+ return c.getEnableAfterTargetSdk();
+ }
+ }
+
+ /**
* Removes an override previously added via {@link #addOverride(long, String, boolean)}. This
* restores the default behaviour for the given change and app, once any app processes have been
* restarted.
@@ -194,34 +209,44 @@
* @param packageName The app package name that was overridden.
* @return {@code true} if an override existed;
*/
- boolean removeOverride(long changeId, String packageName) {
+ boolean removeOverride(long changeId, String packageName)
+ throws RemoteException, SecurityException {
boolean overrideExists = false;
synchronized (mChanges) {
CompatChange c = mChanges.get(changeId);
- if (c != null) {
- overrideExists = true;
- c.removePackageOverride(packageName);
+ try {
+ if (c != null) {
+ OverrideAllowedState allowedState =
+ mOverrideValidator.getOverrideAllowedState(changeId, packageName);
+ allowedState.enforce(changeId, packageName);
+ overrideExists = true;
+ c.removePackageOverride(packageName);
+ }
+ } catch (RemoteException e) {
+ // Should never occur, since validator is in the same process.
+ throw new RuntimeException("Unable to call override validator!", e);
}
}
return overrideExists;
}
/**
- * Overrides the enabled state for a given change and app. This method is intended to be used
- * *only* for debugging purposes.
+ * Overrides the enabled state for a given change and app.
*
* <p>Note, package overrides are not persistent and will be lost on system or runtime restart.
*
* @param overrides list of overrides to default changes config.
* @param packageName app for which the overrides will be applied.
*/
- void addOverrides(CompatibilityChangeConfig overrides, String packageName) {
+ void addOverrides(CompatibilityChangeConfig overrides, String packageName)
+ throws RemoteException, SecurityException {
synchronized (mChanges) {
for (Long changeId : overrides.enabledChanges()) {
addOverride(changeId, packageName, true);
}
for (Long changeId : overrides.disabledChanges()) {
addOverride(changeId, packageName, false);
+
}
}
}
@@ -235,10 +260,22 @@
*
* @param packageName The package for which the overrides should be purged.
*/
- void removePackageOverrides(String packageName) {
+ void removePackageOverrides(String packageName) throws RemoteException, SecurityException {
synchronized (mChanges) {
for (int i = 0; i < mChanges.size(); ++i) {
- mChanges.valueAt(i).removePackageOverride(packageName);
+ try {
+ CompatChange change = mChanges.valueAt(i);
+ OverrideAllowedState allowedState =
+ mOverrideValidator.getOverrideAllowedState(change.getId(),
+ packageName);
+ allowedState.enforce(change.getId(), packageName);
+ if (change != null) {
+ mChanges.valueAt(i).removePackageOverride(packageName);
+ }
+ } catch (RemoteException e) {
+ // Should never occur, since validator is in the same process.
+ throw new RuntimeException("Unable to call override validator!", e);
+ }
}
}
}
@@ -326,17 +363,23 @@
}
}
- CompatConfig initConfigFromLib(File libraryDir) {
+ static CompatConfig create(AndroidBuildClassifier androidBuildClassifier, Context context) {
+ CompatConfig config = new CompatConfig(androidBuildClassifier, context);
+ config.initConfigFromLib(Environment.buildPath(
+ Environment.getRootDirectory(), "etc", "compatconfig"));
+ return config;
+ }
+
+ void initConfigFromLib(File libraryDir) {
if (!libraryDir.exists() || !libraryDir.isDirectory()) {
Slog.e(TAG, "No directory " + libraryDir + ", skipping");
- return this;
+ return;
}
for (File f : libraryDir.listFiles()) {
Slog.d(TAG, "Found a config file: " + f.getPath());
//TODO(b/138222363): Handle duplicate ids across config files.
readConfig(f);
}
- return this;
}
private void readConfig(File configFile) {
@@ -350,4 +393,7 @@
}
}
+ IOverrideValidator getOverrideValidator() {
+ return mOverrideValidator;
+ }
}
diff --git a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
new file mode 100644
index 0000000..dfc0080
--- /dev/null
+++ b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.compat;
+
+import static com.android.internal.compat.OverrideAllowedState.ALLOWED;
+import static com.android.internal.compat.OverrideAllowedState.DISABLED_NON_TARGET_SDK;
+import static com.android.internal.compat.OverrideAllowedState.DISABLED_NOT_DEBUGGABLE;
+import static com.android.internal.compat.OverrideAllowedState.DISABLED_TARGET_SDK_TOO_HIGH;
+import static com.android.internal.compat.OverrideAllowedState.PACKAGE_DOES_NOT_EXIST;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.compat.AndroidBuildClassifier;
+import com.android.internal.compat.IOverrideValidator;
+import com.android.internal.compat.OverrideAllowedState;
+
+/**
+ * Implementation of the policy for allowing compat change overrides.
+ */
+public class OverrideValidatorImpl extends IOverrideValidator.Stub {
+
+ private AndroidBuildClassifier mAndroidBuildClassifier;
+ private Context mContext;
+ private CompatConfig mCompatConfig;
+
+ @VisibleForTesting
+ OverrideValidatorImpl(AndroidBuildClassifier androidBuildClassifier,
+ Context context, CompatConfig config) {
+ mAndroidBuildClassifier = androidBuildClassifier;
+ mContext = context;
+ mCompatConfig = config;
+ }
+
+ @Override
+ public OverrideAllowedState getOverrideAllowedState(long changeId, String packageName) {
+ boolean debuggableBuild = false;
+ boolean finalBuild = false;
+
+ debuggableBuild = mAndroidBuildClassifier.isDebuggableBuild();
+ finalBuild = mAndroidBuildClassifier.isFinalBuild();
+
+ // Allow any override for userdebug or eng builds.
+ if (debuggableBuild) {
+ return new OverrideAllowedState(ALLOWED, -1, -1);
+ }
+ PackageManager packageManager = mContext.getPackageManager();
+ if (packageManager == null) {
+ throw new IllegalStateException("No PackageManager!");
+ }
+ ApplicationInfo applicationInfo;
+ try {
+ applicationInfo = packageManager.getApplicationInfo(packageName, 0);
+ } catch (NameNotFoundException e) {
+ return new OverrideAllowedState(PACKAGE_DOES_NOT_EXIST, -1, -1);
+ }
+ int appTargetSdk = applicationInfo.targetSdkVersion;
+ // Only allow overriding debuggable apps.
+ if ((applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
+ return new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1);
+ }
+ int minTargetSdk = mCompatConfig.minTargetSdkForChangeId(changeId);
+ // Do not allow overriding non-target sdk gated changes on user builds
+ if (minTargetSdk == -1) {
+ return new OverrideAllowedState(DISABLED_NON_TARGET_SDK, appTargetSdk, minTargetSdk);
+ }
+ // Allow overriding any change for debuggable apps on non-final builds.
+ if (!finalBuild) {
+ return new OverrideAllowedState(ALLOWED, appTargetSdk, minTargetSdk);
+ }
+ // Only allow to opt-in for a targetSdk gated change.
+ if (applicationInfo.targetSdkVersion < minTargetSdk) {
+ return new OverrideAllowedState(ALLOWED, appTargetSdk, minTargetSdk);
+ }
+ return new OverrideAllowedState(DISABLED_TARGET_SDK_TOO_HIGH, appTargetSdk, minTargetSdk);
+ }
+}
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 6ec4b9f..bb8b12e 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -27,9 +27,12 @@
import android.util.Slog;
import android.util.StatsLog;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.compat.AndroidBuildClassifier;
import com.android.internal.compat.ChangeReporter;
import com.android.internal.compat.CompatibilityChangeConfig;
import com.android.internal.compat.CompatibilityChangeInfo;
+import com.android.internal.compat.IOverrideValidator;
import com.android.internal.compat.IPlatformCompat;
import com.android.internal.util.DumpUtils;
import com.android.server.LocalServices;
@@ -46,11 +49,21 @@
private final Context mContext;
private final ChangeReporter mChangeReporter;
+ private final CompatConfig mCompatConfig;
public PlatformCompat(Context context) {
mContext = context;
mChangeReporter = new ChangeReporter(
StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__SYSTEM_SERVER);
+ mCompatConfig = CompatConfig.create(new AndroidBuildClassifier(), mContext);
+ }
+
+ @VisibleForTesting
+ PlatformCompat(Context context, CompatConfig compatConfig) {
+ mContext = context;
+ mChangeReporter = new ChangeReporter(
+ StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__SYSTEM_SERVER);
+ mCompatConfig = compatConfig;
}
@Override
@@ -75,7 +88,7 @@
@Override
public boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) {
- if (CompatConfig.get().isChangeEnabled(changeId, appInfo)) {
+ if (mCompatConfig.isChangeEnabled(changeId, appInfo)) {
reportChange(changeId, appInfo.uid,
StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__ENABLED);
return true;
@@ -122,57 +135,59 @@
* otherwise.
*/
public boolean registerListener(long changeId, CompatChange.ChangeListener listener) {
- return CompatConfig.get().registerListener(changeId, listener);
+ return mCompatConfig.registerListener(changeId, listener);
}
@Override
- public void setOverrides(CompatibilityChangeConfig overrides, String packageName) {
- CompatConfig.get().addOverrides(overrides, packageName);
+ public void setOverrides(CompatibilityChangeConfig overrides, String packageName)
+ throws RemoteException, SecurityException {
+ mCompatConfig.addOverrides(overrides, packageName);
killPackage(packageName);
}
@Override
- public void setOverridesForTest(CompatibilityChangeConfig overrides, String packageName) {
- CompatConfig.get().addOverrides(overrides, packageName);
+ public void setOverridesForTest(CompatibilityChangeConfig overrides, String packageName)
+ throws RemoteException, SecurityException {
+ mCompatConfig.addOverrides(overrides, packageName);
}
@Override
- public void clearOverrides(String packageName) {
- CompatConfig config = CompatConfig.get();
- config.removePackageOverrides(packageName);
+ public void clearOverrides(String packageName) throws RemoteException, SecurityException {
+ mCompatConfig.removePackageOverrides(packageName);
killPackage(packageName);
}
@Override
- public void clearOverridesForTest(String packageName) {
- CompatConfig config = CompatConfig.get();
- config.removePackageOverrides(packageName);
+ public void clearOverridesForTest(String packageName)
+ throws RemoteException, SecurityException {
+ mCompatConfig.removePackageOverrides(packageName);
}
@Override
- public boolean clearOverride(long changeId, String packageName) {
- boolean existed = CompatConfig.get().removeOverride(changeId, packageName);
+ public boolean clearOverride(long changeId, String packageName)
+ throws RemoteException, SecurityException {
+ boolean existed = mCompatConfig.removeOverride(changeId, packageName);
killPackage(packageName);
return existed;
}
@Override
public CompatibilityChangeConfig getAppConfig(ApplicationInfo appInfo) {
- return CompatConfig.get().getAppConfig(appInfo);
+ return mCompatConfig.getAppConfig(appInfo);
}
@Override
public CompatibilityChangeInfo[] listAllChanges() {
- return CompatConfig.get().dumpChanges();
+ return mCompatConfig.dumpChanges();
}
/**
* Check whether the change is known to the compat config.
- * @param changeId
+ *
* @return {@code true} if the change is known.
*/
public boolean isKnownChangeId(long changeId) {
- return CompatConfig.get().isKnownChangeId(changeId);
+ return mCompatConfig.isKnownChangeId(changeId);
}
@@ -182,11 +197,11 @@
*
* @param appInfo The app in question
* @return A sorted long array of change IDs. We use a primitive array to minimize memory
- * footprint: Every app process will store this array statically so we aim to reduce
- * overhead as much as possible.
+ * footprint: Every app process will store this array statically so we aim to reduce
+ * overhead as much as possible.
*/
public long[] getDisabledChanges(ApplicationInfo appInfo) {
- return CompatConfig.get().getDisabledChanges(appInfo);
+ return mCompatConfig.getDisabledChanges(appInfo);
}
/**
@@ -196,18 +211,24 @@
* @return The change ID, or {@code -1} if no change with that name exists.
*/
public long lookupChangeId(String name) {
- return CompatConfig.get().lookupChangeId(name);
+ return mCompatConfig.lookupChangeId(name);
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) return;
- CompatConfig.get().dumpConfig(pw);
+ mCompatConfig.dumpConfig(pw);
+ }
+
+ @Override
+ public IOverrideValidator getOverrideValidator() {
+ return mCompatConfig.getOverrideValidator();
}
/**
* Clears information stored about events reported on behalf of an app.
* To be called once upon app start or end. A second call would be a no-op.
+ *
* @param appInfo the app to reset
*/
public void resetReporting(ApplicationInfo appInfo) {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 10386e7..d7ae5b5 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -77,7 +77,6 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
-import android.provider.Settings;
import android.text.TextUtils;
import android.util.IntArray;
import android.util.Pair;
@@ -1306,13 +1305,21 @@
}
private SurfaceControl.ScreenshotGraphicBuffer screenshotInternal(int displayId) {
- final IBinder token = getDisplayToken(displayId);
- if (token == null) {
- return null;
+ synchronized (mSyncRoot) {
+ final IBinder token = getDisplayToken(displayId);
+ if (token == null) {
+ return null;
+ }
+ final LogicalDisplay logicalDisplay = mLogicalDisplays.get(displayId);
+ if (logicalDisplay == null) {
+ return null;
+ }
+
+ final DisplayInfo displayInfo = logicalDisplay.getDisplayInfoLocked();
+ return SurfaceControl.screenshotToBufferWithSecureLayersUnsafe(token, new Rect(),
+ displayInfo.getNaturalWidth(), displayInfo.getNaturalHeight(),
+ false /* useIdentityTransform */, 0 /* rotation */);
}
- return SurfaceControl.screenshotToBufferWithSecureLayersUnsafe(
- token, new Rect(), 0 /* width */, 0 /* height */,
- false /* useIdentityTransform */, 0 /* rotation */);
}
@VisibleForTesting
@@ -1346,6 +1353,35 @@
return SurfaceControl.getDisplayedContentSample(token, maxFrames, timestamp);
}
+ void resetBrightnessConfiguration() {
+ setBrightnessConfigurationForUserInternal(null, mContext.getUserId(),
+ mContext.getPackageName());
+ }
+
+ void setAutoBrightnessLoggingEnabled(boolean enabled) {
+ if (mDisplayPowerController != null) {
+ synchronized (mSyncRoot) {
+ mDisplayPowerController.setAutoBrightnessLoggingEnabled(enabled);
+ }
+ }
+ }
+
+ void setDisplayWhiteBalanceLoggingEnabled(boolean enabled) {
+ if (mDisplayPowerController != null) {
+ synchronized (mSyncRoot) {
+ mDisplayPowerController.setDisplayWhiteBalanceLoggingEnabled(enabled);
+ }
+ }
+ }
+
+ void setAmbientColorTemperatureOverride(float cct) {
+ if (mDisplayPowerController != null) {
+ synchronized (mSyncRoot) {
+ mDisplayPowerController.setAmbientColorTemperatureOverride(cct);
+ }
+ }
+ }
+
private void onDesiredDisplayModeSpecsChangedInternal() {
boolean changed = false;
synchronized (mSyncRoot) {
@@ -2249,13 +2285,8 @@
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback,
ResultReceiver resultReceiver) {
- final long token = Binder.clearCallingIdentity();
- try {
- DisplayManagerShellCommand command = new DisplayManagerShellCommand(this);
- command.exec(this, in, out, err, args, callback, resultReceiver);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ new DisplayManagerShellCommand(DisplayManagerService.this).exec(this, in, out, err,
+ args, callback, resultReceiver);
}
@Override // Binder call
@@ -2278,40 +2309,6 @@
}
}
- void setBrightness(int brightness) {
- Settings.System.putIntForUser(mContext.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS, brightness, UserHandle.USER_CURRENT);
- }
-
- void resetBrightnessConfiguration() {
- setBrightnessConfigurationForUserInternal(null, mContext.getUserId(),
- mContext.getPackageName());
- }
-
- void setAutoBrightnessLoggingEnabled(boolean enabled) {
- if (mDisplayPowerController != null) {
- synchronized (mSyncRoot) {
- mDisplayPowerController.setAutoBrightnessLoggingEnabled(enabled);
- }
- }
- }
-
- void setDisplayWhiteBalanceLoggingEnabled(boolean enabled) {
- if (mDisplayPowerController != null) {
- synchronized (mSyncRoot) {
- mDisplayPowerController.setDisplayWhiteBalanceLoggingEnabled(enabled);
- }
- }
- }
-
- void setAmbientColorTemperatureOverride(float cct) {
- if (mDisplayPowerController != null) {
- synchronized (mSyncRoot) {
- mDisplayPowerController.setAmbientColorTemperatureOverride(cct);
- }
- }
- }
-
private boolean validatePackageName(int uid, String packageName) {
if (packageName != null) {
String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index 04d28ea..e87ad41 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -16,17 +16,22 @@
package com.android.server.display;
+import android.Manifest;
+import android.content.Context;
import android.content.Intent;
+import android.os.Binder;
import android.os.ShellCommand;
+import android.os.UserHandle;
+import android.provider.Settings;
import java.io.PrintWriter;
class DisplayManagerShellCommand extends ShellCommand {
private static final String TAG = "DisplayManagerShellCommand";
- private final DisplayManagerService.BinderService mService;
+ private final DisplayManagerService mService;
- DisplayManagerShellCommand(DisplayManagerService.BinderService service) {
+ DisplayManagerShellCommand(DisplayManagerService service) {
mService = service;
}
@@ -96,7 +101,19 @@
getErrPrintWriter().println("Error: brightness should be a number between 0 and 1");
return 1;
}
- mService.setBrightness((int) (brightness * 255));
+
+ final Context context = mService.getContext();
+ context.enforceCallingOrSelfPermission(
+ Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS,
+ "Permission required to set the display's brightness");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ Settings.System.putIntForUser(context.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS, (int) (brightness * 255),
+ UserHandle.USER_CURRENT);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
return 0;
}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index eebc738..cf94d46 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -37,6 +37,7 @@
import android.view.Surface;
import android.view.SurfaceControl;
+import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
@@ -641,9 +642,7 @@
@Override
public void setRequestedColorModeLocked(int colorMode) {
- if (requestColorModeLocked(colorMode)) {
- updateDeviceInfoLocked();
- }
+ requestColorModeLocked(colorMode);
}
@Override
@@ -671,12 +670,22 @@
if (mDisplayModeSpecsInvalid || !displayModeSpecs.equals(mDisplayModeSpecs)) {
mDisplayModeSpecsInvalid = false;
mDisplayModeSpecs.copyFrom(displayModeSpecs);
- final IBinder token = getDisplayTokenLocked();
- SurfaceControl.setDesiredDisplayConfigSpecs(token,
+ getHandler().sendMessage(PooledLambda.obtainMessage(
+ LocalDisplayDevice::setDesiredDisplayModeSpecsAsync, this,
+ getDisplayTokenLocked(),
new SurfaceControl.DesiredDisplayConfigSpecs(defaultPhysIndex,
mDisplayModeSpecs.refreshRateRange.min,
- mDisplayModeSpecs.refreshRateRange.max));
- int activePhysIndex = SurfaceControl.getActiveConfig(token);
+ mDisplayModeSpecs.refreshRateRange.max)));
+ }
+ }
+
+ private void setDesiredDisplayModeSpecsAsync(IBinder displayToken,
+ SurfaceControl.DesiredDisplayConfigSpecs configSpecs) {
+ // Do not lock when calling these SurfaceControl methods because they are sync
+ // operations that may block for a while when setting display power mode.
+ SurfaceControl.setDesiredDisplayConfigSpecs(displayToken, configSpecs);
+ final int activePhysIndex = SurfaceControl.getActiveConfig(displayToken);
+ synchronized (getSyncRoot()) {
if (updateActiveModeLocked(activePhysIndex)) {
updateDeviceInfoLocked();
}
@@ -708,19 +717,30 @@
return true;
}
- public boolean requestColorModeLocked(int colorMode) {
+ public void requestColorModeLocked(int colorMode) {
if (mActiveColorMode == colorMode) {
- return false;
+ return;
}
if (!mSupportedColorModes.contains(colorMode)) {
Slog.w(TAG, "Unable to find color mode " + colorMode
+ ", ignoring request.");
- return false;
+ return;
}
- SurfaceControl.setActiveColorMode(getDisplayTokenLocked(), colorMode);
+
mActiveColorMode = colorMode;
mActiveColorModeInvalid = false;
- return true;
+ getHandler().sendMessage(PooledLambda.obtainMessage(
+ LocalDisplayDevice::requestColorModeAsync, this,
+ getDisplayTokenLocked(), colorMode));
+ }
+
+ private void requestColorModeAsync(IBinder displayToken, int colorMode) {
+ // Do not lock when calling this SurfaceControl method because it is a sync operation
+ // that may block for a while when setting display power mode.
+ SurfaceControl.setActiveColorMode(displayToken, colorMode);
+ synchronized (getSyncRoot()) {
+ updateDeviceInfoLocked();
+ }
}
@Override
diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index 58f6ba2..c58000f 100644
--- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -50,6 +50,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
/**
* Base class for {@link SystemService SystemServices} that support multi user.
@@ -373,7 +374,7 @@
+ durationMs + "ms");
enforceCallingPermissionForManagement();
- Preconditions.checkNotNull(componentName);
+ Objects.requireNonNull(componentName);
final int maxDurationMs = getMaximumTemporaryServiceDurationMs();
if (durationMs > maxDurationMs) {
throw new IllegalArgumentException(
@@ -735,7 +736,7 @@
* @throws SecurityException when it's not...
*/
protected final void assertCalledByPackageOwner(@NonNull String packageName) {
- Preconditions.checkNotNull(packageName);
+ Objects.requireNonNull(packageName);
final int uid = Binder.getCallingUid();
final String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
if (packages != null) {
@@ -954,6 +955,35 @@
onServicePackageRestartedLocked(userId);
}
+ @Override
+ public void onPackageModified(String packageName) {
+ if (verbose) Slog.v(mTag, "onPackageModified(): " + packageName);
+
+ final int userId = getChangingUserId();
+ final String serviceName = mServiceNameResolver.getDefaultServiceName(userId);
+ if (serviceName == null) {
+ return;
+ }
+
+ final ComponentName serviceComponentName =
+ ComponentName.unflattenFromString(serviceName);
+ if (serviceComponentName == null
+ || !serviceComponentName.getPackageName().equals(packageName)) {
+ return;
+ }
+
+ // The default service package has changed, update the cached if the service
+ // exists but no active component.
+ final S service = peekServiceForUserLocked(userId);
+ if (service != null) {
+ final ComponentName componentName = service.getServiceComponentName();
+ if (componentName == null) {
+ if (verbose) Slog.v(mTag, "update cached");
+ updateCachedServiceLocked(userId);
+ }
+ }
+ }
+
private String getActiveServicePackageNameLocked() {
final int userId = getChangingUserId();
final S service = peekServiceForUserLocked(userId);
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index bf73aa3..ae6d63a 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -1719,7 +1719,7 @@
// Binder call
@Override
public void setCustomPointerIcon(PointerIcon icon) {
- Preconditions.checkNotNull(icon);
+ Objects.requireNonNull(icon);
nativeSetCustomPointerIcon(mPtr, icon);
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index 44c8971..c40e8af 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -19,8 +19,6 @@
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.ComponentName;
-import android.os.RemoteException;
-import android.util.Log;
import android.view.autofill.AutofillId;
import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.inputmethod.InputMethodInfo;
@@ -99,12 +97,6 @@
@Override
public void onCreateInlineSuggestionsRequest(ComponentName componentName,
AutofillId autofillId, IInlineSuggestionsRequestCallback cb) {
- try {
- cb.onInlineSuggestionsUnsupported();
- } catch (RemoteException e) {
- Log.w("IMManagerInternal", "RemoteException calling"
- + " onInlineSuggestionsUnsupported: " + e);
- }
}
};
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index c13d55a..1f9379c 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -59,7 +59,6 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.view.InputChannel;
@@ -198,8 +197,7 @@
//TODO(b/137800469): support multi client IMEs.
cb.onInlineSuggestionsUnsupported();
} catch (RemoteException e) {
- Log.w("MultiClientIMManager", "RemoteException calling"
- + " onInlineSuggestionsUnsupported: " + e);
+ Slog.w(TAG, "Failed to call onInlineSuggestionsUnsupported.", e);
}
}
});
diff --git a/services/core/java/com/android/server/integrity/IntegrityFileManager.java b/services/core/java/com/android/server/integrity/IntegrityFileManager.java
index 30cafaa..31d4816 100644
--- a/services/core/java/com/android/server/integrity/IntegrityFileManager.java
+++ b/services/core/java/com/android/server/integrity/IntegrityFileManager.java
@@ -25,6 +25,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.integrity.model.RuleMetadata;
import com.android.server.integrity.parser.RuleBinaryParser;
+import com.android.server.integrity.parser.RuleIndexRange;
+import com.android.server.integrity.parser.RuleIndexingController;
import com.android.server.integrity.parser.RuleMetadataParser;
import com.android.server.integrity.parser.RuleParseException;
import com.android.server.integrity.parser.RuleParser;
@@ -44,12 +46,9 @@
public class IntegrityFileManager {
private static final String TAG = "IntegrityFileManager";
- // TODO: this is a prototype implementation of this class. Thus no tests are included.
- // Implementing rule indexing will likely overhaul this class and more tests should be included
- // then.
-
private static final String METADATA_FILE = "metadata";
private static final String RULES_FILE = "rules";
+ private static final String INDEXING_FILE = "indexing";
private static final Object RULES_LOCK = new Object();
private static IntegrityFileManager sInstance = null;
@@ -64,7 +63,10 @@
// update rules atomically.
private final File mStagingDir;
- @Nullable private RuleMetadata mRuleMetadataCache;
+ @Nullable
+ private RuleMetadata mRuleMetadataCache;
+ @Nullable
+ private RuleIndexingController mRuleIndexingController;
/** Get the singleton instance of this class. */
public static synchronized IntegrityFileManager getInstance() {
@@ -103,6 +105,8 @@
Slog.e(TAG, "Error reading metadata file.", e);
}
}
+
+ updateRuleIndexingController();
}
/**
@@ -112,7 +116,8 @@
*/
public boolean initialized() {
return new File(mRulesDir, RULES_FILE).exists()
- && new File(mRulesDir, METADATA_FILE).exists();
+ && new File(mRulesDir, METADATA_FILE).exists()
+ && new File(mRulesDir, INDEXING_FILE).exists();
}
/** Write rules to persistent storage. */
@@ -125,12 +130,18 @@
// We don't consider this fatal so we continue execution.
}
- try (FileOutputStream fileOutputStream =
- new FileOutputStream(new File(mStagingDir, RULES_FILE))) {
- mRuleSerializer.serialize(rules, Optional.empty(), fileOutputStream);
+ try (FileOutputStream ruleFileOutputStream =
+ new FileOutputStream(new File(mStagingDir, RULES_FILE));
+ FileOutputStream indexingFileOutputStream =
+ new FileOutputStream(new File(mStagingDir, INDEXING_FILE))) {
+ mRuleSerializer.serialize(
+ rules, Optional.empty(), ruleFileOutputStream, indexingFileOutputStream);
}
switchStagingRulesDir();
+
+ // Update object holding the indexing information.
+ updateRuleIndexingController();
}
/**
@@ -140,10 +151,15 @@
*/
public List<Rule> readRules(AppInstallMetadata appInstallMetadata)
throws IOException, RuleParseException {
- // TODO: select rules by index
synchronized (RULES_LOCK) {
+ // Try to identify indexes from the index file.
+ List<RuleIndexRange> ruleReadingIndexes =
+ mRuleIndexingController.identifyRulesToEvaluate(appInstallMetadata);
+
+ // Read the rules based on the index information.
+ // TODO(b/145493956): Provide the identified indexes to the rule reader.
try (FileInputStream inputStream =
- new FileInputStream(new File(mRulesDir, RULES_FILE))) {
+ new FileInputStream(new File(mRulesDir, RULES_FILE))) {
List<Rule> rules = mRuleParser.parse(inputStream);
return rules;
}
@@ -168,6 +184,17 @@
}
}
+ private void updateRuleIndexingController() {
+ File ruleIndexingFile = new File(mRulesDir, INDEXING_FILE);
+ if (ruleIndexingFile.exists()) {
+ try (FileInputStream inputStream = new FileInputStream(ruleIndexingFile)) {
+ mRuleIndexingController = new RuleIndexingController(inputStream);
+ } catch (Exception e) {
+ Slog.e(TAG, "Error parsing the rule indexing file.", e);
+ }
+ }
+ }
+
private void writeMetadata(File directory, String ruleProvider, String version)
throws IOException {
mRuleMetadataCache = new RuleMetadata(ruleProvider, version);
diff --git a/services/core/java/com/android/server/integrity/model/IndexingFileConstants.java b/services/core/java/com/android/server/integrity/model/IndexingFileConstants.java
new file mode 100644
index 0000000..52df89870
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/model/IndexingFileConstants.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.integrity.model;
+
+/** A helper class containing special indexing file constants. */
+public final class IndexingFileConstants {
+ // The parsing time seems acceptable for this block size based on the tests in
+ // go/ic-rule-file-format.
+ public static final int INDEXING_BLOCK_SIZE = 100;
+
+ public static final String START_INDEXING_KEY = "START_KEY";
+ public static final String END_INDEXING_KEY = "END_KEY";
+}
diff --git a/services/core/java/com/android/server/integrity/parser/BinaryFileOperations.java b/services/core/java/com/android/server/integrity/parser/BinaryFileOperations.java
new file mode 100644
index 0000000..2c5b7d3
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/parser/BinaryFileOperations.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.integrity.parser;
+
+import static com.android.server.integrity.model.ComponentBitSize.IS_HASHED_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
+
+import com.android.server.integrity.IntegrityUtils;
+import com.android.server.integrity.model.BitInputStream;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * Helper methods for reading standard data structures from {@link BitInputStream}.
+ */
+public class BinaryFileOperations {
+
+ /**
+ * Read an string value with the given size and hash status from a {@code BitInputStream}.
+ *
+ * If the value is hashed, get the hex-encoding of the value. Serialized values are in raw form.
+ * All hashed values are hex-encoded.
+ */
+ public static String getStringValue(BitInputStream bitInputStream) throws IOException {
+ boolean isHashedValue = bitInputStream.getNext(IS_HASHED_BITS) == 1;
+ int valueSize = bitInputStream.getNext(VALUE_SIZE_BITS);
+ return getStringValue(bitInputStream, valueSize, isHashedValue);
+ }
+
+ /**
+ * Read an string value with the given size and hash status from a {@code BitInputStream}.
+ *
+ * If the value is hashed, get the hex-encoding of the value. Serialized values are in raw form.
+ * All hashed values are hex-encoded.
+ */
+ public static String getStringValue(
+ BitInputStream bitInputStream, int valueSize, boolean isHashedValue)
+ throws IOException {
+ if (!isHashedValue) {
+ StringBuilder value = new StringBuilder();
+ while (valueSize-- > 0) {
+ value.append((char) bitInputStream.getNext(/* numOfBits= */ 8));
+ }
+ return value.toString();
+ }
+ ByteBuffer byteBuffer = ByteBuffer.allocate(valueSize);
+ while (valueSize-- > 0) {
+ byteBuffer.put((byte) (bitInputStream.getNext(/* numOfBits= */ 8) & 0xFF));
+ }
+ return IntegrityUtils.getHexDigest(byteBuffer.array());
+ }
+
+ /** Read an integer value from a {@code BitInputStream}. */
+ public static int getIntValue(BitInputStream bitInputStream) throws IOException {
+ return bitInputStream.getNext(/* numOfBits= */ 32);
+ }
+
+ /** Read an boolean value from a {@code BitInputStream}. */
+ public static boolean getBooleanValue(BitInputStream bitInputStream) throws IOException {
+ return bitInputStream.getNext(/* numOfBits= */ 1) == 1;
+ }
+}
diff --git a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
index 8f84abc..cbb6e4e 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
@@ -28,18 +28,19 @@
import static com.android.server.integrity.model.ComponentBitSize.SEPARATOR_BITS;
import static com.android.server.integrity.model.ComponentBitSize.SIGNAL_BIT;
import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
+import static com.android.server.integrity.parser.BinaryFileOperations.getBooleanValue;
+import static com.android.server.integrity.parser.BinaryFileOperations.getIntValue;
+import static com.android.server.integrity.parser.BinaryFileOperations.getStringValue;
import android.content.integrity.AtomicFormula;
import android.content.integrity.CompoundFormula;
import android.content.integrity.Formula;
import android.content.integrity.Rule;
-import com.android.server.integrity.IntegrityUtils;
import com.android.server.integrity.model.BitInputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
@@ -145,33 +146,4 @@
throw new IllegalArgumentException(String.format("Unknown key: %d", key));
}
}
-
- // Get value string from stream.
- // If the value is not hashed, get its raw form directly.
- // If the value is hashed, get the hex-encoding of the value. Serialized values are in raw form.
- // All hashed values are hex-encoded.
- private static String getStringValue(
- BitInputStream bitInputStream, int valueSize, boolean isHashedValue)
- throws IOException {
- if (!isHashedValue) {
- StringBuilder value = new StringBuilder();
- while (valueSize-- > 0) {
- value.append((char) bitInputStream.getNext(/* numOfBits= */ 8));
- }
- return value.toString();
- }
- ByteBuffer byteBuffer = ByteBuffer.allocate(valueSize);
- while (valueSize-- > 0) {
- byteBuffer.put((byte) (bitInputStream.getNext(/* numOfBits= */ 8) & 0xFF));
- }
- return IntegrityUtils.getHexDigest(byteBuffer.array());
- }
-
- private static int getIntValue(BitInputStream bitInputStream) throws IOException {
- return bitInputStream.getNext(/* numOfBits= */ 32);
- }
-
- private static boolean getBooleanValue(BitInputStream bitInputStream) throws IOException {
- return bitInputStream.getNext(/* numOfBits= */ 1) == 1;
- }
}
diff --git a/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java b/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java
new file mode 100644
index 0000000..8c8450e
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.integrity.parser;
+
+import android.annotation.Nullable;
+
+/**
+ * A wrapper class to represent an indexing range that is identified by the {@link
+ * RuleIndexingController}.
+ */
+public class RuleIndexRange {
+ private static int sStartIndex;
+ private static int sEndIndex;
+
+ /** Constructor with start and end indexes. */
+ public RuleIndexRange(int startIndex, int endIndex) {
+ this.sStartIndex = startIndex;
+ this.sEndIndex = endIndex;
+ }
+
+ /** Returns the startIndex. */
+ public int getStartIndex() {
+ return sStartIndex;
+ }
+
+ /** Returns the end index. */
+ public int getEndIndex() {
+ return sEndIndex;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object object) {
+ return sStartIndex == ((RuleIndexRange) object).getStartIndex()
+ && sEndIndex == ((RuleIndexRange) object).getEndIndex();
+ }
+}
diff --git a/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java b/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java
new file mode 100644
index 0000000..c971322
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.integrity.parser;
+
+import static com.android.server.integrity.model.IndexingFileConstants.END_INDEXING_KEY;
+import static com.android.server.integrity.model.IndexingFileConstants.START_INDEXING_KEY;
+import static com.android.server.integrity.parser.BinaryFileOperations.getIntValue;
+import static com.android.server.integrity.parser.BinaryFileOperations.getStringValue;
+
+import android.content.integrity.AppInstallMetadata;
+
+import com.android.server.integrity.model.BitInputStream;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.TreeSet;
+import java.util.stream.Collectors;
+
+/** Helper class to identify the necessary indexes that needs to be read. */
+public class RuleIndexingController {
+
+ private static LinkedHashMap<String, Integer> sPackageNameBasedIndexes;
+ private static LinkedHashMap<String, Integer> sAppCertificateBasedIndexes;
+ private static LinkedHashMap<String, Integer> sUnindexedRuleIndexes;
+
+ /**
+ * Provide the indexing file to read and the object will be constructed by reading and
+ * identifying the indexes.
+ */
+ public RuleIndexingController(InputStream inputStream) throws IOException {
+ BitInputStream bitInputStream = new BitInputStream(inputStream);
+ sPackageNameBasedIndexes = getNextIndexGroup(bitInputStream);
+ sAppCertificateBasedIndexes = getNextIndexGroup(bitInputStream);
+ sUnindexedRuleIndexes = getNextIndexGroup(bitInputStream);
+ }
+
+ /**
+ * Returns a list of integers with the starting and ending bytes of the rules that needs to be
+ * read and evaluated.
+ */
+ public List<RuleIndexRange> identifyRulesToEvaluate(AppInstallMetadata appInstallMetadata) {
+ ArrayList<RuleIndexRange> indexRanges = new ArrayList();
+
+ // Add the range for package name indexes rules.
+ indexRanges.add(
+ searchIndexingKeysRangeContainingKey(
+ sPackageNameBasedIndexes, appInstallMetadata.getPackageName()));
+
+ // Add the range for app certificate indexes rules.
+ indexRanges.add(
+ searchIndexingKeysRangeContainingKey(
+ sAppCertificateBasedIndexes, appInstallMetadata.getAppCertificate()));
+
+ // Add the range for unindexed rules.
+ indexRanges.add(
+ new RuleIndexRange(
+ sUnindexedRuleIndexes.get(START_INDEXING_KEY),
+ sUnindexedRuleIndexes.get(END_INDEXING_KEY)));
+
+ return indexRanges;
+ }
+
+ private LinkedHashMap<String, Integer> getNextIndexGroup(BitInputStream bitInputStream)
+ throws IOException {
+ LinkedHashMap<String, Integer> keyToIndexMap = new LinkedHashMap<>();
+ while (bitInputStream.hasNext()) {
+ String key = getStringValue(bitInputStream);
+ int value = getIntValue(bitInputStream);
+
+ keyToIndexMap.put(key, value);
+
+ if (key.matches(END_INDEXING_KEY)) {
+ break;
+ }
+ }
+ return keyToIndexMap;
+ }
+
+ private RuleIndexRange searchIndexingKeysRangeContainingKey(
+ LinkedHashMap<String, Integer> indexMap, String searchedKey) {
+ TreeSet<String> keyTreeSet =
+ indexMap.keySet().stream()
+ .filter(key -> !key.matches(START_INDEXING_KEY) && !key.matches(
+ END_INDEXING_KEY))
+ .collect(Collectors.toCollection(TreeSet::new));
+
+ String minIndex = keyTreeSet.floor(searchedKey);
+ String maxIndex = keyTreeSet.ceiling(searchedKey);
+
+ return new RuleIndexRange(
+ indexMap.get(minIndex == null ? START_INDEXING_KEY : minIndex),
+ indexMap.get(maxIndex == null ? END_INDEXING_KEY : maxIndex));
+ }
+}
diff --git a/services/core/java/com/android/server/integrity/serializer/ByteTrackedOutputStream.java b/services/core/java/com/android/server/integrity/serializer/ByteTrackedOutputStream.java
index c8d318f..62815a9 100644
--- a/services/core/java/com/android/server/integrity/serializer/ByteTrackedOutputStream.java
+++ b/services/core/java/com/android/server/integrity/serializer/ByteTrackedOutputStream.java
@@ -27,7 +27,7 @@
*/
public class ByteTrackedOutputStream {
- private static long sWrittenBytesCount;
+ private static int sWrittenBytesCount;
private static OutputStream sOutputStream;
public ByteTrackedOutputStream(OutputStream outputStream) {
@@ -47,7 +47,7 @@
/**
* Returns the total number of bytes written into the output stream at the requested time.
*/
- public long getWrittenBytesCount() {
+ public int getWrittenBytesCount() {
return sWrittenBytesCount;
}
}
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
index 22af085..b8791c3 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
@@ -27,6 +27,9 @@
import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS;
import static com.android.server.integrity.model.ComponentBitSize.SEPARATOR_BITS;
import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
+import static com.android.server.integrity.model.IndexingFileConstants.END_INDEXING_KEY;
+import static com.android.server.integrity.model.IndexingFileConstants.INDEXING_BLOCK_SIZE;
+import static com.android.server.integrity.model.IndexingFileConstants.START_INDEXING_KEY;
import static com.android.server.integrity.serializer.RuleIndexingDetails.APP_CERTIFICATE_INDEXED;
import static com.android.server.integrity.serializer.RuleIndexingDetails.NOT_INDEXED;
import static com.android.server.integrity.serializer.RuleIndexingDetails.PACKAGE_NAME_INDEXED;
@@ -52,20 +55,14 @@
/** A helper class to serialize rules from the {@link Rule} model to Binary representation. */
public class RuleBinarySerializer implements RuleSerializer {
- // The parsing time seems acceptable for 100 rules based on the tests in go/ic-rule-file-format.
- private static final int INDEXING_BLOCK_SIZE = 100;
-
- private static final String START_INDEXING_KEY = "START_KEY";
- private static final String END_INDEXING_KEY = "END_KEY";
-
// Get the byte representation for a list of rules.
@Override
public byte[] serialize(List<Rule> rules, Optional<Integer> formatVersion)
throws RuleSerializeException {
try {
- ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
- serialize(rules, formatVersion, byteArrayOutputStream);
- return byteArrayOutputStream.toByteArray();
+ ByteArrayOutputStream rulesOutputStream = new ByteArrayOutputStream();
+ serialize(rules, formatVersion, rulesOutputStream, new ByteArrayOutputStream());
+ return rulesOutputStream.toByteArray();
} catch (Exception e) {
throw new RuleSerializeException(e.getMessage(), e);
}
@@ -74,27 +71,38 @@
// Get the byte representation for a list of rules, and write them to an output stream.
@Override
public void serialize(
- List<Rule> rules, Optional<Integer> formatVersion, OutputStream originalOutputStream)
+ List<Rule> rules,
+ Optional<Integer> formatVersion,
+ OutputStream rulesFileOutputStream,
+ OutputStream indexingFileOutputStream)
throws RuleSerializeException {
try {
// Determine the indexing groups and the order of the rules within each indexed group.
Map<Integer, TreeMap<String, List<Rule>>> indexedRules =
RuleIndexingDetailsIdentifier.splitRulesIntoIndexBuckets(rules);
- ByteTrackedOutputStream outputStream =
- new ByteTrackedOutputStream(originalOutputStream);
+ ByteTrackedOutputStream ruleFileByteTrackedOutputStream =
+ new ByteTrackedOutputStream(rulesFileOutputStream);
- serializeRuleFileMetadata(formatVersion, outputStream);
+ serializeRuleFileMetadata(formatVersion, ruleFileByteTrackedOutputStream);
- Map<String, Long> packageNameIndexes =
- serializeRuleList(indexedRules.get(PACKAGE_NAME_INDEXED), outputStream);
- Map<String, Long> appCertificateIndexes =
- serializeRuleList(indexedRules.get(APP_CERTIFICATE_INDEXED), outputStream);
- Map<String, Long> unindexedRulesIndex =
- serializeRuleList(indexedRules.get(NOT_INDEXED), outputStream);
+ Map<String, Integer> packageNameIndexes =
+ serializeRuleList(indexedRules.get(PACKAGE_NAME_INDEXED),
+ ruleFileByteTrackedOutputStream);
+ indexingFileOutputStream.write(
+ serializeIndexes(packageNameIndexes, /* isIndexed= */true));
- // TODO(b/145493956): Write these indexes into a index file provided by integrity file
- // manager.
+ Map<String, Integer> appCertificateIndexes =
+ serializeRuleList(indexedRules.get(APP_CERTIFICATE_INDEXED),
+ ruleFileByteTrackedOutputStream);
+ indexingFileOutputStream.write(
+ serializeIndexes(appCertificateIndexes, /* isIndexed= */true));
+
+ Map<String, Integer> unindexedRulesIndexes =
+ serializeRuleList(indexedRules.get(NOT_INDEXED),
+ ruleFileByteTrackedOutputStream);
+ indexingFileOutputStream.write(
+ serializeIndexes(unindexedRulesIndexes, /* isIndexed= */false));
} catch (Exception e) {
throw new RuleSerializeException(e.getMessage(), e);
}
@@ -109,15 +117,15 @@
outputStream.write(bitOutputStream.toByteArray());
}
- private Map<String, Long> serializeRuleList(TreeMap<String, List<Rule>> rulesMap,
+ private Map<String, Integer> serializeRuleList(TreeMap<String, List<Rule>> rulesMap,
ByteTrackedOutputStream outputStream)
throws IOException {
Preconditions.checkArgument(rulesMap != null,
"serializeRuleList should never be called with null rule list.");
BitOutputStream bitOutputStream = new BitOutputStream();
- Map<String, Long> indexMapping = new TreeMap();
- long indexTracker = 0;
+ Map<String, Integer> indexMapping = new TreeMap();
+ int indexTracker = 0;
indexMapping.put(START_INDEXING_KEY, outputStream.getWrittenBytesCount());
for (Map.Entry<String, List<Rule>> entry : rulesMap.entrySet()) {
@@ -210,6 +218,33 @@
}
}
+ private byte[] serializeIndexes(Map<String, Integer> indexes, boolean isIndexed) {
+ BitOutputStream bitOutputStream = new BitOutputStream();
+
+ // Output the starting location of this indexing group.
+ serializeStringValue(START_INDEXING_KEY, /* isHashedValue= */false,
+ bitOutputStream);
+ serializeIntValue(indexes.get(START_INDEXING_KEY), bitOutputStream);
+
+ // If the group is indexed, output the locations of the indexes.
+ if (isIndexed) {
+ for (Map.Entry<String, Integer> entry : indexes.entrySet()) {
+ if (!entry.getKey().equals(START_INDEXING_KEY)
+ && !entry.getKey().equals(END_INDEXING_KEY)) {
+ serializeStringValue(entry.getKey(), /* isHashedValue= */false,
+ bitOutputStream);
+ serializeIntValue(entry.getValue(), bitOutputStream);
+ }
+ }
+ }
+
+ // Output the end location of this indexing group.
+ serializeStringValue(END_INDEXING_KEY, /*isHashedValue= */ false, bitOutputStream);
+ serializeIntValue(indexes.get(END_INDEXING_KEY), bitOutputStream);
+
+ return bitOutputStream.toByteArray();
+ }
+
private void serializeStringValue(
String value, boolean isHashedValue, BitOutputStream bitOutputStream) {
if (value == null) {
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleSerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleSerializer.java
index 4fcff65..2941856 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleSerializer.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleSerializer.java
@@ -26,10 +26,14 @@
public interface RuleSerializer {
/** Serialize rules to an output stream */
- void serialize(List<Rule> rules, Optional<Integer> formatVersion, OutputStream outputStream)
+ void serialize(
+ List<Rule> rules,
+ Optional<Integer> formatVersion,
+ OutputStream ruleFileOutputStream,
+ OutputStream indexingFileOutputStream)
throws RuleSerializeException;
/** Serialize rules to a ByteArray. */
- byte[] serialize(List<Rule> rule, Optional<Integer> formatVersion)
+ byte[] serialize(List<Rule> rules, Optional<Integer> formatVersion)
throws RuleSerializeException;
}
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java
index 4c04dbc..4194432 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java
@@ -56,12 +56,17 @@
@Override
public void serialize(
- List<Rule> rules, Optional<Integer> formatVersion, OutputStream outputStream)
+ List<Rule> rules,
+ Optional<Integer> formatVersion,
+ OutputStream outputStream,
+ OutputStream indexingOutputStream)
throws RuleSerializeException {
try {
XmlSerializer xmlSerializer = Xml.newSerializer();
xmlSerializer.setOutput(outputStream, StandardCharsets.UTF_8.name());
serializeRules(rules, xmlSerializer);
+
+ // TODO(b/145493956): Implement the indexing logic.
} catch (Exception e) {
throw new RuleSerializeException(e.getMessage(), e);
}
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index f913ba3..d8561b6 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -74,7 +74,6 @@
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
import com.android.internal.location.gnssmetrics.GnssMetrics;
-import com.android.internal.telephony.TelephonyIntents;
import com.android.server.DeviceIdleInternal;
import com.android.server.LocalServices;
import com.android.server.location.GnssSatelliteBlacklistHelper.GnssSatelliteBlacklistCallback;
@@ -508,7 +507,7 @@
mHandler.sendEmptyMessage(UPDATE_LOW_POWER_MODE);
break;
case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED:
- case TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED:
+ case TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED:
subscriptionOrCarrierConfigChanged();
break;
}
@@ -2091,7 +2090,7 @@
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- intentFilter.addAction(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
+ intentFilter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
mNetworkConnectivityHandler.registerNetworkCallbacks();
diff --git a/services/core/java/com/android/server/location/LocationRequestStatistics.java b/services/core/java/com/android/server/location/LocationRequestStatistics.java
index b7ccb26..45c8334 100644
--- a/services/core/java/com/android/server/location/LocationRequestStatistics.java
+++ b/services/core/java/com/android/server/location/LocationRequestStatistics.java
@@ -1,8 +1,29 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.server.location;
import android.os.SystemClock;
import android.util.Log;
+import android.util.TimeUtils;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.util.ArrayList;
import java.util.HashMap;
/**
@@ -17,6 +38,8 @@
public final HashMap<PackageProviderKey, PackageStatistics> statistics
= new HashMap<PackageProviderKey, PackageStatistics>();
+ public final RequestSummaryLimitedHistory history = new RequestSummaryLimitedHistory();
+
/**
* Signals that a package has started requesting locations.
*
@@ -34,6 +57,7 @@
}
stats.startRequesting(intervalMs);
stats.updateForeground(isForeground);
+ history.addRequest(packageName, providerName, intervalMs);
}
/**
@@ -48,6 +72,7 @@
if (stats != null) {
stats.stopRequesting();
}
+ history.removeRequest(packageName, providerName);
}
/**
@@ -77,7 +102,7 @@
*/
public final String providerName;
- public PackageProviderKey(String packageName, String providerName) {
+ PackageProviderKey(String packageName, String providerName) {
this.packageName = packageName;
this.providerName = providerName;
}
@@ -100,6 +125,104 @@
}
/**
+ * A data structure to hold past requests
+ */
+ public static class RequestSummaryLimitedHistory {
+ @VisibleForTesting
+ static final int MAX_SIZE = 100;
+
+ final ArrayList<RequestSummary> mList = new ArrayList<>(MAX_SIZE);
+
+ /**
+ * Append an added location request to the history
+ */
+ @VisibleForTesting
+ void addRequest(String packageName, String providerName, long intervalMs) {
+ addRequestSummary(new RequestSummary(packageName, providerName, intervalMs));
+ }
+
+ /**
+ * Append a removed location request to the history
+ */
+ @VisibleForTesting
+ void removeRequest(String packageName, String providerName) {
+ addRequestSummary(new RequestSummary(
+ packageName, providerName, RequestSummary.REQUEST_ENDED_INTERVAL));
+ }
+
+ private void addRequestSummary(RequestSummary summary) {
+ while (mList.size() >= MAX_SIZE) {
+ mList.remove(0);
+ }
+ mList.add(summary);
+ }
+
+ /**
+ * Dump history to a printwriter (for dumpsys location)
+ */
+ public void dump(IndentingPrintWriter ipw) {
+ long systemElapsedOffsetMillis = System.currentTimeMillis()
+ - SystemClock.elapsedRealtime();
+
+ ipw.println("Last Several Location Requests:");
+ ipw.increaseIndent();
+
+ for (RequestSummary requestSummary : mList) {
+ requestSummary.dump(ipw, systemElapsedOffsetMillis);
+ }
+
+ ipw.decreaseIndent();
+ }
+ }
+
+ /**
+ * A data structure to hold a single request
+ */
+ static class RequestSummary {
+ /**
+ * Name of package requesting location.
+ */
+ private final String mPackageName;
+ /**
+ * Name of provider being requested (e.g. "gps").
+ */
+ private final String mProviderName;
+ /**
+ * Interval Requested, or REQUEST_ENDED_INTERVAL indicating request has ended
+ */
+ private final long mIntervalMillis;
+ /**
+ * Elapsed time of request
+ */
+ private final long mElapsedRealtimeMillis;
+
+ /**
+ * Placeholder for requested ending (other values indicate request started/changed)
+ */
+ static final long REQUEST_ENDED_INTERVAL = -1;
+
+ RequestSummary(String packageName, String providerName, long intervalMillis) {
+ this.mPackageName = packageName;
+ this.mProviderName = providerName;
+ this.mIntervalMillis = intervalMillis;
+ this.mElapsedRealtimeMillis = SystemClock.elapsedRealtime();
+ }
+
+ void dump(IndentingPrintWriter ipw, long systemElapsedOffsetMillis) {
+ StringBuilder s = new StringBuilder();
+ long systemTimeMillis = systemElapsedOffsetMillis + mElapsedRealtimeMillis;
+ s.append("At ").append(TimeUtils.formatForLogging(systemTimeMillis)).append(": ")
+ .append(mIntervalMillis == REQUEST_ENDED_INTERVAL ? "- " : "+ ")
+ .append(String.format("%7s", mProviderName)).append(" request from ")
+ .append(mPackageName);
+ if (mIntervalMillis != REQUEST_ENDED_INTERVAL) {
+ s.append(" at interval ").append(mIntervalMillis / 1000).append(" seconds");
+ }
+ ipw.println(s);
+ }
+ }
+
+ /**
* Usage statistics for a package/provider pair.
*/
public static class PackageStatistics {
diff --git a/services/core/java/com/android/server/location/RemoteListenerHelper.java b/services/core/java/com/android/server/location/RemoteListenerHelper.java
index 60ce1f4..01522739 100644
--- a/services/core/java/com/android/server/location/RemoteListenerHelper.java
+++ b/services/core/java/com/android/server/location/RemoteListenerHelper.java
@@ -25,10 +25,9 @@
import android.os.RemoteException;
import android.util.Log;
-import com.android.internal.util.Preconditions;
-
import java.util.HashMap;
import java.util.Map;
+import java.util.Objects;
/**
* A helper class that handles operations in remote listeners.
@@ -61,7 +60,7 @@
private int mLastReportedResult = RESULT_UNKNOWN;
protected RemoteListenerHelper(Context context, Handler handler, String name) {
- Preconditions.checkNotNull(name);
+ Objects.requireNonNull(name);
mHandler = handler;
mTag = name;
mContext = context;
@@ -77,7 +76,7 @@
* Adds GNSS data listener {@code listener} with caller identify {@code callerIdentify}.
*/
public void addListener(@NonNull TListener listener, CallerIdentity callerIdentity) {
- Preconditions.checkNotNull(listener, "Attempted to register a 'null' listener.");
+ Objects.requireNonNull(listener, "Attempted to register a 'null' listener.");
IBinder binder = listener.asBinder();
synchronized (mListenerMap) {
if (mListenerMap.containsKey(binder)) {
@@ -116,7 +115,7 @@
* Remove GNSS data listener {@code listener}.
*/
public void removeListener(@NonNull TListener listener) {
- Preconditions.checkNotNull(listener, "Attempted to remove a 'null' listener.");
+ Objects.requireNonNull(listener, "Attempted to remove a 'null' listener.");
synchronized (mListenerMap) {
mListenerMap.remove(listener.asBinder());
if (mListenerMap.isEmpty()) {
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 6c65c36..7233f34 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -20,6 +20,7 @@
import static android.Manifest.permission.READ_CONTACTS;
import static android.content.Context.KEYGUARD_SERVICE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.UserHandle.USER_ALL;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
@@ -29,6 +30,7 @@
import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback;
import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_HANDLE_KEY;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
import static com.android.internal.widget.LockPatternUtils.USER_FRP;
import static com.android.internal.widget.LockPatternUtils.frpCredentialEnabled;
import static com.android.internal.widget.LockPatternUtils.userOwnsFrpCredential;
@@ -118,6 +120,7 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockSettingsInternal;
import com.android.internal.widget.LockscreenCredential;
+import com.android.internal.widget.RebootEscrowListener;
import com.android.internal.widget.VerifyCredentialResponse;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
@@ -212,7 +215,6 @@
private final LockSettingsStrongAuth mStrongAuth;
private final SynchronizedStrongAuthTracker mStrongAuthTracker;
- private final LockPatternUtils mLockPatternUtils;
private final NotificationManager mNotificationManager;
private final UserManager mUserManager;
private final IStorageManager mStorageManager;
@@ -223,11 +225,16 @@
private final RecoverableKeyStoreManager mRecoverableKeyStoreManager;
+ private final RebootEscrowManager mRebootEscrowManager;
+
private boolean mFirstCallToVold;
+
// Current password metric for all users on the device. Updated when user unlocks
// the device or changes password. Removed when user is stopped.
@GuardedBy("this")
final SparseArray<PasswordMetrics> mUserPasswordMetrics = new SparseArray<>();
+ @VisibleForTesting
+ protected boolean mHasSecureLockScreen;
protected IGateKeeperService mGateKeeperService;
protected IAuthSecret mAuthSecretService;
@@ -351,7 +358,7 @@
return;
}
// Do not tie managed profile when work challenge is enabled
- if (mLockPatternUtils.isSeparateProfileChallengeEnabled(managedUserId)) {
+ if (getSeparateProfileChallengeEnabledInternal(managedUserId)) {
return;
}
// Do not tie managed profile to parent when it's done already
@@ -434,10 +441,6 @@
return ActivityManager.getService();
}
- public LockPatternUtils getLockPatternUtils() {
- return new LockPatternUtils(mContext);
- }
-
public NotificationManager getNotificationManager() {
return (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
}
@@ -486,6 +489,11 @@
new PasswordSlotManager());
}
+ public RebootEscrowManager getRebootEscrowManager(RebootEscrowManager.Callbacks callbacks,
+ LockSettingsStorage storage) {
+ return new RebootEscrowManager(mContext, callbacks, storage);
+ }
+
public boolean hasEnrolledBiometrics(int userId) {
BiometricManager bm = mContext.getSystemService(BiometricManager.class);
return bm.hasEnrolledBiometrics(userId);
@@ -535,7 +543,6 @@
mStrongAuth = injector.getStrongAuth();
mActivityManager = injector.getActivityManager();
- mLockPatternUtils = injector.getLockPatternUtils();
mFirstCallToVold = true;
IntentFilter filter = new IntentFilter();
@@ -554,6 +561,9 @@
mSpManager = injector.getSyntheticPasswordManager(mStorage);
+ mRebootEscrowManager = injector.getRebootEscrowManager(new RebootEscrowCallbacks(),
+ mStorage);
+
LocalServices.addService(LockSettingsInternal.class, new LocalService());
}
@@ -780,10 +790,20 @@
EventLog.writeEvent(0x534e4554, "28251513", getCallingUid(), ""); // SafetyNet
}
checkWritePermission(UserHandle.USER_SYSTEM);
+
+ mHasSecureLockScreen = mContext.getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN);
migrateOldData();
getGateKeeperService();
mSpManager.initWeaverService();
- // Find the AuthSecret HAL
+ getAuthSecretHal();
+ mDeviceProvisionedObserver.onSystemReady();
+ mRebootEscrowManager.loadRebootEscrowDataIfAvailable();
+ // TODO: maybe skip this for split system user mode.
+ mStorage.prefetchUser(UserHandle.USER_SYSTEM);
+ }
+
+ private void getAuthSecretHal() {
try {
mAuthSecretService = IAuthSecret.getService();
} catch (NoSuchElementException e) {
@@ -791,9 +811,6 @@
} catch (RemoteException e) {
Slog.w(TAG, "Failed to get AuthSecret HAL", e);
}
- mDeviceProvisionedObserver.onSystemReady();
- // TODO: maybe skip this for split system user mode.
- mStorage.prefetchUser(UserHandle.USER_SYSTEM);
}
private void migrateOldData() {
@@ -1019,6 +1036,11 @@
}
@Override
+ public boolean hasSecureLockScreen() {
+ return mHasSecureLockScreen;
+ }
+
+ @Override
public boolean getSeparateProfileChallengeEnabled(int userId) {
checkReadPermission(SEPARATE_PROFILE_CHALLENGE_KEY, userId);
return getSeparateProfileChallengeEnabledInternal(userId);
@@ -1034,7 +1056,7 @@
public void setSeparateProfileChallengeEnabled(int userId, boolean enabled,
LockscreenCredential managedUserPassword) {
checkWritePermission(userId);
- if (!mLockPatternUtils.hasSecureLockScreen()) {
+ if (!mHasSecureLockScreen) {
throw new UnsupportedOperationException(
"This operation requires secure lock screen feature.");
}
@@ -1147,12 +1169,7 @@
private String getStringUnchecked(String key, String defaultValue, int userId) {
if (Settings.Secure.LOCK_PATTERN_ENABLED.equals(key)) {
- long ident = Binder.clearCallingIdentity();
- try {
- return mLockPatternUtils.isLockPatternEnabled(userId) ? "1" : "0";
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
+ return getCredentialTypeInternal(userId) == CREDENTIAL_TYPE_PATTERN ? "1" : "0";
}
if (userId == USER_FRP) {
return null;
@@ -1402,7 +1419,7 @@
private boolean tiedManagedProfileReadyToUnlock(UserInfo userInfo) {
return userInfo.isManagedProfile()
- && !mLockPatternUtils.isSeparateProfileChallengeEnabled(userInfo.id)
+ && !getSeparateProfileChallengeEnabledInternal(userInfo.id)
&& mStorage.hasChildProfileLock(userInfo.id)
&& mUserManager.isUserRunning(userInfo.id);
}
@@ -1420,7 +1437,7 @@
continue;
}
final int managedUserId = profile.id;
- if (mLockPatternUtils.isSeparateProfileChallengeEnabled(managedUserId)) {
+ if (getSeparateProfileChallengeEnabledInternal(managedUserId)) {
continue;
}
try {
@@ -1460,7 +1477,7 @@
final UserInfo profile = profiles.get(i);
if (profile.isManagedProfile()) {
final int managedUserId = profile.id;
- if (mLockPatternUtils.isSeparateProfileChallengeEnabled(managedUserId)) {
+ if (getSeparateProfileChallengeEnabledInternal(managedUserId)) {
continue;
}
if (isSecure) {
@@ -1487,12 +1504,12 @@
private boolean isManagedProfileWithUnifiedLock(int userId) {
return mUserManager.getUserInfo(userId).isManagedProfile()
- && !mLockPatternUtils.isSeparateProfileChallengeEnabled(userId);
+ && !getSeparateProfileChallengeEnabledInternal(userId);
}
private boolean isManagedProfileWithSeparatedLock(int userId) {
return mUserManager.getUserInfo(userId).isManagedProfile()
- && mLockPatternUtils.isSeparateProfileChallengeEnabled(userId);
+ && getSeparateProfileChallengeEnabledInternal(userId);
}
/**
@@ -1565,7 +1582,7 @@
public boolean setLockCredential(LockscreenCredential credential,
LockscreenCredential savedCredential, int userId) {
- if (!mLockPatternUtils.hasSecureLockScreen()) {
+ if (!mHasSecureLockScreen) {
throw new UnsupportedOperationException(
"This operation requires secure lock screen feature");
}
@@ -1872,7 +1889,7 @@
for (UserInfo pi : profiles) {
// Unlock managed profile with unified lock
if (pi.isManagedProfile()
- && !mLockPatternUtils.isSeparateProfileChallengeEnabled(pi.id)
+ && !getSeparateProfileChallengeEnabledInternal(pi.id)
&& mStorage.hasChildProfileLock(pi.id)) {
try {
if (managedUserId == -1) {
@@ -2467,10 +2484,18 @@
private void onAuthTokenKnownForUser(@UserIdInt int userId, AuthenticationToken auth) {
if (mInjector.isGsiRunning()) {
- Slog.w(TAG, "AuthSecret disabled in GSI");
+ Slog.w(TAG, "Running in GSI; skipping calls to AuthSecret and RebootEscrow");
return;
}
+ mRebootEscrowManager.callToRebootEscrowIfNeeded(userId, auth.getVersion(),
+ auth.getSyntheticPassword());
+
+ callToAuthSecretIfNeeded(userId, auth);
+ }
+
+ private void callToAuthSecretIfNeeded(@UserIdInt int userId,
+ AuthenticationToken auth) {
// Pass the primary user's auth secret to the HAL
if (mAuthSecretService != null && mUserManager.getUserInfo(userId).isPrimary()) {
try {
@@ -2867,7 +2892,6 @@
VerifyCredentialResponse response = authResult.gkResponse;
AuthenticationToken auth = authResult.authToken;
-
if (auth == null) {
if (response == null
|| response.getResponseCode() == VerifyCredentialResponse.RESPONSE_ERROR) {
@@ -3075,9 +3099,6 @@
IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
pw.println("Current lock settings service state:");
-
- pw.println(String.format("SP Enabled = %b",
- mLockPatternUtils.isSyntheticPasswordEnabled()));
pw.println();
pw.println("User State:");
@@ -3263,7 +3284,7 @@
@Override
public boolean setLockCredentialWithToken(LockscreenCredential credential, long tokenHandle,
byte[] token, int userId) {
- if (!mLockPatternUtils.hasSecureLockScreen()) {
+ if (!mHasSecureLockScreen) {
throw new UnsupportedOperationException(
"This operation requires secure lock screen feature.");
}
@@ -3293,5 +3314,46 @@
return LockSettingsService.this.getUserPasswordMetrics(userHandle);
}
+ @Override
+ public void prepareRebootEscrow() {
+ if (!mRebootEscrowManager.prepareRebootEscrow()) {
+ return;
+ }
+ mStrongAuth.requireStrongAuth(STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE, USER_ALL);
+ }
+
+ @Override
+ public void setRebootEscrowListener(RebootEscrowListener listener) {
+ mRebootEscrowManager.setRebootEscrowListener(listener);
+ }
+
+ @Override
+ public void clearRebootEscrow() {
+ if (!mRebootEscrowManager.clearRebootEscrow()) {
+ return;
+ }
+ mStrongAuth.noLongerRequireStrongAuth(STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE,
+ USER_ALL);
+ }
+
+ @Override
+ public boolean armRebootEscrow() {
+ return mRebootEscrowManager.armRebootEscrowIfNeeded();
+ }
+ }
+
+ private class RebootEscrowCallbacks implements RebootEscrowManager.Callbacks {
+ @Override
+ public boolean isUserSecure(int userId) {
+ return LockSettingsService.this.isUserSecure(userId);
+ }
+
+ @Override
+ public void onRebootEscrowRestored(byte spVersion, byte[] syntheticPassword, int userId) {
+ SyntheticPasswordManager.AuthenticationToken
+ authToken = new SyntheticPasswordManager.AuthenticationToken(spVersion);
+ authToken.recreateDirectly(syntheticPassword);
+ onCredentialVerified(authToken, CHALLENGE_NONE, 0, null, userId);
+ }
}
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index 3dab3ce..fec0189 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -81,6 +81,8 @@
private static final String LOCK_PASSWORD_FILE = "gatekeeper.password.key";
private static final String CHILD_PROFILE_LOCK_FILE = "gatekeeper.profile.key";
+ private static final String REBOOT_ESCROW_FILE = "reboot.escrow.key";
+
private static final String SYNTHETIC_PASSWORD_DIRECTORY = "spblob/";
private static final Object DEFAULT = new Object();
@@ -261,6 +263,22 @@
return hasFile(getChildProfileLockFile(userId));
}
+ public void writeRebootEscrow(int userId, byte[] rebootEscrow) {
+ writeFile(getRebootEscrowFile(userId), rebootEscrow);
+ }
+
+ public byte[] readRebootEscrow(int userId) {
+ return readFile(getRebootEscrowFile(userId));
+ }
+
+ public boolean hasRebootEscrow(int userId) {
+ return hasFile(getRebootEscrowFile(userId));
+ }
+
+ public void removeRebootEscrow(int userId) {
+ deleteFile(getRebootEscrowFile(userId));
+ }
+
public boolean hasPassword(int userId) {
return hasFile(getLockPasswordFilename(userId));
}
@@ -384,6 +402,11 @@
return getLockCredentialFilePathForUser(userId, CHILD_PROFILE_LOCK_FILE);
}
+ @VisibleForTesting
+ String getRebootEscrowFile(int userId) {
+ return getLockCredentialFilePathForUser(userId, REBOOT_ESCROW_FILE);
+ }
+
private String getLockCredentialFilePathForUser(int userId, String basename) {
String dataSystemDirectory = Environment.getDataDirectory().getAbsolutePath() +
SYSTEM_DIRECTORY;
@@ -479,18 +502,10 @@
if (parentInfo == null) {
// This user owns its lock settings files - safe to delete them
synchronized (mFileWriteLock) {
- String name = getLockPasswordFilename(userId);
- File file = new File(name);
- if (file.exists()) {
- file.delete();
- mCache.putFile(name, null);
- }
- name = getLockPatternFilename(userId);
- file = new File(name);
- if (file.exists()) {
- file.delete();
- mCache.putFile(name, null);
- }
+ deleteFilesAndRemoveCache(
+ getLockPasswordFilename(userId),
+ getLockPatternFilename(userId),
+ getRebootEscrowFile(userId));
}
} else {
// Managed profile
@@ -512,6 +527,16 @@
}
}
+ private void deleteFilesAndRemoveCache(String... names) {
+ for (String name : names) {
+ File file = new File(name);
+ if (file.exists()) {
+ file.delete();
+ mCache.putFile(name, null);
+ }
+ }
+ }
+
@VisibleForTesting
void closeDatabase() {
mOpenHelper.close();
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
index a84306c..91cf53e 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
@@ -16,10 +16,8 @@
package com.android.server.locksettings;
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker
- .STRONG_AUTH_NOT_REQUIRED;
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker
- .STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
import android.app.AlarmManager;
import android.app.AlarmManager.OnAlarmListener;
@@ -50,6 +48,7 @@
private static final int MSG_UNREGISTER_TRACKER = 3;
private static final int MSG_REMOVE_USER = 4;
private static final int MSG_SCHEDULE_STRONG_AUTH_TIMEOUT = 5;
+ private static final int MSG_NO_LONGER_REQUIRE_STRONG_AUTH = 6;
private static final String STRONG_AUTH_TIMEOUT_ALARM_TAG =
"LockSettingsStrongAuth.timeoutForUser";
@@ -109,6 +108,26 @@
}
}
+ private void handleNoLongerRequireStrongAuth(int strongAuthReason, int userId) {
+ if (userId == UserHandle.USER_ALL) {
+ for (int i = 0; i < mStrongAuthForUser.size(); i++) {
+ int key = mStrongAuthForUser.keyAt(i);
+ handleNoLongerRequireStrongAuthOneUser(strongAuthReason, key);
+ }
+ } else {
+ handleNoLongerRequireStrongAuthOneUser(strongAuthReason, userId);
+ }
+ }
+
+ private void handleNoLongerRequireStrongAuthOneUser(int strongAuthReason, int userId) {
+ int oldValue = mStrongAuthForUser.get(userId, mDefaultStrongAuthFlags);
+ int newValue = oldValue & ~strongAuthReason;
+ if (oldValue != newValue) {
+ mStrongAuthForUser.put(userId, newValue);
+ notifyStrongAuthTrackers(newValue, userId);
+ }
+ }
+
private void handleRemoveUser(int userId) {
int index = mStrongAuthForUser.indexOfKey(userId);
if (index >= 0) {
@@ -174,6 +193,16 @@
}
}
+ void noLongerRequireStrongAuth(int strongAuthReason, int userId) {
+ if (userId == UserHandle.USER_ALL || userId >= UserHandle.USER_SYSTEM) {
+ mHandler.obtainMessage(MSG_NO_LONGER_REQUIRE_STRONG_AUTH, strongAuthReason,
+ userId).sendToTarget();
+ } else {
+ throw new IllegalArgumentException(
+ "userId must be an explicit user id or USER_ALL");
+ }
+ }
+
public void reportUnlock(int userId) {
requireStrongAuth(STRONG_AUTH_NOT_REQUIRED, userId);
}
@@ -216,6 +245,9 @@
case MSG_SCHEDULE_STRONG_AUTH_TIMEOUT:
handleScheduleStrongAuthTimeout(msg.arg1);
break;
+ case MSG_NO_LONGER_REQUIRE_STRONG_AUTH:
+ handleNoLongerRequireStrongAuth(msg.arg1, msg.arg2);
+ break;
}
}
};
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowData.java b/services/core/java/com/android/server/locksettings/RebootEscrowData.java
new file mode 100644
index 0000000..aee608e
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowData.java
@@ -0,0 +1,178 @@
+/*
+ * 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.locksettings;
+
+import com.android.internal.util.Preconditions;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.KeyGenerator;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * Holds the data necessary to complete a reboot escrow of the Synthetic Password.
+ */
+class RebootEscrowData {
+ /**
+ * This is the current version of the escrow data format. This should be incremented if the
+ * format on disk is changed.
+ */
+ private static final int CURRENT_VERSION = 1;
+
+ /** The secret key will be of this format. */
+ private static final String KEY_ALGO = "AES";
+
+ /** The key size used for encrypting the reboot escrow data. */
+ private static final int KEY_SIZE_BITS = 256;
+
+ /** The algorithm used for the encryption of the key blob. */
+ private static final String CIPHER_ALGO = "AES/GCM/NoPadding";
+
+ private RebootEscrowData(byte spVersion, byte[] iv, byte[] syntheticPassword, byte[] blob,
+ byte[] key) {
+ mSpVersion = spVersion;
+ mIv = iv;
+ mSyntheticPassword = syntheticPassword;
+ mBlob = blob;
+ mKey = key;
+ }
+
+ private final byte mSpVersion;
+ private final byte[] mIv;
+ private final byte[] mSyntheticPassword;
+ private final byte[] mBlob;
+ private final byte[] mKey;
+
+ public byte getSpVersion() {
+ return mSpVersion;
+ }
+
+ public byte[] getIv() {
+ return mIv;
+ }
+
+ public byte[] getSyntheticPassword() {
+ return mSyntheticPassword;
+ }
+
+ public byte[] getBlob() {
+ return mBlob;
+ }
+
+ public byte[] getKey() {
+ return mKey;
+ }
+
+ static SecretKeySpec fromKeyBytes(byte[] keyBytes) {
+ return new SecretKeySpec(keyBytes, KEY_ALGO);
+ }
+
+ static RebootEscrowData fromEncryptedData(SecretKeySpec keySpec, byte[] blob)
+ throws IOException {
+ Preconditions.checkNotNull(keySpec);
+ Preconditions.checkNotNull(blob);
+
+ DataInputStream dis = new DataInputStream(new ByteArrayInputStream(blob));
+ int version = dis.readInt();
+ if (version != CURRENT_VERSION) {
+ throw new IOException("Unsupported version " + version);
+ }
+
+ byte spVersion = dis.readByte();
+
+ int ivSize = dis.readInt();
+ if (ivSize < 0 || ivSize > 32) {
+ throw new IOException("IV out of range: " + ivSize);
+ }
+ byte[] iv = new byte[ivSize];
+ dis.readFully(iv);
+
+ int cipherTextSize = dis.readInt();
+ if (cipherTextSize < 0) {
+ throw new IOException("Invalid cipher text size: " + cipherTextSize);
+ }
+
+ byte[] cipherText = new byte[cipherTextSize];
+ dis.readFully(cipherText);
+
+ final byte[] syntheticPassword;
+ try {
+ Cipher c = Cipher.getInstance(CIPHER_ALGO);
+ c.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(iv));
+ syntheticPassword = c.doFinal(cipherText);
+ } catch (NoSuchAlgorithmException | InvalidKeyException | BadPaddingException
+ | IllegalBlockSizeException | NoSuchPaddingException
+ | InvalidAlgorithmParameterException e) {
+ throw new IOException("Could not decrypt ciphertext", e);
+ }
+
+ return new RebootEscrowData(spVersion, iv, syntheticPassword, blob, keySpec.getEncoded());
+ }
+
+ static RebootEscrowData fromSyntheticPassword(byte spVersion, byte[] syntheticPassword)
+ throws IOException {
+ Preconditions.checkNotNull(syntheticPassword);
+
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(bos);
+
+ final SecretKey secretKey;
+ try {
+ KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGO);
+ keyGenerator.init(KEY_SIZE_BITS, new SecureRandom());
+ secretKey = keyGenerator.generateKey();
+ } catch (NoSuchAlgorithmException e) {
+ throw new IOException("Could not generate new secret key", e);
+ }
+
+ final byte[] cipherText;
+ final byte[] iv;
+ try {
+ Cipher cipher = Cipher.getInstance(CIPHER_ALGO);
+ cipher.init(Cipher.ENCRYPT_MODE, secretKey);
+ cipherText = cipher.doFinal(syntheticPassword);
+ iv = cipher.getIV();
+ } catch (NoSuchAlgorithmException | BadPaddingException | IllegalBlockSizeException
+ | NoSuchPaddingException | InvalidKeyException e) {
+ throw new IOException("Could not encrypt reboot escrow data", e);
+ }
+
+ dos.writeInt(CURRENT_VERSION);
+ dos.writeByte(spVersion);
+ dos.writeInt(iv.length);
+ dos.write(iv);
+ dos.writeInt(cipherText.length);
+ dos.write(cipherText);
+
+ return new RebootEscrowData(spVersion, iv, syntheticPassword, bos.toByteArray(),
+ secretKey.getEncoded());
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
new file mode 100644
index 0000000..d2e54f9
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings;
+
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.hardware.rebootescrow.IRebootEscrow;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserManager;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.widget.RebootEscrowListener;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.crypto.spec.SecretKeySpec;
+
+class RebootEscrowManager {
+ private static final String TAG = "RebootEscrowManager";
+
+ /**
+ * Used to track when the reboot escrow is wanted. Set to false when mRebootEscrowReady is
+ * true.
+ */
+ private final AtomicBoolean mRebootEscrowWanted = new AtomicBoolean(false);
+
+ /** Used to track when reboot escrow is ready. */
+ private boolean mRebootEscrowReady;
+
+ /** Notified when mRebootEscrowReady changes. */
+ private RebootEscrowListener mRebootEscrowListener;
+
+ /**
+ * Stores the reboot escrow data between when it's supplied and when
+ * {@link #armRebootEscrowIfNeeded()} is called.
+ */
+ private RebootEscrowData mPendingRebootEscrowData;
+
+ private final UserManager mUserManager;
+
+ private final Injector mInjector;
+
+ private final LockSettingsStorage mStorage;
+
+ private final Callbacks mCallbacks;
+
+ interface Callbacks {
+ boolean isUserSecure(int userId);
+ void onRebootEscrowRestored(byte spVersion, byte[] syntheticPassword, int userId);
+ }
+
+ static class Injector {
+ protected Context mContext;
+
+ Injector(Context context) {
+ mContext = context;
+ }
+
+ public Context getContext() {
+ return mContext;
+ }
+ public UserManager getUserManager() {
+ return (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ }
+
+ public @Nullable IRebootEscrow getRebootEscrow() {
+ try {
+ return IRebootEscrow.Stub.asInterface(ServiceManager.getService(
+ "android.hardware.rebootescrow.IRebootEscrow/default"));
+ } catch (NoSuchElementException e) {
+ Slog.i(TAG, "Device doesn't implement RebootEscrow HAL");
+ }
+ return null;
+ }
+ }
+
+ RebootEscrowManager(Context context, Callbacks callbacks, LockSettingsStorage storage) {
+ this(new Injector(context), callbacks, storage);
+ }
+
+ @VisibleForTesting
+ RebootEscrowManager(Injector injector, Callbacks callbacks,
+ LockSettingsStorage storage) {
+ mInjector = injector;
+ mCallbacks = callbacks;
+ mStorage = storage;
+ mUserManager = injector.getUserManager();
+ }
+
+ void loadRebootEscrowDataIfAvailable() {
+ IRebootEscrow rebootEscrow = mInjector.getRebootEscrow();
+ if (rebootEscrow == null) {
+ return;
+ }
+
+ final SecretKeySpec escrowKey;
+ try {
+ byte[] escrowKeyBytes = rebootEscrow.retrieveKey();
+ if (escrowKeyBytes == null) {
+ return;
+ } else if (escrowKeyBytes.length != 32) {
+ Slog.e(TAG, "IRebootEscrow returned key of incorrect size "
+ + escrowKeyBytes.length);
+ return;
+ }
+
+ // Make sure we didn't get the null key.
+ int zero = 0;
+ for (int i = 0; i < escrowKeyBytes.length; i++) {
+ zero |= escrowKeyBytes[i];
+ }
+ if (zero == 0) {
+ Slog.w(TAG, "IRebootEscrow returned an all-zeroes key");
+ return;
+ }
+
+ // Overwrite the existing key with the null key
+ rebootEscrow.storeKey(new byte[32]);
+
+ escrowKey = RebootEscrowData.fromKeyBytes(escrowKeyBytes);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Could not retrieve escrow data");
+ return;
+ }
+
+ List<UserInfo> users = mUserManager.getUsers();
+ for (UserInfo user : users) {
+ if (mCallbacks.isUserSecure(user.id)) {
+ restoreRebootEscrowForUser(user.id, escrowKey);
+ }
+ }
+ }
+
+ private void restoreRebootEscrowForUser(@UserIdInt int userId, SecretKeySpec escrowKey) {
+ if (!mStorage.hasRebootEscrow(userId)) {
+ return;
+ }
+
+ try {
+ byte[] blob = mStorage.readRebootEscrow(userId);
+ mStorage.removeRebootEscrow(userId);
+
+ RebootEscrowData escrowData = RebootEscrowData.fromEncryptedData(escrowKey, blob);
+
+ mCallbacks.onRebootEscrowRestored(escrowData.getSpVersion(),
+ escrowData.getSyntheticPassword(), userId);
+ } catch (IOException e) {
+ Slog.w(TAG, "Could not load reboot escrow data for user " + userId, e);
+ }
+ }
+
+ void callToRebootEscrowIfNeeded(@UserIdInt int userId, byte spVersion,
+ byte[] syntheticPassword) {
+ if (!mRebootEscrowWanted.compareAndSet(true, false)) {
+ return;
+ }
+
+ IRebootEscrow rebootEscrow = mInjector.getRebootEscrow();
+ if (rebootEscrow == null) {
+ setRebootEscrowReady(false);
+ return;
+ }
+
+ final RebootEscrowData escrowData;
+ try {
+ escrowData = RebootEscrowData.fromSyntheticPassword(spVersion, syntheticPassword);
+ } catch (IOException e) {
+ setRebootEscrowReady(false);
+ Slog.w(TAG, "Could not escrow reboot data", e);
+ return;
+ }
+
+ mPendingRebootEscrowData = escrowData;
+ mStorage.writeRebootEscrow(userId, escrowData.getBlob());
+
+ setRebootEscrowReady(true);
+ }
+
+ private void clearRebootEscrowIfNeeded() {
+ mRebootEscrowWanted.set(false);
+ setRebootEscrowReady(false);
+
+ IRebootEscrow rebootEscrow = mInjector.getRebootEscrow();
+ if (rebootEscrow == null) {
+ return;
+ }
+
+ try {
+ rebootEscrow.storeKey(new byte[32]);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Could not call RebootEscrow HAL to shred key");
+ }
+
+ List<UserInfo> users = mUserManager.getUsers();
+ for (UserInfo user : users) {
+ mStorage.removeRebootEscrow(user.id);
+ }
+ }
+
+ boolean armRebootEscrowIfNeeded() {
+ if (!mRebootEscrowReady) {
+ return false;
+ }
+
+ IRebootEscrow rebootEscrow = mInjector.getRebootEscrow();
+ if (rebootEscrow == null) {
+ return false;
+ }
+
+ RebootEscrowData escrowData = mPendingRebootEscrowData;
+ if (escrowData == null) {
+ return false;
+ }
+
+ boolean armedRebootEscrow = false;
+ try {
+ rebootEscrow.storeKey(escrowData.getKey());
+ armedRebootEscrow = true;
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed escrow secret to RebootEscrow HAL", e);
+ }
+ return armedRebootEscrow;
+ }
+
+ private void setRebootEscrowReady(boolean ready) {
+ if (mRebootEscrowReady != ready) {
+ mRebootEscrowListener.onPreparedForReboot(ready);
+ }
+ mRebootEscrowReady = ready;
+ }
+
+ boolean prepareRebootEscrow() {
+ if (mInjector.getRebootEscrow() == null) {
+ return false;
+ }
+
+ clearRebootEscrowIfNeeded();
+ mRebootEscrowWanted.set(true);
+ return true;
+ }
+
+ boolean clearRebootEscrow() {
+ if (mInjector.getRebootEscrow() == null) {
+ return false;
+ }
+
+ clearRebootEscrowIfNeeded();
+ return true;
+ }
+
+ void setRebootEscrowListener(RebootEscrowListener listener) {
+ mRebootEscrowListener = listener;
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 0fe16be..b726e57 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -38,7 +38,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.Preconditions;
import com.android.internal.widget.ICheckCredentialProgressCallback;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockscreenCredential;
@@ -285,6 +284,14 @@
public byte[] getSyntheticPassword() {
return mSyntheticPassword;
}
+
+ /**
+ * Returns the version of this AuthenticationToken for use with reconstructing
+ * this with a synthetic password version.
+ */
+ public byte getVersion() {
+ return mVersion;
+ }
}
static class PasswordData {
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index f11b70e..55c4e21 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -20,7 +20,6 @@
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Intent;
-import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderInfo;
import android.media.RouteSessionInfo;
@@ -50,13 +49,13 @@
String controlCategory, long requestId);
public abstract void releaseSession(int sessionId);
- public abstract void selectRoute(int sessionId, MediaRoute2Info route);
- public abstract void deselectRoute(int sessionId, MediaRoute2Info route);
- public abstract void transferToRoute(int sessionId, MediaRoute2Info route);
+ public abstract void selectRoute(int sessionId, String routeId);
+ public abstract void deselectRoute(int sessionId, String routeId);
+ public abstract void transferToRoute(int sessionId, String routeId);
- public abstract void sendControlRequest(MediaRoute2Info route, Intent request);
- public abstract void requestSetVolume(MediaRoute2Info route, int volume);
- public abstract void requestUpdateVolume(MediaRoute2Info route, int delta);
+ public abstract void sendControlRequest(String routeId, Intent request);
+ public abstract void requestSetVolume(String routeId, int volume);
+ public abstract void requestUpdateVolume(String routeId, int delta);
@NonNull
public String getUniqueId() {
@@ -108,5 +107,8 @@
// TODO: Remove this when MediaRouter2ServiceImpl notifies clients of session changes.
void onSessionInfoChanged(@NonNull MediaRoute2Provider provider,
@NonNull RouteSessionInfo sessionInfo);
+ // TODO: Call this when service actually notifies of session release.
+ void onSessionReleased(@NonNull MediaRoute2Provider provider,
+ @NonNull RouteSessionInfo sessionInfo);
}
}
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
index f8d8f9f..28bb034 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
@@ -24,7 +24,6 @@
import android.content.ServiceConnection;
import android.media.IMediaRoute2Provider;
import android.media.IMediaRoute2ProviderClient;
-import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderInfo;
import android.media.MediaRoute2ProviderService;
import android.media.RouteSessionInfo;
@@ -95,46 +94,46 @@
}
@Override
- public void selectRoute(int sessionId, MediaRoute2Info route) {
+ public void selectRoute(int sessionId, String routeId) {
if (mConnectionReady) {
- mActiveConnection.selectRoute(sessionId, route.getId());
+ mActiveConnection.selectRoute(sessionId, routeId);
}
}
@Override
- public void deselectRoute(int sessionId, MediaRoute2Info route) {
+ public void deselectRoute(int sessionId, String routeId) {
if (mConnectionReady) {
- mActiveConnection.deselectRoute(sessionId, route.getId());
+ mActiveConnection.deselectRoute(sessionId, routeId);
}
}
@Override
- public void transferToRoute(int sessionId, MediaRoute2Info route) {
+ public void transferToRoute(int sessionId, String routeId) {
if (mConnectionReady) {
- mActiveConnection.transferToRoute(sessionId, route.getId());
+ mActiveConnection.transferToRoute(sessionId, routeId);
}
}
@Override
- public void sendControlRequest(MediaRoute2Info route, Intent request) {
+ public void sendControlRequest(String routeId, Intent request) {
if (mConnectionReady) {
- mActiveConnection.sendControlRequest(route.getId(), request);
+ mActiveConnection.sendControlRequest(routeId, request);
updateBinding();
}
}
@Override
- public void requestSetVolume(MediaRoute2Info route, int volume) {
+ public void requestSetVolume(String routeId, int volume) {
if (mConnectionReady) {
- mActiveConnection.requestSetVolume(route.getId(), volume);
+ mActiveConnection.requestSetVolume(routeId, volume);
updateBinding();
}
}
@Override
- public void requestUpdateVolume(MediaRoute2Info route, int delta) {
+ public void requestUpdateVolume(String routeId, int delta) {
if (mConnectionReady) {
- mActiveConnection.requestUpdateVolume(route.getId(), delta);
+ mActiveConnection.requestUpdateVolume(routeId, delta);
updateBinding();
}
}
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 82d2250..a5ffbb8 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -79,7 +79,6 @@
@GuardedBy("mLock")
private int mCurrentUserId = -1;
-
MediaRouter2ServiceImpl(Context context) {
mContext = context;
}
@@ -89,17 +88,23 @@
final int uid = Binder.getCallingUid();
final int userId = UserHandle.getUserId(uid);
- Collection<MediaRoute2Info> systemRoutes;
- synchronized (mLock) {
- UserRecord userRecord = mUserRecords.get(userId);
- if (userRecord == null) {
- userRecord = new UserRecord(userId);
- mUserRecords.put(userId, userRecord);
- initializeUserLocked(userRecord);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ Collection<MediaRoute2Info> systemRoutes;
+ synchronized (mLock) {
+ UserRecord userRecord = getOrCreateUserRecordLocked(userId);
+ MediaRoute2ProviderInfo providerInfo =
+ userRecord.mHandler.mSystemProvider.getProviderInfo();
+ if (providerInfo != null) {
+ systemRoutes = providerInfo.getRoutes();
+ } else {
+ systemRoutes = Collections.emptyList();
+ }
}
- systemRoutes = userRecord.mHandler.mSystemProvider.getProviderInfo().getRoutes();
+ return new ArrayList<>(systemRoutes);
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
- return new ArrayList<>(systemRoutes);
}
public void registerClient(@NonNull IMediaRouter2Client client,
@@ -233,6 +238,19 @@
}
}
+ public void releaseSession(IMediaRouter2Client client, String uniqueSessionId) {
+ Objects.requireNonNull(client, "client must not be null");
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ releaseSessionLocked(client, uniqueSessionId);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
public void sendControlRequest(@NonNull IMediaRouter2Client client,
@NonNull MediaRoute2Info route, @NonNull Intent request) {
Objects.requireNonNull(client, "client must not be null");
@@ -387,12 +405,7 @@
int uid, int pid, String packageName, int userId, boolean trusted) {
final IBinder binder = client.asBinder();
if (mAllClientRecords.get(binder) == null) {
- UserRecord userRecord = mUserRecords.get(userId);
- if (userRecord == null) {
- userRecord = new UserRecord(userId);
- mUserRecords.put(userId, userRecord);
- initializeUserLocked(userRecord);
- }
+ UserRecord userRecord = getOrCreateUserRecordLocked(userId);
Client2Record clientRecord = new Client2Record(userRecord, client, uid, pid,
packageName, trusted);
try {
@@ -477,6 +490,18 @@
}
}
+ private void releaseSessionLocked(@NonNull IMediaRouter2Client client, String uniqueSessionId) {
+ final IBinder binder = client.asBinder();
+ final Client2Record clientRecord = mAllClientRecords.get(binder);
+
+ if (clientRecord != null) {
+ clientRecord.mUserRecord.mHandler.sendMessage(
+ obtainMessage(UserHandler::releaseSessionOnHandler,
+ clientRecord.mUserRecord.mHandler,
+ clientRecord, uniqueSessionId));
+ }
+ }
+
private void setControlCategoriesLocked(Client2Record clientRecord, List<String> categories) {
if (clientRecord != null) {
if (clientRecord.mControlCategories.equals(categories)) {
@@ -531,12 +556,7 @@
final IBinder binder = manager.asBinder();
ManagerRecord managerRecord = mAllManagerRecords.get(binder);
if (managerRecord == null) {
- boolean newUser = false;
- UserRecord userRecord = mUserRecords.get(userId);
- if (userRecord == null) {
- userRecord = new UserRecord(userId);
- newUser = true;
- }
+ UserRecord userRecord = getOrCreateUserRecordLocked(userId);
managerRecord = new ManagerRecord(userRecord, manager, uid, pid, packageName, trusted);
try {
binder.linkToDeath(managerRecord, 0);
@@ -544,11 +564,6 @@
throw new RuntimeException("Media router manager died prematurely.", ex);
}
- if (newUser) {
- mUserRecords.put(userId, userRecord);
- initializeUserLocked(userRecord);
- }
-
userRecord.mManagerRecords.add(managerRecord);
mAllManagerRecords.put(binder, managerRecord);
@@ -636,14 +651,17 @@
return sessionInfos;
}
- private void initializeUserLocked(UserRecord userRecord) {
- if (DEBUG) {
- Slog.d(TAG, userRecord + ": Initialized");
+ private UserRecord getOrCreateUserRecordLocked(int userId) {
+ UserRecord userRecord = mUserRecords.get(userId);
+ if (userRecord == null) {
+ userRecord = new UserRecord(userId);
+ mUserRecords.put(userId, userRecord);
+ if (userId == mCurrentUserId) {
+ userRecord.mHandler.sendMessage(
+ obtainMessage(UserHandler::start, userRecord.mHandler));
+ }
}
- if (userRecord.mUserId == mCurrentUserId) {
- userRecord.mHandler.sendMessage(
- obtainMessage(UserHandler::start, userRecord.mHandler));
- }
+ return userRecord;
}
private void disposeUserIfNeededLocked(UserRecord userRecord) {
@@ -835,25 +853,32 @@
@Override
public void onProviderStateChanged(@NonNull MediaRoute2Provider provider) {
- sendMessage(PooledLambda.obtainMessage(UserHandler::updateProvider, this, provider));
+ sendMessage(PooledLambda.obtainMessage(UserHandler::onProviderStateChangedOnHandler,
+ this, provider));
}
@Override
public void onSessionCreated(@NonNull MediaRoute2Provider provider,
@Nullable RouteSessionInfo sessionInfo, long requestId) {
- sendMessage(PooledLambda.obtainMessage(UserHandler::handleCreateSessionResultOnHandler,
+ sendMessage(PooledLambda.obtainMessage(UserHandler::onSessionCreatedOnHandler,
this, provider, sessionInfo, requestId));
}
@Override
public void onSessionInfoChanged(@NonNull MediaRoute2Provider provider,
@NonNull RouteSessionInfo sessionInfo) {
- sendMessage(PooledLambda.obtainMessage(UserHandler::updateSession,
+ sendMessage(PooledLambda.obtainMessage(UserHandler::onSessionInfoChangedOnHandler,
+ this, provider, sessionInfo));
+ }
+
+ @Override
+ public void onSessionReleased(MediaRoute2Provider provider, RouteSessionInfo sessionInfo) {
+ sendMessage(PooledLambda.obtainMessage(UserHandler::onSessionReleasedOnHandler,
this, provider, sessionInfo));
}
//TODO: notify session info updates
- private void updateProvider(MediaRoute2Provider provider) {
+ private void onProviderStateChangedOnHandler(MediaRoute2Provider provider) {
int providerIndex = getProviderInfoIndex(provider.getUniqueId());
MediaRoute2ProviderInfo providerInfo = provider.getProviderInfo();
MediaRoute2ProviderInfo prevInfo =
@@ -958,7 +983,7 @@
clientRecord, route, controlCategory, requestId);
mSessionCreationRequests.add(request);
- provider.requestCreateSession(clientRecord.mPackageName, route.getId(),
+ provider.requestCreateSession(clientRecord.mPackageName, route.getOriginalId(),
controlCategory, requestId);
}
@@ -975,7 +1000,8 @@
if (provider == null) {
return;
}
- provider.selectRoute(RouteSessionInfo.getSessionId(uniqueSessionId, providerId), route);
+ provider.selectRoute(RouteSessionInfo.getSessionId(uniqueSessionId),
+ route.getOriginalId());
}
private void deselectRouteOnHandler(@NonNull Client2Record clientRecord,
@@ -991,8 +1017,8 @@
if (provider == null) {
return;
}
- provider.deselectRoute(
- RouteSessionInfo.getSessionId(uniqueSessionId, providerId), route);
+ provider.deselectRoute(RouteSessionInfo.getSessionId(uniqueSessionId),
+ route.getOriginalId());
}
private void transferToRouteOnHandler(@NonNull Client2Record clientRecord,
@@ -1008,8 +1034,8 @@
if (provider == null) {
return;
}
- provider.transferToRoute(
- RouteSessionInfo.getSessionId(uniqueSessionId, providerId), route);
+ provider.transferToRoute(RouteSessionInfo.getSessionId(uniqueSessionId),
+ route.getOriginalId());
}
private boolean checkArgumentsForSessionControl(@NonNull Client2Record clientRecord,
@@ -1040,20 +1066,57 @@
return false;
}
- try {
- RouteSessionInfo.getSessionId(uniqueSessionId, providerId);
- } catch (Exception ex) {
+ final Integer sessionId = RouteSessionInfo.getSessionId(uniqueSessionId);
+ if (sessionId == null) {
Slog.w(TAG, "Failed to get int session id from unique session id. "
- + "uniqueSessionId=" + uniqueSessionId + " providerId=" + providerId);
+ + "uniqueSessionId=" + uniqueSessionId);
return false;
}
return true;
}
- private void handleCreateSessionResultOnHandler(
- @NonNull MediaRoute2Provider provider, @Nullable RouteSessionInfo sessionInfo,
- long requestId) {
+ private void releaseSessionOnHandler(@NonNull Client2Record clientRecord,
+ String uniqueSessionId) {
+ if (TextUtils.isEmpty(uniqueSessionId)) {
+ Slog.w(TAG, "Ignoring releasing session with empty unique session ID.");
+ return;
+ }
+
+ final Client2Record matchingRecord = mSessionToClientMap.get(uniqueSessionId);
+ if (matchingRecord != clientRecord) {
+ Slog.w(TAG, "Ignoring releasing session from non-matching client."
+ + " packageName=" + clientRecord.mPackageName
+ + " uniqueSessionId=" + uniqueSessionId);
+ return;
+ }
+
+ final String providerId = RouteSessionInfo.getProviderId(uniqueSessionId);
+ if (providerId == null) {
+ Slog.w(TAG, "Ignoring releasing session with invalid unique session ID. "
+ + "uniqueSessionId=" + uniqueSessionId);
+ return;
+ }
+
+ final Integer sessionId = RouteSessionInfo.getSessionId(uniqueSessionId);
+ if (sessionId == null) {
+ Slog.w(TAG, "Ignoring releasing session with invalid unique session ID. "
+ + "uniqueSessionId=" + uniqueSessionId + " providerId=" + providerId);
+ return;
+ }
+
+ final MediaRoute2Provider provider = findProvider(providerId);
+ if (provider == null) {
+ Slog.w(TAG, "Ignoring releasing session since no provider found for given "
+ + "providerId=" + providerId);
+ return;
+ }
+
+ provider.releaseSession(sessionId);
+ }
+
+ private void onSessionCreatedOnHandler(@NonNull MediaRoute2Provider provider,
+ @Nullable RouteSessionInfo sessionInfo, long requestId) {
SessionCreationRequest matchingRequest = null;
for (SessionCreationRequest request : mSessionCreationRequests) {
@@ -1103,7 +1166,7 @@
// TODO: Tell managers for the session creation
}
- private void updateSession(@NonNull MediaRoute2Provider provider,
+ private void onSessionInfoChangedOnHandler(@NonNull MediaRoute2Provider provider,
@NonNull RouteSessionInfo sessionInfo) {
RouteSessionInfo sessionInfoWithProviderId = new RouteSessionInfo.Builder(sessionInfo)
.setProviderId(provider.getUniqueId())
@@ -1120,6 +1183,23 @@
// TODO: Tell managers for the session update
}
+ private void onSessionReleasedOnHandler(@NonNull MediaRoute2Provider provider,
+ @NonNull RouteSessionInfo sessionInfo) {
+ RouteSessionInfo sessionInfoWithProviderId = new RouteSessionInfo.Builder(sessionInfo)
+ .setProviderId(provider.getUniqueId())
+ .build();
+
+ Client2Record client2Record = mSessionToClientMap.get(
+ sessionInfoWithProviderId.getUniqueSessionId());
+ if (client2Record == null) {
+ Slog.w(TAG, "No matching client found for session=" + sessionInfoWithProviderId);
+ // TODO: Tell managers for the session release
+ return;
+ }
+ notifySessionReleased(client2Record, sessionInfoWithProviderId);
+ // TODO: Tell managers for the session release
+ }
+
private void notifySessionCreated(Client2Record clientRecord, RouteSessionInfo sessionInfo,
int requestId) {
try {
@@ -1149,24 +1229,34 @@
}
}
+ private void notifySessionReleased(Client2Record clientRecord,
+ RouteSessionInfo sessionInfo) {
+ try {
+ clientRecord.mClient.notifySessionReleased(sessionInfo);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to notify client of the session release."
+ + " Client probably died.", ex);
+ }
+ }
+
private void sendControlRequest(MediaRoute2Info route, Intent request) {
final MediaRoute2Provider provider = findProvider(route.getProviderId());
if (provider != null) {
- provider.sendControlRequest(route, request);
+ provider.sendControlRequest(route.getOriginalId(), request);
}
}
private void requestSetVolume(MediaRoute2Info route, int volume) {
final MediaRoute2Provider provider = findProvider(route.getProviderId());
if (provider != null) {
- provider.requestSetVolume(route, volume);
+ provider.requestSetVolume(route.getOriginalId(), volume);
}
}
private void requestUpdateVolume(MediaRoute2Info route, int delta) {
final MediaRoute2Provider provider = findProvider(route.getProviderId());
if (provider != null) {
- provider.requestUpdateVolume(route, delta);
+ provider.requestUpdateVolume(route.getOriginalId(), delta);
}
}
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 3e2bf4e..d77f43b 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -484,6 +484,12 @@
// Binder call
@Override
+ public void releaseSession(IMediaRouter2Client client, String sessionId) {
+ mService2.releaseSession(client, sessionId);
+ }
+
+ // Binder call
+ @Override
public void sendControlRequest(IMediaRouter2Client client, MediaRoute2Info route,
Intent request) {
mService2.sendControlRequest(client, route, request);
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 8fdfcbf..5302765 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -102,33 +102,33 @@
}
@Override
- public void selectRoute(int sessionId, MediaRoute2Info route) {
+ public void selectRoute(int sessionId, String routeId) {
//TODO: implement method
}
@Override
- public void deselectRoute(int sessionId, MediaRoute2Info route) {
+ public void deselectRoute(int sessionId, String routeId) {
//TODO: implement method
}
@Override
- public void transferToRoute(int sessionId, MediaRoute2Info route) {
+ public void transferToRoute(int sessionId, String routeId) {
//TODO: implement method
}
//TODO: implement method
@Override
- public void sendControlRequest(@NonNull MediaRoute2Info route, @NonNull Intent request) {
+ public void sendControlRequest(@NonNull String routeId, @NonNull Intent request) {
}
//TODO: implement method
@Override
- public void requestSetVolume(MediaRoute2Info route, int volume) {
+ public void requestSetVolume(String routeId, int volume) {
}
//TODO: implement method
@Override
- public void requestUpdateVolume(MediaRoute2Info route, int delta) {
+ public void requestUpdateVolume(String routeId, int delta) {
}
void initializeRoutes() {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 2390da5e..4a45730 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1780,7 +1780,7 @@
final TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
for (int i = 0; i < matchingSubIds.size(); i++) {
final int subId = matchingSubIds.get(i);
- tm.setPolicyDataEnabled(enabled, subId);
+ tm.createForSubscriptionId(subId).setPolicyDataEnabled(enabled);
}
}
}
@@ -1819,7 +1819,7 @@
final List<String[]> mergedSubscriberIdsList = new ArrayList();
final SparseArray<String> subIdToSubscriberId = new SparseArray<>(subList.size());
- for (SubscriptionInfo sub : subList) {
+ for (final SubscriptionInfo sub : subList) {
final TelephonyManager tmSub = tm.createForSubscriptionId(sub.getSubscriptionId());
final String subscriberId = tmSub.getSubscriberId();
if (!TextUtils.isEmpty(subscriberId)) {
@@ -3046,12 +3046,13 @@
// Verify they're not lying about package name
mAppOps.checkPackage(callingUid, callingPackage);
+ final SubscriptionManager sm;
final SubscriptionInfo si;
final PersistableBundle config;
final long token = Binder.clearCallingIdentity();
try {
- si = mContext.getSystemService(SubscriptionManager.class)
- .getActiveSubscriptionInfo(subId);
+ sm = mContext.getSystemService(SubscriptionManager.class);
+ si = sm.getActiveSubscriptionInfo(subId);
config = mCarrierConfigManager.getConfigForSubId(subId);
} finally {
Binder.restoreCallingIdentity(token);
@@ -3059,7 +3060,7 @@
// First check: is caller the CarrierService?
if (si != null) {
- if (si.isEmbedded() && si.canManageSubscription(mContext, callingPackage)) {
+ if (si.isEmbedded() && sm.canManageSubscription(si, callingPackage)) {
return;
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f42f4f7..a4f4d07 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -236,7 +236,6 @@
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.internal.util.function.TriPredicate;
import com.android.server.DeviceIdleInternal;
@@ -1182,21 +1181,14 @@
@Override
public void onNotificationBubbleChanged(String key, boolean isBubble) {
- String pkg;
- synchronized (mNotificationLock) {
- NotificationRecord r = mNotificationsByKey.get(key);
- pkg = r != null && r.sbn != null ? r.sbn.getPackageName() : null;
- }
- boolean isAppForeground = pkg != null
- && mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND;
synchronized (mNotificationLock) {
NotificationRecord r = mNotificationsByKey.get(key);
if (r != null) {
final StatusBarNotification n = r.sbn;
final int callingUid = n.getUid();
- pkg = n.getPackageName();
+ final String pkg = n.getPackageName();
if (isBubble && isNotificationAppropriateToBubble(r, pkg, callingUid,
- null /* oldEntry */, isAppForeground)) {
+ null /* oldEntry */)) {
r.getNotification().flags |= FLAG_BUBBLE;
} else {
r.getNotification().flags &= ~FLAG_BUBBLE;
@@ -5386,7 +5378,7 @@
private void flagNotificationForBubbles(NotificationRecord r, String pkg, int userId,
NotificationRecord oldRecord, boolean isAppForeground) {
Notification notification = r.getNotification();
- if (isNotificationAppropriateToBubble(r, pkg, userId, oldRecord, isAppForeground)) {
+ if (isNotificationAppropriateToBubble(r, pkg, userId, oldRecord)) {
notification.flags |= FLAG_BUBBLE;
} else {
notification.flags &= ~FLAG_BUBBLE;
@@ -5406,7 +5398,7 @@
* accounting for user choice & policy.
*/
private boolean isNotificationAppropriateToBubble(NotificationRecord r, String pkg, int userId,
- NotificationRecord oldRecord, boolean isAppForeground) {
+ NotificationRecord oldRecord) {
Notification notification = r.getNotification();
if (!canBubble(r, pkg, userId)) {
// no log: canBubble has its own
@@ -5418,11 +5410,6 @@
return false;
}
- if (isAppForeground) {
- // If the app is foreground it always gets to bubble
- return true;
- }
-
if (oldRecord != null && (oldRecord.getNotification().flags & FLAG_BUBBLE) != 0) {
// This is an update to an active bubble
return true;
@@ -5438,7 +5425,7 @@
boolean isMessageStyle = Notification.MessagingStyle.class.equals(
notification.getNotificationStyle());
if (!isMessageStyle && (peopleList == null || peopleList.isEmpty())) {
- logBubbleError(r.getKey(), "if not foreground, must have a person and be "
+ logBubbleError(r.getKey(), "Must have a person and be "
+ "Notification.MessageStyle or Notification.CATEGORY_CALL");
return false;
}
@@ -5446,6 +5433,11 @@
// Communication is a message or a call
boolean isCall = CATEGORY_CALL.equals(notification.category);
boolean hasForegroundService = (notification.flags & FLAG_FOREGROUND_SERVICE) != 0;
+ if (hasForegroundService && !isCall) {
+ logBubbleError(r.getKey(),
+ "foreground services must be Notification.CATEGORY_CALL to bubble");
+ return false;
+ }
if (isMessageStyle) {
if (hasValidRemoteInput(notification)) {
return true;
@@ -5459,7 +5451,7 @@
logBubbleError(r.getKey(), "calls require foreground service");
return false;
}
- logBubbleError(r.getKey(), "if not foreground, must be "
+ logBubbleError(r.getKey(), "Must be "
+ "Notification.MessageStyle or Notification.CATEGORY_CALL");
return false;
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index f1947ac..b782ca96 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -1154,6 +1154,10 @@
throws RemoteException, IOException {
PackageInfo packageInfo = mPackageManager.getPackageInfo(targetPackageName, 0,
userId);
+ if (packageInfo == null) {
+ throw new IOException("Unable to get target package");
+ }
+
String baseCodePath = packageInfo.applicationInfo.getBaseCodePath();
ApkAssets apkAssets = null;
diff --git a/services/core/java/com/android/server/people/PeopleServiceInternal.java b/services/core/java/com/android/server/people/PeopleServiceInternal.java
new file mode 100644
index 0000000..31d30362
--- /dev/null
+++ b/services/core/java/com/android/server/people/PeopleServiceInternal.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people;
+
+import android.service.appprediction.IPredictionService;
+
+/**
+ * @hide Only for use within the system server.
+ */
+public abstract class PeopleServiceInternal extends IPredictionService.Stub {}
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 5ae8c58..307a07b 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -502,6 +502,9 @@
} catch (RemoteException re) {
Slog.e(TAG, "Unable to contact apexservice", re);
return false;
+ } catch (Exception e) {
+ Slog.e(TAG, e.getMessage(), e);
+ return false;
}
}
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index d2a6b42..b25e1e2 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -48,6 +48,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
private static final String TAG = "CrossProfileAppsService";
@@ -67,7 +68,7 @@
@Override
public List<UserHandle> getTargetUserProfiles(String callingPackage) {
- Preconditions.checkNotNull(callingPackage);
+ Objects.requireNonNull(callingPackage);
verifyCallingPackage(callingPackage);
@@ -87,8 +88,8 @@
ComponentName component,
@UserIdInt int userId,
boolean launchMainActivity) throws RemoteException {
- Preconditions.checkNotNull(callingPackage);
- Preconditions.checkNotNull(component);
+ Objects.requireNonNull(callingPackage);
+ Objects.requireNonNull(component);
verifyCallingPackage(callingPackage);
diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java
index 70c0f8d..f9cfee1 100644
--- a/services/core/java/com/android/server/pm/KeySetManagerService.java
+++ b/services/core/java/com/android/server/pm/KeySetManagerService.java
@@ -38,6 +38,7 @@
import java.io.PrintWriter;
import java.security.PublicKey;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
/*
@@ -219,10 +220,10 @@
}
public void addScannedPackageLPw(AndroidPackage pkg) {
- Preconditions.checkNotNull(pkg, "Attempted to add null pkg to ksms.");
- Preconditions.checkNotNull(pkg.getPackageName(), "Attempted to add null pkg to ksms.");
+ Objects.requireNonNull(pkg, "Attempted to add null pkg to ksms.");
+ Objects.requireNonNull(pkg.getPackageName(), "Attempted to add null pkg to ksms.");
PackageSetting ps = mPackages.get(pkg.getPackageName());
- Preconditions.checkNotNull(ps, "pkg: " + pkg.getPackageName()
+ Objects.requireNonNull(ps, "pkg: " + pkg.getPackageName()
+ "does not have a corresponding entry in mPackages.");
addSigningKeySetToPackageLPw(ps, pkg.getSigningDetails().publicKeys);
if (pkg.getKeySetMapping() != null) {
@@ -504,7 +505,7 @@
* Adds the given PublicKey to the system, deduping as it goes.
*/
private long addPublicKeyLPw(PublicKey key) {
- Preconditions.checkNotNull(key, "Cannot add null public key!");
+ Objects.requireNonNull(key, "Cannot add null public key!");
long id = getIdForPublicKeyLPr(key);
if (id != PUBLIC_KEY_NOT_FOUND) {
@@ -574,7 +575,7 @@
/* remove refs from common keysets and public keys */
PackageSetting pkg = mPackages.get(packageName);
- Preconditions.checkNotNull(pkg, "pkg name: " + packageName
+ Objects.requireNonNull(pkg, "pkg name: " + packageName
+ "does not have a corresponding entry in mPackages.");
long signingKeySetId = pkg.keySetData.getProperSigningKeySet();
decrementKeySetLPw(signingKeySetId);
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 673e265..c4ef856 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -78,6 +78,7 @@
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
+import java.util.Objects;
/**
* Service that manages requests and callbacks for launchers that support
@@ -136,15 +137,15 @@
public LauncherAppsImpl(Context context) {
mContext = context;
mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- mUserManagerInternal = Preconditions.checkNotNull(
+ mUserManagerInternal = Objects.requireNonNull(
LocalServices.getService(UserManagerInternal.class));
- mUsageStatsManagerInternal = Preconditions.checkNotNull(
+ mUsageStatsManagerInternal = Objects.requireNonNull(
LocalServices.getService(UsageStatsManagerInternal.class));
- mActivityManagerInternal = Preconditions.checkNotNull(
+ mActivityManagerInternal = Objects.requireNonNull(
LocalServices.getService(ActivityManagerInternal.class));
- mActivityTaskManagerInternal = Preconditions.checkNotNull(
+ mActivityTaskManagerInternal = Objects.requireNonNull(
LocalServices.getService(ActivityTaskManagerInternal.class));
- mShortcutServiceInternal = Preconditions.checkNotNull(
+ mShortcutServiceInternal = Objects.requireNonNull(
LocalServices.getService(ShortcutServiceInternal.class));
mShortcutServiceInternal.addListener(mPackageMonitor);
mCallbackHandler = BackgroundThread.getHandler();
@@ -558,7 +559,7 @@
if (!canAccessProfile(user.getIdentifier(), "Cannot check package")) {
return null;
}
- Preconditions.checkNotNull(component);
+ Objects.requireNonNull(component);
// All right, create the sender.
Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT).setComponent(component);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index ac183dc..426cd01 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -119,7 +119,6 @@
import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
-import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.dex.DexManager;
@@ -140,6 +139,7 @@
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
@@ -511,7 +511,7 @@
this.userId = userId;
mOriginalInstallerUid = installerUid;
mInstallerUid = installerUid;
- mInstallSource = Preconditions.checkNotNull(installSource);
+ mInstallSource = Objects.requireNonNull(installSource);
this.params = params;
this.createdMillis = createdMillis;
this.updatedMillis = createdMillis;
@@ -1143,7 +1143,7 @@
* permissions.
*/
private boolean markAsCommitted(@NonNull IntentSender statusReceiver) {
- Preconditions.checkNotNull(statusReceiver);
+ Objects.requireNonNull(statusReceiver);
List<PackageInstallerSession> childSessions = getChildSessions();
@@ -1408,8 +1408,8 @@
@Override
public void transfer(String packageName, IntentSender statusReceiver) {
- Preconditions.checkNotNull(statusReceiver);
- Preconditions.checkNotNull(packageName);
+ Objects.requireNonNull(statusReceiver);
+ Objects.requireNonNull(packageName);
try {
assertCanBeTransferredAndReturnNewOwner(packageName);
@@ -1607,9 +1607,9 @@
localObserver = null;
} else {
if (!params.isMultiPackage) {
- Preconditions.checkNotNull(mPackageName);
- Preconditions.checkNotNull(mSigningDetails);
- Preconditions.checkNotNull(mResolvedBaseFile);
+ Objects.requireNonNull(mPackageName);
+ Objects.requireNonNull(mSigningDetails);
+ Objects.requireNonNull(mResolvedBaseFile);
if (needToAskForPermissionsLocked()) {
// User needs to confirm installation;
@@ -1683,7 +1683,7 @@
computeProgressLocked(true);
// Unpack native libraries for non-incremental installation
- if (isIncrementalInstallation()) {
+ if (!isIncrementalInstallation()) {
extractNativeLibraries(stageDir, params.abiOverride, mayInheritNativeLibs());
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 04e7372..6bd9c48 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -13452,9 +13452,7 @@
// Okay!
targetPackageSetting.setInstallerPackageName(installerPackageName);
- if (installerPackageName != null) {
- mSettings.mInstallerPackages.add(installerPackageName);
- }
+ mSettings.addInstallerPackageNames(targetPackageSetting.installSource);
scheduleWriteSettingsLocked();
}
}
@@ -15160,7 +15158,8 @@
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
final String pkgName = pkg.getPackageName();
- final String installerPackageName = installArgs.installSource.installerPackageName;
+ final InstallSource installSource = installArgs.installSource;
+ final String installerPackageName = installSource.installerPackageName;
final int[] installedForUsers = res.origUsers;
final int installReason = installArgs.installReason;
@@ -15171,7 +15170,7 @@
// For system-bundled packages, we assume that installing an upgraded version
// of the package implies that the user actually wants to run that new code,
// so we enable the package.
- PackageSetting ps = mSettings.mPackages.get(pkgName);
+ final PackageSetting ps = mSettings.mPackages.get(pkgName);
final int userId = installArgs.user.getIdentifier();
if (ps != null) {
if (isSystemApp(pkg)) {
@@ -15208,8 +15207,8 @@
ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName);
}
- ps.setInstallSource(installArgs.installSource);
-
+ ps.setInstallSource(installSource);
+ mSettings.addInstallerPackageNames(installSource);
// When replacing an existing package, preserve the original install reason for all
// users that had the package installed before.
@@ -15239,7 +15238,6 @@
res.name = pkgName;
res.uid = pkg.getUid();
res.pkg = pkg;
- mSettings.setInstallerPackageName(pkgName, installerPackageName);
res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
//to update install status
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "writeSettings");
@@ -23057,6 +23055,11 @@
}
@Override
+ public void setDeviceOwnerProtectedPackages(List<String> packageNames) {
+ mProtectedPackages.setDeviceOwnerProtectedPackages(packageNames);
+ }
+
+ @Override
public boolean isPackageDataProtected(int userId, String packageName) {
return mProtectedPackages.isPackageDataProtected(userId, packageName);
}
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 671450d7..0c0b93b 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -41,6 +41,7 @@
import java.io.File;
import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
/**
@@ -172,7 +173,7 @@
}
public void setInstallSource(InstallSource installSource) {
- this.installSource = Preconditions.checkNotNull(installSource);
+ this.installSource = Objects.requireNonNull(installSource);
}
void removeInstallerPackage(String packageName) {
diff --git a/services/core/java/com/android/server/pm/ProtectedPackages.java b/services/core/java/com/android/server/pm/ProtectedPackages.java
index 231168e..4da3cc3 100644
--- a/services/core/java/com/android/server/pm/ProtectedPackages.java
+++ b/services/core/java/com/android/server/pm/ProtectedPackages.java
@@ -24,6 +24,10 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
+
+import java.util.ArrayList;
+import java.util.List;
/**
* Manages package names that need special protection.
@@ -49,6 +53,10 @@
@GuardedBy("this")
private final String mDeviceProvisioningPackage;
+ @Nullable
+ @GuardedBy("this")
+ private List<String> mDeviceOwnerProtectedPackages;
+
private final Context mContext;
public ProtectedPackages(Context context) {
@@ -70,6 +78,10 @@
: profileOwnerPackages.clone();
}
+ public synchronized void setDeviceOwnerProtectedPackages(List<String> packageNames) {
+ mDeviceOwnerProtectedPackages = new ArrayList<String>(packageNames);
+ }
+
private synchronized boolean hasDeviceOwnerOrProfileOwner(int userId, String packageName) {
if (packageName == null) {
return false;
@@ -105,7 +117,8 @@
* can modify its data or package state.
*/
private synchronized boolean isProtectedPackage(String packageName) {
- return packageName != null && packageName.equals(mDeviceProvisioningPackage);
+ return packageName != null && (packageName.equals(mDeviceProvisioningPackage)
+ || ArrayUtils.contains(mDeviceOwnerProtectedPackages, packageName));
}
/**
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 9642a1a..f9a3361 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -280,8 +280,11 @@
/** Map from package name to settings */
final ArrayMap<String, PackageSetting> mPackages = new ArrayMap<>();
- /** List of packages that installed other packages */
- final ArraySet<String> mInstallerPackages = new ArraySet<>();
+ /**
+ * List of packages that were involved in installing other packages, i.e. are listed
+ * in at least one app's InstallSource.
+ */
+ private final ArraySet<String> mInstallerPackages = new ArraySet<>();
/** Map from package name to appId and excluded userids */
private final ArrayMap<String, KernelPackageState> mKernelMapping = new ArrayMap<>();
@@ -441,16 +444,6 @@
return mPermissions.canPropagatePermissionToInstantApp(permName);
}
- void setInstallerPackageName(String pkgName, String installerPkgName) {
- PackageSetting p = mPackages.get(pkgName);
- if (p != null) {
- p.setInstallerPackageName(installerPkgName);
- if (installerPkgName != null) {
- mInstallerPackages.add(installerPkgName);
- }
- }
- }
-
/** Gets and optionally creates a new shared user id. */
SharedUserSetting getSharedUserLPw(String name, int pkgFlags, int pkgPrivateFlags,
boolean create) throws PackageManagerException {
@@ -3777,9 +3770,10 @@
}
if (packageSetting != null) {
packageSetting.uidError = "true".equals(uidError);
- packageSetting.installSource = InstallSource.create(
+ InstallSource installSource = InstallSource.create(
installInitiatingPackageName, installOriginatingPackageName,
installerPackageName, "true".equals(isOrphaned));
+ packageSetting.installSource = installSource;
packageSetting.volumeUuid = volumeUuid;
packageSetting.categoryHint = categoryHint;
packageSetting.legacyNativeLibraryPathString = legacyNativeLibraryPathStr;
@@ -3809,9 +3803,7 @@
packageSetting.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, 0, null);
}
- if (installerPackageName != null) {
- mInstallerPackages.add(installerPackageName);
- }
+ addInstallerPackageNames(installSource);
int outerDepth = parser.getDepth();
int type;
@@ -3870,6 +3862,18 @@
}
}
+ void addInstallerPackageNames(InstallSource installSource) {
+ if (installSource.installerPackageName != null) {
+ mInstallerPackages.add(installSource.installerPackageName);
+ }
+ if (installSource.initiatingPackageName != null) {
+ mInstallerPackages.add(installSource.initiatingPackageName);
+ }
+ if (installSource.originatingPackageName != null) {
+ mInstallerPackages.add(installSource.originatingPackageName);
+ }
+ }
+
private void readDisabledComponentsLPw(PackageSettingBase packageSetting, XmlPullParser parser,
int userId) throws IOException, XmlPullParserException {
int outerDepth = parser.getDepth();
diff --git a/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java b/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java
index 815f885..dc534a7 100644
--- a/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java
+++ b/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java
@@ -38,6 +38,7 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Deque;
+import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingDeque;
@@ -157,7 +158,7 @@
public void saveBitmapLocked(ShortcutInfo shortcut,
int maxDimension, CompressFormat format, int quality) {
final Icon icon = shortcut.getIcon();
- Preconditions.checkNotNull(icon);
+ Objects.requireNonNull(icon);
final Bitmap original = icon.getBitmap();
if (original == null) {
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 06c71ba..0274aee 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -55,6 +55,7 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
import java.util.function.Predicate;
@@ -454,7 +455,7 @@
public void updateInvisibleShortcutForPinRequestWith(@NonNull ShortcutInfo shortcut) {
final ShortcutInfo source = mShortcuts.get(shortcut.getId());
- Preconditions.checkNotNull(source);
+ Objects.requireNonNull(source);
mShortcutUser.mService.validateShortcutForPinRequest(shortcut);
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageItem.java b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
index 0629d9e..6d9d69e 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageItem.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
@@ -28,6 +28,7 @@
import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
+import java.util.Objects;
/**
* All methods should be guarded by {@code #mShortcutUser.mService.mLock}.
@@ -49,7 +50,7 @@
mShortcutUser = shortcutUser;
mPackageUserId = packageUserId;
mPackageName = Preconditions.checkStringNotEmpty(packageName);
- mPackageInfo = Preconditions.checkNotNull(packageInfo);
+ mPackageInfo = Objects.requireNonNull(packageInfo);
}
/**
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index f0a1c70..261418c 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -429,17 +429,17 @@
@VisibleForTesting
ShortcutService(Context context, Looper looper, boolean onlyForPackageManagerApis) {
- mContext = Preconditions.checkNotNull(context);
+ mContext = Objects.requireNonNull(context);
LocalServices.addService(ShortcutServiceInternal.class, new LocalService());
mHandler = new Handler(looper);
mIPackageManager = AppGlobals.getPackageManager();
- mPackageManagerInternal = Preconditions.checkNotNull(
+ mPackageManagerInternal = Objects.requireNonNull(
LocalServices.getService(PackageManagerInternal.class));
- mUserManagerInternal = Preconditions.checkNotNull(
+ mUserManagerInternal = Objects.requireNonNull(
LocalServices.getService(UserManagerInternal.class));
- mUsageStatsManagerInternal = Preconditions.checkNotNull(
+ mUsageStatsManagerInternal = Objects.requireNonNull(
LocalServices.getService(UsageStatsManagerInternal.class));
- mActivityManagerInternal = Preconditions.checkNotNull(
+ mActivityManagerInternal = Objects.requireNonNull(
LocalServices.getService(ActivityManagerInternal.class));
mShortcutRequestPinProcessor = new ShortcutRequestPinProcessor(this, mLock);
@@ -1680,7 +1680,7 @@
"Re-publishing ShortcutInfo returned by server is not supported."
+ " Some information such as icon may lost from shortcut.");
}
- Preconditions.checkNotNull(shortcut, "Null shortcut detected");
+ Objects.requireNonNull(shortcut, "Null shortcut detected");
if (shortcut.getActivity() != null) {
Preconditions.checkState(
shortcut.getPackage().equals(shortcut.getActivity().getPackageName()),
@@ -1947,7 +1947,7 @@
@Override
public boolean requestPinShortcut(String packageName, ShortcutInfo shortcut,
IntentSender resultIntent, int userId) {
- Preconditions.checkNotNull(shortcut);
+ Objects.requireNonNull(shortcut);
Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled");
return requestPinItem(packageName, userId, shortcut, null, null, resultIntent);
}
@@ -1955,7 +1955,7 @@
@Override
public Intent createShortcutResultIntent(String packageName, ShortcutInfo shortcut, int userId)
throws RemoteException {
- Preconditions.checkNotNull(shortcut);
+ Objects.requireNonNull(shortcut);
Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled");
verifyCaller(packageName, userId);
verifyShortcutInfoPackage(packageName, shortcut);
@@ -2019,7 +2019,7 @@
public void disableShortcuts(String packageName, List shortcutIds,
CharSequence disabledMessage, int disabledMessageResId, @UserIdInt int userId) {
verifyCaller(packageName, userId);
- Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
+ Objects.requireNonNull(shortcutIds, "shortcutIds must be provided");
synchronized (mLock) {
throwIfUserLockedL(userId);
@@ -2054,7 +2054,7 @@
@Override
public void enableShortcuts(String packageName, List shortcutIds, @UserIdInt int userId) {
verifyCaller(packageName, userId);
- Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
+ Objects.requireNonNull(shortcutIds, "shortcutIds must be provided");
synchronized (mLock) {
throwIfUserLockedL(userId);
@@ -2081,7 +2081,7 @@
public void removeDynamicShortcuts(String packageName, List shortcutIds,
@UserIdInt int userId) {
verifyCaller(packageName, userId);
- Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
+ Objects.requireNonNull(shortcutIds, "shortcutIds must be provided");
synchronized (mLock) {
throwIfUserLockedL(userId);
@@ -2256,7 +2256,7 @@
public void reportShortcutUsed(String packageName, String shortcutId, int userId) {
verifyCaller(packageName, userId);
- Preconditions.checkNotNull(shortcutId);
+ Objects.requireNonNull(shortcutId);
if (DEBUG) {
Slog.d(TAG, String.format("reportShortcutUsed: Shortcut %s package %s used on user %d",
@@ -2713,7 +2713,7 @@
@NonNull List<String> shortcutIds, int userId) {
// Calling permission must be checked by LauncherAppsImpl.
Preconditions.checkStringNotEmpty(packageName, "packageName");
- Preconditions.checkNotNull(shortcutIds, "shortcutIds");
+ Objects.requireNonNull(shortcutIds, "shortcutIds");
synchronized (mLock) {
throwIfUserLockedL(userId);
@@ -2766,16 +2766,16 @@
@Override
public void addListener(@NonNull ShortcutChangeListener listener) {
synchronized (mLock) {
- mListeners.add(Preconditions.checkNotNull(listener));
+ mListeners.add(Objects.requireNonNull(listener));
}
}
@Override
public int getShortcutIconResId(int launcherUserId, @NonNull String callingPackage,
@NonNull String packageName, @NonNull String shortcutId, int userId) {
- Preconditions.checkNotNull(callingPackage, "callingPackage");
- Preconditions.checkNotNull(packageName, "packageName");
- Preconditions.checkNotNull(shortcutId, "shortcutId");
+ Objects.requireNonNull(callingPackage, "callingPackage");
+ Objects.requireNonNull(packageName, "packageName");
+ Objects.requireNonNull(shortcutId, "shortcutId");
synchronized (mLock) {
throwIfUserLockedL(userId);
@@ -2800,9 +2800,9 @@
public ParcelFileDescriptor getShortcutIconFd(int launcherUserId,
@NonNull String callingPackage, @NonNull String packageName,
@NonNull String shortcutId, int userId) {
- Preconditions.checkNotNull(callingPackage, "callingPackage");
- Preconditions.checkNotNull(packageName, "packageName");
- Preconditions.checkNotNull(shortcutId, "shortcutId");
+ Objects.requireNonNull(callingPackage, "callingPackage");
+ Objects.requireNonNull(packageName, "packageName");
+ Objects.requireNonNull(shortcutId, "shortcutId");
synchronized (mLock) {
throwIfUserLockedL(userId);
@@ -2854,7 +2854,7 @@
public boolean requestPinAppWidget(@NonNull String callingPackage,
@NonNull AppWidgetProviderInfo appWidget, @Nullable Bundle extras,
@Nullable IntentSender resultIntent, int userId) {
- Preconditions.checkNotNull(appWidget);
+ Objects.requireNonNull(appWidget);
return requestPinItem(callingPackage, userId, null, appWidget, extras, resultIntent);
}
@@ -2865,7 +2865,7 @@
@Override
public boolean isForegroundDefaultLauncher(@NonNull String callingPackage, int callingUid) {
- Preconditions.checkNotNull(callingPackage);
+ Objects.requireNonNull(callingPackage);
final int userId = UserHandle.getUserId(callingUid);
final ComponentName defaultLauncher = getDefaultLauncher(userId);
@@ -3411,7 +3411,7 @@
List<ResolveInfo> queryActivities(@NonNull Intent baseIntent,
@NonNull String packageName, @Nullable ComponentName activity, int userId) {
- baseIntent.setPackage(Preconditions.checkNotNull(packageName));
+ baseIntent.setPackage(Objects.requireNonNull(packageName));
if (activity != null) {
baseIntent.setComponent(activity);
}
@@ -3529,7 +3529,7 @@
@Nullable
ComponentName injectGetPinConfirmationActivity(@NonNull String launcherPackageName,
int launcherUserId, int requestType) {
- Preconditions.checkNotNull(launcherPackageName);
+ Objects.requireNonNull(launcherPackageName);
String action = requestType == LauncherApps.PinItemRequest.REQUEST_TYPE_SHORTCUT ?
LauncherApps.ACTION_CONFIRM_PIN_SHORTCUT :
LauncherApps.ACTION_CONFIRM_PIN_APPWIDGET;
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index 8c207a8..eab3f4d 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -75,7 +75,7 @@
private PackageWithUser(int userId, String packageName) {
this.userId = userId;
- this.packageName = Preconditions.checkNotNull(packageName);
+ this.packageName = Objects.requireNonNull(packageName);
}
public static PackageWithUser of(int userId, String packageName) {
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 6c3eb31..9e462cd 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -50,6 +50,7 @@
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
import android.util.IntArray;
import android.util.Slog;
@@ -58,6 +59,7 @@
import android.util.apk.ApkSignatureVerifier;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.content.PackageHelper;
import com.android.internal.os.BackgroundThread;
import java.io.File;
@@ -309,39 +311,100 @@
return sessionContains(session, (s) -> !isApexSession(s));
}
+ // Reverts apex sessions and user data (if checkpoint is supported). Also reboots the device.
+ private void abortCheckpoint() {
+ try {
+ if (supportsCheckpoint() && needsCheckpoint()) {
+ mApexManager.revertActiveSessions();
+ PackageHelper.getStorageManager().abortChanges(
+ "StagingManager initiated", false /*retry*/);
+ }
+ } catch (Exception e) {
+ Slog.wtf(TAG, "Failed to abort checkpoint", e);
+ mApexManager.revertActiveSessions();
+ mPowerManager.reboot(null);
+ }
+ }
+
+ private boolean supportsCheckpoint() throws RemoteException {
+ return PackageHelper.getStorageManager().supportsCheckpoint();
+ }
+
+ private boolean needsCheckpoint() throws RemoteException {
+ return PackageHelper.getStorageManager().needsCheckpoint();
+ }
+
private void resumeSession(@NonNull PackageInstallerSession session) {
Slog.d(TAG, "Resuming session " + session.sessionId);
+
final boolean hasApex = sessionContainsApex(session);
+ ApexSessionInfo apexSessionInfo = null;
if (hasApex) {
// Check with apexservice whether the apex packages have been activated.
- ApexSessionInfo apexSessionInfo = mApexManager.getStagedSessionInfo(session.sessionId);
+ apexSessionInfo = mApexManager.getStagedSessionInfo(session.sessionId);
+
+ if (apexSessionInfo != null && apexSessionInfo.isVerified) {
+ // Session has been previously submitted to apexd, but didn't complete all the
+ // pre-reboot verification, perhaps because the device rebooted in the meantime.
+ // Greedily re-trigger the pre-reboot verification. We want to avoid marking it as
+ // failed when not in checkpoint mode, hence it is being processed separately.
+ Slog.d(TAG, "Found pending staged session " + session.sessionId + " still to "
+ + "be verified, resuming pre-reboot verification");
+ mPreRebootVerificationHandler.startPreRebootVerification(session.sessionId);
+ return;
+ }
+ }
+
+ // Before we resume session, we check if revert is needed or not. Typically, we enter file-
+ // system checkpoint mode when we reboot first time in order to install staged sessions. We
+ // want to install staged sessions in this mode as rebooting now will revert user data. If
+ // something goes wrong, then we reboot again to enter fs-rollback mode. Rebooting now will
+ // have no effect on user data, so mark the sessions as failed instead.
+ try {
+ // If checkpoint is supported, then we only resume sessions if we are in checkpointing
+ // mode. If not, we fail all sessions.
+ if (supportsCheckpoint() && !needsCheckpoint()) {
+ // TODO(b/146343545): Persist failure reason across checkpoint reboot
+ session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN,
+ "Reverting back to safe state");
+ return;
+ }
+ } catch (RemoteException e) {
+ // Cannot continue staged install without knowing if fs-checkpoint is supported
+ Slog.e(TAG, "Checkpoint support unknown. Aborting staged install for session "
+ + session.sessionId, e);
+ // TODO: Mark all staged sessions together and reboot only once
+ session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN,
+ "Checkpoint support unknown. Aborting staged install.");
+ if (hasApex) {
+ mApexManager.revertActiveSessions();
+ }
+ mPowerManager.reboot("Checkpoint support unknown");
+ return;
+ }
+
+ if (hasApex) {
if (apexSessionInfo == null) {
session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
"apexd did not know anything about a staged session supposed to be"
+ "activated");
+ abortCheckpoint();
return;
}
if (isApexSessionFailed(apexSessionInfo)) {
session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
"APEX activation failed. Check logcat messages from apexd for "
+ "more information.");
- return;
- }
- if (apexSessionInfo.isVerified) {
- // Session has been previously submitted to apexd, but didn't complete all the
- // pre-reboot verification, perhaps because the device rebooted in the meantime.
- // Greedily re-trigger the pre-reboot verification.
- Slog.d(TAG, "Found pending staged session " + session.sessionId + " still to be "
- + "verified, resuming pre-reboot verification");
- mPreRebootVerificationHandler.startPreRebootVerification(session.sessionId);
+ abortCheckpoint();
return;
}
if (!apexSessionInfo.isActivated && !apexSessionInfo.isSuccess) {
- // In all the remaining cases apexd will try to apply the session again at next
- // boot. Nothing to do here for now.
- Slog.w(TAG, "Staged session " + session.sessionId + " scheduled to be applied "
- + "at boot didn't activate nor fail. This usually means that apexd will "
- + "retry at next reboot.");
+ // Apexd did not apply the session for some unknown reason. There is no guarantee
+ // that apexd will install it next time. Safer to proactively mark as failed.
+ session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
+ "Staged session " + session.sessionId + "at boot didn't "
+ + "activate nor fail. Marking it as failed anyway.");
+ abortCheckpoint();
return;
}
Slog.i(TAG, "APEX packages in session " + session.sessionId
@@ -353,7 +416,9 @@
installApksInSession(session);
} catch (PackageManagerException e) {
session.setStagedSessionFailed(e.error, e.getMessage());
+ abortCheckpoint();
+ // If checkpoint is not supported, we have to handle failure for one staged session.
if (!hasApex) {
return;
}
@@ -1020,6 +1085,20 @@
* </ul></p>
*/
private void handlePreRebootVerification_End(@NonNull PackageInstallerSession session) {
+ // Before marking the session as ready, start checkpoint service if available
+ try {
+ IStorageManager storageManager = PackageHelper.getStorageManager();
+ if (storageManager.supportsCheckpoint()) {
+ storageManager.startCheckpoint(1);
+ }
+ } catch (Exception e) {
+ // Failed to get hold of StorageManager
+ Slog.e(TAG, "Failed to get hold of StorageManager", e);
+ session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN,
+ "Failed to get hold of StorageManager");
+ return;
+ }
+
// Proactively mark session as ready before calling apexd. Although this call order
// looks counter-intuitive, this is the easiest way to ensure that session won't end up
// in the inconsistent state:
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index 59a5804..f5f4009 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -48,6 +48,44 @@
"include-filter": "android.permission.cts.PermissionUpdateListenerTest"
}
]
+ },
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "install-arg": "-t"
+ },
+ {
+ "include-filter": "com.android.server.pm.UserDataPreparerTest"
+ },
+ {
+ "include-filter": "com.android.server.pm.UserLifecycleStressTest"
+ },
+ {
+ "include-filter": "com.android.server.pm.UserManagerServiceCreateProfileTest"
+ },
+ {
+ "include-filter": "com.android.server.pm.UserManagerServiceIdRecyclingTest"
+ },
+ {
+ "include-filter": "com.android.server.pm.UserManagerServiceTest"
+ },
+ {
+ "include-filter": "com.android.server.pm.UserManagerServiceUserInfoTest"
+ },
+ {
+ "include-filter": "com.android.server.pm.UserManagerServiceUserTypeTest"
+ },
+ {
+ "include-filter": "com.android.server.pm.UserManagerTest"
+ },
+ {
+ "include-filter": "com.android.server.pm.UserRestrictionsUtilsTest"
+ },
+ {
+ "include-filter": "com.android.server.pm.UserSystemPackageInstallerTest"
+ }
+ ]
}
],
"imports": [
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 5854e32..e5d5b57 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -434,7 +434,7 @@
private final IntentSender mTarget;
public DisableQuietModeUserUnlockedCallback(IntentSender target) {
- Preconditions.checkNotNull(target);
+ Objects.requireNonNull(target);
mTarget = target;
}
@@ -884,7 +884,7 @@
@Override
public boolean requestQuietModeEnabled(@NonNull String callingPackage, boolean enableQuietMode,
@UserIdInt int userId, @Nullable IntentSender target) {
- Preconditions.checkNotNull(callingPackage);
+ Objects.requireNonNull(callingPackage);
if (enableQuietMode && target != null) {
throw new IllegalArgumentException(
@@ -1132,14 +1132,13 @@
}
/**
- * Returns the user type, e.g. {@link UserManager#USER_TYPE_FULL_GUEST}, of the given userId,
- * or null if the user doesn't exist.
+ * Returns whether the given user (specified by userId) is of the given user type, such as
+ * {@link UserManager#USER_TYPE_FULL_GUEST}.
*/
@Override
- public @Nullable String getUserTypeForUser(@UserIdInt int userId) {
- // TODO(b/142482943): Decide on the appropriate permission requirements.
- checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getUserTypeForUser");
- return getUserTypeNoChecks(userId);
+ public boolean isUserOfType(@UserIdInt int userId, String userType) {
+ checkManageUsersPermission("check user type");
+ return userType != null && userType.equals(getUserTypeNoChecks(userId));
}
/**
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 90bd947..815f7b4 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -51,6 +51,7 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
/**
@@ -357,7 +358,7 @@
}
public static void merge(@NonNull Bundle dest, @Nullable Bundle in) {
- Preconditions.checkNotNull(dest);
+ Objects.requireNonNull(dest);
Preconditions.checkArgument(dest != in);
if (in == null) {
return;
@@ -661,7 +662,7 @@
public static boolean isSettingRestrictedForUser(Context context, @NonNull String setting,
int userId, String value, int callingUid) {
- Preconditions.checkNotNull(setting);
+ Objects.requireNonNull(setting);
final UserManager mUserManager = context.getSystemService(UserManager.class);
String restriction;
boolean checkAllUser = false;
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 486cfef..0caab6d 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -62,6 +62,7 @@
import java.io.File;
import java.io.FileNotFoundException;
+import java.util.Objects;
/**
* A system service that provides access to runtime and compiler artifacts.
@@ -180,7 +181,7 @@
}
// Sanity checks on the arguments.
- Preconditions.checkNotNull(callback);
+ Objects.requireNonNull(callback);
boolean bootImageProfile = profileType == ArtManager.PROFILE_BOOT_IMAGE;
if (!bootImageProfile) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 9cd6f16..d921f31 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -796,7 +796,7 @@
@Override
public int checkPermission(String permName, String pkgName, int userId) {
- // Not using Preconditions.checkNotNull() here for compatibility reasons.
+ // Not using Objects.requireNonNull() here for compatibility reasons.
if (permName == null || pkgName == null) {
return PackageManager.PERMISSION_DENIED;
}
@@ -872,7 +872,7 @@
@Override
public int checkUidPermission(String permName, int uid) {
- // Not using Preconditions.checkNotNull() here for compatibility reasons.
+ // Not using Objects.requireNonNull() here for compatibility reasons.
if (permName == null) {
return PackageManager.PERMISSION_DENIED;
}
@@ -955,7 +955,7 @@
@Override
@Nullable public List<String> getWhitelistedRestrictedPermissions(@NonNull String packageName,
@PermissionWhitelistFlags int flags, @UserIdInt int userId) {
- Preconditions.checkNotNull(packageName);
+ Objects.requireNonNull(packageName);
Preconditions.checkFlagsArgument(flags,
PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
| PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM
@@ -1043,7 +1043,7 @@
@NonNull String permName, @PermissionWhitelistFlags int flags,
@UserIdInt int userId) {
// Other argument checks are done in get/setWhitelistedRestrictedPermissions
- Preconditions.checkNotNull(permName);
+ Objects.requireNonNull(permName);
if (!checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission(permName)) {
return false;
@@ -1086,7 +1086,7 @@
@NonNull String permName, @PermissionWhitelistFlags int flags,
@UserIdInt int userId) {
// Other argument checks are done in get/setWhitelistedRestrictedPermissions
- Preconditions.checkNotNull(permName);
+ Objects.requireNonNull(permName);
if (!checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission(permName)) {
return false;
@@ -1104,7 +1104,7 @@
private boolean setWhitelistedRestrictedPermissionsInternal(@NonNull String packageName,
@Nullable List<String> permissions, @PermissionWhitelistFlags int flags,
@UserIdInt int userId) {
- Preconditions.checkNotNull(packageName);
+ Objects.requireNonNull(packageName);
Preconditions.checkFlagsArgument(flags,
PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
| PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM
@@ -3028,7 +3028,7 @@
mContext.enforceCallingPermission(Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS,
"Must hold " + Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS
+ " to register permissions as one time.");
- packageName = Preconditions.checkNotNull(packageName);
+ Objects.requireNonNull(packageName);
long token = Binder.clearCallingIdentity();
try {
@@ -3044,7 +3044,7 @@
mContext.enforceCallingPermission(Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS,
"Must hold " + Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS
+ " to remove permissions as one time.");
- Preconditions.checkNotNull(packageName);
+ Objects.requireNonNull(packageName);
long token = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 0d8f257..956962e 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -48,34 +48,19 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR;
-import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
-import static android.view.WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
-import static android.view.WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
-import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
-import static android.view.WindowManager.LayoutParams.TYPE_PHONE;
-import static android.view.WindowManager.LayoutParams.TYPE_POINTER;
import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
-import static android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;
import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_SEARCH_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
-import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
-import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
-import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN;
@@ -577,10 +562,6 @@
private boolean mScreenshotChordPowerKeyTriggered;
private long mScreenshotChordPowerKeyTime;
- private static final long MOVING_DISPLAY_TO_TOP_DURATION_MILLIS = 10;
- private volatile boolean mMovingDisplayToTopKeyTriggered;
- private volatile long mMovingDisplayToTopKeyTime;
-
// Ringer toggle should reuse timing and triggering from screenshot power and a11y vol up
private int mRingerToggleChord = VOLUME_HUSH_OFF;
@@ -648,7 +629,6 @@
private static final int MSG_POWER_VERY_LONG_PRESS = 25;
private static final int MSG_NOTIFY_USER_ACTIVITY = 26;
private static final int MSG_RINGER_TOGGLE_CHORD = 27;
- private static final int MSG_MOVE_DISPLAY_TO_TOP = 28;
private class PolicyHandler extends Handler {
@Override
@@ -712,7 +692,7 @@
accessibilityShortcutActivated();
break;
case MSG_BUGREPORT_TV:
- requestFullBugreport();
+ requestFullBugreportOrLaunchHandlerApp();
break;
case MSG_ACCESSIBILITY_TV:
if (mAccessibilityShortcutController.isAccessibilityShortcutAvailable(false)) {
@@ -738,10 +718,6 @@
case MSG_RINGER_TOGGLE_CHORD:
handleRingerChordGesture();
break;
- case MSG_MOVE_DISPLAY_TO_TOP:
- mWindowManagerFuncs.moveDisplayToTop(msg.arg1);
- mMovingDisplayToTopKeyTriggered = false;
- break;
}
}
}
@@ -2182,52 +2158,6 @@
}
}
- @Override
- public boolean checkShowToOwnerOnly(WindowManager.LayoutParams attrs) {
-
- // If this switch statement is modified, modify the comment in the declarations of
- // the type in {@link WindowManager.LayoutParams} as well.
- switch (attrs.type) {
- default:
- // These are the windows that by default are shown only to the user that created
- // them. If this needs to be overridden, set
- // {@link WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS} in
- // {@link WindowManager.LayoutParams}. Note that permission
- // {@link android.Manifest.permission.INTERNAL_SYSTEM_WINDOW} is required as well.
- if ((attrs.privateFlags & SYSTEM_FLAG_SHOW_FOR_ALL_USERS) == 0) {
- return true;
- }
- break;
-
- // These are the windows that by default are shown to all users. However, to
- // protect against spoofing, check permissions below.
- case TYPE_APPLICATION_STARTING:
- case TYPE_BOOT_PROGRESS:
- case TYPE_DISPLAY_OVERLAY:
- case TYPE_INPUT_CONSUMER:
- case TYPE_KEYGUARD_DIALOG:
- case TYPE_MAGNIFICATION_OVERLAY:
- case TYPE_NAVIGATION_BAR:
- case TYPE_NAVIGATION_BAR_PANEL:
- case TYPE_PHONE:
- case TYPE_POINTER:
- case TYPE_PRIORITY_PHONE:
- case TYPE_SEARCH_BAR:
- case TYPE_STATUS_BAR:
- case TYPE_STATUS_BAR_PANEL:
- case TYPE_STATUS_BAR_SUB_PANEL:
- case TYPE_SYSTEM_DIALOG:
- case TYPE_VOLUME_OVERLAY:
- case TYPE_PRESENTATION:
- case TYPE_PRIVATE_PRESENTATION:
- case TYPE_DOCK_DIVIDER:
- break;
- }
-
- // Check if third party app has set window to system window type.
- return mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW) != PERMISSION_GRANTED;
- }
-
void readLidState() {
mDefaultDisplayPolicy.setLidState(mWindowManagerFuncs.getLidState());
}
@@ -2606,36 +2536,6 @@
@Override
public long interceptKeyBeforeDispatching(IBinder focusedToken, KeyEvent event,
int policyFlags) {
- final long result = interceptKeyBeforeDispatchingInner(focusedToken, event, policyFlags);
- final int eventDisplayId = event.getDisplayId();
- if (result == 0 && !mPerDisplayFocusEnabled
- && eventDisplayId != INVALID_DISPLAY && eventDisplayId != mTopFocusedDisplayId) {
- // An event is targeting a non-focused display. Try to move the display to top so that
- // it can become the focused display to interact with the user.
- final long eventDownTime = event.getDownTime();
- if (mMovingDisplayToTopKeyTime < eventDownTime) {
- // We have not handled this event yet. Move the display to top, and then tell
- // dispatcher to try again later.
- mMovingDisplayToTopKeyTime = eventDownTime;
- mMovingDisplayToTopKeyTriggered = true;
- mHandler.sendMessage(
- mHandler.obtainMessage(MSG_MOVE_DISPLAY_TO_TOP, eventDisplayId, 0));
- return MOVING_DISPLAY_TO_TOP_DURATION_MILLIS;
- } else if (mMovingDisplayToTopKeyTriggered) {
- // The message has not been handled yet. Tell dispatcher to try again later.
- return MOVING_DISPLAY_TO_TOP_DURATION_MILLIS;
- }
- // The target display is still not the top focused display. Drop the event because the
- // display may not contain any window which can receive keys.
- Slog.w(TAG, "Dropping key targeting non-focused display #" + eventDisplayId
- + " keyCode=" + KeyEvent.keyCodeToString(event.getKeyCode()));
- return -1;
- }
- return result;
- }
-
- private long interceptKeyBeforeDispatchingInner(IBinder focusedToken, KeyEvent event,
- int policyFlags) {
final boolean keyguardOn = keyguardOn();
final int keyCode = event.getKeyCode();
final int repeatCount = event.getRepeatCount();
@@ -3122,12 +3022,14 @@
return mAccessibilityTvScheduled;
}
- private void requestFullBugreport() {
+ private void requestFullBugreportOrLaunchHandlerApp() {
if ("1".equals(SystemProperties.get("ro.debuggable"))
|| Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) == 1) {
try {
- ActivityManager.getService().requestFullBugReport();
+ if (!ActivityManager.getService().launchBugReportHandlerApp()) {
+ ActivityManager.getService().requestFullBugReport();
+ }
} catch (RemoteException e) {
Slog.e(TAG, "Error taking bugreport", e);
}
@@ -3674,7 +3576,6 @@
final boolean canceled = event.isCanceled();
final int keyCode = event.getKeyCode();
final int displayId = event.getDisplayId();
-
final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0;
// If screen is off then we treat the case where the keyguard is open but hidden
@@ -4096,6 +3997,23 @@
PowerManager.WAKE_REASON_WAKE_KEY, "android.policy:KEY");
}
+ if ((result & ACTION_PASS_TO_USER) != 0) {
+ // If the key event is targeted to a specific display, then the user is interacting with
+ // that display. Therefore, give focus to the display that the user is interacting with.
+ if (!mPerDisplayFocusEnabled
+ && displayId != INVALID_DISPLAY && displayId != mTopFocusedDisplayId) {
+ // An event is targeting a non-focused display. Move the display to top so that
+ // it can become the focused display to interact with the user.
+ // This should be done asynchronously, once the focus logic is fully moved to input
+ // from windowmanager. Currently, we need to ensure the setInputWindows completes,
+ // which would force the focus event to be queued before the current key event.
+ // TODO(b/70668286): post call to 'moveDisplayToTop' to mHandler instead
+ Log.i(TAG, "Moving non-focused display " + displayId + " to top "
+ + "because a key is targeting it");
+ mWindowManagerFuncs.moveDisplayToTop(displayId);
+ }
+ }
+
return result;
}
diff --git a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
index b0f22e4..f3a6018 100644
--- a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
+++ b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
@@ -145,7 +145,8 @@
}
@Override
public boolean mayAllowExtraAppOp() {
- return !shouldApplyRestriction && hasRequestedLegacyExternalStorage;
+ return !shouldApplyRestriction && hasRequestedLegacyExternalStorage
+ && targetSDK <= Build.VERSION_CODES.Q;
}
@Override
public boolean mayDenyExtraAppOpIfGranted() {
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index a2425a3..b014372 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -702,16 +702,6 @@
public int checkAddPermission(WindowManager.LayoutParams attrs, int[] outAppOp);
/**
- * Check permissions when adding a window.
- *
- * @param attrs The window's LayoutParams.
- *
- * @return True if the window may only be shown to the current user, false if the window can
- * be shown on all users' windows.
- */
- public boolean checkShowToOwnerOnly(WindowManager.LayoutParams attrs);
-
- /**
* After the window manager has computed the current configuration based
* on its knowledge of the display and input devices, it gives the policy
* a chance to adjust the information contained in it. If you want to
diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
index fdb14be..c36d5ef 100644
--- a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
+++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
@@ -17,8 +17,10 @@
package com.android.server.recoverysystem;
import android.content.Context;
+import android.content.IntentSender;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
+import android.os.Binder;
import android.os.IRecoverySystem;
import android.os.IRecoverySystemProgressListener;
import android.os.PowerManager;
@@ -28,6 +30,9 @@
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.widget.LockSettingsInternal;
+import com.android.internal.widget.RebootEscrowListener;
+import com.android.server.LocalServices;
import com.android.server.SystemService;
import libcore.io.IoUtils;
@@ -45,7 +50,7 @@
* triggers /system/bin/uncrypt via init to de-encrypt an OTA package on the
* /data partition so that it can be accessed under the recovery image.
*/
-public class RecoverySystemService extends IRecoverySystem.Stub {
+public class RecoverySystemService extends IRecoverySystem.Stub implements RebootEscrowListener {
private static final String TAG = "RecoverySystemService";
private static final boolean DEBUG = false;
@@ -67,6 +72,10 @@
private final Injector mInjector;
private final Context mContext;
+ private boolean mPreparedForReboot;
+ private String mUnattendedRebootToken;
+ private IntentSender mPreparedForRebootIntentSender;
+
static class Injector {
protected final Context mContext;
@@ -78,6 +87,10 @@
return mContext;
}
+ public LockSettingsInternal getLockSettingsService() {
+ return LocalServices.getService(LockSettingsInternal.class);
+ }
+
public PowerManager getPowerManager() {
return (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
}
@@ -120,14 +133,23 @@
* Handles the lifecycle events for the RecoverySystemService.
*/
public static final class Lifecycle extends SystemService {
+ private RecoverySystemService mRecoverySystemService;
+
public Lifecycle(Context context) {
super(context);
}
@Override
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+ mRecoverySystemService.onSystemServicesReady();
+ }
+ }
+
+ @Override
public void onStart() {
- RecoverySystemService recoverySystemService = new RecoverySystemService(getContext());
- publishBinderService(Context.RECOVERY_SERVICE, recoverySystemService);
+ mRecoverySystemService = new RecoverySystemService(getContext());
+ publishBinderService(Context.RECOVERY_SERVICE, mRecoverySystemService);
}
}
@@ -141,6 +163,11 @@
mContext = injector.getContext();
}
+ @VisibleForTesting
+ void onSystemServicesReady() {
+ mInjector.getLockSettingsService().setRebootEscrowListener(this);
+ }
+
@Override // Binder call
public boolean uncrypt(String filename, IRecoverySystemProgressListener listener) {
if (DEBUG) Slog.d(TAG, "uncrypt: " + filename);
@@ -255,6 +282,95 @@
}
}
+ @Override // Binder call
+ public boolean requestLskf(String updateToken, IntentSender intentSender) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
+
+ if (updateToken == null) {
+ return false;
+ }
+
+ // No need to prepare again for the same token.
+ if (mPreparedForReboot && updateToken.equals(mUnattendedRebootToken)) {
+ return true;
+ }
+
+ mPreparedForReboot = false;
+ mUnattendedRebootToken = updateToken;
+ mPreparedForRebootIntentSender = intentSender;
+
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ mInjector.getLockSettingsService().prepareRebootEscrow();
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+
+ return true;
+ }
+
+ @Override
+ public void onPreparedForReboot(boolean ready) {
+ if (mUnattendedRebootToken == null) {
+ Slog.w(TAG, "onPreparedForReboot called when mUnattendedRebootToken is null");
+ }
+
+ mPreparedForReboot = ready;
+ if (ready) {
+ sendPreparedForRebootIntentIfNeeded();
+ }
+ }
+
+ private void sendPreparedForRebootIntentIfNeeded() {
+ final IntentSender intentSender = mPreparedForRebootIntentSender;
+ if (intentSender != null) {
+ try {
+ intentSender.sendIntent(null, 0, null, null, null);
+ } catch (IntentSender.SendIntentException e) {
+ Slog.w(TAG, "Could not send intent for prepared reboot: " + e.getMessage());
+ }
+ }
+ }
+
+ @Override // Binder call
+ public boolean clearLskf() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
+
+ mPreparedForReboot = false;
+ mUnattendedRebootToken = null;
+ mPreparedForRebootIntentSender = null;
+
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ mInjector.getLockSettingsService().clearRebootEscrow();
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+
+ return true;
+ }
+
+ @Override // Binder call
+ public boolean rebootWithLskf(String updateToken, String reason) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
+
+ if (!mPreparedForReboot) {
+ return false;
+ }
+
+ if (updateToken != null && updateToken.equals(mUnattendedRebootToken)) {
+ if (!mInjector.getLockSettingsService().armRebootEscrow()) {
+ return false;
+ }
+
+ PowerManager pm = mInjector.getPowerManager();
+ pm.reboot(reason);
+ return true;
+ }
+
+ return false;
+ }
+
/**
* Check if any of the init services is still running. If so, we cannot
* start a new uncrypt/setup-bcb/clear-bcb service right away; otherwise
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 9a65ae6..e29d1a7 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -17,8 +17,10 @@
package com.android.server.rollback;
import android.Manifest;
+import android.annotation.AnyThread;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
+import android.annotation.WorkerThread;
import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -81,6 +83,11 @@
/**
* Implementation of service that manages APK level rollbacks.
+ *
+ * Threading model:
+ *
+ * - @AnyThread annotates thread-safe methods.
+ * - @WorkerThread annotates methods that should be called from the handler thread only.
*/
class RollbackManagerServiceImpl extends IRollbackManager.Stub {
@@ -256,6 +263,7 @@
registerTimeChangeReceiver();
}
+ @AnyThread
private void registerUserCallbacks(UserHandle user) {
Context context = getContextAsUser(user);
if (context == null) {
@@ -338,6 +346,7 @@
callerPackageName, statusReceiver));
}
+ @AnyThread
private void registerTimeChangeReceiver() {
final BroadcastReceiver timeChangeIntentReceiver = new BroadcastReceiver() {
@Override
@@ -362,6 +371,7 @@
null /* broadcastPermission */, getHandler());
}
+ @AnyThread
private static long calculateRelativeBootTime() {
return System.currentTimeMillis() - SystemClock.elapsedRealtime();
}
@@ -371,6 +381,7 @@
* The work is done on the current thread. This may be a long running
* operation.
*/
+ @WorkerThread
private void commitRollbackInternal(int rollbackId, List<VersionedPackage> causePackages,
String callerPackageName, IntentSender statusReceiver) {
Slog.i(TAG, "commitRollback id=" + rollbackId + " caller=" + callerPackageName);
@@ -440,6 +451,7 @@
});
}
+ @WorkerThread
private void queueSleepIfNeeded() {
if (mSleepDuration.size() == 0) {
return;
@@ -486,6 +498,7 @@
}
}
+ @WorkerThread
private void updateRollbackLifetimeDurationInMillis() {
mRollbackLifetimeDurationInMillis = DeviceConfig.getLong(
DeviceConfig.NAMESPACE_ROLLBACK_BOOT,
@@ -496,6 +509,7 @@
}
}
+ @AnyThread
void onBootCompleted() {
getHandler().post(() -> updateRollbackLifetimeDurationInMillis());
// Also posts to handler thread
@@ -564,6 +578,7 @@
* Removes all backups for the package not matching the currently
* installed package version.
*/
+ @WorkerThread
private void onPackageReplaced(String packageName) {
// TODO: Could this end up incorrectly deleting a rollback for a
// package that is about to be installed?
@@ -588,6 +603,7 @@
* Called when a package has been completely removed from the device.
* Removes all backups and rollback history for the given package.
*/
+ @WorkerThread
private void onPackageFullyRemoved(String packageName) {
expireRollbackForPackage(packageName);
}
@@ -599,6 +615,7 @@
* @param status the RollbackManager.STATUS_* code with the failure.
* @param message the failure message.
*/
+ @AnyThread
static void sendFailure(Context context, IntentSender statusReceiver,
@RollbackManager.Status int status, String message) {
Slog.e(TAG, message);
@@ -614,6 +631,7 @@
// Check to see if anything needs expiration, and if so, expire it.
// Schedules future expiration as appropriate.
+ @WorkerThread
private void runExpiration() {
Instant now = Instant.now();
Instant oldest = null;
@@ -649,10 +667,12 @@
* Schedules an expiration check to be run after the given duration in
* milliseconds has gone by.
*/
+ @AnyThread
private void scheduleExpiration(long duration) {
getHandler().postDelayed(() -> runExpiration(), duration);
}
+ @AnyThread
private Handler getHandler() {
return mHandlerThread.getThreadHandler();
}
@@ -660,6 +680,7 @@
// Returns true if <code>session</code> has installFlags and code path
// matching the installFlags and new package code path given to
// enableRollback.
+ @WorkerThread
private boolean sessionMatchesForEnableRollback(PackageInstaller.SessionInfo session,
int installFlags, File newPackageCodePath) {
if (session == null || session.resolvedBaseCodePath == null) {
@@ -674,6 +695,7 @@
return false;
}
+ @AnyThread
private Context getContextAsUser(UserHandle user) {
try {
return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
@@ -693,6 +715,7 @@
* @param token the distinct rollback token sent by package manager.
* @return true if enabling the rollback succeeds, false otherwise.
*/
+ @WorkerThread
private boolean enableRollback(
int installFlags, File newPackageCodePath, @UserIdInt int user, int token) {
if (LOCAL_LOGV) {
@@ -780,6 +803,34 @@
return enableRollbackForPackageSession(newRollback.rollback, packageSession);
}
+ @WorkerThread
+ private void removeRollbackForPackageSessionId(int sessionId) {
+ if (LOCAL_LOGV) {
+ Slog.v(TAG, "removeRollbackForPackageSessionId=" + sessionId);
+ }
+
+ synchronized (mLock) {
+ NewRollback newRollback = getNewRollbackForPackageSessionLocked(sessionId);
+ if (newRollback != null) {
+ Slog.w(TAG, "Delete new rollback id=" + newRollback.rollback.info.getRollbackId()
+ + " for session id=" + sessionId);
+ mNewRollbacks.remove(newRollback);
+ newRollback.rollback.delete(mAppDataRollbackHelper);
+ }
+ Iterator<Rollback> iter = mRollbacks.iterator();
+ while (iter.hasNext()) {
+ Rollback rollback = iter.next();
+ if (rollback.getStagedSessionId() == sessionId) {
+ Slog.w(TAG, "Delete rollback id=" + rollback.info.getRollbackId()
+ + " for session id=" + sessionId);
+ iter.remove();
+ rollback.delete(mAppDataRollbackHelper);
+ break;
+ }
+ }
+ }
+ }
+
/**
* Do code and userdata backups to enable rollback of the given session.
* In case of multiPackage sessions, <code>session</code> should be one of
@@ -787,6 +838,7 @@
*
* @return true on success, false on failure.
*/
+ @WorkerThread
private boolean enableRollbackForPackageSession(Rollback rollback,
PackageInstaller.SessionInfo session) {
// TODO: Don't attempt to enable rollback for split installs.
@@ -861,6 +913,7 @@
});
}
+ @WorkerThread
private void snapshotUserDataInternal(String packageName, int[] userIds) {
if (LOCAL_LOGV) {
Slog.v(TAG, "snapshotUserData pkg=" + packageName
@@ -880,6 +933,7 @@
}
}
+ @WorkerThread
private void restoreUserDataInternal(
String packageName, int[] userIds, int appId, String seInfo) {
if (LOCAL_LOGV) {
@@ -944,7 +998,7 @@
}
}
- Rollback rollback = completeEnableRollback(newRollback, true);
+ Rollback rollback = completeEnableRollback(newRollback);
if (rollback == null) {
result.offer(-1);
} else {
@@ -994,6 +1048,7 @@
* Returns true if the installer is allowed to enable rollback for the
* given named package, false otherwise.
*/
+ @AnyThread
private boolean enableRollbackAllowed(String installerPackageName, String packageName) {
if (installerPackageName == null) {
return false;
@@ -1016,6 +1071,7 @@
/**
* Returns true is this package is eligible for enabling rollback.
*/
+ @AnyThread
private boolean isRollbackWhitelisted(String packageName) {
// TODO: Remove #isModule when the white list is ready.
return SystemConfig.getInstance().getRollbackWhitelistedPackages().contains(packageName)
@@ -1024,6 +1080,7 @@
/**
* Returns true if the package name is the name of a module.
*/
+ @AnyThread
private boolean isModule(String packageName) {
PackageManager pm = mContext.getPackageManager();
final ModuleInfo moduleInfo;
@@ -1040,6 +1097,7 @@
* Gets the version of the package currently installed.
* Returns -1 if the package is not currently installed.
*/
+ @AnyThread
private long getInstalledPackageVersion(String packageName) {
PackageInfo pkgInfo;
try {
@@ -1056,6 +1114,7 @@
*
* @throws PackageManager.NameNotFoundException if no such package is installed.
*/
+ @AnyThread
private PackageInfo getPackageInfo(String packageName)
throws PackageManager.NameNotFoundException {
PackageManager pm = mContext.getPackageManager();
@@ -1070,6 +1129,7 @@
}
}
+ @WorkerThread
private class SessionCallback extends PackageInstaller.SessionCallback {
@Override
@@ -1089,23 +1149,29 @@
if (LOCAL_LOGV) {
Slog.v(TAG, "SessionCallback.onFinished id=" + sessionId + " success=" + success);
}
- NewRollback newRollback;
- synchronized (mLock) {
- newRollback = getNewRollbackForPackageSessionLocked(sessionId);
+
+ if (success) {
+ NewRollback newRollback;
+ synchronized (mLock) {
+ newRollback = getNewRollbackForPackageSessionLocked(sessionId);
+ if (newRollback != null && newRollback.notifySessionWithSuccess()) {
+ mNewRollbacks.remove(newRollback);
+ } else {
+ // Not all child sessions finished with success.
+ // Don't enable the rollback yet.
+ newRollback = null;
+ }
+ }
+
if (newRollback != null) {
- mNewRollbacks.remove(newRollback);
+ Rollback rollback = completeEnableRollback(newRollback);
+ if (rollback != null && !rollback.isStaged()) {
+ makeRollbackAvailable(rollback);
+ }
}
+ } else {
+ removeRollbackForPackageSessionId(sessionId);
}
-
- if (newRollback != null) {
- Rollback rollback = completeEnableRollback(newRollback, success);
- if (rollback != null && !rollback.isStaged()) {
- makeRollbackAvailable(rollback);
- }
- }
-
- // Clear the queue so it will never be leaked to next tests.
- mSleepDuration.clear();
}
}
@@ -1116,16 +1182,11 @@
* @return the Rollback instance for a successfully enable-completed rollback,
* or null on error.
*/
- private Rollback completeEnableRollback(NewRollback newRollback, boolean success) {
+ @WorkerThread
+ private Rollback completeEnableRollback(NewRollback newRollback) {
Rollback rollback = newRollback.rollback;
if (LOCAL_LOGV) {
- Slog.v(TAG, "completeEnableRollback id="
- + rollback.info.getRollbackId() + " success=" + success);
- }
- if (!success) {
- // The install session was aborted, clean up the pending install.
- rollback.delete(mAppDataRollbackHelper);
- return null;
+ Slog.v(TAG, "completeEnableRollback id=" + rollback.info.getRollbackId());
}
if (newRollback.isCancelled()) {
@@ -1158,6 +1219,7 @@
return rollback;
}
+ @WorkerThread
@GuardedBy("rollback.getLock")
private void makeRollbackAvailable(Rollback rollback) {
if (LOCAL_LOGV) {
@@ -1178,6 +1240,7 @@
/*
* Returns the rollback with the given rollbackId, if any.
*/
+ @WorkerThread
private Rollback getRollbackForId(int rollbackId) {
synchronized (mLock) {
for (int i = 0; i < mRollbacks.size(); ++i) {
@@ -1191,6 +1254,7 @@
return null;
}
+ @WorkerThread
@GuardedBy("mLock")
private int allocateRollbackIdLocked() {
int n = 0;
@@ -1220,6 +1284,7 @@
}
}
+ @AnyThread
private void enforceManageRollbacks(@NonNull String message) {
if ((PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(
Manifest.permission.MANAGE_ROLLBACKS))
@@ -1251,6 +1316,14 @@
@GuardedBy("mNewRollbackLock")
private boolean mIsCancelled = false;
+ /**
+ * The number of sessions in the install which are notified with success by
+ * {@link PackageInstaller.SessionCallback#onFinished(int, boolean)}.
+ * This NewRollback will be enabled only after all child sessions finished with success.
+ */
+ @GuardedBy("mNewRollbackLock")
+ private int mNumPackageSessionsWithSuccess;
+
private final Object mNewRollbackLock = new Object();
NewRollback(Rollback rollback, int[] packageSessionIds) {
@@ -1322,8 +1395,20 @@
int getPackageSessionIdCount() {
return mPackageSessionIds.length;
}
+
+ /**
+ * Called when a child session finished with success.
+ * Returns true when all child sessions are notified with success. This NewRollback will be
+ * enabled only after all child sessions finished with success.
+ */
+ boolean notifySessionWithSuccess() {
+ synchronized (mNewRollbackLock) {
+ return ++mNumPackageSessionsWithSuccess == mPackageSessionIds.length;
+ }
+ }
}
+ @WorkerThread
@GuardedBy("mLock")
private NewRollback createNewRollbackLocked(PackageInstaller.SessionInfo parentSession) {
int rollbackId = allocateRollbackIdLocked();
@@ -1365,6 +1450,7 @@
* Returns null if no NewRollback is found for the given package
* session.
*/
+ @WorkerThread
@GuardedBy("mLock")
NewRollback getNewRollbackForPackageSessionLocked(int packageSessionId) {
// We expect mNewRollbacks to be a very small list; linear search
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 162a695..a0ef8cf 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -333,9 +333,24 @@
return rollbackId;
}
+ private static String rollbackTypeToString(int type) {
+ switch (type) {
+ case StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE:
+ return "ROLLBACK_INITIATE";
+ case StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS:
+ return "ROLLBACK_SUCCESS";
+ case StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE:
+ return "ROLLBACK_FAILURE";
+ case StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED:
+ return "ROLLBACK_BOOT_TRIGGERED";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
private static void logEvent(@Nullable VersionedPackage moduleMetadataPackage, int type,
int rollbackReason, @NonNull String failingPackageName) {
- Slog.i(TAG, "Watchdog event occurred of type: " + type);
+ Slog.i(TAG, "Watchdog event occurred of type: " + rollbackTypeToString(type));
if (moduleMetadataPackage != null) {
StatsLog.logWatchdogRollbackOccurred(type, moduleMetadataPackage.getPackageName(),
moduleMetadataPackage.getVersionCode(), rollbackReason, failingPackageName);
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
index 9b22f33..0b89646 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
@@ -40,7 +40,6 @@
import android.os.HidlMemoryUtil;
import java.util.regex.Matcher;
-import java.util.regex.Pattern;
/**
* Utilities for type conversion between SoundTrigger HAL types and SoundTriggerMiddleware service
@@ -60,7 +59,8 @@
aidlProperties.maxSoundModels = hidlProperties.maxSoundModels;
aidlProperties.maxKeyPhrases = hidlProperties.maxKeyPhrases;
aidlProperties.maxUsers = hidlProperties.maxUsers;
- aidlProperties.recognitionModes = hidlProperties.recognitionModes;
+ aidlProperties.recognitionModes =
+ hidl2aidlRecognitionModes(hidlProperties.recognitionModes);
aidlProperties.captureTransition = hidlProperties.captureTransition;
aidlProperties.maxBufferMs = hidlProperties.maxBufferMs;
aidlProperties.concurrentCapture = hidlProperties.concurrentCapture;
diff --git a/services/core/java/com/android/server/stats/StatsPullAtomService.java b/services/core/java/com/android/server/stats/StatsPullAtomService.java
new file mode 100644
index 0000000..e367f28
--- /dev/null
+++ b/services/core/java/com/android/server/stats/StatsPullAtomService.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.stats;
+
+import android.content.Context;
+import android.util.Slog;
+
+import com.android.internal.os.BackgroundThread;
+import com.android.server.SystemService;
+
+/**
+ * SystemService containing PullAtomCallbacks that are registered with statsd.
+ *
+ * @hide
+ */
+public class StatsPullAtomService extends SystemService {
+ private static final String TAG = "StatsPullAtomService";
+ private static final boolean DEBUG = true;
+
+ public StatsPullAtomService(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ // No op.
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ super.onBootPhase(phase);
+ if (phase == PHASE_SYSTEM_SERVICES_READY) {
+ BackgroundThread.getHandler().post(() -> {
+ registerAllPullers();
+ });
+ }
+ }
+
+ void registerAllPullers() {
+ if (DEBUG) {
+ Slog.d(TAG, "Registering all pullers with statsd");
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index 172367a..b7d6360 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.app.timedetector.ITimeDetectorService;
import android.app.timedetector.ManualTimeSuggestion;
+import android.app.timedetector.NetworkTimeSuggestion;
import android.app.timedetector.PhoneTimeSuggestion;
import android.content.ContentResolver;
import android.content.Context;
@@ -105,6 +106,14 @@
mHandler.post(() -> mTimeDetectorStrategy.suggestManualTime(timeSignal));
}
+ @Override
+ public void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSignal) {
+ enforceSuggestNetworkTimePermission();
+ Objects.requireNonNull(timeSignal);
+
+ mHandler.post(() -> mTimeDetectorStrategy.suggestNetworkTime(timeSignal));
+ }
+
@VisibleForTesting
public void handleAutoTimeDetectionToggle() {
mHandler.post(mTimeDetectorStrategy::handleAutoTimeDetectionChanged);
@@ -119,10 +128,20 @@
}
private void enforceSuggestPhoneTimePermission() {
- mContext.enforceCallingPermission(android.Manifest.permission.SET_TIME, "set time");
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE,
+ "suggest phone time and time zone");
}
private void enforceSuggestManualTimePermission() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SET_TIME, "set time");
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE,
+ "suggest manual time and time zone");
+ }
+
+ private void enforceSuggestNetworkTimePermission() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.SET_TIME,
+ "set time");
}
}
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
index 0a6c2e7..f661b5e 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
@@ -19,9 +19,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.timedetector.ManualTimeSuggestion;
+import android.app.timedetector.NetworkTimeSuggestion;
import android.app.timedetector.PhoneTimeSuggestion;
import android.content.Intent;
-import android.util.TimestampedValue;
+import android.os.TimestampedValue;
import java.io.PrintWriter;
@@ -86,6 +87,9 @@
/** Process the suggested manually entered time. */
void suggestManualTime(@NonNull ManualTimeSuggestion timeSuggestion);
+ /** Process the suggested time from network sources. */
+ void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSuggestion);
+
/** Handle the auto-time setting being toggled on or off. */
void handleAutoTimeDetectionChanged();
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index c50248d..da848d8 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -21,17 +21,19 @@
import android.annotation.Nullable;
import android.app.AlarmManager;
import android.app.timedetector.ManualTimeSuggestion;
+import android.app.timedetector.NetworkTimeSuggestion;
import android.app.timedetector.PhoneTimeSuggestion;
import android.content.Intent;
+import android.os.TimestampedValue;
import android.telephony.TelephonyManager;
import android.util.LocalLog;
import android.util.Slog;
-import android.util.TimestampedValue;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.timezonedetector.ArrayMapWithHistory;
+import com.android.server.timezonedetector.ReferenceWithHistory;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -56,11 +58,11 @@
/** Each bucket is this size. All buckets are equally sized. */
@VisibleForTesting
static final int PHONE_BUCKET_SIZE_MILLIS = 60 * 60 * 1000;
- /** Phone suggestions older than this value are considered too old. */
+ /** Phone and network suggestions older than this value are considered too old to be used. */
@VisibleForTesting
- static final long PHONE_MAX_AGE_MILLIS = PHONE_BUCKET_COUNT * PHONE_BUCKET_SIZE_MILLIS;
+ static final long MAX_UTC_TIME_AGE_MILLIS = PHONE_BUCKET_COUNT * PHONE_BUCKET_SIZE_MILLIS;
- @IntDef({ ORIGIN_PHONE, ORIGIN_MANUAL })
+ @IntDef({ ORIGIN_PHONE, ORIGIN_MANUAL, ORIGIN_NETWORK })
@Retention(RetentionPolicy.SOURCE)
public @interface Origin {}
@@ -72,6 +74,10 @@
@Origin
private static final int ORIGIN_MANUAL = 2;
+ /** Used when a time value originated from a network signal. */
+ @Origin
+ private static final int ORIGIN_NETWORK = 3;
+
/**
* CLOCK_PARANOIA: The maximum difference allowed between the expected system clock time and the
* actual system clock time before a warning is logged. Used to help identify situations where
@@ -101,9 +107,13 @@
* will have a small number of telephony devices and phoneIds are assumed to be stable.
*/
@GuardedBy("this")
- private ArrayMapWithHistory<Integer, PhoneTimeSuggestion> mSuggestionByPhoneId =
+ private final ArrayMapWithHistory<Integer, PhoneTimeSuggestion> mSuggestionByPhoneId =
new ArrayMapWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
+ @GuardedBy("this")
+ private final ReferenceWithHistory<NetworkTimeSuggestion> mLastNetworkSuggestion =
+ new ReferenceWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
+
@Override
public void initialize(@NonNull Callback callback) {
mCallback = callback;
@@ -122,6 +132,19 @@
}
@Override
+ public synchronized void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSuggestion) {
+ if (!validateSuggestionTime(timeSuggestion.getUtcTime(), timeSuggestion)) {
+ return;
+ }
+ mLastNetworkSuggestion.set(timeSuggestion);
+
+ // Now perform auto time detection. The new suggestion may be used to modify the system
+ // clock.
+ String reason = "New network time suggested. timeSuggestion=" + timeSuggestion;
+ doAutoTimeDetection(reason);
+ }
+
+ @Override
public synchronized void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) {
// Empty time suggestion means that telephony network connectivity has been lost.
// The passage of time is relentless, and we don't expect our users to use a time machine,
@@ -167,6 +190,12 @@
ipw.increaseIndent(); // level 1
ipw.println("mLastAutoSystemClockTimeSet=" + mLastAutoSystemClockTimeSet);
+ ipw.println("mCallback.isAutoTimeDetectionEnabled()="
+ + mCallback.isAutoTimeDetectionEnabled());
+ ipw.println("mCallback.elapsedRealtimeMillis()=" + mCallback.elapsedRealtimeMillis());
+ ipw.println("mCallback.systemClockMillis()=" + mCallback.systemClockMillis());
+ ipw.println("mCallback.systemClockUpdateThresholdMillis()="
+ + mCallback.systemClockUpdateThresholdMillis());
ipw.println("Time change log:");
ipw.increaseIndent(); // level 2
@@ -178,6 +207,11 @@
mSuggestionByPhoneId.dump(ipw);
ipw.decreaseIndent(); // level 2
+ ipw.println("Network suggestion history:");
+ ipw.increaseIndent(); // level 2
+ mLastNetworkSuggestion.dump(ipw);
+ ipw.decreaseIndent(); // level 2
+
ipw.decreaseIndent(); // level 1
ipw.flush();
}
@@ -247,23 +281,34 @@
return;
}
+ // Android devices currently prioritize any telephony over network signals. There are
+ // carrier compliance tests that would need to be changed before we could ignore NITZ or
+ // prefer NTP generally. This check is cheap on devices without phone hardware.
PhoneTimeSuggestion bestPhoneSuggestion = findBestPhoneSuggestion();
-
- // Work out what to do with the best suggestion.
- if (bestPhoneSuggestion == null) {
- // There is no good phone suggestion.
- if (DBG) {
- Slog.d(LOG_TAG, "Could not determine time: No best phone suggestion."
- + " detectionReason=" + detectionReason);
- }
+ if (bestPhoneSuggestion != null) {
+ final TimestampedValue<Long> newUtcTime = bestPhoneSuggestion.getUtcTime();
+ String cause = "Found good phone suggestion."
+ + ", bestPhoneSuggestion=" + bestPhoneSuggestion
+ + ", detectionReason=" + detectionReason;
+ setSystemClockIfRequired(ORIGIN_PHONE, newUtcTime, cause);
return;
}
- final TimestampedValue<Long> newUtcTime = bestPhoneSuggestion.getUtcTime();
- String cause = "Found good suggestion."
- + ", bestPhoneSuggestion=" + bestPhoneSuggestion
- + ", detectionReason=" + detectionReason;
- setSystemClockIfRequired(ORIGIN_PHONE, newUtcTime, cause);
+ // There is no good phone suggestion, try network.
+ NetworkTimeSuggestion networkSuggestion = findLatestValidNetworkSuggestion();
+ if (networkSuggestion != null) {
+ final TimestampedValue<Long> newUtcTime = networkSuggestion.getUtcTime();
+ String cause = "Found good network suggestion."
+ + ", networkSuggestion=" + networkSuggestion
+ + ", detectionReason=" + detectionReason;
+ setSystemClockIfRequired(ORIGIN_NETWORK, newUtcTime, cause);
+ return;
+ }
+
+ if (DBG) {
+ Slog.d(LOG_TAG, "Could not determine time: No best phone or network suggestion."
+ + " detectionReason=" + detectionReason);
+ }
}
@GuardedBy("this")
@@ -342,37 +387,50 @@
private static int scorePhoneSuggestion(
long elapsedRealtimeMillis, @NonNull PhoneTimeSuggestion timeSuggestion) {
- // The score is based on the age since receipt. Suggestions are bucketed so two
- // suggestions in the same bucket from different phoneIds are scored the same.
+
+ // Validate first.
TimestampedValue<Long> utcTime = timeSuggestion.getUtcTime();
- long referenceTimeMillis = utcTime.getReferenceTimeMillis();
- if (referenceTimeMillis > elapsedRealtimeMillis) {
- // Future times are ignored. They imply the reference time was wrong, or the elapsed
- // realtime clock has gone backwards, neither of which are supportable situations.
- Slog.w(LOG_TAG, "Existing suggestion found to be in the future. "
+ if (!validateSuggestionUtcTime(elapsedRealtimeMillis, utcTime)) {
+ Slog.w(LOG_TAG, "Existing suggestion found to be invalid "
+ " elapsedRealtimeMillis=" + elapsedRealtimeMillis
+ ", timeSuggestion=" + timeSuggestion);
return PHONE_INVALID_SCORE;
}
- long ageMillis = elapsedRealtimeMillis - referenceTimeMillis;
+ // The score is based on the age since receipt. Suggestions are bucketed so two
+ // suggestions in the same bucket from different phoneIds are scored the same.
+ long ageMillis = elapsedRealtimeMillis - utcTime.getReferenceTimeMillis();
- // Any suggestion > MAX_AGE_MILLIS is treated as too old. Although time is relentless and
- // predictable, the accuracy of the reference time clock may be poor over long periods which
- // would lead to errors creeping in. Also, in edge cases where a bad suggestion has been
- // made and never replaced, it could also mean that the time detection code remains
- // opinionated using a bad invalid suggestion. This caps that edge case at MAX_AGE_MILLIS.
- if (ageMillis > PHONE_MAX_AGE_MILLIS) {
+ // Turn the age into a discrete value: 0 <= bucketIndex < PHONE_BUCKET_COUNT.
+ int bucketIndex = (int) (ageMillis / PHONE_BUCKET_SIZE_MILLIS);
+ if (bucketIndex >= PHONE_BUCKET_COUNT) {
return PHONE_INVALID_SCORE;
}
- // Turn the age into a discrete value: 0 <= bucketIndex < MAX_AGE_HOURS.
- int bucketIndex = (int) (ageMillis / PHONE_BUCKET_SIZE_MILLIS);
-
// We want the lowest bucket index to have the highest score. 0 > score >= BUCKET_COUNT.
return PHONE_BUCKET_COUNT - bucketIndex;
}
+ /** Returns the latest, valid, network suggestion. Returns {@code null} if there isn't one. */
+ @GuardedBy("this")
+ @Nullable
+ private NetworkTimeSuggestion findLatestValidNetworkSuggestion() {
+ NetworkTimeSuggestion networkSuggestion = mLastNetworkSuggestion.get();
+ if (networkSuggestion == null) {
+ // No network suggestions received. This is normal if there's no connectivity.
+ return null;
+ }
+
+ TimestampedValue<Long> utcTime = networkSuggestion.getUtcTime();
+ long elapsedRealTimeMillis = mCallback.elapsedRealtimeMillis();
+ if (!validateSuggestionUtcTime(elapsedRealTimeMillis, utcTime)) {
+ // The latest suggestion is not valid, usually due to its age.
+ return null;
+ }
+
+ return networkSuggestion;
+ }
+
@GuardedBy("this")
private void setSystemClockIfRequired(
@Origin int origin, @NonNull TimestampedValue<Long> time, @NonNull String cause) {
@@ -409,7 +467,7 @@
}
private static boolean isOriginAutomatic(@Origin int origin) {
- return origin == ORIGIN_PHONE;
+ return origin != ORIGIN_MANUAL;
}
@GuardedBy("this")
@@ -501,6 +559,16 @@
}
/**
+ * Returns the latest valid network suggestion. Not intended for general use: it is used during
+ * tests to check strategy behavior.
+ */
+ @VisibleForTesting
+ @Nullable
+ public NetworkTimeSuggestion findLatestValidNetworkSuggestionForTests() {
+ return findLatestValidNetworkSuggestion();
+ }
+
+ /**
* A method used to inspect state during tests. Not intended for general use.
*/
@VisibleForTesting
@@ -508,4 +576,32 @@
public synchronized PhoneTimeSuggestion getLatestPhoneSuggestion(int phoneId) {
return mSuggestionByPhoneId.get(phoneId);
}
+
+ /**
+ * A method used to inspect state during tests. Not intended for general use.
+ */
+ @VisibleForTesting
+ @Nullable
+ public NetworkTimeSuggestion getLatestNetworkSuggestion() {
+ return mLastNetworkSuggestion.get();
+ }
+
+ private static boolean validateSuggestionUtcTime(
+ long elapsedRealtimeMillis, TimestampedValue<Long> utcTime) {
+ long referenceTimeMillis = utcTime.getReferenceTimeMillis();
+ if (referenceTimeMillis > elapsedRealtimeMillis) {
+ // Future reference times are ignored. They imply the reference time was wrong, or the
+ // elapsed realtime clock used to derive it has gone backwards, neither of which are
+ // supportable situations.
+ return false;
+ }
+
+ // Any suggestion > MAX_AGE_MILLIS is treated as too old. Although time is relentless and
+ // predictable, the accuracy of the reference time clock may be poor over long periods which
+ // would lead to errors creeping in. Also, in edge cases where a bad suggestion has been
+ // made and never replaced, it could also mean that the time detection code remains
+ // opinionated using a bad invalid suggestion. This caps that edge case at MAX_AGE_MILLIS.
+ long ageMillis = elapsedRealtimeMillis - referenceTimeMillis;
+ return ageMillis <= MAX_UTC_TIME_AGE_MILLIS;
+ }
}
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 18ed51a..5b58199 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -1814,8 +1814,8 @@
}
@Override
- public ParcelFileDescriptor openDvbDevice(DvbDeviceInfo info, int device)
- throws RemoteException {
+ public ParcelFileDescriptor openDvbDevice(DvbDeviceInfo info,
+ @TvInputManager.DvbDeviceType int deviceType) throws RemoteException {
if (mContext.checkCallingPermission(android.Manifest.permission.DVB_DEVICE)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires DVB_DEVICE permission");
@@ -1852,7 +1852,7 @@
final long identity = Binder.clearCallingIdentity();
try {
String deviceFileName;
- switch (device) {
+ switch (deviceType) {
case TvInputManager.DVB_DEVICE_DEMUX:
deviceFileName = String.format(dvbDeviceFound
? "/dev/dvb/adapter%d/demux%d" : "/dev/dvb%d.demux%d",
@@ -1869,14 +1869,14 @@
info.getAdapterId(), info.getDeviceId());
break;
default:
- throw new IllegalArgumentException("Invalid DVB device: " + device);
+ throw new IllegalArgumentException("Invalid DVB device: " + deviceType);
}
try {
// The DVB frontend device only needs to be opened in read/write mode, which
// allows performing tuning operations. The DVB demux and DVR device are enough
// to be opened in read only mode.
return ParcelFileDescriptor.open(new File(deviceFileName),
- TvInputManager.DVB_DEVICE_FRONTEND == device
+ TvInputManager.DVB_DEVICE_FRONTEND == deviceType
? ParcelFileDescriptor.MODE_READ_WRITE
: ParcelFileDescriptor.MODE_READ_ONLY);
} catch (FileNotFoundException e) {
diff --git a/services/core/java/com/android/server/utils/quota/CountQuotaTracker.java b/services/core/java/com/android/server/utils/quota/CountQuotaTracker.java
new file mode 100644
index 0000000..7fe4bf8
--- /dev/null
+++ b/services/core/java/com/android/server/utils/quota/CountQuotaTracker.java
@@ -0,0 +1,802 @@
+/*
+ * 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.utils.quota;
+
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+
+import static com.android.server.utils.quota.Uptc.string;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AlarmManager;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.LongArrayQueue;
+import android.util.Slog;
+import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
+import android.util.quota.CountQuotaTrackerProto;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * Class that tracks whether an app has exceeded its defined count quota.
+ *
+ * Quotas are applied per userId-package-tag combination (UPTC). Tags can be null.
+ *
+ * This tracker tracks the count of instantaneous events.
+ *
+ * Limits are applied according to the category the UPTC is placed in. If a UPTC reaches its limit,
+ * it will be considered out of quota until it is below that limit again. A {@link Category} is a
+ * basic construct to apply different limits to different groups of UPTCs. For example, standby
+ * buckets can be a set of categories, or foreground & background could be two categories. If every
+ * UPTC should have the same limits applied, then only one category is needed
+ * ({@see Category.SINGLE_CATEGORY}).
+ *
+ * Note: all limits are enforced per category unless explicitly stated otherwise.
+ *
+ * Test: atest com.android.server.utils.quota.CountQuotaTrackerTest
+ *
+ * @hide
+ */
+public class CountQuotaTracker extends QuotaTracker {
+ private static final String TAG = CountQuotaTracker.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
+ private static final String ALARM_TAG_CLEANUP = "*" + TAG + ".cleanup*";
+
+ @VisibleForTesting
+ static class ExecutionStats {
+ /**
+ * The time after which this record should be considered invalid (out of date), in the
+ * elapsed realtime timebase.
+ */
+ public long expirationTimeElapsed;
+
+ /** The window size that's used when counting the number of events. */
+ public long windowSizeMs;
+ /** The maximum number of events allowed within the window size. */
+ public int countLimit;
+
+ /** The total number of events that occurred in the window. */
+ public int countInWindow;
+
+ /**
+ * The time after which the app will be under the category quota again. This is only valid
+ * if {@link #countInWindow} >= {@link #countLimit}.
+ */
+ public long inQuotaTimeElapsed;
+
+ @Override
+ public String toString() {
+ return "expirationTime=" + expirationTimeElapsed + ", "
+ + "windowSizeMs=" + windowSizeMs + ", "
+ + "countLimit=" + countLimit + ", "
+ + "countInWindow=" + countInWindow + ", "
+ + "inQuotaTime=" + inQuotaTimeElapsed;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof ExecutionStats) {
+ ExecutionStats other = (ExecutionStats) obj;
+ return this.expirationTimeElapsed == other.expirationTimeElapsed
+ && this.windowSizeMs == other.windowSizeMs
+ && this.countLimit == other.countLimit
+ && this.countInWindow == other.countInWindow
+ && this.inQuotaTimeElapsed == other.inQuotaTimeElapsed;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 0;
+ result = 31 * result + Long.hashCode(expirationTimeElapsed);
+ result = 31 * result + Long.hashCode(windowSizeMs);
+ result = 31 * result + countLimit;
+ result = 31 * result + countInWindow;
+ result = 31 * result + Long.hashCode(inQuotaTimeElapsed);
+ return result;
+ }
+ }
+
+ /** List of times of all instantaneous events for a UPTC, in chronological order. */
+ // TODO(146148168): introduce a bucketized mode that's more efficient but less accurate
+ @GuardedBy("mLock")
+ private final UptcMap<LongArrayQueue> mEventTimes = new UptcMap<>();
+
+ /** Cached calculation results for each app. */
+ @GuardedBy("mLock")
+ private final UptcMap<ExecutionStats> mExecutionStatsCache = new UptcMap<>();
+
+ private final Handler mHandler;
+
+ @GuardedBy("mLock")
+ private long mNextCleanupTimeElapsed = 0;
+ @GuardedBy("mLock")
+ private final AlarmManager.OnAlarmListener mEventCleanupAlarmListener = () ->
+ CountQuotaTracker.this.mHandler.obtainMessage(MSG_CLEAN_UP_EVENTS).sendToTarget();
+
+ /** The rolling window size for each Category's count limit. */
+ @GuardedBy("mLock")
+ private final ArrayMap<Category, Long> mCategoryCountWindowSizesMs = new ArrayMap<>();
+
+ /**
+ * The maximum count for each Category. For each max value count in the map, the app will
+ * not be allowed any more events within the latest time interval of its rolling window size.
+ *
+ * @see #mCategoryCountWindowSizesMs
+ */
+ @GuardedBy("mLock")
+ private final ArrayMap<Category, Integer> mMaxCategoryCounts = new ArrayMap<>();
+
+ /** The longest period a registered category applies to. */
+ @GuardedBy("mLock")
+ private long mMaxPeriodMs = 0;
+
+ /** Drop any old events. */
+ private static final int MSG_CLEAN_UP_EVENTS = 1;
+
+ public CountQuotaTracker(@NonNull Context context, @NonNull Categorizer categorizer) {
+ this(context, categorizer, new Injector());
+ }
+
+ @VisibleForTesting
+ CountQuotaTracker(@NonNull Context context, @NonNull Categorizer categorizer,
+ Injector injector) {
+ super(context, categorizer, injector);
+
+ mHandler = new CqtHandler(context.getMainLooper());
+ }
+
+ // Exposed API to users.
+
+ /**
+ * Record that an instantaneous event happened.
+ *
+ * @return true if the UPTC is within quota, false otherwise.
+ */
+ public boolean noteEvent(int userId, @NonNull String packageName, @Nullable String tag) {
+ synchronized (mLock) {
+ if (!isEnabledLocked() || isQuotaFreeLocked(userId, packageName)) {
+ return true;
+ }
+ final long nowElapsed = mInjector.getElapsedRealtime();
+
+ final LongArrayQueue times = mEventTimes
+ .getOrCreate(userId, packageName, tag, mCreateLongArrayQueue);
+ times.addLast(nowElapsed);
+ final ExecutionStats stats = getExecutionStatsLocked(userId, packageName, tag);
+ stats.countInWindow++;
+ stats.expirationTimeElapsed = Math.min(stats.expirationTimeElapsed,
+ nowElapsed + stats.windowSizeMs);
+ if (stats.countInWindow == stats.countLimit) {
+ final long windowEdgeElapsed = nowElapsed - stats.windowSizeMs;
+ while (times.size() > 0 && times.peekFirst() < windowEdgeElapsed) {
+ times.removeFirst();
+ }
+ stats.inQuotaTimeElapsed = times.peekFirst() + stats.windowSizeMs;
+ postQuotaStatusChanged(userId, packageName, tag);
+ } else if (stats.countLimit > 9
+ && stats.countInWindow == stats.countLimit * 4 / 5) {
+ // TODO: log high watermark to statsd
+ Slog.w(TAG, string(userId, packageName, tag)
+ + " has reached 80% of it's count limit of " + stats.countLimit);
+ }
+ maybeScheduleCleanupAlarmLocked();
+ return isWithinQuotaLocked(stats);
+ }
+ }
+
+ /**
+ * Set count limit over a rolling time window for the specified category.
+ *
+ * @param category The category these limits apply to.
+ * @param limit The maximum event count an app can have in the rolling window. Must be
+ * nonnegative.
+ * @param timeWindowMs The rolling time window (in milliseconds) to use when checking quota
+ * usage. Must be at least {@value #MIN_WINDOW_SIZE_MS} and no longer than
+ * {@value #MAX_WINDOW_SIZE_MS}
+ */
+ public void setCountLimit(@NonNull Category category, int limit, long timeWindowMs) {
+ if (limit < 0 || timeWindowMs < 0) {
+ throw new IllegalArgumentException("Limit and window size must be nonnegative.");
+ }
+ synchronized (mLock) {
+ final Integer oldLimit = mMaxCategoryCounts.put(category, limit);
+ final long newWindowSizeMs = Math.max(MIN_WINDOW_SIZE_MS,
+ Math.min(timeWindowMs, MAX_WINDOW_SIZE_MS));
+ final Long oldWindowSizeMs = mCategoryCountWindowSizesMs.put(category, newWindowSizeMs);
+ if (oldLimit != null && oldWindowSizeMs != null
+ && oldLimit == limit && oldWindowSizeMs == newWindowSizeMs) {
+ // No change.
+ return;
+ }
+ mDeleteOldEventTimesFunctor.updateMaxPeriod();
+ mMaxPeriodMs = mDeleteOldEventTimesFunctor.mMaxPeriodMs;
+ invalidateAllExecutionStatsLocked();
+ }
+ scheduleQuotaCheck();
+ }
+
+ /**
+ * Gets the count limit for the specified category.
+ */
+ public int getLimit(@NonNull Category category) {
+ synchronized (mLock) {
+ final Integer limit = mMaxCategoryCounts.get(category);
+ if (limit == null) {
+ throw new IllegalArgumentException("Limit for " + category + " not defined");
+ }
+ return limit;
+ }
+ }
+
+ /**
+ * Gets the count time window for the specified category.
+ */
+ public long getWindowSizeMs(@NonNull Category category) {
+ synchronized (mLock) {
+ final Long limitMs = mCategoryCountWindowSizesMs.get(category);
+ if (limitMs == null) {
+ throw new IllegalArgumentException("Limit for " + category + " not defined");
+ }
+ return limitMs;
+ }
+ }
+
+ // Internal implementation.
+
+ @Override
+ @GuardedBy("mLock")
+ void dropEverythingLocked() {
+ mExecutionStatsCache.clear();
+ mEventTimes.clear();
+ }
+
+ @Override
+ @GuardedBy("mLock")
+ @NonNull
+ Handler getHandler() {
+ return mHandler;
+ }
+
+ @Override
+ @GuardedBy("mLock")
+ long getInQuotaTimeElapsedLocked(final int userId, @NonNull final String packageName,
+ @Nullable final String tag) {
+ return getExecutionStatsLocked(userId, packageName, tag).inQuotaTimeElapsed;
+ }
+
+ @Override
+ @GuardedBy("mLock")
+ void handleRemovedAppLocked(String packageName, int uid) {
+ if (packageName == null) {
+ Slog.wtf(TAG, "Told app removed but given null package name.");
+ return;
+ }
+ final int userId = UserHandle.getUserId(uid);
+
+ mEventTimes.delete(userId, packageName);
+ mExecutionStatsCache.delete(userId, packageName);
+ }
+
+ @Override
+ @GuardedBy("mLock")
+ void handleRemovedUserLocked(int userId) {
+ mEventTimes.delete(userId);
+ mExecutionStatsCache.delete(userId);
+ }
+
+ @Override
+ @GuardedBy("mLock")
+ boolean isWithinQuotaLocked(final int userId, @NonNull final String packageName,
+ @Nullable final String tag) {
+ if (!isEnabledLocked()) return true;
+
+ // Quota constraint is not enforced when quota is free.
+ if (isQuotaFreeLocked(userId, packageName)) {
+ return true;
+ }
+
+ return isWithinQuotaLocked(getExecutionStatsLocked(userId, packageName, tag));
+ }
+
+ @Override
+ @GuardedBy("mLock")
+ void maybeUpdateAllQuotaStatusLocked() {
+ final UptcMap<Boolean> doneMap = new UptcMap<>();
+ mEventTimes.forEach((userId, packageName, tag, events) -> {
+ if (!doneMap.contains(userId, packageName, tag)) {
+ maybeUpdateStatusForUptcLocked(userId, packageName, tag);
+ doneMap.add(userId, packageName, tag, Boolean.TRUE);
+ }
+ });
+
+ }
+
+ @Override
+ void maybeUpdateQuotaStatus(final int userId, @NonNull final String packageName,
+ @Nullable final String tag) {
+ synchronized (mLock) {
+ maybeUpdateStatusForUptcLocked(userId, packageName, tag);
+ }
+ }
+
+ @Override
+ @GuardedBy("mLock")
+ void onQuotaFreeChangedLocked(boolean isFree) {
+ // Nothing to do here.
+ }
+
+ @Override
+ @GuardedBy("mLock")
+ void onQuotaFreeChangedLocked(int userId, @NonNull String packageName, boolean isFree) {
+ maybeUpdateStatusForPkgLocked(userId, packageName);
+ }
+
+ @GuardedBy("mLock")
+ private boolean isWithinQuotaLocked(@NonNull final ExecutionStats stats) {
+ return isUnderCountQuotaLocked(stats);
+ }
+
+ @GuardedBy("mLock")
+ private boolean isUnderCountQuotaLocked(@NonNull ExecutionStats stats) {
+ return stats.countInWindow < stats.countLimit;
+ }
+
+ /** Returns the execution stats of the app in the most recent window. */
+ @GuardedBy("mLock")
+ @VisibleForTesting
+ @NonNull
+ ExecutionStats getExecutionStatsLocked(final int userId, @NonNull final String packageName,
+ @Nullable final String tag) {
+ return getExecutionStatsLocked(userId, packageName, tag, true);
+ }
+
+ @GuardedBy("mLock")
+ @NonNull
+ private ExecutionStats getExecutionStatsLocked(final int userId,
+ @NonNull final String packageName, @Nullable String tag,
+ final boolean refreshStatsIfOld) {
+ final ExecutionStats stats =
+ mExecutionStatsCache.getOrCreate(userId, packageName, tag, mCreateExecutionStats);
+ if (refreshStatsIfOld) {
+ final Category category = mCategorizer.getCategory(userId, packageName, tag);
+ final long countWindowSizeMs = mCategoryCountWindowSizesMs.getOrDefault(category,
+ Long.MAX_VALUE);
+ final int countLimit = mMaxCategoryCounts.getOrDefault(category, Integer.MAX_VALUE);
+ if (stats.expirationTimeElapsed <= mInjector.getElapsedRealtime()
+ || stats.windowSizeMs != countWindowSizeMs
+ || stats.countLimit != countLimit) {
+ // The stats are no longer valid.
+ stats.windowSizeMs = countWindowSizeMs;
+ stats.countLimit = countLimit;
+ updateExecutionStatsLocked(userId, packageName, tag, stats);
+ }
+ }
+
+ return stats;
+ }
+
+ @GuardedBy("mLock")
+ @VisibleForTesting
+ void updateExecutionStatsLocked(final int userId, @NonNull final String packageName,
+ @Nullable final String tag, @NonNull ExecutionStats stats) {
+ stats.countInWindow = 0;
+ stats.inQuotaTimeElapsed = 0;
+
+ // This can be used to determine when an app will have enough quota to transition from
+ // out-of-quota to in-quota.
+ final long nowElapsed = mInjector.getElapsedRealtime();
+ stats.expirationTimeElapsed = nowElapsed + mMaxPeriodMs;
+
+ final LongArrayQueue events = mEventTimes.get(userId, packageName, tag);
+ if (events == null) {
+ return;
+ }
+
+ // The minimum time between the start time and the beginning of the events that were
+ // looked at --> how much time the stats will be valid for.
+ long emptyTimeMs = Long.MAX_VALUE - nowElapsed;
+
+ final long eventStartWindowElapsed = nowElapsed - stats.windowSizeMs;
+ for (int i = events.size() - 1; i >= 0; --i) {
+ final long eventTimeElapsed = events.get(i);
+ if (eventTimeElapsed < eventStartWindowElapsed) {
+ // This event happened before the window. No point in going any further.
+ break;
+ }
+ stats.countInWindow++;
+ emptyTimeMs = Math.min(emptyTimeMs, eventTimeElapsed - eventStartWindowElapsed);
+
+ if (stats.countInWindow >= stats.countLimit) {
+ stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed,
+ eventTimeElapsed + stats.windowSizeMs);
+ }
+ }
+
+ stats.expirationTimeElapsed = nowElapsed + emptyTimeMs;
+ }
+
+ /** Invalidate ExecutionStats for all apps. */
+ @GuardedBy("mLock")
+ private void invalidateAllExecutionStatsLocked() {
+ final long nowElapsed = mInjector.getElapsedRealtime();
+ mExecutionStatsCache.forEach((appStats) -> {
+ if (appStats != null) {
+ appStats.expirationTimeElapsed = nowElapsed;
+ }
+ });
+ }
+
+ @GuardedBy("mLock")
+ private void invalidateAllExecutionStatsLocked(final int userId,
+ @NonNull final String packageName) {
+ final ArrayMap<String, ExecutionStats> appStats =
+ mExecutionStatsCache.get(userId, packageName);
+ if (appStats != null) {
+ final long nowElapsed = mInjector.getElapsedRealtime();
+ final int numStats = appStats.size();
+ for (int i = 0; i < numStats; ++i) {
+ final ExecutionStats stats = appStats.valueAt(i);
+ if (stats != null) {
+ stats.expirationTimeElapsed = nowElapsed;
+ }
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void invalidateExecutionStatsLocked(final int userId, @NonNull final String packageName,
+ @Nullable String tag) {
+ final ExecutionStats stats = mExecutionStatsCache.get(userId, packageName, tag);
+ if (stats != null) {
+ stats.expirationTimeElapsed = mInjector.getElapsedRealtime();
+ }
+ }
+
+ private static final class EarliestEventTimeFunctor implements Consumer<LongArrayQueue> {
+ long earliestTimeElapsed = Long.MAX_VALUE;
+
+ @Override
+ public void accept(LongArrayQueue events) {
+ if (events != null && events.size() > 0) {
+ earliestTimeElapsed = Math.min(earliestTimeElapsed, events.get(0));
+ }
+ }
+
+ void reset() {
+ earliestTimeElapsed = Long.MAX_VALUE;
+ }
+ }
+
+ private final EarliestEventTimeFunctor mEarliestEventTimeFunctor =
+ new EarliestEventTimeFunctor();
+
+ /** Schedule a cleanup alarm if necessary and there isn't already one scheduled. */
+ @GuardedBy("mLock")
+ @VisibleForTesting
+ void maybeScheduleCleanupAlarmLocked() {
+ if (mNextCleanupTimeElapsed > mInjector.getElapsedRealtime()) {
+ // There's already an alarm scheduled. Just stick with that one. There's no way we'll
+ // end up scheduling an earlier alarm.
+ if (DEBUG) {
+ Slog.v(TAG, "Not scheduling cleanup since there's already one at "
+ + mNextCleanupTimeElapsed + " (in " + (mNextCleanupTimeElapsed
+ - mInjector.getElapsedRealtime()) + "ms)");
+ }
+ return;
+ }
+
+ mEarliestEventTimeFunctor.reset();
+ mEventTimes.forEach(mEarliestEventTimeFunctor);
+ final long earliestEndElapsed = mEarliestEventTimeFunctor.earliestTimeElapsed;
+ if (earliestEndElapsed == Long.MAX_VALUE) {
+ // Couldn't find a good time to clean up. Maybe this was called after we deleted all
+ // events.
+ if (DEBUG) {
+ Slog.d(TAG, "Didn't find a time to schedule cleanup");
+ }
+ return;
+ }
+
+ // Need to keep events for all apps up to the max period, regardless of their current
+ // category.
+ long nextCleanupElapsed = earliestEndElapsed + mMaxPeriodMs;
+ if (nextCleanupElapsed - mNextCleanupTimeElapsed <= 10 * MINUTE_IN_MILLIS) {
+ // No need to clean up too often. Delay the alarm if the next cleanup would be too soon
+ // after it.
+ nextCleanupElapsed += 10 * MINUTE_IN_MILLIS;
+ }
+ mNextCleanupTimeElapsed = nextCleanupElapsed;
+ scheduleAlarm(AlarmManager.ELAPSED_REALTIME, nextCleanupElapsed, ALARM_TAG_CLEANUP,
+ mEventCleanupAlarmListener);
+ if (DEBUG) {
+ Slog.d(TAG, "Scheduled next cleanup for " + mNextCleanupTimeElapsed);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private boolean maybeUpdateStatusForPkgLocked(final int userId,
+ @NonNull final String packageName) {
+ final UptcMap<Boolean> done = new UptcMap<>();
+
+ if (!mEventTimes.contains(userId, packageName)) {
+ return false;
+ }
+ final ArrayMap<String, LongArrayQueue> events = mEventTimes.get(userId, packageName);
+ if (events == null) {
+ Slog.wtf(TAG,
+ "Events map was null even though mEventTimes said it contained "
+ + string(userId, packageName, null));
+ return false;
+ }
+
+ // Lambdas can't interact with non-final outer variables.
+ final boolean[] changed = {false};
+ events.forEach((tag, eventList) -> {
+ if (!done.contains(userId, packageName, tag)) {
+ changed[0] |= maybeUpdateStatusForUptcLocked(userId, packageName, tag);
+ done.add(userId, packageName, tag, Boolean.TRUE);
+ }
+ });
+
+ return changed[0];
+ }
+
+ /**
+ * Posts that the quota status for the UPTC has changed if it has changed. Avoid calling if
+ * there are no {@link QuotaChangeListener}s registered as the work done will be useless.
+ *
+ * @return true if the in/out quota status changed
+ */
+ @GuardedBy("mLock")
+ private boolean maybeUpdateStatusForUptcLocked(final int userId,
+ @NonNull final String packageName, @Nullable final String tag) {
+ final boolean oldInQuota = isWithinQuotaLocked(
+ getExecutionStatsLocked(userId, packageName, tag, false));
+
+ final boolean newInQuota;
+ if (!isEnabledLocked() || isQuotaFreeLocked(userId, packageName)) {
+ newInQuota = true;
+ } else {
+ newInQuota = isWithinQuotaLocked(
+ getExecutionStatsLocked(userId, packageName, tag, true));
+ }
+
+ if (!newInQuota) {
+ maybeScheduleStartAlarmLocked(userId, packageName, tag);
+ } else {
+ cancelScheduledStartAlarmLocked(userId, packageName, tag);
+ }
+
+ if (oldInQuota != newInQuota) {
+ if (DEBUG) {
+ Slog.d(TAG,
+ "Quota status changed from " + oldInQuota + " to " + newInQuota + " for "
+ + string(userId, packageName, tag));
+ }
+ postQuotaStatusChanged(userId, packageName, tag);
+ return true;
+ }
+
+ return false;
+ }
+
+ private final class DeleteEventTimesFunctor implements Consumer<LongArrayQueue> {
+ private long mMaxPeriodMs;
+
+ @Override
+ public void accept(LongArrayQueue times) {
+ if (times != null) {
+ // Remove everything older than mMaxPeriodMs time ago.
+ while (times.size() > 0
+ && times.peekFirst() <= mInjector.getElapsedRealtime() - mMaxPeriodMs) {
+ times.removeFirst();
+ }
+ }
+ }
+
+ private void updateMaxPeriod() {
+ long maxPeriodMs = 0;
+ for (int i = mCategoryCountWindowSizesMs.size() - 1; i >= 0; --i) {
+ maxPeriodMs = Long.max(maxPeriodMs, mCategoryCountWindowSizesMs.valueAt(i));
+ }
+ mMaxPeriodMs = maxPeriodMs;
+ }
+ }
+
+ private final DeleteEventTimesFunctor mDeleteOldEventTimesFunctor =
+ new DeleteEventTimesFunctor();
+
+ @GuardedBy("mLock")
+ @VisibleForTesting
+ void deleteObsoleteEventsLocked() {
+ mEventTimes.forEach(mDeleteOldEventTimesFunctor);
+ }
+
+ private class CqtHandler extends Handler {
+ CqtHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ synchronized (mLock) {
+ switch (msg.what) {
+ case MSG_CLEAN_UP_EVENTS: {
+ if (DEBUG) {
+ Slog.d(TAG, "Cleaning up events.");
+ }
+ deleteObsoleteEventsLocked();
+ maybeScheduleCleanupAlarmLocked();
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ private Function<Void, LongArrayQueue> mCreateLongArrayQueue = aVoid -> new LongArrayQueue();
+ private Function<Void, ExecutionStats> mCreateExecutionStats = aVoid -> new ExecutionStats();
+
+ //////////////////////// TESTING HELPERS /////////////////////////////
+
+ @VisibleForTesting
+ @Nullable
+ LongArrayQueue getEvents(int userId, String packageName, String tag) {
+ return mEventTimes.get(userId, packageName, tag);
+ }
+
+ //////////////////////////// DATA DUMP //////////////////////////////
+
+ /** Dump state in text format. */
+ public void dump(final IndentingPrintWriter pw) {
+ pw.print(TAG);
+ pw.println(":");
+ pw.increaseIndent();
+
+ synchronized (mLock) {
+ super.dump(pw);
+ pw.println();
+
+ pw.println("Instantaneous events:");
+ pw.increaseIndent();
+ mEventTimes.forEach((userId, pkgName, tag, events) -> {
+ if (events.size() > 0) {
+ pw.print(string(userId, pkgName, tag));
+ pw.println(":");
+ pw.increaseIndent();
+ pw.print(events.get(0));
+ for (int i = 1; i < events.size(); ++i) {
+ pw.print(", ");
+ pw.print(events.get(i));
+ }
+ pw.decreaseIndent();
+ pw.println();
+ }
+ });
+ pw.decreaseIndent();
+
+ pw.println();
+ pw.println("Cached execution stats:");
+ pw.increaseIndent();
+ mExecutionStatsCache.forEach((userId, pkgName, tag, stats) -> {
+ if (stats != null) {
+ pw.print(string(userId, pkgName, tag));
+ pw.println(":");
+ pw.increaseIndent();
+ pw.println(stats);
+ pw.decreaseIndent();
+ }
+ });
+ pw.decreaseIndent();
+
+ pw.println();
+ pw.println("Limits:");
+ pw.increaseIndent();
+ final int numCategories = mCategoryCountWindowSizesMs.size();
+ for (int i = 0; i < numCategories; ++i) {
+ final Category category = mCategoryCountWindowSizesMs.keyAt(i);
+ pw.print(category);
+ pw.print(": ");
+ pw.print(mMaxCategoryCounts.get(category));
+ pw.print(" events in ");
+ pw.println(TimeUtils.formatDuration(mCategoryCountWindowSizesMs.get(category)));
+ }
+ pw.decreaseIndent();
+ }
+ pw.decreaseIndent();
+ }
+
+ /**
+ * Dump state to proto.
+ *
+ * @param proto The ProtoOutputStream to write to.
+ * @param fieldId The field ID of the {@link CountQuotaTrackerProto}.
+ */
+ public void dump(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+
+ synchronized (mLock) {
+ super.dump(proto, CountQuotaTrackerProto.BASE_QUOTA_DATA);
+
+ for (int i = 0; i < mCategoryCountWindowSizesMs.size(); ++i) {
+ final Category category = mCategoryCountWindowSizesMs.keyAt(i);
+ final long clToken = proto.start(CountQuotaTrackerProto.COUNT_LIMIT);
+ category.dumpDebug(proto, CountQuotaTrackerProto.CountLimit.CATEGORY);
+ proto.write(CountQuotaTrackerProto.CountLimit.LIMIT,
+ mMaxCategoryCounts.get(category));
+ proto.write(CountQuotaTrackerProto.CountLimit.WINDOW_SIZE_MS,
+ mCategoryCountWindowSizesMs.get(category));
+ proto.end(clToken);
+ }
+
+ mExecutionStatsCache.forEach((userId, pkgName, tag, stats) -> {
+ final boolean isQuotaFree = isIndividualQuotaFreeLocked(userId, pkgName);
+
+ final long usToken = proto.start(CountQuotaTrackerProto.UPTC_STATS);
+
+ (new Uptc(userId, pkgName, tag))
+ .dumpDebug(proto, CountQuotaTrackerProto.UptcStats.UPTC);
+
+ proto.write(CountQuotaTrackerProto.UptcStats.IS_QUOTA_FREE, isQuotaFree);
+
+ final LongArrayQueue events = mEventTimes.get(userId, pkgName, tag);
+ if (events != null) {
+ for (int j = events.size() - 1; j >= 0; --j) {
+ final long eToken = proto.start(CountQuotaTrackerProto.UptcStats.EVENTS);
+ proto.write(CountQuotaTrackerProto.Event.TIMESTAMP_ELAPSED, events.get(j));
+ proto.end(eToken);
+ }
+ }
+
+ final long statsToken = proto.start(
+ CountQuotaTrackerProto.UptcStats.EXECUTION_STATS);
+ proto.write(
+ CountQuotaTrackerProto.ExecutionStats.EXPIRATION_TIME_ELAPSED,
+ stats.expirationTimeElapsed);
+ proto.write(
+ CountQuotaTrackerProto.ExecutionStats.WINDOW_SIZE_MS,
+ stats.windowSizeMs);
+ proto.write(CountQuotaTrackerProto.ExecutionStats.COUNT_LIMIT, stats.countLimit);
+ proto.write(
+ CountQuotaTrackerProto.ExecutionStats.COUNT_IN_WINDOW,
+ stats.countInWindow);
+ proto.write(
+ CountQuotaTrackerProto.ExecutionStats.IN_QUOTA_TIME_ELAPSED,
+ stats.inQuotaTimeElapsed);
+ proto.end(statsToken);
+
+ proto.end(usToken);
+ });
+
+ proto.end(token);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/utils/quota/UptcMap.java b/services/core/java/com/android/server/utils/quota/UptcMap.java
index 7b49913..a3d6ee5 100644
--- a/services/core/java/com/android/server/utils/quota/UptcMap.java
+++ b/services/core/java/com/android/server/utils/quota/UptcMap.java
@@ -22,6 +22,7 @@
import android.util.SparseArrayMap;
import java.util.function.Consumer;
+import java.util.function.Function;
/**
* A SparseArrayMap of ArrayMaps, which is suitable for holding userId-packageName-tag combination
@@ -95,36 +96,36 @@
}
/**
- * Returns the index for which {@link #getUserIdAtIndex(int)} would return the specified userId,
- * or a negative number if the specified userId is not mapped.
+ * Returns the saved object for the given UPTC. If there was no saved object, it will create a
+ * new object using creator, insert it, and return it.
*/
- public int indexOfUserId(int userId) {
- return mData.indexOfKey(userId);
- }
-
- /**
- * Returns the index for which {@link #getPackageNameAtIndex(int, int)} would return the
- * specified userId, or a negative number if the specified userId and packageName are not mapped
- * together.
- */
- public int indexOfUserIdAndPackage(int userId, @NonNull String packageName) {
- return mData.indexOfKey(userId, packageName);
+ @Nullable
+ public T getOrCreate(int userId, @NonNull String packageName, @Nullable String tag,
+ Function<Void, T> creator) {
+ final ArrayMap<String, T> data = mData.get(userId, packageName);
+ if (data == null || !data.containsKey(tag)) {
+ // We've never inserted data for this combination before. Create a new object.
+ final T val = creator.apply(null);
+ add(userId, packageName, tag, val);
+ return val;
+ }
+ return data.get(tag);
}
/** Returns the userId at the given index. */
- public int getUserIdAtIndex(int index) {
+ private int getUserIdAtIndex(int index) {
return mData.keyAt(index);
}
/** Returns the package name at the given index. */
@NonNull
- public String getPackageNameAtIndex(int userIndex, int packageIndex) {
+ private String getPackageNameAtIndex(int userIndex, int packageIndex) {
return mData.keyAt(userIndex, packageIndex);
}
/** Returns the tag at the given index. */
@NonNull
- public String getTagAtIndex(int userIndex, int packageIndex, int tagIndex) {
+ private String getTagAtIndex(int userIndex, int packageIndex, int tagIndex) {
// This structure never inserts a null ArrayMap, so if the indices are valid, valueAt()
// won't return null.
return mData.valueAt(userIndex, packageIndex).keyAt(tagIndex);
@@ -160,4 +161,26 @@
}
});
}
+
+ public void forEach(UptcDataConsumer<T> consumer) {
+ final int uCount = userCount();
+ for (int u = 0; u < uCount; ++u) {
+ final int userId = getUserIdAtIndex(u);
+
+ final int pkgCount = packageCountForUser(userId);
+ for (int p = 0; p < pkgCount; ++p) {
+ final String pkgName = getPackageNameAtIndex(u, p);
+
+ final int tagCount = tagCountForUserAndPackage(userId, pkgName);
+ for (int t = 0; t < tagCount; ++t) {
+ final String tag = getTagAtIndex(u, p, t);
+ consumer.accept(userId, pkgName, tag, get(userId, pkgName, tag));
+ }
+ }
+ }
+ }
+
+ interface UptcDataConsumer<D> {
+ void accept(int userId, @NonNull String packageName, @Nullable String tag, @Nullable D obj);
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 94c2192..23b94bd 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -751,10 +751,10 @@
if (abort) {
launchObserverNotifyActivityLaunchCancelled(info);
} else {
- logAppTransitionFinished(info);
if (info.isInterestingToLoggerAndObserver()) {
launchObserverNotifyActivityLaunchFinished(info, timestampNs);
}
+ logAppTransitionFinished(info);
}
info.mPendingDrawActivities.clear();
mTransitionInfoList.remove(info);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index d49270d..e281712 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -555,7 +555,7 @@
private RemoteAnimationDefinition mRemoteAnimationDefinition;
- private AnimatingActivityRegistry mAnimatingActivityRegistry;
+ AnimatingActivityRegistry mAnimatingActivityRegistry;
private Task mLastParent;
@@ -1974,15 +1974,6 @@
+ " is already the parent of r=" + this);
}
- // TODO: Ensure that we do not directly reparent activities across stacks, as that may leave
- // the stacks in strange states. For now, we should use Task.reparent() to ensure that
- // the stack is left in an OK state.
- if (prevTask != null && newTask != null && prevTask.getStack() != newTask.getStack()) {
- throw new IllegalArgumentException(reason + ": task=" + newTask
- + " is in a different stack (" + newTask.getStackId() + ") than the parent of"
- + " r=" + this + " (" + prevTask.getStackId() + ")");
- }
-
ProtoLog.i(WM_DEBUG_ADD_REMOVE, "reparent: moving activity=%s"
+ " to task=%d at %d", this, task.mTaskId, position);
reparent(newTask, position);
@@ -2633,14 +2624,13 @@
// TODO(b/137329632): find the next activity directly underneath this one, not just anywhere
final ActivityRecord next = getDisplay().topRunningActivity(
true /* considerKeyguardState */);
- final boolean isVisible = mVisibleRequested || nowVisible;
// isNextNotYetVisible is to check if the next activity is invisible, or it has been
// requested to be invisible but its windows haven't reported as invisible. If so, it
// implied that the current finishing activity should be added into stopping list rather
// than destroy immediately.
final boolean isNextNotYetVisible = next != null
&& (!next.nowVisible || !next.mVisibleRequested);
- if (isVisible && isNextNotYetVisible) {
+ if ((mVisibleRequested || isState(PAUSED)) && isNextNotYetVisible) {
// Add this activity to the list of stopping activities. It will be processed and
// destroyed when the next activity reports idle.
addToStopping(false /* scheduleIdle */, false /* idleDelayed */,
@@ -3098,7 +3088,7 @@
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"Removing app %s delayed=%b animation=%s animating=%b", this, delayed,
- getAnimation(), isAnimating(TRANSITION));
+ getAnimation(), isAnimating(TRANSITION | PARENTS));
ProtoLog.v(WM_DEBUG_ADD_REMOVE, "removeAppToken: %s"
+ " delayed=%b Callers=%s", this, delayed, Debug.getCallers(4));
@@ -3110,7 +3100,7 @@
// If this window was animating, then we need to ensure that the app transition notifies
// that animations have completed in DisplayContent.handleAnimatingStoppedAndTransition(),
// so add to that list now
- if (isAnimating(TRANSITION)) {
+ if (isAnimating(TRANSITION | PARENTS)) {
getDisplayContent().mNoAnimationNotifyOnTransitionFinished.add(token);
}
@@ -3446,7 +3436,7 @@
* color mode set to avoid jank in the middle of the transition.
*/
boolean canShowWindows() {
- return allDrawn && !(isAnimating() && hasNonDefaultColorWindow());
+ return allDrawn && !(isAnimating(PARENTS) && hasNonDefaultColorWindow());
}
/**
@@ -4772,7 +4762,7 @@
}
// Schedule an idle timeout in case the app doesn't do it for us.
- mStackSupervisor.scheduleIdleTimeoutLocked(this);
+ mStackSupervisor.scheduleIdleTimeout(this);
mStackSupervisor.reportResumedActivityLocked(this);
@@ -4871,8 +4861,7 @@
void stopIfPossible() {
if (DEBUG_SWITCH) Slog.d(TAG_SWITCH, "Stopping: " + this);
final ActivityStack stack = getActivityStack();
- if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
- || (info.flags & ActivityInfo.FLAG_NO_HISTORY) != 0) {
+ if (isNoHistory()) {
if (!finishing) {
if (!stack.shouldSleepActivities()) {
if (DEBUG_STATES) Slog.d(TAG_STATES, "no-history finish of " + this);
@@ -4993,9 +4982,9 @@
+ "immediate=" + !idleDelayed);
}
if (!idleDelayed) {
- mStackSupervisor.scheduleIdleLocked();
+ mStackSupervisor.scheduleIdle();
} else {
- mStackSupervisor.scheduleIdleTimeoutLocked(this);
+ mStackSupervisor.scheduleIdleTimeout(this);
}
} else {
stack.checkReadyForSleep();
@@ -5354,13 +5343,13 @@
if (!allDrawn && w.mightAffectAllDrawn()) {
if (DEBUG_VISIBILITY || WM_DEBUG_ORIENTATION.isLogToLogcat()) {
Slog.v(TAG, "Eval win " + w + ": isDrawn=" + w.isDrawnLw()
- + ", isAnimationSet=" + isAnimating(TRANSITION));
+ + ", isAnimationSet=" + isAnimating(TRANSITION | PARENTS));
if (!w.isDrawnLw()) {
Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurfaceController
+ " pv=" + w.isVisibleByPolicy()
+ " mDrawState=" + winAnimator.drawStateToString()
+ " ph=" + w.isParentWindowHidden() + " th=" + mVisibleRequested
- + " a=" + isAnimating(TRANSITION));
+ + " a=" + isAnimating(TRANSITION | PARENTS));
}
}
@@ -5961,7 +5950,7 @@
@Override
void prepareSurfaces() {
- final boolean show = isVisible() || isAnimating();
+ final boolean show = isVisible() || isAnimating(PARENTS);
if (mSurfaceControl != null) {
if (show && !mLastSurfaceShowing) {
@@ -5989,7 +5978,7 @@
}
void attachThumbnailAnimation() {
- if (!isAnimating()) {
+ if (!isAnimating(PARENTS)) {
return;
}
final GraphicBuffer thumbnailHeader =
@@ -6009,7 +5998,7 @@
* {@link android.app.ActivityOptions#ANIM_OPEN_CROSS_PROFILE_APPS} animation.
*/
void attachCrossProfileAppsThumbnailAnimation() {
- if (!isAnimating()) {
+ if (!isAnimating(PARENTS)) {
return;
}
clearThumbnail();
@@ -6104,20 +6093,21 @@
getDisplayContent().mAppTransition.notifyAppTransitionFinishedLocked(token);
scheduleAnimation();
- if (mAtmService.mRootWindowContainer.allResumedActivitiesIdle()
- || mAtmService.mStackSupervisor.isStoppingNoHistoryActivity()) {
- // If all activities are already idle or there is an activity that must be
- // stopped immediately after visible, then we now need to make sure we perform
- // the full stop of this activity. This is because we won't do that while they are still
- // waiting for the animation to finish.
- if (mAtmService.mStackSupervisor.mStoppingActivities.contains(this)) {
- mAtmService.mStackSupervisor.scheduleIdleLocked();
+ if (!mStackSupervisor.mStoppingActivities.isEmpty()
+ || !mStackSupervisor.mFinishingActivities.isEmpty()) {
+ if (mRootWindowContainer.allResumedActivitiesIdle()) {
+ // If all activities are already idle then we now need to make sure we perform
+ // the full stop of this activity. This is because we won't do that while they
+ // are still waiting for the animation to finish.
+ mStackSupervisor.scheduleIdle();
+ } else if (mRootWindowContainer.allResumedActivitiesVisible()) {
+ // If all resumed activities are already visible (and should be drawn, see
+ // updateReportedVisibility ~ nowVisible) but not idle, we still schedule to
+ // process the stopping and finishing activities because the transition is done.
+ // This also avoids if the next activity never reports idle (e.g. animating view),
+ // the previous will need to wait until idle timeout to be stopped or destroyed.
+ mStackSupervisor.scheduleProcessStoppingAndFinishingActivities();
}
- } else {
- // Instead of doing the full stop routine here, let's just hide any activities
- // we now can, and let them stop when the normal idle happens.
- mAtmService.mStackSupervisor.processStoppingActivitiesLocked(null /* idleActivity */,
- false /* remove */, true /* processPausingActivities */);
}
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@@ -7461,6 +7451,11 @@
}
@Override
+ boolean showToCurrentUser() {
+ return mShowForAllUsers || mWmService.isCurrentProfile(mUserId);
+ }
+
+ @Override
public String toString() {
if (stringName != null) {
return stringName + " t" + (task == null ? INVALID_TASK_ID : task.mTaskId) +
@@ -7516,7 +7511,7 @@
super.dumpDebug(proto, WINDOW_TOKEN, logLevel);
proto.write(LAST_SURFACE_SHOWING, mLastSurfaceShowing);
proto.write(IS_WAITING_FOR_TRANSITION_START, isWaitingForTransitionStart());
- proto.write(IS_ANIMATING, isAnimating());
+ proto.write(IS_ANIMATING, isAnimating(PARENTS));
if (mThumbnail != null){
mThumbnail.dumpDebug(proto, THUMBNAIL);
}
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 842edb3..c959439 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -70,6 +70,7 @@
import static com.android.server.wm.ActivityStack.ActivityState.STARTED;
import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
+import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME;
import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.ActivityStackSupervisor.dumpHistoryList;
import static com.android.server.wm.ActivityStackSupervisor.printThisActivity;
@@ -783,6 +784,10 @@
null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
null /* tempOtherTaskBounds */, null /* tempOtherTaskInsetBounds */,
PRESERVE_WINDOWS, true /* deferResume */);
+ } else if (overrideWindowingMode != WINDOWING_MODE_PINNED) {
+ // For pinned stack, resize is now part of the {@link WindowContainerTransaction}
+ resize(new Rect(newBounds), null /* tempTaskBounds */,
+ null /* tempTaskInsetBounds */, PRESERVE_WINDOWS, true /* deferResume */);
}
}
if (prevIsAlwaysOnTop != isAlwaysOnTop()) {
@@ -1257,7 +1262,7 @@
super.switchUser(userId);
forAllTasks((t) -> {
- if (t.mWmService.isCurrentProfileLocked(t.mUserId) || t.showForAllUsers()) {
+ if (t.mWmService.isCurrentProfile(t.mUserId) || t.showForAllUsers()) {
mChildren.remove(t);
mChildren.add(t);
}
@@ -1275,7 +1280,7 @@
// Make sure that there is no activity waiting for this to launch.
if (!mStackSupervisor.mWaitingActivityLaunched.isEmpty()) {
mStackSupervisor.removeIdleTimeoutForActivity(r);
- mStackSupervisor.scheduleIdleTimeoutLocked(r);
+ mStackSupervisor.scheduleIdleTimeout(r);
}
}
@@ -1328,7 +1333,7 @@
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep still need to stop "
+ mStackSupervisor.mStoppingActivities.size() + " activities");
- mStackSupervisor.scheduleIdleLocked();
+ mStackSupervisor.scheduleIdle();
shouldSleep = false;
}
@@ -1413,8 +1418,7 @@
else if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Start pausing: " + prev);
mPausingActivity = prev;
mLastPausedActivity = prev;
- mLastNoHistoryActivity = (prev.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
- || (prev.info.flags & ActivityInfo.FLAG_NO_HISTORY) != 0 ? prev : null;
+ mLastNoHistoryActivity = prev.isNoHistory() ? prev : null;
prev.setState(PAUSING, "startPausingLocked");
prev.getTask().touchActiveTime();
clearLaunchTime(prev);
@@ -1834,6 +1838,15 @@
&& (topTask == null || topTask.supportsSplitScreenWindowingMode());
}
+ /**
+ * Returns {@code true} if this is the top-most split-screen-primary or
+ * split-screen-secondary stack, {@code false} otherwise.
+ */
+ boolean isTopSplitScreenStack() {
+ return inSplitScreenWindowingMode()
+ && this == getDisplay().getTopStackInWindowingMode(getWindowingMode());
+ }
+
/** @return True if the resizing of the primary-split-screen stack affects this stack size. */
boolean affectedBySplitScreenResize() {
if (!supportsSplitScreenWindowingMode()) {
@@ -2669,7 +2682,7 @@
final Task task = taskTop.getTask();
// If ActivityOptions are moved out and need to be aborted or moved to taskTop.
- final ActivityOptions topOptions = sResetTargetTaskHelper.process(this, task, forceReset);
+ final ActivityOptions topOptions = sResetTargetTaskHelper.process(task, forceReset);
if (mChildren.contains(task)) {
final ActivityRecord newTop = task.getTopNonFinishingActivity();
@@ -3003,6 +3016,11 @@
final void moveTaskToFrontLocked(Task tr, boolean noAnimation, ActivityOptions options,
AppTimeTracker timeTracker, String reason) {
+ moveTaskToFrontLocked(tr, noAnimation, options, timeTracker, !DEFER_RESUME, reason);
+ }
+
+ final void moveTaskToFrontLocked(Task tr, boolean noAnimation, ActivityOptions options,
+ AppTimeTracker timeTracker, boolean deferResume, String reason) {
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "moveTaskToFront: " + tr);
final ActivityStack topStack = getDisplay().getTopStack();
@@ -3074,7 +3092,9 @@
topActivity.supportsEnterPipOnTaskSwitch = true;
}
- mRootWindowContainer.resumeFocusedStacksTopActivities();
+ if (!deferResume) {
+ mRootWindowContainer.resumeFocusedStacksTopActivities();
+ }
EventLogTags.writeWmTaskToFront(tr.mUserId, tr.mTaskId);
mAtmService.getTaskChangeNotificationController().notifyTaskMovedToFront(tr.getTaskInfo());
} finally {
@@ -3969,11 +3989,19 @@
* @param showForAllUsers Whether to show the task regardless of the current user.
*/
private void addChild(Task task, int position, boolean showForAllUsers, boolean moveParents) {
- // Add child task.
- addChild(task, null);
+ try {
+ // Force show for all user so task can be position correctly based on which user is
+ // active. We clear the force show below.
+ task.setForceShowForAllUsers(showForAllUsers);
+ // Add child task.
+ addChild(task, null);
- // Move child to a proper position, as some restriction for position might apply.
- positionChildAt(position, task, moveParents /* includingParents */, showForAllUsers);
+ // Move child to a proper position, as some restriction for position might apply.
+ positionChildAt(position, task, moveParents /* includingParents */);
+
+ } finally {
+ task.setForceShowForAllUsers(false);
+ }
}
@Override
@@ -4019,18 +4047,7 @@
@Override
void positionChildAt(int position, WindowContainer child, boolean includingParents) {
final Task task = (Task) child;
- positionChildAt(position, task, includingParents, task.showForAllUsers());
- }
-
- /**
- * Overridden version of {@link ActivityStack#positionChildAt(int, WindowContainer, boolean)}.
- * Used in {@link ActivityStack#addChild(Task, int, boolean showForAllUsers, boolean)}, as it
- * can receive showForAllUsers param from {@link ActivityRecord} instead of
- * {@link Task#showForAllUsers()}.
- */
- private int positionChildAt(int position, Task child, boolean includingParents,
- boolean showForAllUsers) {
- final int targetPosition = findPositionForTask(child, position, showForAllUsers);
+ final int targetPosition = findPositionForTask(task, position);
super.positionChildAt(targetPosition, child, includingParents);
// Log positioning.
@@ -4039,8 +4056,7 @@
}
final int toTop = targetPosition == mChildren.size() - 1 ? 1 : 0;
- EventLogTags.writeWmTaskMoved(child.mTaskId, toTop, targetPosition);
- return targetPosition;
+ EventLogTags.writeWmTaskMoved(task.mTaskId, toTop, targetPosition);
}
@Override
@@ -4110,9 +4126,8 @@
// TODO: We should really have users as a window container in the hierarchy so that we don't
// have to do complicated things like we are doing in this method.
- int findPositionForTask(Task task, int targetPosition, boolean showForAllUsers) {
- final boolean canShowTask =
- showForAllUsers || mWmService.isCurrentProfileLocked(task.mUserId);
+ int findPositionForTask(Task task, int targetPosition) {
+ final boolean canShowTask = task.showToCurrentUser();
final int stackSize = mChildren.size();
int minPosition = 0;
@@ -4144,9 +4159,7 @@
private int computeMinPosition(int minPosition, int size) {
while (minPosition < size) {
final Task tmpTask = (Task) mChildren.get(minPosition);
- final boolean canShowTmpTask =
- tmpTask.showForAllUsers()
- || mWmService.isCurrentProfileLocked(tmpTask.mUserId);
+ final boolean canShowTmpTask = tmpTask.showToCurrentUser();
if (canShowTmpTask) {
break;
}
@@ -4164,9 +4177,7 @@
private int computeMaxPosition(int maxPosition) {
while (maxPosition > 0) {
final Task tmpTask = (Task) mChildren.get(maxPosition);
- final boolean canShowTmpTask =
- tmpTask.showForAllUsers()
- || mWmService.isCurrentProfileLocked(tmpTask.mUserId);
+ final boolean canShowTmpTask = tmpTask.showToCurrentUser();
if (!canShowTmpTask) {
break;
}
@@ -4881,6 +4892,18 @@
}
}
+ @Override
+ protected void onAnimationFinished() {
+ super.onAnimationFinished();
+ // TODO(b/142617871): we may need to add animation type parameter on onAnimationFinished to
+ // identify if the callback is for launch animation finish and then calling
+ // activity#onAnimationFinished.
+ final ActivityRecord activity = getTopMostActivity();
+ if (activity != null) {
+ activity.onAnimationFinished();
+ }
+ }
+
/**
* Sets the current picture-in-picture aspect ratio.
*/
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 88b5cd3..fd871bd 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -183,6 +183,7 @@
private static final int RESUME_TOP_ACTIVITY_MSG = FIRST_SUPERVISOR_STACK_MSG + 2;
private static final int SLEEP_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 3;
private static final int LAUNCH_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 4;
+ private static final int PROCESS_STOPPING_AND_FINISHING_MSG = FIRST_SUPERVISOR_STACK_MSG + 5;
private static final int LAUNCH_TASK_BEHIND_COMPLETE = FIRST_SUPERVISOR_STACK_MSG + 12;
private static final int RESTART_ACTIVITY_PROCESS_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 13;
private static final int REPORT_MULTI_WINDOW_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 14;
@@ -1301,22 +1302,14 @@
return booting;
}
- // Checked.
- @GuardedBy("mService")
- final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
+ void activityIdleInternal(ActivityRecord r, boolean fromTimeout,
boolean processPausingActivities, Configuration config) {
- if (DEBUG_ALL) Slog.v(TAG, "Activity idle: " + token);
+ if (DEBUG_ALL) Slog.v(TAG, "Activity idle: " + r);
- ArrayList<ActivityRecord> finishes = null;
- ArrayList<UserState> startingUsers = null;
- int NS = 0;
- int NF = 0;
boolean booting = false;
- boolean activityRemoved = false;
- ActivityRecord r = ActivityRecord.forTokenLocked(token);
if (r != null) {
- if (DEBUG_IDLE) Slog.d(TAG_IDLE, "activityIdleInternalLocked: Callers="
+ if (DEBUG_IDLE) Slog.d(TAG_IDLE, "activityIdleInternal: Callers="
+ Debug.getCallers(4));
mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
r.finishLaunchTickingLocked();
@@ -1369,47 +1362,14 @@
}
// Atomically retrieve all of the other things to do.
- final ArrayList<ActivityRecord> stops = processStoppingActivitiesLocked(r,
- true /* remove */, processPausingActivities);
- NS = stops != null ? stops.size() : 0;
- if ((NF = mFinishingActivities.size()) > 0) {
- finishes = new ArrayList<>(mFinishingActivities);
- mFinishingActivities.clear();
- }
+ processStoppingAndFinishingActivities(r, processPausingActivities, "idle");
- if (mStartingUsers.size() > 0) {
- startingUsers = new ArrayList<>(mStartingUsers);
+ if (!mStartingUsers.isEmpty()) {
+ final ArrayList<UserState> startingUsers = new ArrayList<>(mStartingUsers);
mStartingUsers.clear();
- }
- // Stop any activities that are scheduled to do so but have been
- // waiting for the next one to start.
- for (int i = 0; i < NS; i++) {
- r = stops.get(i);
- final ActivityStack stack = r.getActivityStack();
- if (stack != null) {
- if (r.finishing) {
- // TODO(b/137329632): Wait for idle of the right activity, not just any.
- r.destroyIfPossible("activityIdleInternalLocked");
- } else {
- r.stopIfPossible();
- }
- }
- }
-
- // Finish any activities that are scheduled to do so but have been
- // waiting for the next one to start.
- for (int i = 0; i < NF; i++) {
- r = finishes.get(i);
- final ActivityStack stack = r.getActivityStack();
- if (stack != null) {
- activityRemoved |= r.destroyImmediately(true /* removeFromApp */, "finish-idle");
- }
- }
-
- if (!booting) {
- // Complete user switch
- if (startingUsers != null) {
+ if (!booting) {
+ // Complete user switch.
for (int i = 0; i < startingUsers.size(); i++) {
mService.mAmInternal.finishUserSwitch(startingUsers.get(i));
}
@@ -1417,14 +1377,6 @@
}
mService.mH.post(() -> mService.mAmInternal.trimApplications());
- //dump();
- //mWindowManager.dump();
-
- if (activityRemoved) {
- mRootWindowContainer.resumeFocusedStacksTopActivities();
- }
-
- return r;
}
/** This doesn't just find a task, it also moves the task to front. */
@@ -1765,7 +1717,7 @@
stack.mForceHidden = true;
stack.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
stack.mForceHidden = false;
- activityIdleInternalLocked(null, false /* fromTimeout */,
+ activityIdleInternal(null /* idleActivity */, false /* fromTimeout */,
true /* processPausingActivities */, null /* configuration */);
// Move all the tasks to the bottom of the fullscreen stack
@@ -2117,71 +2069,71 @@
}
/**
- * Returns whether a stopping activity is present that should be stopped after visible, rather
- * than idle.
- * @return {@code true} if such activity is present. {@code false} otherwise.
+ * Processes the activities to be stopped or destroyed. This should be called when the resumed
+ * activities are idle or drawn.
*/
- boolean isStoppingNoHistoryActivity() {
- // Activities that are marked as nohistory should be stopped immediately after the resumed
- // activity has become visible.
- for (ActivityRecord record : mStoppingActivities) {
- if (record.isNoHistory()) {
- return true;
+ private void processStoppingAndFinishingActivities(ActivityRecord launchedActivity,
+ boolean processPausingActivities, String reason) {
+ // Stop any activities that are scheduled to do so but have been waiting for the transition
+ // animation to finish.
+ ArrayList<ActivityRecord> readyToStopActivities = null;
+ for (int i = mStoppingActivities.size() - 1; i >= 0; --i) {
+ final ActivityRecord s = mStoppingActivities.get(i);
+ final boolean animating = s.isAnimating(TRANSITION);
+ if (DEBUG_STATES) Slog.v(TAG, "Stopping " + s + ": nowVisible=" + s.nowVisible
+ + " animating=" + animating + " finishing=" + s.finishing);
+
+ final ActivityStack stack = s.getActivityStack();
+ final boolean shouldSleepOrShutDown = stack != null
+ ? stack.shouldSleepOrShutDownActivities()
+ : mService.isSleepingOrShuttingDownLocked();
+ if (!animating || shouldSleepOrShutDown) {
+ if (!processPausingActivities && s.isState(PAUSING)) {
+ // Defer processing pausing activities in this iteration and reschedule
+ // a delayed idle to reprocess it again
+ removeIdleTimeoutForActivity(launchedActivity);
+ scheduleIdleTimeout(launchedActivity);
+ continue;
+ }
+
+ if (DEBUG_STATES) Slog.v(TAG, "Ready to stop: " + s);
+ if (readyToStopActivities == null) {
+ readyToStopActivities = new ArrayList<>();
+ }
+ readyToStopActivities.add(s);
+
+ mStoppingActivities.remove(i);
}
}
- return false;
- }
-
- // TODO: Change method name to reflect what it actually does.
- final ArrayList<ActivityRecord> processStoppingActivitiesLocked(ActivityRecord idleActivity,
- boolean remove, boolean processPausingActivities) {
- ArrayList<ActivityRecord> stops = null;
-
- final boolean nowVisible = mRootWindowContainer.allResumedActivitiesVisible();
- for (int activityNdx = mStoppingActivities.size() - 1; activityNdx >= 0; --activityNdx) {
- ActivityRecord s = mStoppingActivities.get(activityNdx);
-
- final boolean animating = s.isAnimating(TRANSITION);
-
- if (DEBUG_STATES) Slog.v(TAG, "Stopping " + s + ": nowVisible=" + nowVisible
- + " animating=" + animating + " finishing=" + s.finishing);
- if (nowVisible && s.finishing) {
-
- // If this activity is finishing, it is sitting on top of
- // everyone else but we now know it is no longer needed...
- // so get rid of it. Otherwise, we need to go through the
- // normal flow and hide it once we determine that it is
- // hidden by the activities in front of it.
- if (DEBUG_STATES) Slog.v(TAG, "Before stopping, can hide: " + s);
- s.setVisibility(false);
- }
- if (remove) {
- final ActivityStack stack = s.getActivityStack();
- final boolean shouldSleepOrShutDown = stack != null
- ? stack.shouldSleepOrShutDownActivities()
- : mService.isSleepingOrShuttingDownLocked();
- if (!animating || shouldSleepOrShutDown) {
- if (!processPausingActivities && s.isState(PAUSING)) {
- // Defer processing pausing activities in this iteration and reschedule
- // a delayed idle to reprocess it again
- removeIdleTimeoutForActivity(idleActivity);
- scheduleIdleTimeoutLocked(idleActivity);
- continue;
- }
-
- if (DEBUG_STATES) Slog.v(TAG, "Ready to stop: " + s);
- if (stops == null) {
- stops = new ArrayList<>();
- }
- stops.add(s);
-
- mStoppingActivities.remove(activityNdx);
+ final int numReadyStops = readyToStopActivities == null ? 0 : readyToStopActivities.size();
+ for (int i = 0; i < numReadyStops; i++) {
+ final ActivityRecord r = readyToStopActivities.get(i);
+ if (r.isInHistory()) {
+ if (r.finishing) {
+ // TODO(b/137329632): Wait for idle of the right activity, not just any.
+ r.destroyIfPossible(reason);
+ } else {
+ r.stopIfPossible();
}
}
}
- return stops;
+ final int numFinishingActivities = mFinishingActivities.size();
+ if (numFinishingActivities == 0) {
+ return;
+ }
+
+ // Finish any activities that are scheduled to do so but have been waiting for the next one
+ // to start.
+ final ArrayList<ActivityRecord> finishingActivities = new ArrayList<>(mFinishingActivities);
+ mFinishingActivities.clear();
+ for (int i = 0; i < numFinishingActivities; i++) {
+ final ActivityRecord r = finishingActivities.get(i);
+ if (r.isInHistory()) {
+ r.destroyImmediately(true /* removeFromApp */, "finish-" + reason);
+ }
+ }
}
void removeHistoryRecords(WindowProcessController app) {
@@ -2319,14 +2271,13 @@
return printed;
}
- void scheduleIdleTimeoutLocked(ActivityRecord next) {
- if (DEBUG_IDLE) Slog.d(TAG_IDLE,
- "scheduleIdleTimeoutLocked: Callers=" + Debug.getCallers(4));
+ void scheduleIdleTimeout(ActivityRecord next) {
+ if (DEBUG_IDLE) Slog.d(TAG_IDLE, "scheduleIdleTimeout: Callers=" + Debug.getCallers(4));
Message msg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG, next);
mHandler.sendMessageDelayed(msg, IDLE_TIMEOUT);
}
- final void scheduleIdleLocked() {
+ final void scheduleIdle() {
mHandler.sendEmptyMessage(IDLE_NOW_MSG);
}
@@ -2409,6 +2360,12 @@
}
}
+ void scheduleProcessStoppingAndFinishingActivities() {
+ if (!mHandler.hasMessages(PROCESS_STOPPING_AND_FINISHING_MSG)) {
+ mHandler.sendEmptyMessage(PROCESS_STOPPING_AND_FINISHING_MSG);
+ }
+ }
+
void removeSleepTimeouts() {
mHandler.removeMessages(SLEEP_TIMEOUT_MSG);
}
@@ -2620,83 +2577,19 @@
private final class ActivityStackSupervisorHandler extends Handler {
- public ActivityStackSupervisorHandler(Looper looper) {
+ ActivityStackSupervisorHandler(Looper looper) {
super(looper);
}
- void activityIdleInternal(ActivityRecord r, boolean processPausingActivities) {
- synchronized (mService.mGlobalLock) {
- activityIdleInternalLocked(r != null ? r.appToken : null, true /* fromTimeout */,
- processPausingActivities, null);
- }
- }
-
@Override
public void handleMessage(Message msg) {
+ synchronized (mService.mGlobalLock) {
+ if (handleMessageInner(msg)) {
+ return;
+ }
+ }
+ // The cases that some invocations cannot be locked by WM.
switch (msg.what) {
- case REPORT_MULTI_WINDOW_MODE_CHANGED_MSG: {
- synchronized (mService.mGlobalLock) {
- for (int i = mMultiWindowModeChangedActivities.size() - 1; i >= 0; i--) {
- final ActivityRecord r = mMultiWindowModeChangedActivities.remove(i);
- r.updateMultiWindowMode();
- }
- }
- } break;
- case REPORT_PIP_MODE_CHANGED_MSG: {
- synchronized (mService.mGlobalLock) {
- for (int i = mPipModeChangedActivities.size() - 1; i >= 0; i--) {
- final ActivityRecord r = mPipModeChangedActivities.remove(i);
- r.updatePictureInPictureMode(mPipModeChangedTargetStackBounds,
- false /* forceUpdate */);
- }
- }
- } break;
- case IDLE_TIMEOUT_MSG: {
- if (DEBUG_IDLE) Slog.d(TAG_IDLE,
- "handleMessage: IDLE_TIMEOUT_MSG: r=" + msg.obj);
- // We don't at this point know if the activity is fullscreen,
- // so we need to be conservative and assume it isn't.
- activityIdleInternal((ActivityRecord) msg.obj,
- true /* processPausingActivities */);
- } break;
- case IDLE_NOW_MSG: {
- if (DEBUG_IDLE) Slog.d(TAG_IDLE, "handleMessage: IDLE_NOW_MSG: r=" + msg.obj);
- activityIdleInternal((ActivityRecord) msg.obj,
- false /* processPausingActivities */);
- } break;
- case RESUME_TOP_ACTIVITY_MSG: {
- synchronized (mService.mGlobalLock) {
- mRootWindowContainer.resumeFocusedStacksTopActivities();
- }
- } break;
- case SLEEP_TIMEOUT_MSG: {
- synchronized (mService.mGlobalLock) {
- if (mService.isSleepingOrShuttingDownLocked()) {
- Slog.w(TAG, "Sleep timeout! Sleeping now.");
- checkReadyForSleepLocked(false /* allowDelay */);
- }
- }
- } break;
- case LAUNCH_TIMEOUT_MSG: {
- synchronized (mService.mGlobalLock) {
- if (mLaunchingActivityWakeLock.isHeld()) {
- Slog.w(TAG, "Launch timeout has expired, giving up wake lock!");
- if (VALIDATE_WAKE_LOCK_CALLER
- && Binder.getCallingUid() != Process.myUid()) {
- throw new IllegalStateException("Calling must be system uid");
- }
- mLaunchingActivityWakeLock.release();
- }
- }
- } break;
- case LAUNCH_TASK_BEHIND_COMPLETE: {
- synchronized (mService.mGlobalLock) {
- ActivityRecord r = ActivityRecord.forTokenLocked((IBinder) msg.obj);
- if (r != null) {
- handleLaunchTaskBehindCompleteLocked(r);
- }
- }
- } break;
case RESTART_ACTIVITY_PROCESS_TIMEOUT_MSG: {
final ActivityRecord r = (ActivityRecord) msg.obj;
String processName = null;
@@ -2713,26 +2606,93 @@
"restartActivityProcessTimeout");
}
} break;
- case REPORT_HOME_CHANGED_MSG: {
- synchronized (mService.mGlobalLock) {
- mHandler.removeMessages(REPORT_HOME_CHANGED_MSG);
+ }
+ }
- // Start home activities on displays with no activities.
- mRootWindowContainer.startHomeOnEmptyDisplays("homeChanged");
+ private void activityIdleFromMessage(ActivityRecord idleActivity, boolean fromTimeout) {
+ activityIdleInternal(idleActivity, fromTimeout,
+ fromTimeout /* processPausingActivities */, null /* config */);
+ }
+
+ /**
+ * Handles the message with lock held.
+ *
+ * @return {@code true} if the message is handled.
+ */
+ private boolean handleMessageInner(Message msg) {
+ switch (msg.what) {
+ case REPORT_MULTI_WINDOW_MODE_CHANGED_MSG: {
+ for (int i = mMultiWindowModeChangedActivities.size() - 1; i >= 0; i--) {
+ final ActivityRecord r = mMultiWindowModeChangedActivities.remove(i);
+ r.updateMultiWindowMode();
}
} break;
- case TOP_RESUMED_STATE_LOSS_TIMEOUT_MSG: {
- ActivityRecord r = (ActivityRecord) msg.obj;
- Slog.w(TAG, "Activity top resumed state loss timeout for " + r);
- synchronized (mService.mGlobalLock) {
- if (r.hasProcess()) {
- mService.logAppTooSlow(r.app, r.topResumedStateLossTime,
- "top state loss for " + r);
+ case REPORT_PIP_MODE_CHANGED_MSG: {
+ for (int i = mPipModeChangedActivities.size() - 1; i >= 0; i--) {
+ final ActivityRecord r = mPipModeChangedActivities.remove(i);
+ r.updatePictureInPictureMode(mPipModeChangedTargetStackBounds,
+ false /* forceUpdate */);
+ }
+ } break;
+ case IDLE_TIMEOUT_MSG: {
+ if (DEBUG_IDLE) Slog.d(TAG_IDLE,
+ "handleMessage: IDLE_TIMEOUT_MSG: r=" + msg.obj);
+ // We don't at this point know if the activity is fullscreen, so we need to be
+ // conservative and assume it isn't.
+ activityIdleFromMessage((ActivityRecord) msg.obj, true /* fromTimeout */);
+ } break;
+ case IDLE_NOW_MSG: {
+ if (DEBUG_IDLE) Slog.d(TAG_IDLE, "handleMessage: IDLE_NOW_MSG: r=" + msg.obj);
+ activityIdleFromMessage((ActivityRecord) msg.obj, false /* fromTimeout */);
+ } break;
+ case RESUME_TOP_ACTIVITY_MSG: {
+ mRootWindowContainer.resumeFocusedStacksTopActivities();
+ } break;
+ case SLEEP_TIMEOUT_MSG: {
+ if (mService.isSleepingOrShuttingDownLocked()) {
+ Slog.w(TAG, "Sleep timeout! Sleeping now.");
+ checkReadyForSleepLocked(false /* allowDelay */);
+ }
+ } break;
+ case LAUNCH_TIMEOUT_MSG: {
+ if (mLaunchingActivityWakeLock.isHeld()) {
+ Slog.w(TAG, "Launch timeout has expired, giving up wake lock!");
+ if (VALIDATE_WAKE_LOCK_CALLER
+ && Binder.getCallingUid() != Process.myUid()) {
+ throw new IllegalStateException("Calling must be system uid");
}
+ mLaunchingActivityWakeLock.release();
+ }
+ } break;
+ case PROCESS_STOPPING_AND_FINISHING_MSG: {
+ processStoppingAndFinishingActivities(null /* launchedActivity */,
+ false /* processPausingActivities */, "transit");
+ } break;
+ case LAUNCH_TASK_BEHIND_COMPLETE: {
+ final ActivityRecord r = ActivityRecord.forTokenLocked((IBinder) msg.obj);
+ if (r != null) {
+ handleLaunchTaskBehindCompleteLocked(r);
+ }
+ } break;
+ case REPORT_HOME_CHANGED_MSG: {
+ mHandler.removeMessages(REPORT_HOME_CHANGED_MSG);
+
+ // Start home activities on displays with no activities.
+ mRootWindowContainer.startHomeOnEmptyDisplays("homeChanged");
+ } break;
+ case TOP_RESUMED_STATE_LOSS_TIMEOUT_MSG: {
+ final ActivityRecord r = (ActivityRecord) msg.obj;
+ Slog.w(TAG, "Activity top resumed state loss timeout for " + r);
+ if (r.hasProcess()) {
+ mService.logAppTooSlow(r.app, r.topResumedStateLossTime,
+ "top state loss for " + r);
}
handleTopResumedStateReleased(true /* timeout */);
} break;
+ default:
+ return false;
}
+ return true;
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 955e581..75d87ed 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -135,7 +135,7 @@
mHandler = new StartHandler(mService.mH.getLooper());
mFactory = factory;
mFactory.setController(this);
- mPendingRemoteAnimationRegistry = new PendingRemoteAnimationRegistry(service,
+ mPendingRemoteAnimationRegistry = new PendingRemoteAnimationRegistry(service.mGlobalLock,
service.mH);
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index c688612..61ba15c 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -29,8 +29,6 @@
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.WaitResult.LAUNCH_STATE_COLD;
import static android.app.WaitResult.LAUNCH_STATE_HOT;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
@@ -65,10 +63,8 @@
import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.ActivityStackSupervisor.TAG_TASKS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_FOCUS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
@@ -1488,11 +1484,6 @@
mIntent.setFlags(mLaunchFlags);
final Task reusedTask = getReusableTask();
- mSupervisor.getLaunchParamsController().calculate(reusedTask != null ? reusedTask : mInTask,
- r.info.windowLayout, r, sourceRecord, options, PHASE_BOUNDS, mLaunchParams);
- mPreferredDisplayId =
- mLaunchParams.hasPreferredDisplay() ? mLaunchParams.mPreferredDisplayId
- : DEFAULT_DISPLAY;
// If requested, freeze the task list
if (mOptions != null && mOptions.freezeRecentTasksReordering()
@@ -1506,6 +1497,8 @@
final Task targetTask = reusedTask != null ? reusedTask : computeTargetTask();
final boolean newTask = targetTask == null;
+ computeLaunchParams(r, sourceRecord, targetTask);
+
// Check if starting activity on given task or on a new task is allowed.
int startResult = isAllowedToStart(r, newTask, targetTask);
if (startResult != START_SUCCESS) {
@@ -1533,7 +1526,7 @@
}
if (mTargetStack == null) {
- mTargetStack = computeStackFocus(mStartActivity, true, mLaunchFlags, mOptions);
+ mTargetStack = getLaunchStack(mStartActivity, mLaunchFlags, targetTask, mOptions);
}
if (newTask) {
final Task taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
@@ -1623,15 +1616,49 @@
} else if (mInTask != null) {
return mInTask;
} else {
- final ActivityRecord top = computeStackFocus(mStartActivity, false /* newTask */,
- mLaunchFlags, mOptions).getTopNonFinishingActivity();
+ final ActivityStack stack = getLaunchStack(mStartActivity, mLaunchFlags,
+ null /* task */, mOptions);
+ final ActivityRecord top = stack.getTopNonFinishingActivity();
if (top != null) {
return top.getTask();
+ } else {
+ // Remove the stack if no activity in the stack.
+ stack.removeIfPossible();
}
}
return null;
}
+ private void computeLaunchParams(ActivityRecord r, ActivityRecord sourceRecord,
+ Task targetTask) {
+ final ActivityStack sourceStack = mSourceStack != null ? mSourceStack
+ : mRootWindowContainer.getTopDisplayFocusedStack();
+ if (sourceStack != null && sourceStack.inSplitScreenWindowingMode()
+ && (mOptions == null
+ || mOptions.getLaunchWindowingMode() == WINDOWING_MODE_UNDEFINED)) {
+ int windowingMode =
+ targetTask != null ? targetTask.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
+ if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
+ if (sourceStack.inSplitScreenPrimaryWindowingMode()) {
+ windowingMode = WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+ } else if (sourceStack.inSplitScreenSecondaryWindowingMode()) {
+ windowingMode = WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+ }
+ }
+
+ if (mOptions == null) {
+ mOptions = ActivityOptions.makeBasic();
+ }
+ mOptions.setLaunchWindowingMode(windowingMode);
+ }
+
+ mSupervisor.getLaunchParamsController().calculate(targetTask, r.info.windowLayout, r,
+ sourceRecord, mOptions, PHASE_BOUNDS, mLaunchParams);
+ mPreferredDisplayId =
+ mLaunchParams.hasPreferredDisplay() ? mLaunchParams.mPreferredDisplayId
+ : DEFAULT_DISPLAY;
+ }
+
private int isAllowedToStart(ActivityRecord r, boolean newTask, Task targetTask) {
if (mStartActivity.packageName == null) {
if (mStartActivity.resultTo != null) {
@@ -1870,8 +1897,8 @@
if (targetTask.getStack() == null) {
// Target stack got cleared when we all activities were removed above.
// Go ahead and reset it.
- mTargetStack = computeStackFocus(mSourceRecord, false /* newTask */,
- mLaunchFlags, mOptions);
+ mTargetStack =
+ getLaunchStack(mStartActivity, mLaunchFlags, null /* task */, mOptions);
mTargetStack.addChild(targetTask, !mLaunchTaskBehind /* toTop */,
(mStartActivity.info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);
}
@@ -2320,57 +2347,25 @@
final ActivityStack launchStack =
getLaunchStack(mStartActivity, mLaunchFlags, intentTask, mOptions);
if (launchStack == null || launchStack == mTargetStack) {
+ // Do not set mMovedToFront to true below for split-screen-top stack, or
+ // START_TASK_TO_FRONT will be returned and trigger unexpected animations when a
+ // new intent has delivered.
+ final boolean isSplitScreenTopStack = mTargetStack.isTopSplitScreenStack();
+
// We only want to move to the front, if we aren't going to launch on a
// different stack. If we launch on a different stack, we will put the
// task on top there.
+ // Defer resuming the top activity while moving task to top, since the
+ // current task-top activity may not be the activity that should be resumed.
mTargetStack.moveTaskToFrontLocked(intentTask, mNoAnimation, mOptions,
- mStartActivity.appTimeTracker, "bringingFoundTaskToFront");
- mMovedToFront = true;
- } else if (launchStack.inSplitScreenWindowingMode()) {
- if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
- // If we want to launch adjacent and mTargetStack is not the computed
- // launch stack - move task to top of computed stack.
- intentTask.reparent(launchStack, ON_TOP,
- REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
- "launchToSide");
- } else {
- // TODO: This should be reevaluated in MW v2.
- // We choose to move task to front instead of launching it adjacent
- // when specific stack was requested explicitly and it appeared to be
- // adjacent stack, but FLAG_ACTIVITY_LAUNCH_ADJACENT was not set.
- mTargetStack.moveTaskToFrontLocked(intentTask,
- mNoAnimation, mOptions, mStartActivity.appTimeTracker,
- "bringToFrontInsteadOfAdjacentLaunch");
- }
- mMovedToFront = launchStack != launchStack.getDisplay()
- .getTopStackInWindowingMode(launchStack.getWindowingMode());
- } else if (launchStack.getDisplayId() != mTargetStack.getDisplayId()) {
- // Target and computed stacks are on different displays and we've
- // found a matching task - move the existing instance to that display and
- // move it to front.
- intentActivity.getTask().reparent(launchStack, ON_TOP,
- REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
- "reparentToDisplay");
- mMovedToFront = true;
- } else if (launchStack.isActivityTypeHome()
- && !mTargetStack.isActivityTypeHome()) {
- // It is possible for the home activity to be in another stack initially.
- // For example, the activity may have been initially started with an intent
- // which placed it in the fullscreen stack. To ensure the proper handling of
- // the activity based on home stack assumptions, we must move it over.
- intentActivity.getTask().reparent(launchStack, ON_TOP,
- REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
- "reparentingHome");
+ mStartActivity.appTimeTracker, DEFER_RESUME,
+ "bringingFoundTaskToFront");
+ mMovedToFront = !isSplitScreenTopStack;
+ } else {
+ intentTask.reparent(launchStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, ANIMATE,
+ DEFER_RESUME, "reparentToTargetStack");
mMovedToFront = true;
}
-
- if (launchStack != null && launchStack.getTopMostTask() == null) {
- // The task does not need to be reparented to the launch stack. Remove the
- // launch stack if there is no activity in it.
- Slog.w(TAG, "Removing an empty stack: " + launchStack);
- launchStack.removeIfPossible();
- }
-
mOptions = null;
}
}
@@ -2470,90 +2465,6 @@
return launchFlags;
}
- private ActivityStack computeStackFocus(ActivityRecord r, boolean newTask, int launchFlags,
- ActivityOptions aOptions) {
- final Task task = r.getTask();
- ActivityStack stack = getLaunchStack(r, launchFlags, task, aOptions);
- if (stack != null) {
- return stack;
- }
-
- final ActivityStack currentStack = task != null ? task.getStack() : null;
- final ActivityStack focusedStack = mRootWindowContainer.getTopDisplayFocusedStack();
- if (currentStack != null) {
- if (focusedStack != currentStack) {
- if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
- "computeStackFocus: Setting " + "focused stack to r=" + r
- + " task=" + task);
- } else {
- if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
- "computeStackFocus: Focused stack already=" + focusedStack);
- }
- return currentStack;
- }
-
- if (canLaunchIntoFocusedStack(r, newTask)) {
- if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
- "computeStackFocus: Have a focused stack=" + focusedStack);
- return focusedStack;
- }
-
- if (mPreferredDisplayId != DEFAULT_DISPLAY) {
- // Try to put the activity in a stack on a secondary display.
- stack = mRootWindowContainer.getValidLaunchStackOnDisplay(
- mPreferredDisplayId, r, aOptions, mLaunchParams);
- if (stack == null) {
- // If source display is not suitable - look for topmost valid stack in the system.
- if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
- "computeStackFocus: Can't launch on mPreferredDisplayId="
- + mPreferredDisplayId + ", looking on all displays.");
- stack = mRootWindowContainer.getNextValidLaunchStack(r, mPreferredDisplayId);
- }
- }
- if (stack == null) {
- stack = mRootWindowContainer.getLaunchStack(r, aOptions, task, ON_TOP);
- }
- if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: New stack r="
- + r + " stackId=" + stack.mStackId);
- return stack;
- }
-
- /** Check if provided activity record can launch in currently focused stack. */
- // TODO: This method can probably be consolidated into getLaunchStack() below.
- private boolean canLaunchIntoFocusedStack(ActivityRecord r, boolean newTask) {
- final ActivityStack focusedStack = mRootWindowContainer.getTopDisplayFocusedStack();
- final boolean canUseFocusedStack;
- if (focusedStack.isActivityTypeAssistant()) {
- canUseFocusedStack = r.isActivityTypeAssistant();
- } else {
- switch (focusedStack.getWindowingMode()) {
- case WINDOWING_MODE_FULLSCREEN:
- // The fullscreen stack can contain any task regardless of if the task is
- // resizeable or not. So, we let the task go in the fullscreen task if it is the
- // focus stack.
- canUseFocusedStack = true;
- break;
- case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
- case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY:
- // Any activity which supports split screen can go in the docked stack.
- canUseFocusedStack = r.supportsSplitScreenWindowingMode();
- break;
- case WINDOWING_MODE_FREEFORM:
- // Any activity which supports freeform can go in the freeform stack.
- canUseFocusedStack = r.supportsFreeform();
- break;
- default:
- // Dynamic stacks behave similarly to the fullscreen stack and can contain any
- // resizeable task.
- canUseFocusedStack = !focusedStack.isOnHomeDisplay()
- && r.canBeLaunchedOnDisplay(focusedStack.getDisplayId());
- }
- }
- return canUseFocusedStack && !newTask
- // Using the focus stack isn't important enough to override the preferred display.
- && (mPreferredDisplayId == focusedStack.getDisplayId());
- }
-
private ActivityStack getLaunchStack(ActivityRecord r, int launchFlags, Task task,
ActivityOptions aOptions) {
// We are reusing a task, keep the stack!
@@ -2561,53 +2472,10 @@
return mReuseTask.getStack();
}
- if (((launchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) == 0)
- || mPreferredDisplayId != DEFAULT_DISPLAY) {
- final boolean onTop =
- (aOptions == null || !aOptions.getAvoidMoveToFront()) && !mLaunchTaskBehind;
- final ActivityStack stack =
- mRootWindowContainer.getLaunchStack(r, aOptions, task, onTop, mLaunchParams,
- mRequest.realCallingPid, mRequest.realCallingUid);
- return stack;
- }
- // Otherwise handle adjacent launch.
-
- final ActivityStack focusedStack = mRootWindowContainer.getTopDisplayFocusedStack();
- // The parent activity doesn't want to launch the activity on top of itself, but
- // instead tries to put it onto other side in side-by-side mode.
- final ActivityStack parentStack = task != null ? task.getStack(): focusedStack;
-
- if (parentStack != focusedStack) {
- // If task's parent stack is not focused - use it during adjacent launch.
- return parentStack;
- } else {
- if (focusedStack != null && task == focusedStack.getTopMostTask()) {
- // If task is already on top of focused stack - use it. We don't want to move the
- // existing focused task to adjacent stack, just deliver new intent in this case.
- return focusedStack;
- }
-
- if (parentStack != null && parentStack.inSplitScreenPrimaryWindowingMode()) {
- // If parent was in docked stack, the natural place to launch another activity
- // will be fullscreen, so it can appear alongside the docked window.
- final int activityType =
- mRootWindowContainer.resolveActivityType(r, mOptions, task);
- return parentStack.getDisplay().getOrCreateStack(
- WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, activityType, ON_TOP);
- } else {
- // If the parent is not in the docked stack, we check if there is docked window
- // and if yes, we will launch into that stack. If not, we just put the new
- // activity into parent's stack, because we can't find a better place.
- final ActivityStack dockedStack =
- mRootWindowContainer.getDefaultDisplay().getSplitScreenPrimaryStack();
- if (dockedStack != null && !dockedStack.shouldBeVisible(r)) {
- // There is a docked stack, but it isn't visible, so we can't launch into that.
- return mRootWindowContainer.getLaunchStack(r, aOptions, task, ON_TOP);
- } else {
- return dockedStack;
- }
- }
- }
+ final boolean onTop =
+ (aOptions == null || !aOptions.getAvoidMoveToFront()) && !mLaunchTaskBehind;
+ return mRootWindowContainer.getLaunchStack(r, aOptions, task, onTop, mLaunchParams,
+ mRequest.realCallingPid, mRequest.realCallingUid);
}
private boolean isLaunchModeOneOf(int mode1, int mode2) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 374af5a..76c0e4e 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1685,20 +1685,16 @@
public final void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
final long origId = Binder.clearCallingIdentity();
try {
- WindowProcessController proc = null;
synchronized (mGlobalLock) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityIdle");
- ActivityStack stack = ActivityRecord.getStackLocked(token);
- if (stack == null) {
+ final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+ if (r == null) {
return;
}
- final ActivityRecord r = mStackSupervisor.activityIdleInternalLocked(token,
- false /* fromTimeout */, false /* processPausingActivities */, config);
- if (r != null) {
- proc = r.app;
- }
- if (stopProfiling && proc != null) {
- proc.clearProfilerIfNeeded();
+ mStackSupervisor.activityIdleInternal(r, false /* fromTimeout */,
+ false /* processPausingActivities */, config);
+ if (stopProfiling && r.hasProcess()) {
+ r.app.clearProfilerIfNeeded();
}
}
} finally {
@@ -6905,7 +6901,7 @@
if (mRootWindowContainer.finishDisabledPackageActivities(
packageName, disabledClasses, true, false, userId) && booted) {
mRootWindowContainer.resumeFocusedStacksTopActivities();
- mStackSupervisor.scheduleIdleLocked();
+ mStackSupervisor.scheduleIdle();
}
// Clean-up disabled tasks
@@ -6932,7 +6928,7 @@
synchronized (mGlobalLock) {
mRootWindowContainer.resumeFocusedStacksTopActivities();
if (scheduleIdle) {
- mStackSupervisor.scheduleIdleLocked();
+ mStackSupervisor.scheduleIdle();
}
}
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 3a33a3d..09111d0 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -436,9 +436,11 @@
mNextAppTransition = TRANSIT_UNSET;
mNextAppTransitionFlags = 0;
setAppTransitionState(APP_STATE_RUNNING);
- final AnimationAdapter topOpeningAnim = topOpeningApp != null
- ? topOpeningApp.getAnimation()
- : null;
+ final AnimationAdapter topOpeningAnim =
+ (topOpeningApp != null && topOpeningApp.getAnimatingContainer() != null)
+ ? topOpeningApp.getAnimatingContainer().getAnimation()
+ : null;
+
int redoLayout = notifyAppTransitionStartingLocked(transit,
topOpeningAnim != null ? topOpeningAnim.getDurationHint() : 0,
topOpeningAnim != null
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 896147e..ba9d757 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -205,6 +205,7 @@
import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.Gravity;
+import android.view.IDisplayWindowInsetsController;
import android.view.ISystemGestureExclusionListener;
import android.view.IWindow;
import android.view.InputChannel;
@@ -218,6 +219,7 @@
import android.view.SurfaceControl.Transaction;
import android.view.SurfaceSession;
import android.view.View;
+import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
@@ -230,7 +232,6 @@
import com.android.internal.util.function.pooled.PooledFunction;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.internal.util.function.pooled.PooledPredicate;
-import com.android.server.AnimationThread;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.protolog.common.ProtoLog;
import com.android.server.wm.utils.DisplayRotationUtil;
@@ -571,6 +572,8 @@
*/
WindowState mInputMethodTarget;
+ InsetsControlTarget mInputMethodControlTarget;
+
/** If true hold off on modifying the animation layer of mInputMethodTarget */
boolean mInputMethodTargetWaitingAnim;
@@ -599,6 +602,13 @@
private final float mWindowCornerRadius;
private final SparseArray<ShellRoot> mShellRoots = new SparseArray<>();
+ RemoteInsetsControlTarget mRemoteInsetsControlTarget = null;
+ private final IBinder.DeathRecipient mRemoteInsetsDeath =
+ () -> {
+ synchronized (mWmService.mGlobalLock) {
+ mRemoteInsetsControlTarget = null;
+ }
+ };
private RootWindowContainer mRootWindowContainer;
@@ -773,7 +783,11 @@
// If this is the first layout, we need to initialize the last frames and inset values,
// as otherwise we'd immediately cause an unnecessary resize.
if (firstLayout) {
- w.updateLastFrames();
+ // The client may compute its actual requested size according to the first layout,
+ // so we still request the window to resize if the current frame is empty.
+ if (!w.getFrameLw().isEmpty()) {
+ w.updateLastFrames();
+ }
w.updateLastInsetValues();
w.updateLocationInParentDisplayIfNeeded();
}
@@ -977,7 +991,7 @@
AnimationHandler animationHandler = new AnimationHandler();
mBoundsAnimationController = new BoundsAnimationController(mWmService.mContext,
- mAppTransition, AnimationThread.getHandler(), animationHandler);
+ mAppTransition, mWmService.mAnimationHandler, animationHandler);
final InputChannel inputChannel = mWmService.mInputManager.monitorInput(
"PointerEventDispatcher" + mDisplayId, mDisplayId);
@@ -1033,31 +1047,13 @@
// Sets the display content for the children.
onDisplayChanged(this);
- // Add itself as a child to the root container.
- mWmService.mRoot.addChild(this, POSITION_BOTTOM);
-
- // TODO(b/62541591): evaluate whether this is the best spot to declare the
- // {@link DisplayContent} ready for use.
- mDisplayReady = true;
-
- mWmService.mAnimator.addDisplayLocked(mDisplayId);
- mInputMonitor = new InputMonitor(mWmService, mDisplayId);
+ mInputMonitor = new InputMonitor(mWmService, this);
mInsetsStateController = new InsetsStateController(this);
mInsetsPolicy = new InsetsPolicy(mInsetsStateController, this);
- if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Adding display=" + display);
+ if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Creating display=" + display);
mWmService.mDisplayWindowSettings.applySettingsToDisplayLocked(this);
-
- if (mWmService.mDisplayManagerInternal != null) {
- mWmService.mDisplayManagerInternal
- .setDisplayInfoOverrideFromWindowManager(mDisplayId, getDisplayInfo());
- configureDisplayPolicy();
- }
-
- reconfigureDisplayLocked();
- onRequestedOverrideConfigurationChanged(getRequestedOverrideConfiguration());
- mWmService.mDisplayNotificationController.dispatchDisplayAdded(this);
}
boolean isReady() {
@@ -1171,6 +1167,22 @@
mShellRoots.remove(windowType);
}
+ void setRemoteInsetsController(IDisplayWindowInsetsController controller) {
+ if (mRemoteInsetsControlTarget != null) {
+ mRemoteInsetsControlTarget.mRemoteInsetsController.asBinder().unlinkToDeath(
+ mRemoteInsetsDeath, 0);
+ mRemoteInsetsControlTarget = null;
+ }
+ if (controller != null) {
+ try {
+ controller.asBinder().linkToDeath(mRemoteInsetsDeath, 0);
+ mRemoteInsetsControlTarget = new RemoteInsetsControlTarget(controller);
+ } catch (RemoteException e) {
+ return;
+ }
+ }
+ }
+
/** Changes the display the input window token is housed on to this one. */
void reParentWindowToken(WindowToken token) {
final DisplayContent prevDc = token.getDisplayContent();
@@ -3361,7 +3373,7 @@
// to look at all windows below the current target that are in this app, finding the
// highest visible one in layering.
WindowState highestTarget = null;
- if (activity.isAnimating(TRANSITION)) {
+ if (activity.isAnimating(PARENTS | TRANSITION)) {
highestTarget = activity.getHighestAnimLayerWindow(curTarget);
}
@@ -3398,6 +3410,14 @@
}
}
+ boolean isImeAttachedToApp() {
+ return (mInputMethodTarget != null && mInputMethodTarget.mActivityRecord != null
+ && mInputMethodTarget.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
+ // An activity with override bounds should be letterboxed inside its parent bounds,
+ // so it doesn't fill the screen.
+ && mInputMethodTarget.mActivityRecord.matchParentBounds());
+ }
+
private void setInputMethodTarget(WindowState target, boolean targetWaitingAnim) {
if (target == mInputMethodTarget && mInputMethodTargetWaitingAnim == targetWaitingAnim) {
return;
@@ -3406,7 +3426,8 @@
mInputMethodTarget = target;
mInputMethodTargetWaitingAnim = targetWaitingAnim;
assignWindowLayers(false /* setLayoutNeeded */);
- mInsetsStateController.onImeTargetChanged(target);
+ mInputMethodControlTarget = computeImeControlTarget();
+ mInsetsStateController.onImeTargetChanged(mInputMethodControlTarget);
updateImeParent();
}
@@ -3431,11 +3452,7 @@
// Attach it to app if the target is part of an app and such app is covering the entire
// screen. If it's not covering the entire screen the IME might extend beyond the apps
// bounds.
- if (mInputMethodTarget != null && mInputMethodTarget.mActivityRecord != null
- && mInputMethodTarget.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
- // An activity with override bounds should be letterboxed inside its parent bounds,
- // so it doesn't fill the screen.
- && mInputMethodTarget.mActivityRecord.matchParentBounds()) {
+ if (isImeAttachedToApp()) {
return mInputMethodTarget.mActivityRecord.getSurfaceControl();
}
@@ -3443,6 +3460,19 @@
return mWindowContainers.getSurfaceControl();
}
+ /**
+ * Computes which control-target the IME should be attached to.
+ */
+ @VisibleForTesting
+ InsetsControlTarget computeImeControlTarget() {
+ if (!isImeAttachedToApp() && mRemoteInsetsControlTarget != null) {
+ return mRemoteInsetsControlTarget;
+ }
+
+ // Otherwise, we just use the ime target
+ return mInputMethodTarget;
+ }
+
void setLayoutNeeded() {
if (DEBUG_LAYOUT) Slog.w(TAG_WM, "setLayoutNeeded: callers=" + Debug.getCallers(3));
mLayoutNeeded = true;
@@ -4803,10 +4833,8 @@
} else {
final int count = mChildren.size();
for (int i = 0; i < count; i++) {
- Slog.d(TAG, "child " + mChildren.get(i));
final WindowContainer child = mChildren.get(i);
if (skipTraverseChild(child)) {
- Slog.d(TAG, "child skipped");
continue;
}
@@ -4995,6 +5023,24 @@
// we create the root surfaces explicitly rather than chaining
// up as the default implementation in onParentChanged does. So we
// explicitly do NOT call super here.
+
+ if (!isReady()) {
+ // TODO(b/62541591): evaluate whether this is the best spot to declare the
+ // {@link DisplayContent} ready for use.
+ mDisplayReady = true;
+
+ mWmService.mAnimator.addDisplayLocked(mDisplayId);
+
+ if (mWmService.mDisplayManagerInternal != null) {
+ mWmService.mDisplayManagerInternal
+ .setDisplayInfoOverrideFromWindowManager(mDisplayId, getDisplayInfo());
+ configureDisplayPolicy();
+ }
+
+ reconfigureDisplayLocked();
+ onRequestedOverrideConfigurationChanged(getRequestedOverrideConfiguration());
+ mWmService.mDisplayNotificationController.dispatchDisplayAdded(this);
+ }
}
@Override
@@ -6687,4 +6733,50 @@
Context getDisplayUiContext() {
return mDisplayPolicy.getSystemUiContext();
}
+
+ class RemoteInsetsControlTarget implements InsetsControlTarget {
+ private final IDisplayWindowInsetsController mRemoteInsetsController;
+
+ RemoteInsetsControlTarget(IDisplayWindowInsetsController controller) {
+ mRemoteInsetsController = controller;
+ }
+
+ void notifyInsetsChanged() {
+ try {
+ mRemoteInsetsController.insetsChanged(
+ getInsetsStateController().getRawInsetsState());
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to deliver inset state change", e);
+ }
+ }
+
+ @Override
+ public void notifyInsetsControlChanged() {
+ final InsetsStateController stateController = getInsetsStateController();
+ try {
+ mRemoteInsetsController.insetsControlChanged(stateController.getRawInsetsState(),
+ stateController.getControlsForDispatch(this));
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to deliver inset state change", e);
+ }
+ }
+
+ @Override
+ public void showInsets(@WindowInsets.Type.InsetsType int types, boolean fromIme) {
+ try {
+ mRemoteInsetsController.showInsets(types, fromIme);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to deliver showInsets", e);
+ }
+ }
+
+ @Override
+ public void hideInsets(@WindowInsets.Type.InsetsType int types, boolean fromIme) {
+ try {
+ mRemoteInsetsController.hideInsets(types, fromIme);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to deliver showInsets", e);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 694a73d..f9ad03f 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -2192,10 +2192,13 @@
final boolean attachedInParent = attached != null && !layoutInScreen;
final boolean requestedFullscreen = (fl & FLAG_FULLSCREEN) != 0
|| (requestedSysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0
- || !win.getClientInsetsState().getSource(ITYPE_STATUS_BAR).isVisible();
+ || (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL
+ && !win.getRequestedInsetsState().getSource(ITYPE_STATUS_BAR).isVisible());
final boolean requestedHideNavigation =
(requestedSysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
- || !win.getClientInsetsState().getSource(ITYPE_NAVIGATION_BAR).isVisible();
+ || (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL
+ && !win.getRequestedInsetsState().getSource(ITYPE_NAVIGATION_BAR)
+ .isVisible());
// TYPE_BASE_APPLICATION windows are never considered floating here because they don't get
// cropped / shifted to the displayFrame in WindowState.
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index e74f61d..55f5e28 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -156,7 +156,8 @@
// determined individually unlike other stacks where the visibility or fullscreen
// status of an activity in a previous task affects other.
mBehindFullscreenActivity = !mContainerShouldBeVisible;
- } else if (mContiner.isActivityTypeHome()) {
+ } else if (!mBehindFullscreenActivity && mContiner.isActivityTypeHome()
+ && r.isRootOfTask()) {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Home task: at " + mContiner
+ " stackShouldBeVisible=" + mContainerShouldBeVisible
+ " behindFullscreenActivity=" + mBehindFullscreenActivity);
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 05ede21..fb97ecf 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -69,7 +69,7 @@
mShowImeRunner = () -> {
// Target should still be the same.
if (isImeTargetFromDisplayContentAndImeSame()) {
- mDisplayContent.mInputMethodTarget.showInsets(
+ mDisplayContent.mInputMethodControlTarget.showInsets(
WindowInsets.Type.ime(), true /* fromIme */);
}
abortShowImePostLayout();
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 1f9f883..5b892f8 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -10,23 +10,40 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.H.ON_POINTER_DOWN_OUTSIDE_FOCUS;
+import android.os.Build;
import android.os.Debug;
import android.os.IBinder;
+import android.os.Process;
import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.ArrayMap;
import android.util.Slog;
import android.view.IWindow;
import android.view.InputApplicationHandle;
import android.view.KeyEvent;
import android.view.WindowManager;
+import com.android.server.am.ActivityManagerService;
import com.android.server.input.InputManagerService;
import com.android.server.wm.EmbeddedWindowController.EmbeddedWindow;
+import java.io.File;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
final class InputManagerCallback implements InputManagerService.WindowManagerCallbacks {
private static final String TAG = TAG_WITH_CLASS_NAME ? "InputManagerCallback" : TAG_WM;
+
+ /** Prevent spamming the traces because pre-dump cannot aware duplicated ANR. */
+ private static final long PRE_DUMP_MIN_INTERVAL_MS = TimeUnit.SECONDS.toMillis(20);
+ /** The timeout to detect if a monitor is held for a while. */
+ private static final long PRE_DUMP_MONITOR_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(1);
+ /** The last time pre-dump was executed. */
+ private volatile long mLastPreDumpTimeMs;
+
private final WindowManagerService mService;
// Set to true when the first input device configuration change notification
@@ -77,6 +94,77 @@
}
/**
+ * Pre-dump stack trace if the locks of activity manager or window manager (they may be locked
+ * in the path of reporting ANR) cannot be acquired in time. That provides the stack traces
+ * before the real blocking symptom has gone.
+ * <p>
+ * Do not hold the {@link WindowManagerGlobalLock} while calling this method.
+ */
+ private void preDumpIfLockTooSlow() {
+ if (!Build.IS_DEBUGGABLE) {
+ return;
+ }
+ final long now = SystemClock.uptimeMillis();
+ if (mLastPreDumpTimeMs > 0 && now - mLastPreDumpTimeMs < PRE_DUMP_MIN_INTERVAL_MS) {
+ return;
+ }
+
+ final boolean[] shouldDumpSf = { true };
+ final ArrayMap<String, Runnable> monitors = new ArrayMap<>(2);
+ monitors.put(TAG_WM, mService::monitor);
+ monitors.put("ActivityManager", mService.mAmInternal::monitor);
+ final CountDownLatch latch = new CountDownLatch(monitors.size());
+ // The pre-dump will execute if one of the monitors doesn't complete within the timeout.
+ for (int i = 0; i < monitors.size(); i++) {
+ final String name = monitors.keyAt(i);
+ final Runnable monitor = monitors.valueAt(i);
+ // Always create new thread to avoid noise of existing threads. Suppose here won't
+ // create too many threads because it means that watchdog will be triggered first.
+ new Thread() {
+ @Override
+ public void run() {
+ monitor.run();
+ latch.countDown();
+ final long elapsed = SystemClock.uptimeMillis() - now;
+ if (elapsed > PRE_DUMP_MONITOR_TIMEOUT_MS) {
+ Slog.i(TAG_WM, "Pre-dump acquired " + name + " in " + elapsed + "ms");
+ } else if (TAG_WM.equals(name)) {
+ // Window manager is the main client of SurfaceFlinger. If window manager
+ // is responsive, the stack traces of SurfaceFlinger may not be important.
+ shouldDumpSf[0] = false;
+ }
+ };
+ }.start();
+ }
+ try {
+ if (latch.await(PRE_DUMP_MONITOR_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ return;
+ }
+ } catch (InterruptedException ignored) { }
+ mLastPreDumpTimeMs = now;
+ Slog.i(TAG_WM, "Pre-dump for unresponsive");
+
+ final ArrayList<Integer> firstPids = new ArrayList<>(1);
+ firstPids.add(ActivityManagerService.MY_PID);
+ ArrayList<Integer> nativePids = null;
+ final int[] pids = shouldDumpSf[0]
+ ? Process.getPidsForCommands(new String[] { "/system/bin/surfaceflinger" })
+ : null;
+ if (pids != null) {
+ nativePids = new ArrayList<>(1);
+ for (int pid : pids) {
+ nativePids.add(pid);
+ }
+ }
+
+ final File tracesFile = ActivityManagerService.dumpStackTraces(firstPids,
+ null /* processCpuTracker */, null /* lastPids */, nativePids);
+ if (tracesFile != null) {
+ tracesFile.renameTo(new File(tracesFile.getParent(), tracesFile.getName() + "_pre"));
+ }
+ }
+
+ /**
* Notifies the window manager about an application that is not responding.
* Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
*
@@ -89,6 +177,9 @@
WindowState windowState = null;
boolean aboveSystem = false;
int windowPid = INVALID_PID;
+
+ preDumpIfLockTooSlow();
+
//TODO(b/141764879) Limit scope of wm lock when input calls notifyANR
synchronized (mService.mGlobalLock) {
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index b9a9c12..c4b67d7 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -47,7 +47,6 @@
import android.view.InputWindowHandle;
import android.view.SurfaceControl;
-import com.android.server.AnimationThread;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.protolog.common.ProtoLog;
@@ -152,12 +151,12 @@
private final UpdateInputWindows mUpdateInputWindows = new UpdateInputWindows();
- public InputMonitor(WindowManagerService service, int displayId) {
+ InputMonitor(WindowManagerService service, DisplayContent displayContent) {
mService = service;
- mDisplayContent = mService.mRoot.getDisplayContent(displayId);
- mDisplayId = displayId;
+ mDisplayContent = displayContent;
+ mDisplayId = displayContent.getDisplayId();
mInputTransaction = mService.mTransactionFactory.get();
- mHandler = AnimationThread.getHandler();
+ mHandler = mService.mAnimationHandler;
mUpdateInputForAllWindowsConsumer = new UpdateInputForAllWindowsConsumer();
}
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index af84836..a008963 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -66,10 +66,11 @@
}
mStatusBar.setVisible(focusedWin == null
|| focusedWin != getStatusControlTarget(focusedWin)
- || focusedWin.getClientInsetsState().getSource(ITYPE_STATUS_BAR).isVisible());
+ || focusedWin.getRequestedInsetsState().getSource(ITYPE_STATUS_BAR).isVisible());
mNavBar.setVisible(focusedWin == null
|| focusedWin != getNavControlTarget(focusedWin)
- || focusedWin.getClientInsetsState().getSource(ITYPE_NAVIGATION_BAR).isVisible());
+ || focusedWin.getRequestedInsetsState().getSource(ITYPE_NAVIGATION_BAR)
+ .isVisible());
}
boolean isHidden(@InternalInsetsType int type) {
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 184e7d6..7b40f60 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -210,7 +210,7 @@
new Point(mWin.getWindowFrames().mFrame.left, mWin.getWindowFrames().mFrame.top));
}
- boolean onInsetsModified(WindowState caller, InsetsSource modifiedSource) {
+ boolean onInsetsModified(InsetsControlTarget caller, InsetsSource modifiedSource) {
if (mControlTarget != caller || modifiedSource.isVisible() == mClientVisible) {
return false;
}
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 1526074..720493f 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -91,6 +91,10 @@
return state;
}
+ InsetsState getRawInsetsState() {
+ return mState;
+ }
+
@Nullable InsetsSourceControl[] getControlsForDispatch(InsetsControlTarget target) {
ArrayList<Integer> controlled = mControlTargetTypeMap.get(target);
if (controlled == null) {
@@ -144,7 +148,7 @@
getImeSourceProvider().onPostInsetsDispatched();
}
- void onInsetsModified(WindowState windowState, InsetsState state) {
+ void onInsetsModified(InsetsControlTarget windowState, InsetsState state) {
boolean changed = false;
for (int i = state.getSourcesCount() - 1; i >= 0; i--) {
final InsetsSource source = state.sourceAt(i);
@@ -296,6 +300,9 @@
void notifyInsetsChanged() {
mDisplayContent.forAllWindows(mDispatchInsetsChanged, true /* traverseTopToBottom */);
+ if (mDisplayContent.mRemoteInsetsControlTarget != null) {
+ mDisplayContent.mRemoteInsetsControlTarget.notifyInsetsChanged();
+ }
}
void dump(String prefix, PrintWriter pw) {
diff --git a/services/core/java/com/android/server/wm/PendingRemoteAnimationRegistry.java b/services/core/java/com/android/server/wm/PendingRemoteAnimationRegistry.java
index dcb9a6a..3b8631a 100644
--- a/services/core/java/com/android/server/wm/PendingRemoteAnimationRegistry.java
+++ b/services/core/java/com/android/server/wm/PendingRemoteAnimationRegistry.java
@@ -22,12 +22,10 @@
import android.util.ArrayMap;
import android.view.RemoteAnimationAdapter;
-import com.android.server.am.ActivityManagerService;
-
/**
* Registry to keep track of remote animations to be run for activity starts from a certain package.
*
- * @see ActivityManagerService#registerRemoteAnimationForNextActivityStart
+ * @see ActivityTaskManagerService#registerRemoteAnimationForNextActivityStart
*/
class PendingRemoteAnimationRegistry {
@@ -35,10 +33,10 @@
private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();
private final Handler mHandler;
- private final ActivityTaskManagerService mService;
+ private final WindowManagerGlobalLock mLock;
- PendingRemoteAnimationRegistry(ActivityTaskManagerService service, Handler handler) {
- mService = service;
+ PendingRemoteAnimationRegistry(WindowManagerGlobalLock lock, Handler handler) {
+ mLock = lock;
mHandler = handler;
}
@@ -76,7 +74,7 @@
this.packageName = packageName;
this.adapter = adapter;
mHandler.postDelayed(() -> {
- synchronized (mService.mGlobalLock) {
+ synchronized (mLock) {
final Entry entry = mEntries.get(packageName);
if (entry == this) {
mEntries.remove(packageName);
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 0b9be1a..6f7eeab 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -378,9 +378,10 @@
int getMode() {
final DisplayContent dc = mWindowContainer.getDisplayContent();
- if (dc.mOpeningApps.contains(mWindowContainer)) {
+ final ActivityRecord topActivity = mWindowContainer.getTopMostActivity();
+ if (dc.mOpeningApps.contains(topActivity)) {
return RemoteAnimationTarget.MODE_OPENING;
- } else if (dc.mChangingApps.contains(mWindowContainer)) {
+ } else if (dc.mChangingApps.contains(topActivity)) {
return RemoteAnimationTarget.MODE_CHANGING;
} else {
return RemoteAnimationTarget.MODE_CLOSING;
diff --git a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
index 53d7688..e310fc1 100644
--- a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
+++ b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
@@ -37,8 +37,8 @@
/** Helper class for processing the reset of a task. */
class ResetTargetTaskHelper {
private Task mTask;
- private ActivityStack mParent;
private Task mTargetTask;
+ private ActivityStack mTargetStack;
private ActivityRecord mRoot;
private boolean mForceReset;
private boolean mCanMoveOptions;
@@ -47,7 +47,7 @@
private ActivityOptions mTopOptions;
private ArrayList<ActivityRecord> mResultActivities = new ArrayList<>();
private ArrayList<ActivityRecord> mAllActivities = new ArrayList<>();
- private ArrayList<Task> mCreatedTasks = new ArrayList<>();
+ private ArrayList<ActivityRecord> mPendingReparentActivities = new ArrayList<>();
private void reset(Task task) {
mTask = task;
@@ -56,23 +56,22 @@
mTopOptions = null;
mResultActivities.clear();
mAllActivities.clear();
- mCreatedTasks.clear();
}
- ActivityOptions process(ActivityStack parent, Task targetTask, boolean forceReset) {
- mParent = parent;
+ ActivityOptions process(Task targetTask, boolean forceReset) {
mForceReset = forceReset;
mTargetTask = targetTask;
mTargetTaskFound = false;
+ mTargetStack = targetTask.getStack();
mActivityReparentPosition = -1;
final PooledConsumer c = PooledLambda.obtainConsumer(
ResetTargetTaskHelper::processTask, this, PooledLambda.__(Task.class));
- parent.forAllTasks(c);
+ targetTask.mWmService.mRoot.forAllTasks(c);
c.recycle();
+ processPendingReparentActivities();
reset(null);
- mParent = null;
return mTopOptions;
}
@@ -89,8 +88,6 @@
PooledLambda.__(ActivityRecord.class), isTargetTask);
task.forAllActivities(f);
f.recycle();
-
- processCreatedTasks();
}
private boolean processActivity(ActivityRecord r, boolean isTargetTask) {
@@ -122,32 +119,9 @@
// it out of here. We will move it as far out of the way as possible, to the
// bottom of the activity stack. This also keeps it correctly ordered with
// any activities we previously moved.
- // TODO: We should probably look for other stacks also, since corresponding
- // task with the same affinity is unlikely to be in the same stack.
- final Task targetTask;
- final ActivityRecord bottom = mParent.getBottomMostActivity();
- if (bottom != null && r.taskAffinity.equals(bottom.getTask().affinity)) {
- // If the activity currently at the bottom has the same task affinity as
- // the one we are moving, then merge it into the same task.
- targetTask = bottom.getTask();
- if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity "
- + r + " out to bottom task " + targetTask);
- } else {
- targetTask = mParent.createTask(
- mParent.mStackSupervisor.getNextTaskIdForUser(r.mUserId),
- r.info, null /* intent */, null /* voiceSession */,
- null /* voiceInteractor */, false /* toTop */);
- targetTask.affinityIntent = r.intent;
- mCreatedTasks.add(targetTask);
- if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity "
- + r + " out to new task " + targetTask);
- }
-
- mResultActivities.add(r);
- processResultActivities(r, targetTask, 0 /*bottom*/, true, true);
- mParent.positionChildAtBottom(targetTask);
- mParent.mStackSupervisor.mRecentTasks.add(targetTask);
+ // Handle this activity after we have done traversing the hierarchy.
+ mPendingReparentActivities.add(r);
return false;
}
}
@@ -203,8 +177,6 @@
processResultActivities(
r, mTargetTask, mActivityReparentPosition, false, false);
- mParent.positionChildAtTop(mTargetTask);
-
// Now we've moved it in to place...but what if this is a singleTop activity and
// we have put it on top of another instance of the same activity? Then we drop
// the instance below so it remains singleTop.
@@ -255,23 +227,51 @@
}
}
- private void processCreatedTasks() {
- if (mCreatedTasks.isEmpty()) return;
-
- DisplayContent display = mParent.getDisplay();
- final boolean singleTaskInstanceDisplay = display.isSingleTaskInstance();
- if (singleTaskInstanceDisplay) {
- display = mParent.mRootWindowContainer.getDefaultDisplay();
+ private void processPendingReparentActivities() {
+ if (mPendingReparentActivities.isEmpty()) {
+ return;
}
- final int windowingMode = mParent.getWindowingMode();
- final int activityType = mParent.getActivityType();
+ final ActivityTaskManagerService atmService = mTargetStack.mAtmService;
+ final ArrayList<Task> createdTasks = new ArrayList<>();
+ while (!mPendingReparentActivities.isEmpty()) {
+ final ActivityRecord r = mPendingReparentActivities.remove(0);
+ final ActivityRecord bottom = mTargetStack.getBottomMostActivity();
+ final Task targetTask;
+ if (bottom != null && r.taskAffinity.equals(bottom.getTask().affinity)) {
+ // If the activity currently at the bottom has the same task affinity as
+ // the one we are moving, then merge it into the same task.
+ targetTask = bottom.getTask();
+ if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity "
+ + r + " out to bottom task " + targetTask);
+ } else {
+ targetTask = mTargetStack.createTask(
+ atmService.mStackSupervisor.getNextTaskIdForUser(r.mUserId), r.info,
+ null /* intent */, null /* voiceSession */, null /* voiceInteractor */,
+ false /* toTop */);
+ targetTask.affinityIntent = r.intent;
+ createdTasks.add(targetTask);
+ if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity "
+ + r + " out to new task " + targetTask);
+ }
+ r.reparent(targetTask, 0 /* position */, "resetTargetTaskIfNeeded");
+ atmService.mStackSupervisor.mRecentTasks.add(targetTask);
+ }
+
+ DisplayContent display = mTargetStack.getDisplay();
+ final boolean singleTaskInstanceDisplay = display.isSingleTaskInstance();
+ if (singleTaskInstanceDisplay) {
+ display = atmService.mRootWindowContainer.getDefaultDisplay();
+ }
+
+ final int windowingMode = mTargetStack.getWindowingMode();
+ final int activityType = mTargetStack.getActivityType();
if (!singleTaskInstanceDisplay && !display.alwaysCreateStack(windowingMode, activityType)) {
return;
}
- while (!mCreatedTasks.isEmpty()) {
- final Task targetTask = mCreatedTasks.remove(mCreatedTasks.size() - 1);
+ while (!createdTasks.isEmpty()) {
+ final Task targetTask = createdTasks.remove(createdTasks.size() - 1);
final ActivityStack targetStack = display.getOrCreateStack(
windowingMode, activityType, false /* onTop */);
targetTask.reparent(targetStack, false /* toTop */, REPARENT_LEAVE_STACK_IN_PLACE,
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 93362b9..a7bf660 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1377,6 +1377,7 @@
for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) {
final Display display = displays[displayNdx];
final DisplayContent displayContent = new DisplayContent(display, this);
+ addChild(displayContent, POSITION_BOTTOM);
if (displayContent.mDisplayId == DEFAULT_DISPLAY) {
mDefaultDisplay = displayContent;
}
@@ -1445,6 +1446,7 @@
}
// The display hasn't been added to ActivityManager yet, create a new record now.
displayContent = new DisplayContent(display, this);
+ addChild(displayContent, POSITION_BOTTOM);
return displayContent;
}
@@ -2805,7 +2807,6 @@
int realCallingUid) {
int taskId = INVALID_TASK_ID;
int displayId = INVALID_DISPLAY;
- //Rect bounds = null;
// We give preference to the launch preference in activity options.
if (options != null) {
@@ -2827,7 +2828,7 @@
}
final int activityType = resolveActivityType(r, options, candidateTask);
- ActivityStack stack;
+ ActivityStack stack = null;
// Next preference for stack goes to the display Id set the candidate display.
if (launchParams != null && launchParams.mPreferredDisplayId != INVALID_DISPLAY) {
@@ -2860,7 +2861,6 @@
// Give preference to the stack and display of the input task and activity if they match the
// mode we want to launch into.
- stack = null;
DisplayContent display = null;
if (candidateTask != null) {
stack = candidateTask.getStack();
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 399c5d3..eaa0ea7 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -16,8 +16,12 @@
package com.android.server.wm;
+import static com.android.server.wm.AnimationSpecProto.ROTATE;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.server.wm.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC;
+import static com.android.server.wm.RotationAnimationSpecProto.DURATION_MS;
+import static com.android.server.wm.RotationAnimationSpecProto.END_LUMA;
+import static com.android.server.wm.RotationAnimationSpecProto.START_LUMA;
import static com.android.server.wm.ScreenRotationAnimationProto.ANIMATION_RUNNING;
import static com.android.server.wm.ScreenRotationAnimationProto.STARTED;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -25,7 +29,9 @@
import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
import static com.android.server.wm.WindowStateAnimator.WINDOW_FREEZE_LAYER;
+import android.animation.ArgbEvaluator;
import android.content.Context;
+import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
@@ -40,7 +46,9 @@
import android.view.animation.AnimationUtils;
import android.view.animation.Transformation;
+import com.android.internal.R;
import com.android.server.protolog.common.ProtoLog;
+import com.android.server.wm.utils.RotationAnimationUtils;
import java.io.PrintWriter;
@@ -60,10 +68,10 @@
* animation first rotate the new content into the old orientation to then be able to
* animate to the new orientation
*
- * <li> The exiting Blackframe: <p>
- * Because the change of orientation might change the width and height of the content (i.e
- * when rotating from portrait to landscape) we "crop" the new content using black frames
- * around the screenshot so the new content does not go beyond the screenshot's bounds
+ * <li> The Background color frame: <p>
+ * To have the animation seem more seamless, we add a color transitioning background behind the
+ * exiting and entering layouts. We compute the brightness of the start and end
+ * layouts and transition from the two brightness values as grayscale underneath the animation
*
* <li> The entering Blackframe: <p>
* The enter Blackframe is similar to the exit Blackframe but is only used when a custom
@@ -81,8 +89,6 @@
*/
private static final int SCREEN_FREEZE_LAYER_BASE = WINDOW_FREEZE_LAYER + TYPE_LAYER_MULTIPLIER;
private static final int SCREEN_FREEZE_LAYER_ENTER = SCREEN_FREEZE_LAYER_BASE;
- private static final int SCREEN_FREEZE_LAYER_SCREENSHOT = SCREEN_FREEZE_LAYER_BASE + 1;
- private static final int SCREEN_FREEZE_LAYER_EXIT = SCREEN_FREEZE_LAYER_BASE + 2;
private final Context mContext;
private final DisplayContent mDisplayContent;
@@ -90,16 +96,18 @@
private final Transformation mRotateExitTransformation = new Transformation();
private final Transformation mRotateEnterTransformation = new Transformation();
// Complete transformations being applied.
- private final Transformation mExitTransformation = new Transformation();
private final Transformation mEnterTransformation = new Transformation();
- private final Matrix mFrameInitialMatrix = new Matrix();
private final Matrix mSnapshotInitialMatrix = new Matrix();
- private final Matrix mSnapshotFinalMatrix = new Matrix();
- private final Matrix mExitFrameFinalMatrix = new Matrix();
private final WindowManagerService mService;
+ /** Only used for custom animations and not screen rotation. */
private SurfaceControl mEnterBlackFrameLayer;
- private SurfaceControl mRotationLayer;
- private SurfaceControl mSurfaceControl;
+ /** This layer contains the actual screenshot that is to be faded out. */
+ private SurfaceControl mScreenshotLayer;
+ /**
+ * Only used for screen rotation and not custom animations. Layered behind all other layers
+ * to avoid showing any "empty" spots
+ */
+ private SurfaceControl mBackColorSurface;
private BlackFrame mEnteringBlackFrame;
private int mWidth, mHeight;
@@ -120,8 +128,11 @@
private boolean mFinishAnimReady;
private long mFinishAnimStartTime;
private boolean mForceDefaultOrientation;
- private BlackFrame mExitingBlackFrame;
private SurfaceRotationAnimationController mSurfaceRotationAnimationController;
+ /** Intensity of light/whiteness of the layout before rotation occurs. */
+ private float mStartLuma;
+ /** Intensity of light/whiteness of the layout after rotation occurs. */
+ private float mEndLuma;
public ScreenRotationAnimation(Context context, DisplayContent displayContent,
boolean fixedToUserRotation, boolean isSecure, WindowManagerService service) {
@@ -162,9 +173,15 @@
final SurfaceControl.Transaction t = mService.mTransactionFactory.get();
try {
- mRotationLayer = displayContent.makeOverlay()
+ mBackColorSurface = displayContent.makeChildSurface(null)
+ .setName("BackColorSurface")
+ .setColorLayer()
+ .build();
+
+ mScreenshotLayer = displayContent.makeOverlay()
.setName("RotationLayer")
- .setContainerLayer()
+ .setBufferSize(mWidth, mHeight)
+ .setSecure(isSecure)
.build();
mEnterBlackFrameLayer = displayContent.makeOverlay()
@@ -172,26 +189,21 @@
.setContainerLayer()
.build();
- mSurfaceControl = mService.makeSurfaceBuilder(null)
- .setName("ScreenshotSurface")
- .setParent(mRotationLayer)
- .setBufferSize(mWidth, mHeight)
- .setSecure(isSecure)
- .build();
-
// In case display bounds change, screenshot buffer and surface may mismatch so set a
// scaling mode.
SurfaceControl.Transaction t2 = mService.mTransactionFactory.get();
- t2.setOverrideScalingMode(mSurfaceControl, Surface.SCALING_MODE_SCALE_TO_WINDOW);
+ t2.setOverrideScalingMode(mScreenshotLayer, Surface.SCALING_MODE_SCALE_TO_WINDOW);
t2.apply(true /* sync */);
// Capture a screenshot into the surface we just created.
final int displayId = display.getDisplayId();
final Surface surface = mService.mSurfaceFactory.get();
- surface.copyFrom(mSurfaceControl);
+ surface.copyFrom(mScreenshotLayer);
SurfaceControl.ScreenshotGraphicBuffer gb =
mService.mDisplayManagerInternal.screenshot(displayId);
if (gb != null) {
+ mStartLuma = RotationAnimationUtils.getAvgBorderLuma(gb.getGraphicBuffer(),
+ gb.getColorSpace());
try {
surface.attachAndQueueBufferWithColorSpace(gb.getGraphicBuffer(),
gb.getColorSpace());
@@ -202,13 +214,15 @@
// screenshot surface we display it in also has FLAG_SECURE so that
// the user can not screenshot secure layers via the screenshot surface.
if (gb.containsSecureLayers()) {
- t.setSecure(mSurfaceControl, true);
+ t.setSecure(mScreenshotLayer, true);
}
- t.setLayer(mRotationLayer, SCREEN_FREEZE_LAYER_BASE);
- t.setLayer(mSurfaceControl, SCREEN_FREEZE_LAYER_SCREENSHOT);
- t.setAlpha(mSurfaceControl, 0);
- t.show(mRotationLayer);
- t.show(mSurfaceControl);
+ t.setLayer(mScreenshotLayer, SCREEN_FREEZE_LAYER_BASE);
+ t.reparent(mBackColorSurface, displayContent.getSurfaceControl());
+ t.setLayer(mBackColorSurface, -1);
+ t.setColor(mBackColorSurface, new float[]{mStartLuma, mStartLuma, mStartLuma});
+ t.setAlpha(mBackColorSurface, 1);
+ t.show(mScreenshotLayer);
+ t.show(mBackColorSurface);
} else {
Slog.w(TAG, "Unable to take screenshot of display " + displayId);
}
@@ -218,32 +232,11 @@
}
ProtoLog.i(WM_SHOW_SURFACE_ALLOC,
- " FREEZE %s: CREATE", mSurfaceControl);
+ " FREEZE %s: CREATE", mScreenshotLayer);
setRotation(t, originalRotation);
t.apply();
}
- private static void createRotationMatrix(int rotation, int width, int height,
- Matrix outMatrix) {
- switch (rotation) {
- case Surface.ROTATION_0:
- outMatrix.reset();
- break;
- case Surface.ROTATION_90:
- outMatrix.setRotate(90, 0, 0);
- outMatrix.postTranslate(height, 0);
- break;
- case Surface.ROTATION_180:
- outMatrix.setRotate(180, 0, 0);
- outMatrix.postTranslate(width, height);
- break;
- case Surface.ROTATION_270:
- outMatrix.setRotate(270, 0, 0);
- outMatrix.postTranslate(0, width);
- break;
- }
- }
-
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(STARTED, mStarted);
@@ -252,11 +245,11 @@
}
boolean hasScreenshot() {
- return mSurfaceControl != null;
+ return mScreenshotLayer != null;
}
private void setRotationTransform(SurfaceControl.Transaction t, Matrix matrix) {
- if (mRotationLayer == null) {
+ if (mScreenshotLayer == null) {
return;
}
matrix.getValues(mTmpFloats);
@@ -267,24 +260,19 @@
x -= mCurrentDisplayRect.left;
y -= mCurrentDisplayRect.top;
}
- t.setPosition(mRotationLayer, x, y);
- t.setMatrix(mRotationLayer,
+ t.setPosition(mScreenshotLayer, x, y);
+ t.setMatrix(mScreenshotLayer,
mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
- t.setAlpha(mSurfaceControl, (float) 1.0);
- t.setAlpha(mRotationLayer, (float) 1.0);
- t.show(mRotationLayer);
+ t.setAlpha(mScreenshotLayer, (float) 1.0);
+ t.show(mScreenshotLayer);
}
public void printTo(String prefix, PrintWriter pw) {
- pw.print(prefix); pw.print("mSurface="); pw.print(mSurfaceControl);
+ pw.print(prefix); pw.print("mSurface="); pw.print(mScreenshotLayer);
pw.print(" mWidth="); pw.print(mWidth);
pw.print(" mHeight="); pw.println(mHeight);
- pw.print(prefix); pw.print("mExitingBlackFrame="); pw.println(mExitingBlackFrame);
- if (mExitingBlackFrame != null) {
- mExitingBlackFrame.printTo(prefix + " ", pw);
- }
pw.print(prefix);
pw.print("mEnteringBlackFrame=");
pw.println(mEnteringBlackFrame);
@@ -303,20 +291,10 @@
pw.print(" "); mRotateExitTransformation.printShortString(pw); pw.println();
pw.print(prefix); pw.print("mRotateEnterAnimation="); pw.print(mRotateEnterAnimation);
pw.print(" "); mRotateEnterTransformation.printShortString(pw); pw.println();
- pw.print(prefix); pw.print("mExitTransformation=");
- mExitTransformation.printShortString(pw); pw.println();
pw.print(prefix); pw.print("mEnterTransformation=");
mEnterTransformation.printShortString(pw); pw.println();
- pw.print(prefix); pw.print("mFrameInitialMatrix=");
- mFrameInitialMatrix.printShortString(pw);
- pw.println();
pw.print(prefix); pw.print("mSnapshotInitialMatrix=");
- mSnapshotInitialMatrix.printShortString(pw);
- pw.print(" mSnapshotFinalMatrix="); mSnapshotFinalMatrix.printShortString(pw);
- pw.println();
- pw.print(prefix); pw.print("mExitFrameFinalMatrix=");
- mExitFrameFinalMatrix.printShortString(pw);
- pw.println();
+ mSnapshotInitialMatrix.printShortString(pw);pw.println();
pw.print(prefix); pw.print("mForceDefaultOrientation="); pw.print(mForceDefaultOrientation);
if (mForceDefaultOrientation) {
pw.print(" mOriginalDisplayRect="); pw.print(mOriginalDisplayRect.toShortString());
@@ -331,7 +309,7 @@
// to the snapshot to make it stay in the same original position
// with the current screen rotation.
int delta = DisplayContent.deltaRotation(rotation, Surface.ROTATION_0);
- createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix);
+ RotationAnimationUtils.createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix);
setRotationTransform(t, mSnapshotInitialMatrix);
}
@@ -341,7 +319,7 @@
*/
private boolean startAnimation(SurfaceControl.Transaction t, long maxAnimationDuration,
float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim) {
- if (mSurfaceControl == null) {
+ if (mScreenshotLayer == null) {
// Can't do animation.
return false;
}
@@ -354,89 +332,58 @@
// Figure out how the screen has moved from the original rotation.
int delta = DisplayContent.deltaRotation(mCurRotation, mOriginalRotation);
- mRotateAlphaAnimation = AnimationUtils.loadAnimation(mContext,
- com.android.internal.R.anim.screen_rotate_alpha);
final boolean customAnim;
if (exitAnim != 0 && enterAnim != 0) {
customAnim = true;
mRotateExitAnimation = AnimationUtils.loadAnimation(mContext, exitAnim);
mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext, enterAnim);
+ mRotateAlphaAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.screen_rotate_alpha);
} else {
customAnim = false;
- switch (delta) {
+ switch (delta) { /* Counter-Clockwise Rotations */
case Surface.ROTATION_0:
mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
- com.android.internal.R.anim.screen_rotate_0_exit);
+ R.anim.screen_rotate_0_exit);
mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
- com.android.internal.R.anim.screen_rotate_0_enter);
+ R.anim.screen_rotate_0_enter);
break;
case Surface.ROTATION_90:
mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
- com.android.internal.R.anim.screen_rotate_plus_90_exit);
+ R.anim.screen_rotate_plus_90_exit);
mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
- com.android.internal.R.anim.screen_rotate_plus_90_enter);
+ R.anim.screen_rotate_plus_90_enter);
break;
case Surface.ROTATION_180:
mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
- com.android.internal.R.anim.screen_rotate_180_exit);
+ R.anim.screen_rotate_180_exit);
mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
- com.android.internal.R.anim.screen_rotate_180_enter);
+ R.anim.screen_rotate_180_enter);
break;
case Surface.ROTATION_270:
mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
- com.android.internal.R.anim.screen_rotate_minus_90_exit);
+ R.anim.screen_rotate_minus_90_exit);
mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
- com.android.internal.R.anim.screen_rotate_minus_90_enter);
+ R.anim.screen_rotate_minus_90_enter);
break;
}
}
- // Initialize the animations. This is a hack, redefining what "parent"
- // means to allow supplying the last and next size. In this definition
- // "%p" is the original (let's call it "previous") size, and "%" is the
- // screen's current/new size.
- mRotateEnterAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
mRotateExitAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
+ mRotateExitAnimation.restrictDuration(maxAnimationDuration);
+ mRotateExitAnimation.scaleCurrentDuration(animationScale);
+ mRotateEnterAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
+ mRotateEnterAnimation.restrictDuration(maxAnimationDuration);
+ mRotateEnterAnimation.scaleCurrentDuration(animationScale);
+
mAnimRunning = false;
mFinishAnimReady = false;
mFinishAnimStartTime = -1;
- mRotateExitAnimation.restrictDuration(maxAnimationDuration);
- mRotateExitAnimation.scaleCurrentDuration(animationScale);
- mRotateEnterAnimation.restrictDuration(maxAnimationDuration);
- mRotateEnterAnimation.scaleCurrentDuration(animationScale);
- mRotateAlphaAnimation.restrictDuration(maxAnimationDuration);
- mRotateAlphaAnimation.scaleCurrentDuration(animationScale);
-
- if (!customAnim && mExitingBlackFrame == null) {
- try {
- // Compute the transformation matrix that must be applied
- // the the black frame to make it stay in the initial position
- // before the new screen rotation. This is different than the
- // snapshot transformation because the snapshot is always based
- // of the native orientation of the screen, not the orientation
- // we were last in.
- createRotationMatrix(delta, mOriginalWidth, mOriginalHeight, mFrameInitialMatrix);
-
- final Rect outer;
- final Rect inner;
- if (mForceDefaultOrientation) {
- // Going from a smaller Display to a larger Display, add curtains to sides
- // or top and bottom. Going from a larger to smaller display will result in
- // no BlackSurfaces being constructed.
- outer = mCurrentDisplayRect;
- inner = mOriginalDisplayRect;
- } else {
- outer = new Rect(-mWidth, -mHeight, mWidth * 2, mHeight * 2);
- inner = new Rect(0, 0, mWidth, mHeight);
- }
- mExitingBlackFrame = new BlackFrame(mService.mTransactionFactory, t, outer, inner,
- SCREEN_FREEZE_LAYER_EXIT, mDisplayContent, mForceDefaultOrientation,
- mRotationLayer);
- } catch (OutOfResourcesException e) {
- Slog.w(TAG, "Unable to allocate black surface", e);
- }
+ if (customAnim) {
+ mRotateAlphaAnimation.restrictDuration(maxAnimationDuration);
+ mRotateAlphaAnimation.scaleCurrentDuration(animationScale);
}
if (customAnim && mEnteringBlackFrame == null) {
@@ -451,7 +398,12 @@
}
}
- mSurfaceRotationAnimationController.startAnimation();
+ if (customAnim) {
+ mSurfaceRotationAnimationController.startCustomAnimation();
+ } else {
+ mSurfaceRotationAnimationController.startScreenRotationAnimation();
+ }
+
return true;
}
@@ -460,11 +412,13 @@
*/
public boolean dismiss(SurfaceControl.Transaction t, long maxAnimationDuration,
float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim) {
- if (mSurfaceControl == null) {
+ if (mScreenshotLayer == null) {
// Can't do animation.
return false;
}
if (!mStarted) {
+ mEndLuma = RotationAnimationUtils.getLumaOfSurfaceControl(mDisplayContent.getDisplay(),
+ mDisplayContent.getWindowingLayer());
startAnimation(t, maxAnimationDuration, animationScale, finalWidth, finalHeight,
exitAnim, enterAnim);
}
@@ -480,28 +434,28 @@
mSurfaceRotationAnimationController.cancel();
mSurfaceRotationAnimationController = null;
}
- if (mSurfaceControl != null) {
- ProtoLog.i(WM_SHOW_SURFACE_ALLOC, " FREEZE %s: DESTROY", mSurfaceControl);
- mSurfaceControl = null;
+
+ if (mScreenshotLayer != null) {
+ ProtoLog.i(WM_SHOW_SURFACE_ALLOC, " FREEZE %s: DESTROY", mScreenshotLayer);
SurfaceControl.Transaction t = mService.mTransactionFactory.get();
- if (mRotationLayer != null) {
- if (mRotationLayer.isValid()) {
- t.remove(mRotationLayer);
- }
- mRotationLayer = null;
+ if (mScreenshotLayer.isValid()) {
+ t.remove(mScreenshotLayer);
}
+ mScreenshotLayer = null;
+
if (mEnterBlackFrameLayer != null) {
if (mEnterBlackFrameLayer.isValid()) {
t.remove(mEnterBlackFrameLayer);
}
mEnterBlackFrameLayer = null;
}
+ if (mBackColorSurface != null) {
+ t.remove(mBackColorSurface);
+ mBackColorSurface = null;
+ }
t.apply();
}
- if (mExitingBlackFrame != null) {
- mExitingBlackFrame.kill();
- mExitingBlackFrame = null;
- }
+
if (mEnteringBlackFrame != null) {
mEnteringBlackFrame.kill();
mEnteringBlackFrame = null;
@@ -537,18 +491,28 @@
* Utility class that runs a {@link ScreenRotationAnimation} on the {@link
* SurfaceAnimationRunner}.
* <p>
- * The rotation animation is divided into the following hierarchy:
+ * The rotation animation supports both screen rotation and custom animations
+ *
+ * For custom animations:
* <ul>
- * <li> A first rotation layer, containing the blackframes. This layer is animated by the
- * "screen_rotate_X_exit" that applies a scale and rotate and where X is value of the rotation.
- * <ul>
- * <li> A child layer containing the screenshot on which is added an animation of it's
- * alpha channel ("screen_rotate_alpha") and that will rotate with his parent layer.</li>
- * </ul>
- * <li> A second rotation layer used when custom animations are passed in
+ * <li>
+ * The screenshot layer which has an added animation of it's alpha channel
+ * ("screen_rotate_alpha") and that will be applied along with the custom animation.
+ * </li>
+ * <li> A device layer that is animated with the provided custom animation </li>
+ * </ul>
+ *
+ * For screen rotation:
+ * <ul>
+ * <li> A rotation layer that is both rotated and faded out during a single animation </li>
+ * <li> A device layer that is both rotated and faded in during a single animation </li>
+ * <li> A background color layer that transitions colors behind the first two layers </li>
+ * </ul>
+ *
* {@link ScreenRotationAnimation#startAnimation(
* SurfaceControl.Transaction, long, float, int, int, int, int)}.
* </ul>
+ *
* <p>
* Thus an {@link LocalAnimationAdapter.AnimationSpec} is created for each of
* this three {@link SurfaceControl}s which then delegates the animation to the
@@ -556,22 +520,35 @@
*/
class SurfaceRotationAnimationController {
private SurfaceAnimator mDisplayAnimator;
- private SurfaceAnimator mEnterBlackFrameAnimator;
private SurfaceAnimator mScreenshotRotationAnimator;
private SurfaceAnimator mRotateScreenAnimator;
+ private SurfaceAnimator mEnterBlackFrameAnimator;
+
+ void startCustomAnimation() {
+ try {
+ mService.mSurfaceAnimationRunner.deferStartingAnimations();
+ mRotateScreenAnimator = startScreenshotAlphaAnimation();
+ mDisplayAnimator = startDisplayRotation();
+ if (mEnteringBlackFrame != null) {
+ mEnterBlackFrameAnimator = startEnterBlackFrameAnimation();
+ }
+ } finally {
+ mService.mSurfaceAnimationRunner.continueStartingAnimations();
+ }
+ }
/**
* Start the rotation animation of the display and the screenshot on the
* {@link SurfaceAnimationRunner}.
*/
- void startAnimation() {
- mRotateScreenAnimator = startScreenshotAlphaAnimation();
- mDisplayAnimator = startDisplayRotation();
- if (mExitingBlackFrame != null) {
+ void startScreenRotationAnimation() {
+ try {
+ mService.mSurfaceAnimationRunner.deferStartingAnimations();
+ mDisplayAnimator = startDisplayRotation();
mScreenshotRotationAnimator = startScreenshotRotationAnimation();
- }
- if (mEnteringBlackFrame != null) {
- mEnterBlackFrameAnimator = startEnterBlackFrameAnimation();
+ startColorAnimation();
+ } finally {
+ mService.mSurfaceAnimationRunner.continueStartingAnimations();
}
}
@@ -596,8 +573,8 @@
private SurfaceAnimator startScreenshotAlphaAnimation() {
return startAnimation(initializeBuilder()
- .setSurfaceControl(mSurfaceControl)
- .setAnimationLeashParent(mRotationLayer)
+ .setSurfaceControl(mScreenshotLayer)
+ .setAnimationLeashParent(mDisplayContent.getOverlayLayer())
.setWidth(mWidth)
.setHeight(mHeight)
.build(),
@@ -616,13 +593,67 @@
private SurfaceAnimator startScreenshotRotationAnimation() {
return startAnimation(initializeBuilder()
- .setSurfaceControl(mRotationLayer)
+ .setSurfaceControl(mScreenshotLayer)
.setAnimationLeashParent(mDisplayContent.getOverlayLayer())
.build(),
createWindowAnimationSpec(mRotateExitAnimation),
this::onAnimationEnd);
}
+
+ /**
+ * Applies the color change from {@link #mStartLuma} to {@link #mEndLuma} as a
+ * grayscale color
+ */
+ private void startColorAnimation() {
+ int colorTransitionMs = mContext.getResources().getInteger(
+ R.integer.config_screen_rotation_color_transition);
+ final SurfaceAnimationRunner runner = mService.mSurfaceAnimationRunner;
+ final float[] rgbTmpFloat = new float[3];
+ final int startColor = Color.rgb(mStartLuma, mStartLuma, mStartLuma);
+ final int endColor = Color.rgb(mEndLuma, mEndLuma, mEndLuma);
+ final long duration = colorTransitionMs * (long) mService.getCurrentAnimatorScale();
+ final ArgbEvaluator va = ArgbEvaluator.getInstance();
+ runner.startAnimation(
+ new LocalAnimationAdapter.AnimationSpec() {
+ @Override
+ public long getDuration() {
+ return duration;
+ }
+
+ @Override
+ public void apply(SurfaceControl.Transaction t, SurfaceControl leash,
+ long currentPlayTime) {
+ float fraction = (float)currentPlayTime / (float)getDuration();
+ int color = (Integer) va.evaluate(fraction, startColor, endColor);
+ Color middleColor = Color.valueOf(color);
+ rgbTmpFloat[0] = middleColor.red();
+ rgbTmpFloat[1] = middleColor.green();
+ rgbTmpFloat[2] = middleColor.blue();
+ if (leash.isValid()) {
+ t.setColor(leash, rgbTmpFloat);
+ }
+ }
+
+ @Override
+ public void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "startLuma=" + mStartLuma
+ + " endLuma=" + mEndLuma
+ + " durationMs=" + colorTransitionMs);
+ }
+
+ @Override
+ public void dumpDebugInner(ProtoOutputStream proto) {
+ final long token = proto.start(ROTATE);
+ proto.write(START_LUMA, mStartLuma);
+ proto.write(END_LUMA, mEndLuma);
+ proto.write(DURATION_MS, colorTransitionMs);
+ proto.end(token);
+ }
+ },
+ mBackColorSurface, mDisplayContent.getPendingTransaction(), null);
+ }
+
private WindowAnimationSpec createWindowAnimationSpec(Animation mAnimation) {
return new WindowAnimationSpec(mAnimation, new Point(0, 0) /* position */,
false /* canSkipFirstFrame */, 0 /* WindowCornerRadius */);
@@ -646,7 +677,6 @@
LocalAnimationAdapter localAnimationAdapter = new LocalAnimationAdapter(
animationSpec, mService.mSurfaceAnimationRunner);
-
animator.startAnimation(mDisplayContent.getPendingTransaction(),
localAnimationAdapter, false);
return animator;
@@ -692,7 +722,6 @@
if (mEnterBlackFrameAnimator != null) {
mEnterBlackFrameAnimator.cancelAnimation();
}
-
if (mScreenshotRotationAnimator != null) {
mScreenshotRotationAnimator.cancelAnimation();
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 5198602..3b349b8 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -463,7 +463,7 @@
final WindowState windowState = mService.windowForClientLocked(this, window,
false /* throwOnError */);
if (windowState != null) {
- windowState.setClientInsetsState(state);
+ windowState.updateRequestedInsetsState(state);
windowState.getDisplayContent().getInsetsPolicy().onInsetsModified(
windowState, state);
}
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
index bbd986f..5633b6b 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
@@ -27,6 +27,7 @@
import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.hardware.power.V1_0.PowerHint;
+import android.os.Handler;
import android.os.PowerManagerInternal;
import android.util.ArrayMap;
import android.view.Choreographer;
@@ -57,6 +58,8 @@
@VisibleForTesting
Choreographer mChoreographer;
+ private final Handler mAnimationThreadHandler = AnimationThread.getHandler();
+ private final Handler mSurfaceAnimationHandler = SurfaceAnimationThread.getHandler();
private final Runnable mApplyTransactionRunnable = this::applyTransaction;
private final AnimationHandler mAnimationHandler;
private final Transaction mFrameTransaction;
@@ -75,6 +78,10 @@
@GuardedBy("mLock")
private boolean mAnimationStartDeferred;
+ /**
+ * There should only ever be one instance of this class. Usual spot for it is with
+ * {@link WindowManagerService}
+ */
SurfaceAnimationRunner(Supplier<Transaction> transactionFactory,
PowerManagerInternal powerManagerInternal) {
this(null /* callbackProvider */, null /* animatorFactory */,
@@ -85,7 +92,7 @@
SurfaceAnimationRunner(@Nullable AnimationFrameCallbackProvider callbackProvider,
AnimatorFactory animatorFactory, Transaction frameTransaction,
PowerManagerInternal powerManagerInternal) {
- SurfaceAnimationThread.getHandler().runWithScissors(() -> mChoreographer = getSfInstance(),
+ mSurfaceAnimationHandler.runWithScissors(() -> mChoreographer = getSfInstance(),
0 /* timeout */);
mFrameTransaction = frameTransaction;
mAnimationHandler = new AnimationHandler();
@@ -152,7 +159,7 @@
synchronized (mCancelLock) {
anim.mCancelled = true;
}
- SurfaceAnimationThread.getHandler().post(() -> {
+ mSurfaceAnimationHandler.post(() -> {
anim.mAnim.cancel();
applyTransaction();
});
@@ -211,7 +218,7 @@
if (!a.mCancelled) {
// Post on other thread that we can push final state without jank.
- AnimationThread.getHandler().post(a.mFinishCallback);
+ mAnimationThreadHandler.post(a.mFinishCallback);
}
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 06af095..9a140da 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -94,6 +94,7 @@
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
import static java.lang.Integer.MAX_VALUE;
@@ -385,6 +386,8 @@
private static Exception sTmpException;
+ private boolean mForceShowForAllUsers;
+
private final FindRootHelper mFindRootHelper = new FindRootHelper();
private class FindRootHelper {
private ActivityRecord mRoot;
@@ -2185,7 +2188,7 @@
+ " from stack=" + getStack());
EventLogTags.writeWmTaskRemoved(mTaskId, "reParentTask:" + reason);
- position = stack.findPositionForTask(this, position, showForAllUsers());
+ position = stack.findPositionForTask(this, position);
reparent(stack, position);
@@ -2522,6 +2525,15 @@
return r != null && r.mShowForAllUsers;
}
+ @Override
+ boolean showToCurrentUser() {
+ return mForceShowForAllUsers || showForAllUsers() || mWmService.isCurrentProfile(mUserId);
+ }
+
+ void setForceShowForAllUsers(boolean forceShowForAllUsers) {
+ mForceShowForAllUsers = forceShowForAllUsers;
+ }
+
/**
* When we are in a floating stack (Freeform, Pinned, ...) we calculate
* insets differently. However if we are animating to the fullscreen stack
@@ -2544,6 +2556,16 @@
return getAppAnimationLayer(ANIMATION_LAYER_HOME);
}
+ @Override
+ Rect getAnimationBounds(int appStackClipMode) {
+ // TODO(b/131661052): we should remove appStackClipMode with hierarchical animations.
+ if (appStackClipMode == STACK_CLIP_BEFORE_ANIM && getStack() != null) {
+ // Using the stack bounds here effectively applies the clipping before animation.
+ return getStack().getBounds();
+ }
+ return super.getAnimationBounds(appStackClipMode);
+ }
+
boolean shouldAnimate() {
// Don't animate while the task runs recents animation but only if we are in the mode
// where we cancel with deferred screenshot, which means that the controller has
@@ -2557,6 +2579,18 @@
}
@Override
+ protected void onAnimationFinished() {
+ super.onAnimationFinished();
+ // TODO(b/142617871): we may need to add animation type parameter on onAnimationFinished to
+ // identify if the callback is for launch animation finish and then calling
+ // activity#onAnimationFinished.
+ final ActivityRecord activity = getTopMostActivity();
+ if (activity != null) {
+ activity.onAnimationFinished();
+ }
+ }
+
+ @Override
SurfaceControl.Builder makeSurface() {
return super.makeSurface().setMetadata(METADATA_TASK_ID, mTaskId);
}
@@ -2583,7 +2617,7 @@
@Override
RemoteAnimationTarget createRemoteAnimationTarget(
RemoteAnimationController.RemoteAnimationRecord record) {
- final ActivityRecord activity = getTopVisibleActivity();
+ final ActivityRecord activity = getTopMostActivity();
return activity != null ? activity.createRemoteAnimationTarget(record) : null;
}
@@ -2710,7 +2744,13 @@
getDimBounds(mTmpDimBoundsRect);
// Bounds need to be relative, as the dim layer is a child.
- mTmpDimBoundsRect.offsetTo(0, 0);
+ if (inFreeformWindowingMode()) {
+ getBounds(mTmpRect);
+ mTmpDimBoundsRect.offsetTo(mTmpDimBoundsRect.left - mTmpRect.left,
+ mTmpDimBoundsRect.top - mTmpRect.top);
+ } else {
+ mTmpDimBoundsRect.offsetTo(0, 0);
+ }
if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
scheduleAnimation();
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index e4744db..4cb5de4 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -470,7 +470,7 @@
final LayoutParams attrs = mainWindow.getAttrs();
final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags,
attrs.privateFlags, attrs.systemUiVisibility, task.getTaskDescription(),
- mFullSnapshotScale, mainWindow.getClientInsetsState());
+ mFullSnapshotScale, mainWindow.getRequestedInsetsState());
final int width = (int) (task.getBounds().width() * mFullSnapshotScale);
final int height = (int) (task.getBounds().height() * mFullSnapshotScale);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 5b458d8..e2a21a9 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -222,7 +222,7 @@
final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window,
surfaceControl, snapshot, layoutParams.getTitle(), taskDescription, sysUiVis,
windowFlags, windowPrivateFlags, taskBounds,
- currentOrientation, topFullscreenOpaqueWindow.getClientInsetsState());
+ currentOrientation, topFullscreenOpaqueWindow.getRequestedInsetsState());
window.setOuter(snapshotSurface);
try {
session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, -1,
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 6ff4b2e..137d122 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -121,7 +121,7 @@
mFindResults.resetTopWallpaper = true;
if (w.mActivityRecord != null && !w.mActivityRecord.isVisible()
- && !w.mActivityRecord.isAnimating(TRANSITION)) {
+ && !w.mActivityRecord.isAnimating(TRANSITION | PARENTS)) {
// If this window's app token is hidden and not animating, it is of no interest to us.
if (DEBUG_WALLPAPER) Slog.v(TAG, "Skipping hidden and not animating token: " + w);
@@ -139,7 +139,7 @@
}
final boolean keyguardGoingAwayWithWallpaper = (w.mActivityRecord != null
- && w.mActivityRecord.isAnimating(TRANSITION)
+ && w.mActivityRecord.isAnimating(TRANSITION | PARENTS)
&& AppTransition.isKeyguardGoingAwayTransit(w.mActivityRecord.getTransit())
&& (w.mActivityRecord.getTransitFlags()
& TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0);
@@ -162,9 +162,11 @@
final RecentsAnimationController recentsAnimationController =
mService.getRecentsAnimationController();
- final boolean animationWallpaper = w.mActivityRecord != null
- && w.mActivityRecord.getAnimation() != null
- && w.mActivityRecord.getAnimation().getShowWallpaper();
+ final WindowContainer animatingContainer =
+ w.mActivityRecord != null ? w.mActivityRecord.getAnimatingContainer() : null;
+ final boolean animationWallpaper = animatingContainer != null
+ && animatingContainer.getAnimation() != null
+ && animatingContainer.getAnimation().getShowWallpaper();
final boolean hasWallpaper = (w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0
|| animationWallpaper;
final boolean isRecentsTransitionTarget = (recentsAnimationController != null
@@ -228,14 +230,14 @@
if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper vis: target " + wallpaperTarget + ", obscured="
+ (wallpaperTarget != null ? Boolean.toString(wallpaperTarget.mObscured) : "??")
+ " animating=" + ((wallpaperTarget != null && wallpaperTarget.mActivityRecord != null)
- ? wallpaperTarget.mActivityRecord.isAnimating(TRANSITION) : null)
+ ? wallpaperTarget.mActivityRecord.isAnimating(TRANSITION | PARENTS) : null)
+ " prev=" + mPrevWallpaperTarget
+ " recentsAnimationWallpaperVisible=" + isAnimatingWithRecentsComponent);
return (wallpaperTarget != null
&& (!wallpaperTarget.mObscured
|| isAnimatingWithRecentsComponent
|| (wallpaperTarget.mActivityRecord != null
- && wallpaperTarget.mActivityRecord.isAnimating(TRANSITION))))
+ && wallpaperTarget.mActivityRecord.isAnimating(TRANSITION | PARENTS))))
|| mPrevWallpaperTarget != null;
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index f7525a9..fd91bc5 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -32,7 +32,6 @@
import android.view.Choreographer;
import android.view.SurfaceControl;
-import com.android.server.AnimationThread;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.protolog.common.ProtoLog;
@@ -92,7 +91,7 @@
mContext = service.mContext;
mPolicy = service.mPolicy;
mTransaction = service.mTransactionFactory.get();
- AnimationThread.getHandler().runWithScissors(
+ service.mAnimationHandler.runWithScissors(
() -> mChoreographer = Choreographer.getSfInstance(), 0 /* timeout */);
mAnimationFrameCallback = frameTimeNs -> {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 62630a6..cefef37 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -777,7 +777,7 @@
* otherwise.
*/
boolean isWaitingForTransitionStart() {
- return false;
+ return getActivity(app -> app.isWaitingForTransitionStart()) != null;
}
/**
@@ -785,7 +785,7 @@
* {@code ActivityRecord#isAnimating(TRANSITION)}, {@code false} otherwise.
*/
boolean isAppTransitioning() {
- return getActivity(app -> app.isAnimating(TRANSITION)) != null;
+ return getActivity(app -> app.isAnimating(PARENTS | TRANSITION)) != null;
}
/**
@@ -1071,6 +1071,10 @@
}
}
+ boolean showToCurrentUser() {
+ return true;
+ }
+
/**
* For all windows at or below this container call the callback.
* @param callback Calls the {@link ToBooleanFunction#apply} method for each window found and
@@ -1891,7 +1895,7 @@
if (adapter != null) {
startAnimation(getPendingTransaction(), adapter, !isVisible());
if (adapter.getShowWallpaper()) {
- mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+ getDisplayContent().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
if (thumbnailAdapter != null) {
mThumbnail.startAnimation(
@@ -2036,7 +2040,8 @@
}
boolean okToDisplay() {
- return mDisplayContent != null && mDisplayContent.okToDisplay();
+ final DisplayContent dc = getDisplayContent();
+ return dc != null && dc.okToDisplay();
}
boolean okToAnimate() {
@@ -2044,7 +2049,8 @@
}
boolean okToAnimate(boolean ignoreFrozen) {
- return mDisplayContent != null && mDisplayContent.okToAnimate(ignoreFrozen);
+ final DisplayContent dc = getDisplayContent();
+ return dc != null && dc.okToAnimate(ignoreFrozen);
}
@Override
@@ -2086,6 +2092,21 @@
}
/**
+ * @return The {@link WindowContainer} which is running an animation.
+ *
+ * It traverses from the current container to its parents recursively. If nothing is animating,
+ * it will return {@code null}.
+ */
+ @Nullable
+ WindowContainer getAnimatingContainer() {
+ if (isAnimating()) {
+ return this;
+ }
+ final WindowContainer parent = getParent();
+ return (parent != null) ? parent.getAnimatingContainer() : null;
+ }
+
+ /**
* @see SurfaceAnimator#startDelayingAnimationStart
*/
void startDelayingAnimationStart() {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8a91d9e..74fdba1 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -205,6 +205,7 @@
import android.view.Gravity;
import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.IDisplayFoldListener;
+import android.view.IDisplayWindowInsetsController;
import android.view.IDisplayWindowListener;
import android.view.IDisplayWindowRotationController;
import android.view.IDockedStackListener;
@@ -255,7 +256,6 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.LatencyTracker;
-import com.android.internal.util.Preconditions;
import com.android.internal.util.function.pooled.PooledConsumer;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.internal.view.WindowManagerPolicyThread;
@@ -1508,7 +1508,6 @@
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
displayPolicy.adjustWindowParamsLw(win, win.mAttrs, Binder.getCallingPid(),
Binder.getCallingUid());
- win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
res = displayPolicy.validateAddingWindowLw(attrs);
if (res != WindowManagerGlobal.ADD_OKAY) {
@@ -2770,6 +2769,7 @@
true /* includingParents */);
}
}
+ syncInputTransactions();
}
/**
@@ -3175,7 +3175,7 @@
}
/* Called by WindowState */
- boolean isCurrentProfileLocked(int userId) {
+ boolean isCurrentProfile(int userId) {
if (userId == mCurrentUserId) return true;
for (int i = 0; i < mCurrentProfileIds.length; i++) {
if (mCurrentProfileIds[i] == userId) return true;
@@ -3729,6 +3729,48 @@
}
@Override
+ public void setDisplayWindowInsetsController(
+ int displayId, IDisplayWindowInsetsController insetsController) {
+ if (mContext.checkCallingOrSelfPermission(MANAGE_APP_TOKENS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Must hold permission " + MANAGE_APP_TOKENS);
+ }
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final DisplayContent dc = mRoot.getDisplayContent(displayId);
+ if (dc == null) {
+ return;
+ }
+ dc.setRemoteInsetsController(insetsController);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
+ public void modifyDisplayWindowInsets(int displayId, InsetsState state) {
+ if (mContext.checkCallingOrSelfPermission(MANAGE_APP_TOKENS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Must hold permission " + MANAGE_APP_TOKENS);
+ }
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final DisplayContent dc = mRoot.getDisplayContent(displayId);
+ if (dc == null || dc.mRemoteInsetsControlTarget == null) {
+ return;
+ }
+ dc.getInsetsStateController().onInsetsModified(
+ dc.mRemoteInsetsControlTarget, state);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
public int watchRotation(IRotationWatcher watcher, int displayId) {
final DisplayContent displayContent;
synchronized (mGlobalLock) {
@@ -7316,7 +7358,8 @@
// If there was a pending IME show(), reset it as IME has been
// requested to be hidden.
dc.getInsetsStateController().getImeSourceProvider().abortShowImePostLayout();
- dc.mInputMethodTarget.hideInsets(WindowInsets.Type.ime(), true /* fromIme */);
+ dc.mInputMethodControlTarget.hideInsets(WindowInsets.Type.ime(),
+ true /* fromIme */);
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index b681923..96bc8e9 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -56,19 +56,35 @@
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
+import static android.view.WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_PHONE;
+import static android.view.WindowManager.LayoutParams.TYPE_POINTER;
+import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
+import static android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;
+import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
+import static android.view.WindowManager.LayoutParams.TYPE_SEARCH_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
+import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_DOCKED;
@@ -194,6 +210,7 @@
import android.view.InputEvent;
import android.view.InputEventReceiver;
import android.view.InputWindowHandle;
+import android.view.InsetsSource;
import android.view.InsetsState;
import android.view.Surface.Rotation;
import android.view.SurfaceControl;
@@ -523,9 +540,6 @@
boolean mHasSurface = false;
- /** When true this window can be displayed on screens owther than mOwnerUid's */
- private boolean mShowToOwnerOnly;
-
// This window will be replaced due to relaunch. This allows window manager
// to differentiate between simple removal of a window and replacement. In the latter case it
// will preserve the old window until the new one is drawn.
@@ -637,17 +651,29 @@
private boolean mIsDimming = false;
private @Nullable InsetsSourceProvider mControllableInsetProvider;
- private InsetsState mClientInsetsState;
+ private InsetsState mRequestedInsetsState;
private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
private KeyInterceptionInfo mKeyInterceptionInfo;
- InsetsState getClientInsetsState() {
- return mClientInsetsState;
+ /**
+ * @return The insets state as requested by the client, i.e. the dispatched insets state
+ * for which the visibilities are overridden with what the client requested.
+ */
+ InsetsState getRequestedInsetsState() {
+ return mRequestedInsetsState;
}
- void setClientInsetsState(InsetsState state) {
- mClientInsetsState = state;
+ /**
+ * @see #getRequestedInsetsState()
+ */
+ void updateRequestedInsetsState(InsetsState state) {
+
+ // Only update the sources the client is actually controlling.
+ for (int i = state.getSourcesCount() - 1; i >= 0; i--) {
+ final InsetsSource source = state.sourceAt(i);
+ mRequestedInsetsState.addSource(source);
+ }
}
void seamlesslyRotateIfAllowed(Transaction transaction, @Rotation int oldRotation,
@@ -766,8 +792,9 @@
mSeq = seq;
mPowerManagerWrapper = powerManagerWrapper;
mForceSeamlesslyRotate = token.mRoundedCornerOverlay;
- mClientInsetsState =
- getDisplayContent().getInsetsPolicy().getInsetsForDispatch(this);
+ mRequestedInsetsState = new InsetsState(
+ getDisplayContent().getInsetsPolicy().getInsetsForDispatch(this),
+ true /* copySources */);
if (DEBUG) {
Slog.v(TAG, "Window " + this + " client=" + c.asBinder()
+ " token=" + token + " (" + mAttrs.token + ")" + " params=" + a);
@@ -958,8 +985,11 @@
final int layoutXDiff;
final int layoutYDiff;
final WindowState imeWin = mWmService.mRoot.getCurrentInputMethodWindow();
+ final boolean isInputMethodAdjustTarget = windowsAreFloating
+ ? dc.mInputMethodTarget != null && task == dc.mInputMethodTarget.getTask()
+ : isInputMethodTarget();
final boolean isImeTarget =
- imeWin != null && imeWin.isVisibleNow() && isInputMethodTarget();
+ imeWin != null && imeWin.isVisibleNow() && isInputMethodAdjustTarget;
if (isFullscreenAndFillsDisplay || layoutInParentFrame()) {
// We use the parent frame as the containing frame for fullscreen and child windows
mWindowFrames.mContainingFrame.set(mWindowFrames.mParentFrame);
@@ -990,7 +1020,8 @@
final int distanceToTop = Math.max(mWindowFrames.mContainingFrame.top
- mWindowFrames.mContentFrame.top, 0);
int offs = Math.min(bottomOverlap, distanceToTop);
- mWindowFrames.mContainingFrame.top -= offs;
+ mWindowFrames.mContainingFrame.offset(0, -offs);
+ mInsetFrame.offset(0, -offs);
}
} else if (!inPinnedWindowingMode() && mWindowFrames.mContainingFrame.bottom
> mWindowFrames.mParentFrame.bottom) {
@@ -1560,7 +1591,7 @@
// TODO: Can we consolidate this with #isVisible() or have a more appropriate name for this?
boolean isWinVisibleLw() {
return (mActivityRecord == null || mActivityRecord.mVisibleRequested
- || mActivityRecord.isAnimating(TRANSITION)) && isVisible();
+ || mActivityRecord.isAnimating(TRANSITION | PARENTS)) && isVisible();
}
/**
@@ -1786,7 +1817,7 @@
// Starting window that's exiting will be removed when the animation finishes.
// Mark all relevant flags for that onExitAnimationDone will proceed all the way
// to actually remove it.
- if (!visible && isVisibleNow && mActivityRecord.isAnimating(TRANSITION)) {
+ if (!visible && isVisibleNow && mActivityRecord.isAnimating(PARENTS | TRANSITION)) {
mAnimatingExit = true;
mRemoveOnExit = true;
mWindowRemovalAllowed = true;
@@ -2055,7 +2086,7 @@
this, mWinAnimator.mSurfaceController, mAnimatingExit, mRemoveOnExit,
mHasSurface, mWinAnimator.getShown(),
isAnimating(TRANSITION | PARENTS),
- mActivityRecord != null && mActivityRecord.isAnimating(TRANSITION),
+ mActivityRecord != null && mActivityRecord.isAnimating(PARENTS | TRANSITION),
mWillReplaceWindow,
mWmService.mDisplayFrozen, Debug.getCallers(6));
@@ -2357,12 +2388,13 @@
@Override
void switchUser(int userId) {
super.switchUser(userId);
- if (isHiddenFromUserLocked()) {
+
+ if (showToCurrentUser()) {
+ setPolicyVisibilityFlag(VISIBLE_FOR_USER);
+ } else {
if (DEBUG_VISIBILITY) Slog.w(TAG_WM, "user changing, hiding " + this
+ ", attrs=" + mAttrs.type + ", belonging to " + mOwnerUid);
clearPolicyVisibilityFlag(VISIBLE_FOR_USER);
- } else {
- setPolicyVisibilityFlag(VISIBLE_FOR_USER);
}
}
@@ -2725,7 +2757,7 @@
// Already showing.
return false;
}
- if (isHiddenFromUserLocked()) {
+ if (!showToCurrentUser()) {
return false;
}
if (!mAppOpVisibility) {
@@ -3133,11 +3165,55 @@
return displayContent.isDefaultDisplay;
}
- void setShowToOwnerOnlyLocked(boolean showToOwnerOnly) {
- mShowToOwnerOnly = showToOwnerOnly;
+ /** @return {@code true} if this window can be shown to all users. */
+ boolean showForAllUsers() {
+
+ // If this switch statement is modified, modify the comment in the declarations of
+ // the type in {@link WindowManager.LayoutParams} as well.
+ switch (mAttrs.type) {
+ default:
+ // These are the windows that by default are shown only to the user that created
+ // them. If this needs to be overridden, set
+ // {@link WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS} in
+ // {@link WindowManager.LayoutParams}. Note that permission
+ // {@link android.Manifest.permission.INTERNAL_SYSTEM_WINDOW} is required as well.
+ if ((mAttrs.privateFlags & SYSTEM_FLAG_SHOW_FOR_ALL_USERS) == 0) {
+ return false;
+ }
+ break;
+
+ // These are the windows that by default are shown to all users. However, to
+ // protect against spoofing, check permissions below.
+ case TYPE_APPLICATION_STARTING:
+ case TYPE_BOOT_PROGRESS:
+ case TYPE_DISPLAY_OVERLAY:
+ case TYPE_INPUT_CONSUMER:
+ case TYPE_KEYGUARD_DIALOG:
+ case TYPE_MAGNIFICATION_OVERLAY:
+ case TYPE_NAVIGATION_BAR:
+ case TYPE_NAVIGATION_BAR_PANEL:
+ case TYPE_PHONE:
+ case TYPE_POINTER:
+ case TYPE_PRIORITY_PHONE:
+ case TYPE_SEARCH_BAR:
+ case TYPE_STATUS_BAR:
+ case TYPE_STATUS_BAR_PANEL:
+ case TYPE_STATUS_BAR_SUB_PANEL:
+ case TYPE_SYSTEM_DIALOG:
+ case TYPE_VOLUME_OVERLAY:
+ case TYPE_PRESENTATION:
+ case TYPE_PRIVATE_PRESENTATION:
+ case TYPE_DOCK_DIVIDER:
+ break;
+ }
+
+ // Only the system can show free windows to all users.
+ return mOwnerCanAddInternalSystemWindow;
+
}
- private boolean isHiddenFromUserLocked() {
+ @Override
+ boolean showToCurrentUser() {
// Child windows are evaluated based on their parent window.
final WindowState win = getTopParentWindow();
if (win.mAttrs.type < FIRST_SYSTEM_WINDOW
@@ -3151,12 +3227,12 @@
&& win.getFrameLw().right >= win.getStableFrameLw().right
&& win.getFrameLw().bottom >= win.getStableFrameLw().bottom) {
// Is a fullscreen window, like the clock alarm. Show to everyone.
- return false;
+ return true;
}
}
- return win.mShowToOwnerOnly
- && !mWmService.isCurrentProfileLocked(UserHandle.getUserId(win.mOwnerUid));
+ return win.showForAllUsers()
+ || mWmService.isCurrentProfile(UserHandle.getUserId(win.mOwnerUid));
}
private static void applyInsets(Region outRegion, Rect frame, Rect inset) {
@@ -3716,7 +3792,7 @@
pw.println(" mSession=" + mSession
+ " mClient=" + mClient.asBinder());
pw.println(prefix + "mOwnerUid=" + mOwnerUid
- + " mShowToOwnerOnly=" + mShowToOwnerOnly
+ + " showForAllUsers=" + showForAllUsers()
+ " package=" + mAttrs.packageName
+ " appop=" + AppOpsManager.opToName(mAppOp));
pw.println(prefix + "mAttrs=" + mAttrs.toString(prefix));
@@ -4160,7 +4236,7 @@
// This must be called while inside a transaction.
boolean performShowLocked() {
- if (isHiddenFromUserLocked()) {
+ if (!showToCurrentUser()) {
if (DEBUG_VISIBILITY) Slog.w(TAG, "hiding " + this + ", belonging to " + mOwnerUid);
clearPolicyVisibilityFlag(VISIBLE_FOR_USER);
return false;
@@ -4229,7 +4305,7 @@
+ " tok.visible=" + (mActivityRecord != null && mActivityRecord.isVisible())
+ " animating=" + isAnimating(TRANSITION | PARENTS)
+ " tok animating="
- + (mActivityRecord != null && mActivityRecord.isAnimating(TRANSITION))
+ + (mActivityRecord != null && mActivityRecord.isAnimating(TRANSITION | PARENTS))
+ " Callers=" + Debug.getCallers(4));
}
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 1a11766..486616d 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1398,7 +1398,7 @@
mWin.getDisplayContent().adjustForImeIfNeeded();
}
- return mWin.isAnimating(TRANSITION | PARENTS);
+ return mWin.isAnimating(PARENTS);
}
void dumpDebug(ProtoOutputStream proto, long fieldId) {
diff --git a/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java b/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java
new file mode 100644
index 0000000..94f6676
--- /dev/null
+++ b/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java
@@ -0,0 +1,97 @@
+/*
+ * 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.wm.utils;
+
+import android.graphics.Bitmap;
+import android.graphics.ColorSpace;
+import android.graphics.GraphicBuffer;
+import android.graphics.Matrix;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.view.Display;
+import android.view.Surface;
+import android.view.SurfaceControl;
+
+
+/** Helper functions for the {@link com.android.server.wm.ScreenRotationAnimation} class*/
+public class RotationAnimationUtils {
+
+ /**
+ * Converts the provided {@link GraphicBuffer} and converts it to a bitmap to then sample the
+ * luminance at the borders of the bitmap
+ * @return the average luminance of all the pixels at the borders of the bitmap
+ */
+ public static float getAvgBorderLuma(GraphicBuffer graphicBuffer, ColorSpace colorSpace) {
+ Bitmap hwBitmap = Bitmap.wrapHardwareBuffer(graphicBuffer, colorSpace);
+ if (hwBitmap == null) {
+ return 0;
+ }
+
+ Bitmap swaBitmap = hwBitmap.copy(Bitmap.Config.ARGB_8888, false);
+ float totalLuma = 0;
+ int height = swaBitmap.getHeight();
+ int width = swaBitmap.getWidth();
+ int i;
+ for (i = 0; i < width; i++) {
+ totalLuma += swaBitmap.getColor(i, 0).luminance();
+ totalLuma += swaBitmap.getColor(i, height - 1).luminance();
+ }
+ for (i = 0; i < height; i++) {
+ totalLuma += swaBitmap.getColor(0, i).luminance();
+ totalLuma += swaBitmap.getColor(width - 1, i).luminance();
+ }
+ return totalLuma / (2 * width + 2 * height);
+ }
+
+ /**
+ * Gets the average border luma by taking a screenshot of the {@param surfaceControl}.
+ * @see #getAvgBorderLuma(GraphicBuffer, ColorSpace)
+ */
+ public static float getLumaOfSurfaceControl(Display display, SurfaceControl surfaceControl) {
+ if (surfaceControl == null) {
+ return 0;
+ }
+
+ Point size = new Point();
+ display.getSize(size);
+ Rect crop = new Rect(0, 0, size.x, size.y);
+ SurfaceControl.ScreenshotGraphicBuffer buffer =
+ SurfaceControl.captureLayers(surfaceControl, crop, 1);
+ return RotationAnimationUtils.getAvgBorderLuma(buffer.getGraphicBuffer(),
+ buffer.getColorSpace());
+ }
+
+ public static void createRotationMatrix(int rotation, int width, int height, Matrix outMatrix) {
+ switch (rotation) {
+ case Surface.ROTATION_0:
+ outMatrix.reset();
+ break;
+ case Surface.ROTATION_90:
+ outMatrix.setRotate(90, 0, 0);
+ outMatrix.postTranslate(height, 0);
+ break;
+ case Surface.ROTATION_180:
+ outMatrix.setRotate(180, 0, 0);
+ outMatrix.postTranslate(width, height);
+ break;
+ case Surface.ROTATION_270:
+ outMatrix.setRotate(270, 0, 0);
+ outMatrix.postTranslate(0, width);
+ break;
+ }
+ }
+}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index fee29db..1ad6e86 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -138,6 +138,7 @@
"android.hardware.thermal@1.0",
"android.hardware.tv.cec@1.0",
"android.hardware.tv.input@1.0",
+ "android.hardware.vibrator-cpp",
"android.hardware.vibrator@1.0",
"android.hardware.vibrator@1.1",
"android.hardware.vibrator@1.2",
@@ -148,7 +149,6 @@
"android.system.suspend@1.0",
"service.incremental",
"suspend_control_aidl_interface-cpp",
- "vintf-vibrator-cpp",
],
static_libs: [
diff --git a/services/core/jni/com_android_server_net_NetworkStatsService.cpp b/services/core/jni/com_android_server_net_NetworkStatsService.cpp
index 4d4a7b4..4696dd0 100644
--- a/services/core/jni/com_android_server_net_NetworkStatsService.cpp
+++ b/services/core/jni/com_android_server_net_NetworkStatsService.cpp
@@ -54,7 +54,7 @@
TCP_TX_PACKETS = 5
};
-static uint64_t getStatsType(struct Stats* stats, StatsType type) {
+static uint64_t getStatsType(Stats* stats, StatsType type) {
switch (type) {
case RX_BYTES:
return stats->rxBytes;
@@ -73,7 +73,7 @@
}
}
-static int parseIfaceStats(const char* iface, struct Stats* stats) {
+static int parseIfaceStats(const char* iface, Stats* stats) {
FILE *fp = fopen(QTAGUID_IFACE_STATS, "r");
if (fp == NULL) {
return -1;
@@ -117,7 +117,7 @@
return 0;
}
-static int parseUidStats(const uint32_t uid, struct Stats* stats) {
+static int parseUidStats(const uint32_t uid, Stats* stats) {
FILE *fp = fopen(QTAGUID_UID_STATS, "r");
if (fp == NULL) {
return -1;
@@ -150,8 +150,7 @@
}
static jlong getTotalStat(JNIEnv* env, jclass clazz, jint type, jboolean useBpfStats) {
- struct Stats stats;
- memset(&stats, 0, sizeof(Stats));
+ Stats stats = {};
if (useBpfStats) {
if (bpfGetIfaceStats(NULL, &stats) == 0) {
@@ -175,8 +174,7 @@
return UNKNOWN;
}
- struct Stats stats;
- memset(&stats, 0, sizeof(Stats));
+ Stats stats = {};
if (useBpfStats) {
if (bpfGetIfaceStats(iface8.c_str(), &stats) == 0) {
@@ -194,8 +192,7 @@
}
static jlong getUidStat(JNIEnv* env, jclass clazz, jint uid, jint type, jboolean useBpfStats) {
- struct Stats stats;
- memset(&stats, 0, sizeof(Stats));
+ Stats stats = {};
if (useBpfStats) {
if (bpfGetUidStats(uid, &stats) == 0) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 5a1d552..8641059 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -64,4 +64,8 @@
}
public void setLocationEnabled(ComponentName who, boolean locationEnabled) {}
+
+ public boolean isOrganizationOwnedDeviceWithManagedProfile() {
+ return false;
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 272edf7..2a08f5c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -215,6 +215,7 @@
import android.provider.ContactsInternal;
import android.provider.Settings;
import android.provider.Settings.Global;
+import android.provider.Telephony;
import android.security.IKeyChainAliasCallback;
import android.security.IKeyChainService;
import android.security.KeyChain;
@@ -366,6 +367,8 @@
private static final String TAG_TRANSFER_OWNERSHIP_BUNDLE = "transfer-ownership-bundle";
+ private static final String TAG_PROTECTED_PACKAGES = "protected-packages";
+
private static final int REQUEST_EXPIRE_PASSWORD = 5571;
private static final long MS_PER_DAY = TimeUnit.DAYS.toMillis(1);
@@ -741,6 +744,9 @@
// This is the list of component allowed to start lock task mode.
List<String> mLockTaskPackages = new ArrayList<>();
+ // List of packages protected by device owner
+ List<String> mProtectedPackages = new ArrayList<>();
+
// Bitfield of feature flags to be enabled during LockTask mode.
// We default on the power button menu, in order to be consistent with pre-P behaviour.
int mLockTaskFeatures = DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS;
@@ -3244,6 +3250,13 @@
out.endTag(null, TAG_OWNER_INSTALLED_CA_CERT);
}
+ for (int i = 0, size = policy.mProtectedPackages.size(); i < size; i++) {
+ String packageName = policy.mProtectedPackages.get(i);
+ out.startTag(null, TAG_PROTECTED_PACKAGES);
+ out.attribute(null, ATTR_NAME, packageName);
+ out.endTag(null, TAG_PROTECTED_PACKAGES);
+ }
+
out.endTag(null, "policies");
out.endDocument();
@@ -3357,6 +3370,7 @@
policy.mAdminMap.clear();
policy.mAffiliationIds.clear();
policy.mOwnerInstalledCaCerts.clear();
+ policy.mProtectedPackages.clear();
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
@@ -3454,6 +3468,8 @@
policy.mCurrentInputMethodSet = true;
} else if (TAG_OWNER_INSTALLED_CA_CERT.equals(tag)) {
policy.mOwnerInstalledCaCerts.add(parser.getAttributeValue(null, ATTR_ALIAS));
+ } else if (TAG_PROTECTED_PACKAGES.equals(tag)) {
+ policy.mProtectedPackages.add(parser.getAttributeValue(null, ATTR_NAME));
} else {
Slog.w(LOG_TAG, "Unknown tag: " + tag);
XmlUtils.skipCurrentTag(parser);
@@ -3485,6 +3501,7 @@
updateMaximumTimeToLockLocked(userHandle);
updateLockTaskPackagesLocked(policy.mLockTaskPackages, userHandle);
updateLockTaskFeaturesLocked(policy.mLockTaskFeatures, userHandle);
+ updateProtectedPackagesLocked(policy.mProtectedPackages);
if (policy.mStatusBarDisabled) {
setStatusBarDisabledInternal(policy.mStatusBarDisabled, userHandle);
}
@@ -3510,6 +3527,10 @@
}
}
+ private void updateProtectedPackagesLocked(List<String> packages) {
+ mInjector.getPackageManagerInternal().setDeviceOwnerProtectedPackages(packages);
+ }
+
private void updateLockTaskFeaturesLocked(int flags, int userId) {
long ident = mInjector.binderClearCallingIdentity();
try {
@@ -5806,8 +5827,9 @@
// Make sure that the caller is the profile owner or delegate.
enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
DELEGATION_CERT_INSTALL);
- // Verify that the profile owner was granted access to Device IDs.
- if (canProfileOwnerAccessDeviceIds(userId)) {
+ // Verify that the managed profile is on an organization-owned device and as such
+ // the profile owner can access Device IDs.
+ if (isProfileOwnerOfOrganizationOwnedDevice(userId)) {
return;
}
throw new SecurityException(
@@ -7724,7 +7746,13 @@
}
}
// Tell the user manager that the restrictions have changed.
- pushUserRestrictions(parent ? getProfileParentId(userHandle) : userHandle);
+ final int affectedUserId = parent ? getProfileParentId(userHandle) : userHandle;
+ pushUserRestrictions(affectedUserId);
+
+ if (SecurityLog.isLoggingEnabled()) {
+ SecurityLog.writeEvent(SecurityLog.TAG_CAMERA_POLICY_SET,
+ who.getPackageName(), userHandle, affectedUserId, disabled ? 1 : 0);
+ }
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_CAMERA_DISABLED)
.setAdmin(who)
@@ -8004,7 +8032,7 @@
}
}
- private boolean canProfileOwnerAccessDeviceIds(int userId) {
+ private boolean isProfileOwnerOfOrganizationOwnedDevice(int userId) {
synchronized (getLockObject()) {
return mOwners.isProfileOwnerOfOrganizationOwnedDevice(userId);
}
@@ -8027,7 +8055,7 @@
}
private boolean isProfileOwnerOfOrganizationOwnedDevice(ComponentName who, int userId) {
- return isProfileOwner(who, userId) && canProfileOwnerAccessDeviceIds(userId);
+ return isProfileOwner(who, userId) && isProfileOwnerOfOrganizationOwnedDevice(userId);
}
@Override
@@ -8341,6 +8369,8 @@
policy.mLockTaskPackages.clear();
updateLockTaskPackagesLocked(policy.mLockTaskPackages, userId);
policy.mLockTaskFeatures = DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
+ policy.mProtectedPackages.clear();
+ updateProtectedPackagesLocked(policy.mProtectedPackages);
saveSettingsLocked(userId);
try {
@@ -8523,7 +8553,6 @@
if (!mHasFeature) {
return null;
}
-
synchronized (getLockObject()) {
return mOwners.getProfileOwnerComponent(userHandle);
}
@@ -8554,7 +8583,7 @@
for (UserInfo userInfo : mUserManager.getProfiles(userHandle)) {
if (userInfo.isManagedProfile()) {
if (getProfileOwner(userInfo.id) != null
- && canProfileOwnerAccessDeviceIds(userInfo.id)) {
+ && isProfileOwnerOfOrganizationOwnedDevice(userInfo.id)) {
ComponentName who = getProfileOwner(userInfo.id);
return getActiveAdminUncheckedLocked(who, userInfo.id);
}
@@ -8578,6 +8607,24 @@
}
@Override
+ public boolean isOrganizationOwnedDeviceWithManagedProfile() {
+ if (!mHasFeature) {
+ return false;
+ }
+ enforceManageUsers();
+
+ return mInjector.binderWithCleanCallingIdentity(() -> {
+ for (UserInfo ui : mUserManager.getUsers()) {
+ if (ui.isManagedProfile() && isProfileOwnerOfOrganizationOwnedDevice(ui.id)) {
+ return true;
+ }
+ }
+
+ return false;
+ });
+ }
+
+ @Override
public boolean checkDeviceIdentifierAccess(String packageName, int pid, int uid) {
ensureCallerIdentityMatchesIfNotSystem(packageName, pid, uid);
@@ -8605,7 +8652,7 @@
final boolean isCallerProfileOwnerOrDelegate = profileOwner != null
&& (profileOwner.getPackageName().equals(packageName)
|| isCallerDelegate(packageName, uid, DELEGATION_CERT_INSTALL));
- if (isCallerProfileOwnerOrDelegate && canProfileOwnerAccessDeviceIds(userId)) {
+ if (isCallerProfileOwnerOrDelegate && isProfileOwnerOfOrganizationOwnedDevice(userId)) {
return true;
}
//TODO(b/130844684): Temporarily allow profile owner on non-organization-owned devices
@@ -8818,6 +8865,26 @@
}
}
+ private void enforceAcrossUsersPermissions() {
+ if (isCallerWithSystemUid() || mInjector.binderGetCallingUid() == Process.ROOT_UID) {
+ return;
+ }
+ if (mContext.checkCallingPermission(permission.INTERACT_ACROSS_PROFILES)
+ == PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ if (mContext.checkCallingPermission(permission.INTERACT_ACROSS_USERS)
+ == PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ if (mContext.checkCallingPermission(permission.INTERACT_ACROSS_USERS_FULL)
+ == PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ throw new SecurityException("Calling user does not have INTERACT_ACROSS_PROFILES or"
+ + "INTERACT_ACROSS_USERS or INTERACT_ACROSS_USERS_FULL permissions");
+ }
+
private void enforceFullCrossUsersPermission(int userHandle) {
enforceSystemUserOrPermissionIfCrossUser(userHandle,
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
@@ -9038,6 +9105,10 @@
pw.increaseIndent();
pw.print("mPasswordOwner="); pw.println(policy.mPasswordOwner);
pw.decreaseIndent();
+ pw.println();
+ pw.increaseIndent();
+ pw.print("mProtectedPackages="); pw.println(policy.mProtectedPackages);
+ pw.decreaseIndent();
}
}
@@ -11059,7 +11130,7 @@
@Override
public boolean setTime(ComponentName who, long millis) {
Objects.requireNonNull(who, "ComponentName is null in setTime");
- enforceDeviceOwner(who);
+ enforceDeviceOwnerOrProfileOwnerOnOrganizationOwnedDevice(who);
// Don't allow set time when auto time is on.
if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME, 0) == 1) {
return false;
@@ -11074,7 +11145,7 @@
@Override
public boolean setTimeZone(ComponentName who, String timeZone) {
Objects.requireNonNull(who, "ComponentName is null in setTimeZone");
- enforceDeviceOwner(who);
+ enforceDeviceOwnerOrProfileOwnerOnOrganizationOwnedDevice(who);
// Don't allow set timezone when auto timezone is on.
if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) == 1) {
return false;
@@ -14113,18 +14184,14 @@
Objects.requireNonNull(apnSetting, "ApnSetting is null in addOverrideApn");
enforceDeviceOwner(who);
- int operatedId = -1;
- Uri resultUri = mInjector.binderWithCleanCallingIdentity(() ->
- mContext.getContentResolver().insert(DPC_URI, apnSetting.toContentValues()));
- if (resultUri != null) {
- try {
- operatedId = Integer.parseInt(resultUri.getLastPathSegment());
- } catch (NumberFormatException e) {
- Slog.e(LOG_TAG, "Failed to parse inserted override APN id.", e);
- }
+ TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+ if (tm != null) {
+ return mInjector.binderWithCleanCallingIdentity(
+ () -> tm.addDevicePolicyOverrideApn(mContext, apnSetting));
+ } else {
+ Log.w(LOG_TAG, "TelephonyManager is null when trying to add override apn");
+ return Telephony.Carriers.INVALID_APN_ID;
}
-
- return operatedId;
}
@Override
@@ -14140,10 +14207,14 @@
if (apnId < 0) {
return false;
}
- return mInjector.binderWithCleanCallingIdentity(() ->
- mContext.getContentResolver().update(
- Uri.withAppendedPath(DPC_URI, Integer.toString(apnId)),
- apnSetting.toContentValues(), null, null) > 0);
+ TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+ if (tm != null) {
+ return mInjector.binderWithCleanCallingIdentity(
+ () -> tm.modifyDevicePolicyOverrideApn(mContext, apnId, apnSetting));
+ } else {
+ Log.w(LOG_TAG, "TelephonyManager is null when trying to modify override apn");
+ return false;
+ }
}
@Override
@@ -14179,23 +14250,13 @@
}
private List<ApnSetting> getOverrideApnsUnchecked() {
- final Cursor cursor = mInjector.binderWithCleanCallingIdentity(
- () -> mContext.getContentResolver().query(DPC_URI, null, null, null, null));
-
- if (cursor == null) {
- return Collections.emptyList();
+ TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+ if (tm != null) {
+ return mInjector.binderWithCleanCallingIdentity(
+ () -> tm.getDevicePolicyOverrideApns(mContext));
}
- try {
- List<ApnSetting> apnList = new ArrayList<ApnSetting>();
- cursor.moveToPosition(-1);
- while (cursor.moveToNext()) {
- ApnSetting apn = ApnSetting.makeApnSetting(cursor);
- apnList.add(apn);
- }
- return apnList;
- } finally {
- cursor.close();
- }
+ Log.w(LOG_TAG, "TelephonyManager is null when trying to get override apns");
+ return Collections.emptyList();
}
@Override
@@ -14505,6 +14566,53 @@
}
@Override
+ public List<String> getAllCrossProfilePackages() {
+ if (!mHasFeature) {
+ return Collections.emptyList();
+ }
+ enforceAcrossUsersPermissions();
+
+ synchronized (getLockObject()) {
+ final List<ActiveAdmin> admins = getProfileOwnerAdminsForCurrentProfileGroup();
+ final List<String> packages = getCrossProfilePackagesForAdmins(admins);
+
+ packages.addAll(getDefaultCrossProfilePackages());
+
+ return packages;
+ }
+ }
+
+ private List<String> getCrossProfilePackagesForAdmins(List<ActiveAdmin> admins) {
+ final List<String> packages = new ArrayList<>();
+ for (int i = 0; i < admins.size(); i++) {
+ packages.addAll(admins.get(i).mCrossProfilePackages);
+ }
+ return packages;
+ }
+
+ private List<String> getDefaultCrossProfilePackages() {
+ return Arrays.asList(mContext.getResources()
+ .getStringArray(R.array.cross_profile_apps));
+ }
+
+ private List<ActiveAdmin> getProfileOwnerAdminsForCurrentProfileGroup() {
+ synchronized (getLockObject()) {
+ final List<ActiveAdmin> admins = new ArrayList<>();
+ int[] users = mUserManager.getProfileIdsWithDisabled(UserHandle.getCallingUserId());
+ for (int i = 0; i < users.length; i++) {
+ final ComponentName componentName = getProfileOwner(users[i]);
+ if (componentName != null) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(componentName, users[i]);
+ if (admin != null) {
+ admins.add(admin);
+ }
+ }
+ }
+ return admins;
+ }
+ }
+
+ @Override
public boolean isManagedKiosk() {
if (!mHasFeature) {
return false;
@@ -14636,4 +14744,42 @@
return DevicePolicyConstants.loadFromString(
mInjector.settingsGlobalGetString(Global.DEVICE_POLICY_CONSTANTS));
}
+
+ @Override
+ public void setProtectedPackages(ComponentName who, List<String> packages) {
+ Preconditions.checkNotNull(who, "ComponentName is null");
+ Preconditions.checkNotNull(packages, "packages is null");
+
+ enforceDeviceOwner(who);
+ synchronized (getLockObject()) {
+ final int userHandle = mInjector.userHandleGetCallingUserId();
+ setProtectedPackagesLocked(userHandle, packages);
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_PACKAGES_PROTECTED)
+ .setAdmin(who)
+ .setStrings(packages.toArray(new String[packages.size()]))
+ .write();
+ }
+ }
+
+ private void setProtectedPackagesLocked(int userHandle, List<String> packages) {
+ final DevicePolicyData policy = getUserData(userHandle);
+ policy.mProtectedPackages = packages;
+
+ // Store the settings persistently.
+ saveSettingsLocked(userHandle);
+ updateProtectedPackagesLocked(packages);
+ }
+
+ @Override
+ public List<String> getProtectedPackages(ComponentName who) {
+ Preconditions.checkNotNull(who, "ComponentName is null");
+
+ enforceDeviceOwner(who);
+ final int userHandle = mInjector.binderGetCallingUserHandle().getIdentifier();
+ synchronized (getLockObject()) {
+ final List<String> packages = getUserData(userHandle).mProtectedPackages;
+ return packages == null ? Collections.EMPTY_LIST : packages;
+ }
+ }
}
diff --git a/services/incremental/Android.bp b/services/incremental/Android.bp
index 2661925..ddf4dd5 100644
--- a/services/incremental/Android.bp
+++ b/services/incremental/Android.bp
@@ -69,7 +69,6 @@
name: "service.incremental",
defaults: [
"service.incremental-defaults",
- "linux_bionic_supported",
],
export_include_dirs: ["include/",],
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index cfe1318..bfec51c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -126,6 +126,7 @@
import com.android.server.os.BugreportManagerService;
import com.android.server.os.DeviceIdentifiersPolicyService;
import com.android.server.os.SchedulingPolicyService;
+import com.android.server.people.PeopleService;
import com.android.server.pm.BackgroundDexOptService;
import com.android.server.pm.CrossProfileAppsService;
import com.android.server.pm.DataLoaderManagerService;
@@ -214,6 +215,8 @@
"com.android.server.companion.CompanionDeviceManagerService";
private static final String STATS_COMPANION_LIFECYCLE_CLASS =
"com.android.server.stats.StatsCompanion$Lifecycle";
+ private static final String STATS_PULL_ATOM_SERVICE_CLASS =
+ "com.android.server.stats.StatsPullAtomService";
private static final String USB_SERVICE_CLASS =
"com.android.server.usb.UsbService$Lifecycle";
private static final String MIDI_SERVICE_CLASS =
@@ -1915,6 +1918,10 @@
t.traceBegin("StartCrossProfileAppsService");
mSystemServiceManager.startService(CrossProfileAppsService.class);
t.traceEnd();
+
+ t.traceBegin("StartPeopleService");
+ mSystemServiceManager.startService(PeopleService.class);
+ t.traceEnd();
}
if (!isWatch) {
@@ -1975,6 +1982,11 @@
mSystemServiceManager.startService(STATS_COMPANION_LIFECYCLE_CLASS);
t.traceEnd();
+ // Statsd pulled atoms
+ t.traceBegin("StartStatsPullAtomService");
+ mSystemServiceManager.startService(STATS_PULL_ATOM_SERVICE_CLASS);
+ t.traceEnd();
+
// Incidentd and dumpstated helper
t.traceBegin("StartIncidentCompanionService");
mSystemServiceManager.startService(IncidentCompanionService.class);
diff --git a/services/people/Android.bp b/services/people/Android.bp
new file mode 100644
index 0000000..d64097a
--- /dev/null
+++ b/services/people/Android.bp
@@ -0,0 +1,5 @@
+java_library_static {
+ name: "services.people",
+ srcs: ["java/**/*.java"],
+ libs: ["services.core"],
+}
diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java
new file mode 100644
index 0000000..ef3da60
--- /dev/null
+++ b/services/people/java/com/android/server/people/PeopleService.java
@@ -0,0 +1,141 @@
+/*
+ * 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.people;
+
+import android.app.prediction.AppPredictionContext;
+import android.app.prediction.AppPredictionSessionId;
+import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetEvent;
+import android.app.prediction.IPredictionCallback;
+import android.content.Context;
+import android.content.pm.ParceledListSlice;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.SystemService;
+
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+
+/**
+ * A service that manages the people and conversations provided by apps.
+ */
+public class PeopleService extends SystemService {
+
+ private static final String TAG = "PeopleService";
+
+ /**
+ * Initializes the system service.
+ *
+ * @param context The system server context.
+ */
+ public PeopleService(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ publishLocalService(PeopleServiceInternal.class, new LocalService());
+ }
+
+ @VisibleForTesting
+ final class LocalService extends PeopleServiceInternal {
+
+ private Map<AppPredictionSessionId, SessionInfo> mSessions = new ArrayMap<>();
+
+ @Override
+ public void onCreatePredictionSession(AppPredictionContext context,
+ AppPredictionSessionId sessionId) {
+ mSessions.put(sessionId, new SessionInfo(context));
+ }
+
+ @Override
+ public void notifyAppTargetEvent(AppPredictionSessionId sessionId, AppTargetEvent event) {
+ runForSession(sessionId,
+ sessionInfo -> sessionInfo.getPredictor().onAppTargetEvent(event));
+ }
+
+ @Override
+ public void notifyLaunchLocationShown(AppPredictionSessionId sessionId,
+ String launchLocation, ParceledListSlice targetIds) {
+ runForSession(sessionId,
+ sessionInfo -> sessionInfo.getPredictor().onLaunchLocationShown(
+ launchLocation, targetIds.getList()));
+ }
+
+ @Override
+ public void sortAppTargets(AppPredictionSessionId sessionId, ParceledListSlice targets,
+ IPredictionCallback callback) {
+ runForSession(sessionId,
+ sessionInfo -> sessionInfo.getPredictor().onSortAppTargets(
+ targets.getList(),
+ targetList -> invokePredictionCallback(callback, targetList)));
+ }
+
+ @Override
+ public void registerPredictionUpdates(AppPredictionSessionId sessionId,
+ IPredictionCallback callback) {
+ runForSession(sessionId, sessionInfo -> sessionInfo.addCallback(callback));
+ }
+
+ @Override
+ public void unregisterPredictionUpdates(AppPredictionSessionId sessionId,
+ IPredictionCallback callback) {
+ runForSession(sessionId, sessionInfo -> sessionInfo.removeCallback(callback));
+ }
+
+ @Override
+ public void requestPredictionUpdate(AppPredictionSessionId sessionId) {
+ runForSession(sessionId,
+ sessionInfo -> sessionInfo.getPredictor().onRequestPredictionUpdate());
+ }
+
+ @Override
+ public void onDestroyPredictionSession(AppPredictionSessionId sessionId) {
+ runForSession(sessionId, sessionInfo -> {
+ sessionInfo.onDestroy();
+ mSessions.remove(sessionId);
+ });
+ }
+
+ @VisibleForTesting
+ SessionInfo getSessionInfo(AppPredictionSessionId sessionId) {
+ return mSessions.get(sessionId);
+ }
+
+ private void runForSession(AppPredictionSessionId sessionId, Consumer<SessionInfo> method) {
+ SessionInfo sessionInfo = mSessions.get(sessionId);
+ if (sessionInfo == null) {
+ Slog.e(TAG, "Failed to find the session: " + sessionId);
+ return;
+ }
+ method.accept(sessionInfo);
+ }
+
+ private void invokePredictionCallback(IPredictionCallback callback,
+ List<AppTarget> targets) {
+ try {
+ callback.onResult(new ParceledListSlice<>(targets));
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to calling callback" + e);
+ }
+ }
+ }
+}
diff --git a/services/people/java/com/android/server/people/SessionInfo.java b/services/people/java/com/android/server/people/SessionInfo.java
new file mode 100644
index 0000000..df7cedf
--- /dev/null
+++ b/services/people/java/com/android/server/people/SessionInfo.java
@@ -0,0 +1,72 @@
+/*
+ * 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.people;
+
+import android.app.prediction.AppPredictionContext;
+import android.app.prediction.AppTarget;
+import android.app.prediction.IPredictionCallback;
+import android.content.pm.ParceledListSlice;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.people.prediction.ConversationPredictor;
+
+import java.util.List;
+
+/** Manages the information and callbacks in an app prediction request session. */
+class SessionInfo {
+
+ private static final String TAG = "SessionInfo";
+
+ private final ConversationPredictor mConversationPredictor;
+ private final RemoteCallbackList<IPredictionCallback> mCallbacks =
+ new RemoteCallbackList<>();
+
+ SessionInfo(AppPredictionContext predictionContext) {
+ mConversationPredictor = new ConversationPredictor(predictionContext,
+ this::updatePredictions);
+ }
+
+ void addCallback(IPredictionCallback callback) {
+ mCallbacks.register(callback);
+ }
+
+ void removeCallback(IPredictionCallback callback) {
+ mCallbacks.unregister(callback);
+ }
+
+ ConversationPredictor getPredictor() {
+ return mConversationPredictor;
+ }
+
+ void onDestroy() {
+ mCallbacks.kill();
+ }
+
+ private void updatePredictions(List<AppTarget> targets) {
+ int callbackCount = mCallbacks.beginBroadcast();
+ for (int i = 0; i < callbackCount; i++) {
+ try {
+ mCallbacks.getBroadcastItem(i).onResult(new ParceledListSlice<>(targets));
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to calling callback" + e);
+ }
+ }
+ mCallbacks.finishBroadcast();
+ }
+}
diff --git a/services/people/java/com/android/server/people/prediction/ConversationPredictor.java b/services/people/java/com/android/server/people/prediction/ConversationPredictor.java
new file mode 100644
index 0000000..de71d29
--- /dev/null
+++ b/services/people/java/com/android/server/people/prediction/ConversationPredictor.java
@@ -0,0 +1,85 @@
+/*
+ * 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.people.prediction;
+
+import android.annotation.MainThread;
+import android.app.prediction.AppPredictionContext;
+import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetEvent;
+import android.app.prediction.AppTargetId;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.function.Consumer;
+
+/**
+ * Predictor that predicts the conversations or apps the user is most likely to open.
+ */
+public class ConversationPredictor {
+
+ private final AppPredictionContext mPredictionContext;
+ private final Consumer<List<AppTarget>> mUpdatePredictionsMethod;
+ private final ExecutorService mCallbackExecutor;
+
+ public ConversationPredictor(AppPredictionContext predictionContext,
+ Consumer<List<AppTarget>> updatePredictionsMethod) {
+ mPredictionContext = predictionContext;
+ mUpdatePredictionsMethod = updatePredictionsMethod;
+ mCallbackExecutor = Executors.newSingleThreadExecutor();
+ }
+
+ /**
+ * Called by the client app to indicate a target launch.
+ */
+ @MainThread
+ public void onAppTargetEvent(AppTargetEvent event) {
+ }
+
+ /**
+ * Called by the client app to indicate a particular location has been shown to the user.
+ */
+ @MainThread
+ public void onLaunchLocationShown(String launchLocation, List<AppTargetId> targetIds) {
+ }
+
+ /**
+ * Called by the client app to request sorting of the provided targets based on the prediction
+ * ranking.
+ */
+ @MainThread
+ public void onSortAppTargets(List<AppTarget> targets, Consumer<List<AppTarget>> callback) {
+ mCallbackExecutor.execute(() -> callback.accept(targets));
+ }
+
+ /**
+ * Called by the client app to request target predictions.
+ */
+ @MainThread
+ public void onRequestPredictionUpdate() {
+ List<AppTarget> targets = new ArrayList<>();
+ mCallbackExecutor.execute(() -> mUpdatePredictionsMethod.accept(targets));
+ }
+
+ @VisibleForTesting
+ public Consumer<List<AppTarget>> getUpdatePredictionsMethod() {
+ return mUpdatePredictionsMethod;
+ }
+}
diff --git a/services/robotests/src/com/android/server/location/LocationRequestStatisticsTest.java b/services/robotests/src/com/android/server/location/LocationRequestStatisticsTest.java
new file mode 100644
index 0000000..4cbdbd17
--- /dev/null
+++ b/services/robotests/src/com/android/server/location/LocationRequestStatisticsTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+
+import com.android.internal.util.IndentingPrintWriter;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * Unit tests for {@link LocationRequestStatistics}.
+ */
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class LocationRequestStatisticsTest {
+
+ /**
+ * Check adding and removing requests & strings
+ */
+ @Test
+ public void testRequestSummary() {
+ LocationRequestStatistics.RequestSummary summary =
+ new LocationRequestStatistics.RequestSummary(
+ "com.example", "gps", 1000);
+ StringWriter stringWriter = new StringWriter();
+ summary.dump(new IndentingPrintWriter(new PrintWriter(stringWriter), " "), 1234);
+ assertThat(stringWriter.toString()).startsWith("At");
+
+ StringWriter stringWriterRemove = new StringWriter();
+ summary = new LocationRequestStatistics.RequestSummary(
+ "com.example", "gps",
+ LocationRequestStatistics.RequestSummary.REQUEST_ENDED_INTERVAL);
+ summary.dump(new IndentingPrintWriter(new PrintWriter(stringWriterRemove), " "), 2345);
+ assertThat(stringWriterRemove.toString()).contains("-");
+ }
+
+ /**
+ * Check summary list size capping
+ */
+ @Test
+ public void testSummaryList() {
+ LocationRequestStatistics statistics = new LocationRequestStatistics();
+ statistics.history.addRequest("com.example", "gps", 1000);
+ assertThat(statistics.history.mList.size()).isEqualTo(1);
+ // Try (not) to overflow
+ for (int i = 0; i < LocationRequestStatistics.RequestSummaryLimitedHistory.MAX_SIZE; i++) {
+ statistics.history.addRequest("com.example", "gps", 1000);
+ }
+ assertThat(statistics.history.mList.size()).isEqualTo(
+ LocationRequestStatistics.RequestSummaryLimitedHistory.MAX_SIZE);
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
index 0504d14..067f23a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
@@ -16,6 +16,8 @@
package com.android.server.appop;
import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+import static android.app.AppOpsManager.FILTER_BY_PACKAGE_NAME;
+import static android.app.AppOpsManager.FILTER_BY_UID;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_ERRORED;
import static android.app.AppOpsManager.MODE_FOREGROUND;
@@ -287,7 +289,7 @@
mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null, false, null);
AppOpsManager.HistoricalOps historicalOps = new AppOpsManager.HistoricalOps(0, 15000);
- historicalOps.increaseAccessCount(OP_READ_SMS, mMyUid, sMyPackageName,
+ historicalOps.increaseAccessCount(OP_READ_SMS, mMyUid, sMyPackageName, null,
AppOpsManager.UID_STATE_PERSISTENT, 0, 1);
mAppOpsService.addHistoricalOps(historicalOps);
@@ -300,8 +302,8 @@
});
// First, do a fetch to ensure it's written
- mAppOpsService.getHistoricalOps(mMyUid, sMyPackageName, null, 0, Long.MAX_VALUE, 0,
- callback);
+ mAppOpsService.getHistoricalOps(mMyUid, sMyPackageName, null, null,
+ FILTER_BY_UID | FILTER_BY_PACKAGE_NAME, 0, Long.MAX_VALUE, 0, callback);
latchRef.get().await(5, TimeUnit.SECONDS);
assertThat(latchRef.get().getCount()).isEqualTo(0);
@@ -312,8 +314,8 @@
latchRef.set(new CountDownLatch(1));
- mAppOpsService.getHistoricalOps(mMyUid, sMyPackageName, null, 0, Long.MAX_VALUE, 0,
- callback);
+ mAppOpsService.getHistoricalOps(mMyUid, sMyPackageName, null, null,
+ FILTER_BY_UID | FILTER_BY_PACKAGE_NAME, 0, Long.MAX_VALUE, 0, callback);
latchRef.get().await(5, TimeUnit.SECONDS);
assertThat(latchRef.get().getCount()).isEqualTo(0);
diff --git a/services/tests/mockingservicestests/src/com/android/server/utils/quota/CountQuotaTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/utils/quota/CountQuotaTrackerTest.java
new file mode 100644
index 0000000..80aec73
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/utils/quota/CountQuotaTrackerTest.java
@@ -0,0 +1,848 @@
+/*
+ * 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.utils.quota;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.utils.quota.Category.SINGLE_CATEGORY;
+import static com.android.server.utils.quota.QuotaTracker.MAX_WINDOW_SIZE_MS;
+import static com.android.server.utils.quota.QuotaTracker.MIN_WINDOW_SIZE_MS;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.AlarmManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.LongArrayQueue;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+import com.android.server.utils.quota.CountQuotaTracker.ExecutionStats;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+/**
+ * Tests for {@link CountQuotaTracker}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class CountQuotaTrackerTest {
+ private static final long SECOND_IN_MILLIS = 1000L;
+ private static final long MINUTE_IN_MILLIS = 60 * SECOND_IN_MILLIS;
+ private static final long HOUR_IN_MILLIS = 60 * MINUTE_IN_MILLIS;
+ private static final String TAG_CLEANUP = "*CountQuotaTracker.cleanup*";
+ private static final String TAG_QUOTA_CHECK = "*QuotaTracker.quota_check*";
+ private static final String TEST_PACKAGE = "com.android.frameworks.mockingservicestests";
+ private static final String TEST_TAG = "testing";
+ private static final int TEST_UID = 10987;
+ private static final int TEST_USER_ID = 0;
+
+ /** A {@link Category} to represent the ACTIVE standby bucket. */
+ private static final Category ACTIVE_BUCKET_CATEGORY = new Category("ACTIVE");
+
+ /** A {@link Category} to represent the WORKING_SET standby bucket. */
+ private static final Category WORKING_SET_BUCKET_CATEGORY = new Category("WORKING_SET");
+
+ /** A {@link Category} to represent the FREQUENT standby bucket. */
+ private static final Category FREQUENT_BUCKET_CATEGORY = new Category("FREQUENT");
+
+ /** A {@link Category} to represent the RARE standby bucket. */
+ private static final Category RARE_BUCKET_CATEGORY = new Category("RARE");
+
+ private CountQuotaTracker mQuotaTracker;
+ private final CategorizerForTest mCategorizer = new CategorizerForTest();
+ private final InjectorForTest mInjector = new InjectorForTest();
+ private final TestQuotaChangeListener mQuotaChangeListener = new TestQuotaChangeListener();
+ private BroadcastReceiver mReceiver;
+ private MockitoSession mMockingSession;
+ @Mock
+ private AlarmManager mAlarmManager;
+ @Mock
+ private Context mContext;
+
+ static class CategorizerForTest implements Categorizer {
+ private Category mCategoryToUse = SINGLE_CATEGORY;
+
+ @Override
+ public Category getCategory(int userId,
+ String packageName, String tag) {
+ return mCategoryToUse;
+ }
+ }
+
+ private static class InjectorForTest extends QuotaTracker.Injector {
+ private long mElapsedTime = SystemClock.elapsedRealtime();
+
+ @Override
+ long getElapsedRealtime() {
+ return mElapsedTime;
+ }
+
+ @Override
+ boolean isAlarmManagerReady() {
+ return true;
+ }
+ }
+
+ private static class TestQuotaChangeListener implements QuotaChangeListener {
+
+ @Override
+ public void onQuotaStateChanged(int userId, String packageName, String tag) {
+
+ }
+ }
+
+ @Before
+ public void setUp() {
+ mMockingSession = mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.LENIENT)
+ .mockStatic(LocalServices.class)
+ .startMocking();
+
+ when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
+ when(mContext.getSystemService(AlarmManager.class)).thenReturn(mAlarmManager);
+
+ // Freeze the clocks at 24 hours after this moment in time. Several tests create sessions
+ // in the past, and QuotaController sometimes floors values at 0, so if the test time
+ // causes sessions with negative timestamps, they will fail.
+ advanceElapsedClock(24 * HOUR_IN_MILLIS);
+
+ // Initialize real objects.
+ // Capture the listeners.
+ ArgumentCaptor<BroadcastReceiver> receiverCaptor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ mQuotaTracker = new CountQuotaTracker(mContext, mCategorizer, mInjector);
+ mQuotaTracker.setEnabled(true);
+ mQuotaTracker.setQuotaFree(false);
+ mQuotaTracker.registerQuotaChangeListener(mQuotaChangeListener);
+ verify(mContext, atLeastOnce()).registerReceiverAsUser(
+ receiverCaptor.capture(), eq(UserHandle.ALL), any(), any(), any());
+ mReceiver = receiverCaptor.getValue();
+ }
+
+ @After
+ public void tearDown() {
+ if (mMockingSession != null) {
+ mMockingSession.finishMocking();
+ }
+ }
+
+ /**
+ * Returns true if the two {@link LongArrayQueue}s have the same size and the same elements in
+ * the same order.
+ */
+ private static boolean longArrayQueueEquals(LongArrayQueue queue1, LongArrayQueue queue2) {
+ if (queue1 == queue2) {
+ return true;
+ } else if (queue1 == null || queue2 == null) {
+ return false;
+ }
+ if (queue1.size() == queue2.size()) {
+ for (int i = 0; i < queue1.size(); ++i) {
+ if (queue1.get(i) != queue2.get(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private void advanceElapsedClock(long incrementMs) {
+ mInjector.mElapsedTime += incrementMs;
+ }
+
+ private void logEvents(int count) {
+ logEvents(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, count);
+ }
+
+ private void logEvents(int userId, String pkgName, String tag, int count) {
+ for (int i = 0; i < count; ++i) {
+ mQuotaTracker.noteEvent(userId, pkgName, tag);
+ }
+ }
+
+ private void logEventAt(long timeElapsed) {
+ logEventAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, timeElapsed);
+ }
+
+ private void logEventAt(int userId, String pkgName, String tag, long timeElapsed) {
+ long now = mInjector.getElapsedRealtime();
+ mInjector.mElapsedTime = timeElapsed;
+ mQuotaTracker.noteEvent(userId, pkgName, tag);
+ mInjector.mElapsedTime = now;
+ }
+
+ private void logEventsAt(int userId, String pkgName, String tag, long timeElapsed, int count) {
+ for (int i = 0; i < count; ++i) {
+ logEventAt(userId, pkgName, tag, timeElapsed);
+ }
+ }
+
+ @Test
+ public void testDeleteObsoleteEventsLocked() {
+ // Count window size should only apply to event list.
+ mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 7, 2 * HOUR_IN_MILLIS);
+
+ final long now = mInjector.getElapsedRealtime();
+
+ logEventAt(now - 6 * HOUR_IN_MILLIS);
+ logEventAt(now - 5 * HOUR_IN_MILLIS);
+ logEventAt(now - 4 * HOUR_IN_MILLIS);
+ logEventAt(now - 3 * HOUR_IN_MILLIS);
+ logEventAt(now - 2 * HOUR_IN_MILLIS);
+ logEventAt(now - HOUR_IN_MILLIS);
+ logEventAt(now - 1);
+
+ LongArrayQueue expectedEvents = new LongArrayQueue();
+ expectedEvents.addLast(now - HOUR_IN_MILLIS);
+ expectedEvents.addLast(now - 1);
+
+ mQuotaTracker.deleteObsoleteEventsLocked();
+
+ LongArrayQueue remainingEvents = mQuotaTracker.getEvents(TEST_USER_ID, TEST_PACKAGE,
+ TEST_TAG);
+ assertTrue(longArrayQueueEquals(expectedEvents, remainingEvents));
+ }
+
+ @Test
+ public void testAppRemoval() {
+ final long now = mInjector.getElapsedRealtime();
+ logEventAt(TEST_USER_ID, "com.android.test.remove", "tag1", now - (6 * HOUR_IN_MILLIS));
+ logEventAt(TEST_USER_ID, "com.android.test.remove", "tag2",
+ now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS));
+ logEventAt(TEST_USER_ID, "com.android.test.remove", "tag3", now - (HOUR_IN_MILLIS));
+ // Test that another app isn't affected.
+ LongArrayQueue expected1 = new LongArrayQueue();
+ expected1.addLast(now - 10 * MINUTE_IN_MILLIS);
+ LongArrayQueue expected2 = new LongArrayQueue();
+ expected2.addLast(now - 70 * MINUTE_IN_MILLIS);
+ logEventAt(TEST_USER_ID, "com.android.test.stay", "tag1", now - 10 * MINUTE_IN_MILLIS);
+ logEventAt(TEST_USER_ID, "com.android.test.stay", "tag2", now - 70 * MINUTE_IN_MILLIS);
+
+ Intent removal = new Intent(Intent.ACTION_PACKAGE_FULLY_REMOVED,
+ Uri.fromParts("package", "com.android.test.remove", null));
+ removal.putExtra(Intent.EXTRA_UID, TEST_UID);
+ mReceiver.onReceive(mContext, removal);
+ assertNull(
+ mQuotaTracker.getEvents(TEST_USER_ID, "com.android.test.remove", "tag1"));
+ assertNull(
+ mQuotaTracker.getEvents(TEST_USER_ID, "com.android.test.remove", "tag2"));
+ assertNull(
+ mQuotaTracker.getEvents(TEST_USER_ID, "com.android.test.remove", "tag3"));
+ assertTrue(longArrayQueueEquals(expected1,
+ mQuotaTracker.getEvents(TEST_USER_ID, "com.android.test.stay", "tag1")));
+ assertTrue(longArrayQueueEquals(expected2,
+ mQuotaTracker.getEvents(TEST_USER_ID, "com.android.test.stay", "tag2")));
+ }
+
+ @Test
+ public void testUserRemoval() {
+ final long now = mInjector.getElapsedRealtime();
+ logEventAt(TEST_USER_ID, TEST_PACKAGE, "tag1", now - (6 * HOUR_IN_MILLIS));
+ logEventAt(TEST_USER_ID, TEST_PACKAGE, "tag2",
+ now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS));
+ logEventAt(TEST_USER_ID, TEST_PACKAGE, "tag3", now - (HOUR_IN_MILLIS));
+ // Test that another user isn't affected.
+ LongArrayQueue expected = new LongArrayQueue();
+ expected.addLast(now - (70 * MINUTE_IN_MILLIS));
+ expected.addLast(now - (10 * MINUTE_IN_MILLIS));
+ logEventAt(10, TEST_PACKAGE, "tag4", now - (70 * MINUTE_IN_MILLIS));
+ logEventAt(10, TEST_PACKAGE, "tag4", now - 10 * MINUTE_IN_MILLIS);
+
+ Intent removal = new Intent(Intent.ACTION_USER_REMOVED);
+ removal.putExtra(Intent.EXTRA_USER_HANDLE, TEST_USER_ID);
+ mReceiver.onReceive(mContext, removal);
+ assertNull(mQuotaTracker.getEvents(TEST_USER_ID, TEST_PACKAGE, "tag1"));
+ assertNull(mQuotaTracker.getEvents(TEST_USER_ID, TEST_PACKAGE, "tag2"));
+ assertNull(mQuotaTracker.getEvents(TEST_USER_ID, TEST_PACKAGE, "tag3"));
+ longArrayQueueEquals(expected, mQuotaTracker.getEvents(10, TEST_PACKAGE, "tag4"));
+ }
+
+ @Test
+ public void testUpdateExecutionStatsLocked_NoTimer() {
+ mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 3, 24 * HOUR_IN_MILLIS);
+ final long now = mInjector.getElapsedRealtime();
+
+ // Added in chronological order.
+ logEventAt(now - 4 * HOUR_IN_MILLIS);
+ logEventAt(now - HOUR_IN_MILLIS);
+ logEventAt(now - 5 * MINUTE_IN_MILLIS);
+ logEventAt(now - MINUTE_IN_MILLIS);
+
+ // Test an app that hasn't had any activity.
+ ExecutionStats expectedStats = new ExecutionStats();
+ ExecutionStats inputStats = new ExecutionStats();
+
+ inputStats.windowSizeMs = expectedStats.windowSizeMs = 12 * HOUR_IN_MILLIS;
+ inputStats.countLimit = expectedStats.countLimit = 3;
+ // Invalid time is now +24 hours since there are no sessions at all for the app.
+ expectedStats.expirationTimeElapsed = now + 24 * HOUR_IN_MILLIS;
+ mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, "com.android.test.not.run", TEST_TAG,
+ inputStats);
+ assertEquals(expectedStats, inputStats);
+
+ // Now test app that has had activity.
+
+ inputStats.windowSizeMs = expectedStats.windowSizeMs = MINUTE_IN_MILLIS;
+ // Invalid time is now since there was an event exactly windowSizeMs ago.
+ expectedStats.expirationTimeElapsed = now;
+ expectedStats.countInWindow = 1;
+ mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+ assertEquals(expectedStats, inputStats);
+
+ inputStats.windowSizeMs = expectedStats.windowSizeMs = 3 * MINUTE_IN_MILLIS;
+ expectedStats.expirationTimeElapsed = now + 2 * MINUTE_IN_MILLIS;
+ expectedStats.countInWindow = 1;
+ mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+ assertEquals(expectedStats, inputStats);
+
+ inputStats.windowSizeMs = expectedStats.windowSizeMs = 4 * MINUTE_IN_MILLIS;
+ expectedStats.expirationTimeElapsed = now + 3 * MINUTE_IN_MILLIS;
+ expectedStats.countInWindow = 1;
+ mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+ assertEquals(expectedStats, inputStats);
+
+ inputStats.windowSizeMs = expectedStats.windowSizeMs = 49 * MINUTE_IN_MILLIS;
+ // Invalid time is now +44 minutes since the earliest session in the window is now-5
+ // minutes.
+ expectedStats.expirationTimeElapsed = now + 44 * MINUTE_IN_MILLIS;
+ expectedStats.countInWindow = 2;
+ mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+ assertEquals(expectedStats, inputStats);
+
+ inputStats.windowSizeMs = expectedStats.windowSizeMs = 50 * MINUTE_IN_MILLIS;
+ expectedStats.expirationTimeElapsed = now + 45 * MINUTE_IN_MILLIS;
+ expectedStats.countInWindow = 2;
+ mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+ assertEquals(expectedStats, inputStats);
+
+ inputStats.windowSizeMs = expectedStats.windowSizeMs = HOUR_IN_MILLIS;
+ // Invalid time is now since the event is at the very edge of the window
+ // cutoff time.
+ expectedStats.expirationTimeElapsed = now;
+ expectedStats.countInWindow = 3;
+ // App is at event count limit but the oldest session is at the edge of the window, so
+ // in quota time is now.
+ expectedStats.inQuotaTimeElapsed = now;
+ mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+ assertEquals(expectedStats, inputStats);
+
+ inputStats.windowSizeMs = expectedStats.windowSizeMs = 2 * HOUR_IN_MILLIS;
+ expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
+ expectedStats.countInWindow = 3;
+ expectedStats.inQuotaTimeElapsed = now + HOUR_IN_MILLIS;
+ mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+ assertEquals(expectedStats, inputStats);
+
+ inputStats.windowSizeMs = expectedStats.windowSizeMs = 5 * HOUR_IN_MILLIS;
+ expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
+ expectedStats.countInWindow = 4;
+ expectedStats.inQuotaTimeElapsed = now + 4 * HOUR_IN_MILLIS;
+ mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+ assertEquals(expectedStats, inputStats);
+
+ inputStats.windowSizeMs = expectedStats.windowSizeMs = 6 * HOUR_IN_MILLIS;
+ expectedStats.expirationTimeElapsed = now + 2 * HOUR_IN_MILLIS;
+ expectedStats.countInWindow = 4;
+ expectedStats.inQuotaTimeElapsed = now + 5 * HOUR_IN_MILLIS;
+ mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+ assertEquals(expectedStats, inputStats);
+ }
+
+ /**
+ * Tests that getExecutionStatsLocked returns the correct stats.
+ */
+ @Test
+ public void testGetExecutionStatsLocked_Values() {
+ // The handler could cause changes to the cached stats, so prevent it from operating in
+ // this test.
+ Handler handler = mQuotaTracker.getHandler();
+ spyOn(handler);
+ doNothing().when(handler).handleMessage(any());
+
+ mQuotaTracker.setCountLimit(RARE_BUCKET_CATEGORY, 3, 24 * HOUR_IN_MILLIS);
+ mQuotaTracker.setCountLimit(FREQUENT_BUCKET_CATEGORY, 4, 8 * HOUR_IN_MILLIS);
+ mQuotaTracker.setCountLimit(WORKING_SET_BUCKET_CATEGORY, 9, 2 * HOUR_IN_MILLIS);
+ mQuotaTracker.setCountLimit(ACTIVE_BUCKET_CATEGORY, 10, 10 * MINUTE_IN_MILLIS);
+
+ final long now = mInjector.getElapsedRealtime();
+
+ logEventAt(now - 23 * HOUR_IN_MILLIS);
+ logEventAt(now - 7 * HOUR_IN_MILLIS);
+ logEventAt(now - 5 * HOUR_IN_MILLIS);
+ logEventAt(now - 2 * HOUR_IN_MILLIS);
+ logEventAt(now - 5 * MINUTE_IN_MILLIS);
+
+ ExecutionStats expectedStats = new ExecutionStats();
+
+ // Active
+ expectedStats.expirationTimeElapsed = now + 5 * MINUTE_IN_MILLIS;
+ expectedStats.windowSizeMs = 10 * MINUTE_IN_MILLIS;
+ expectedStats.countLimit = 10;
+ expectedStats.countInWindow = 1;
+ mCategorizer.mCategoryToUse = ACTIVE_BUCKET_CATEGORY;
+ assertEquals(expectedStats,
+ mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+
+ // Working
+ expectedStats.expirationTimeElapsed = now;
+ expectedStats.windowSizeMs = 2 * HOUR_IN_MILLIS;
+ expectedStats.countLimit = 9;
+ expectedStats.countInWindow = 2;
+ mCategorizer.mCategoryToUse = WORKING_SET_BUCKET_CATEGORY;
+ assertEquals(expectedStats,
+ mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+
+ // Frequent
+ expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
+ expectedStats.windowSizeMs = 8 * HOUR_IN_MILLIS;
+ expectedStats.countLimit = 4;
+ expectedStats.countInWindow = 4;
+ expectedStats.inQuotaTimeElapsed = now + HOUR_IN_MILLIS;
+ mCategorizer.mCategoryToUse = FREQUENT_BUCKET_CATEGORY;
+ assertEquals(expectedStats,
+ mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+
+ // Rare
+ expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
+ expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS;
+ expectedStats.countLimit = 3;
+ expectedStats.countInWindow = 5;
+ expectedStats.inQuotaTimeElapsed = now + 19 * HOUR_IN_MILLIS;
+ mCategorizer.mCategoryToUse = RARE_BUCKET_CATEGORY;
+ assertEquals(expectedStats,
+ mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+ }
+
+ /**
+ * Tests that getExecutionStatsLocked returns the correct stats soon after device startup.
+ */
+ @Test
+ public void testGetExecutionStatsLocked_Values_BeginningOfTime() {
+ // Set time to 3 minutes after boot.
+ mInjector.mElapsedTime = 3 * MINUTE_IN_MILLIS;
+
+ logEventAt(30_000);
+ logEventAt(MINUTE_IN_MILLIS);
+ logEventAt(2 * MINUTE_IN_MILLIS);
+
+ mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 10, 2 * HOUR_IN_MILLIS);
+
+ ExecutionStats expectedStats = new ExecutionStats();
+
+ expectedStats.windowSizeMs = 2 * HOUR_IN_MILLIS;
+ expectedStats.countLimit = 10;
+ expectedStats.countInWindow = 3;
+ expectedStats.expirationTimeElapsed = 2 * HOUR_IN_MILLIS + 30_000;
+ assertEquals(expectedStats,
+ mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+ }
+
+ @Test
+ public void testisWithinQuota_GlobalQuotaFree() {
+ mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 0, 2 * HOUR_IN_MILLIS);
+ mQuotaTracker.setQuotaFree(true);
+ assertTrue(mQuotaTracker.isWithinQuota(TEST_USER_ID, TEST_PACKAGE, null));
+ assertTrue(mQuotaTracker.isWithinQuota(TEST_USER_ID, "com.android.random.app", null));
+ }
+
+ @Test
+ public void testisWithinQuota_UptcQuotaFree() {
+ mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 0, 2 * HOUR_IN_MILLIS);
+ mQuotaTracker.setQuotaFree(TEST_USER_ID, TEST_PACKAGE, true);
+ assertTrue(mQuotaTracker.isWithinQuota(TEST_USER_ID, TEST_PACKAGE, null));
+ assertFalse(
+ mQuotaTracker.isWithinQuota(TEST_USER_ID, "com.android.random.app", null));
+ }
+
+ @Test
+ public void testisWithinQuota_UnderCount() {
+ mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 10, 2 * HOUR_IN_MILLIS);
+ logEvents(5);
+ assertTrue(mQuotaTracker.isWithinQuota(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+ }
+
+ @Test
+ public void testisWithinQuota_OverCount() {
+ mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 25, HOUR_IN_MILLIS);
+ logEvents(TEST_USER_ID, "com.android.test.spam", TEST_TAG, 30);
+ assertFalse(mQuotaTracker.isWithinQuota(TEST_USER_ID, "com.android.test.spam", TEST_TAG));
+ }
+
+ @Test
+ public void testisWithinQuota_EqualsCount() {
+ mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 25, HOUR_IN_MILLIS);
+ logEvents(25);
+ assertFalse(mQuotaTracker.isWithinQuota(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+ }
+
+ @Test
+ public void testisWithinQuota_DifferentCategories() {
+ mQuotaTracker.setCountLimit(RARE_BUCKET_CATEGORY, 3, 24 * HOUR_IN_MILLIS);
+ mQuotaTracker.setCountLimit(FREQUENT_BUCKET_CATEGORY, 4, 24 * HOUR_IN_MILLIS);
+ mQuotaTracker.setCountLimit(WORKING_SET_BUCKET_CATEGORY, 5, 24 * HOUR_IN_MILLIS);
+ mQuotaTracker.setCountLimit(ACTIVE_BUCKET_CATEGORY, 6, 24 * HOUR_IN_MILLIS);
+
+ for (int i = 0; i < 7; ++i) {
+ logEvents(1);
+
+ mCategorizer.mCategoryToUse = RARE_BUCKET_CATEGORY;
+ assertEquals("Rare has incorrect quota status with " + (i + 1) + " events",
+ i < 2,
+ mQuotaTracker.isWithinQuota(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+ mCategorizer.mCategoryToUse = FREQUENT_BUCKET_CATEGORY;
+ assertEquals("Frequent has incorrect quota status with " + (i + 1) + " events",
+ i < 3,
+ mQuotaTracker.isWithinQuota(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+ mCategorizer.mCategoryToUse = WORKING_SET_BUCKET_CATEGORY;
+ assertEquals("Working has incorrect quota status with " + (i + 1) + " events",
+ i < 4,
+ mQuotaTracker.isWithinQuota(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+ mCategorizer.mCategoryToUse = ACTIVE_BUCKET_CATEGORY;
+ assertEquals("Active has incorrect quota status with " + (i + 1) + " events",
+ i < 5,
+ mQuotaTracker.isWithinQuota(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+ }
+ }
+
+ @Test
+ public void testMaybeScheduleCleanupAlarmLocked() {
+ mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 5, 24 * HOUR_IN_MILLIS);
+
+ // No sessions saved yet.
+ mQuotaTracker.maybeScheduleCleanupAlarmLocked();
+ verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_CLEANUP), any(), any());
+
+ // Test with only one timing session saved.
+ final long now = mInjector.getElapsedRealtime();
+ logEventAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - 6 * HOUR_IN_MILLIS);
+ mQuotaTracker.maybeScheduleCleanupAlarmLocked();
+ verify(mAlarmManager, timeout(1000).times(1))
+ .set(anyInt(), eq(now + 18 * HOUR_IN_MILLIS), eq(TAG_CLEANUP), any(), any());
+
+ // Test with new (more recent) timing sessions saved. AlarmManger shouldn't be called again.
+ logEventAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - 3 * HOUR_IN_MILLIS);
+ logEventAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - HOUR_IN_MILLIS);
+ mQuotaTracker.maybeScheduleCleanupAlarmLocked();
+ verify(mAlarmManager, times(1))
+ .set(anyInt(), eq(now + 18 * HOUR_IN_MILLIS), eq(TAG_CLEANUP), any(), any());
+ }
+
+ /**
+ * Tests that maybeScheduleStartAlarm schedules an alarm for the right time.
+ */
+ @Test
+ public void testMaybeScheduleStartAlarmLocked() {
+ // logEvent calls maybeScheduleCleanupAlarmLocked which interferes with these tests
+ // because it schedules an alarm too. Prevent it from doing so.
+ spyOn(mQuotaTracker);
+ doNothing().when(mQuotaTracker).maybeScheduleCleanupAlarmLocked();
+
+ mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 10, 8 * HOUR_IN_MILLIS);
+
+ // No sessions saved yet.
+ mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+ // Test with timing sessions out of window.
+ final long now = mInjector.getElapsedRealtime();
+ logEventsAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - 10 * HOUR_IN_MILLIS, 20);
+ mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+ // Test with timing sessions in window but still in quota.
+ final long start = now - (6 * HOUR_IN_MILLIS);
+ final long expectedAlarmTime = start + 8 * HOUR_IN_MILLIS;
+ logEventsAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, start, 5);
+ mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+ // Add some more sessions, but still in quota.
+ logEventsAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - 3 * HOUR_IN_MILLIS, 1);
+ logEventsAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - HOUR_IN_MILLIS, 3);
+ mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+ // Test when out of quota.
+ logEventsAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - HOUR_IN_MILLIS, 1);
+ mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ verify(mAlarmManager, timeout(1000).times(1))
+ .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+
+ // Alarm already scheduled, so make sure it's not scheduled again.
+ mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ verify(mAlarmManager, times(1))
+ .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+ }
+
+ /** Tests that the start alarm is properly rescheduled if the app's category is changed. */
+ @Test
+ public void testMaybeScheduleStartAlarmLocked_CategoryChange() {
+ // logEvent calls maybeScheduleCleanupAlarmLocked which interferes with these tests
+ // because it schedules an alarm too. Prevent it from doing so.
+ spyOn(mQuotaTracker);
+ doNothing().when(mQuotaTracker).maybeScheduleCleanupAlarmLocked();
+
+ mQuotaTracker.setCountLimit(RARE_BUCKET_CATEGORY, 10, 24 * HOUR_IN_MILLIS);
+ mQuotaTracker.setCountLimit(FREQUENT_BUCKET_CATEGORY, 10, 8 * HOUR_IN_MILLIS);
+ mQuotaTracker.setCountLimit(WORKING_SET_BUCKET_CATEGORY, 10, 2 * HOUR_IN_MILLIS);
+ mQuotaTracker.setCountLimit(ACTIVE_BUCKET_CATEGORY, 10, 10 * MINUTE_IN_MILLIS);
+
+ final long now = mInjector.getElapsedRealtime();
+
+ // Affects rare bucket
+ logEventsAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - 12 * HOUR_IN_MILLIS, 9);
+ // Affects frequent and rare buckets
+ logEventsAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - 4 * HOUR_IN_MILLIS, 4);
+ // Affects working, frequent, and rare buckets
+ final long outOfQuotaTime = now - HOUR_IN_MILLIS;
+ logEventsAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, outOfQuotaTime, 7);
+ // Affects all buckets
+ logEventsAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - 5 * MINUTE_IN_MILLIS, 3);
+
+ InOrder inOrder = inOrder(mAlarmManager);
+
+ // Start in ACTIVE bucket.
+ mCategorizer.mCategoryToUse = ACTIVE_BUCKET_CATEGORY;
+ mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ inOrder.verify(mAlarmManager, never())
+ .set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+ inOrder.verify(mAlarmManager, never()).cancel(any(AlarmManager.OnAlarmListener.class));
+
+ // And down from there.
+ final long expectedWorkingAlarmTime = outOfQuotaTime + (2 * HOUR_IN_MILLIS);
+ mCategorizer.mCategoryToUse = WORKING_SET_BUCKET_CATEGORY;
+ mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ inOrder.verify(mAlarmManager, timeout(1000).times(1))
+ .set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+
+ final long expectedFrequentAlarmTime = outOfQuotaTime + (8 * HOUR_IN_MILLIS);
+ mCategorizer.mCategoryToUse = FREQUENT_BUCKET_CATEGORY;
+ mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ inOrder.verify(mAlarmManager, timeout(1000).times(1))
+ .set(anyInt(), eq(expectedFrequentAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+
+ final long expectedRareAlarmTime = outOfQuotaTime + (24 * HOUR_IN_MILLIS);
+ mCategorizer.mCategoryToUse = RARE_BUCKET_CATEGORY;
+ mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ inOrder.verify(mAlarmManager, timeout(1000).times(1))
+ .set(anyInt(), eq(expectedRareAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+
+ // And back up again.
+ mCategorizer.mCategoryToUse = FREQUENT_BUCKET_CATEGORY;
+ mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ inOrder.verify(mAlarmManager, timeout(1000).times(1))
+ .set(anyInt(), eq(expectedFrequentAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+
+ mCategorizer.mCategoryToUse = WORKING_SET_BUCKET_CATEGORY;
+ mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ inOrder.verify(mAlarmManager, timeout(1000).times(1))
+ .set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+
+ mCategorizer.mCategoryToUse = ACTIVE_BUCKET_CATEGORY;
+ mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ inOrder.verify(mAlarmManager, timeout(1000).times(1))
+ .cancel(any(AlarmManager.OnAlarmListener.class));
+ inOrder.verify(mAlarmManager, timeout(1000).times(0))
+ .set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+ }
+
+ @Test
+ public void testConstantsUpdating_ValidValues() {
+ mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 0, 60_000);
+ assertEquals(0, mQuotaTracker.getLimit(SINGLE_CATEGORY));
+ assertEquals(60_000, mQuotaTracker.getWindowSizeMs(SINGLE_CATEGORY));
+ }
+
+ @Test
+ public void testConstantsUpdating_InvalidValues() {
+ // Test negatives.
+ try {
+ mQuotaTracker.setCountLimit(SINGLE_CATEGORY, -1, 5000);
+ fail("Negative count limit didn't throw an exception");
+ } catch (IllegalArgumentException e) {
+ // Success
+ }
+ try {
+ mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 1, -1);
+ fail("Negative count window size didn't throw an exception");
+ } catch (IllegalArgumentException e) {
+ // Success
+ }
+
+ // Test window sizes too low.
+ mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 0, 1);
+ assertEquals(MIN_WINDOW_SIZE_MS, mQuotaTracker.getWindowSizeMs(SINGLE_CATEGORY));
+
+ // Test window sizes too high.
+ mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 0, 365 * 24 * HOUR_IN_MILLIS);
+ assertEquals(MAX_WINDOW_SIZE_MS, mQuotaTracker.getWindowSizeMs(SINGLE_CATEGORY));
+ }
+
+ /** Tests that events aren't counted when global quota is free. */
+ @Test
+ public void testLogEvent_GlobalQuotaFree() {
+ mQuotaTracker.setQuotaFree(true);
+ mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 10, 2 * HOUR_IN_MILLIS);
+
+ ExecutionStats stats =
+ mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ assertEquals(0, stats.countInWindow);
+
+ for (int i = 0; i < 10; ++i) {
+ mQuotaTracker.noteEvent(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+
+ mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, stats);
+ assertEquals(0, stats.countInWindow);
+ }
+ }
+
+ /**
+ * Tests that events are counted when global quota is not free.
+ */
+ @Test
+ public void testLogEvent_GlobalQuotaNotFree() {
+ mQuotaTracker.setQuotaFree(false);
+ mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 10, 2 * HOUR_IN_MILLIS);
+
+ ExecutionStats stats =
+ mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ assertEquals(0, stats.countInWindow);
+
+ for (int i = 0; i < 10; ++i) {
+ mQuotaTracker.noteEvent(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+
+ mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, stats);
+ assertEquals(i + 1, stats.countInWindow);
+ }
+ }
+
+ /** Tests that events aren't counted when the uptc quota is free. */
+ @Test
+ public void testLogEvent_UptcQuotaFree() {
+ mQuotaTracker.setQuotaFree(TEST_USER_ID, TEST_PACKAGE, true);
+ mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 10, 2 * HOUR_IN_MILLIS);
+
+ ExecutionStats stats =
+ mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ assertEquals(0, stats.countInWindow);
+
+ for (int i = 0; i < 10; ++i) {
+ mQuotaTracker.noteEvent(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+
+ mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, stats);
+ assertEquals(0, stats.countInWindow);
+ }
+ }
+
+ /**
+ * Tests that events are counted when UPTC quota is not free.
+ */
+ @Test
+ public void testLogEvent_UptcQuotaNotFree() {
+ mQuotaTracker.setQuotaFree(TEST_USER_ID, TEST_PACKAGE, false);
+ mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 10, 2 * HOUR_IN_MILLIS);
+
+ ExecutionStats stats =
+ mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ assertEquals(0, stats.countInWindow);
+
+ for (int i = 0; i < 10; ++i) {
+ mQuotaTracker.noteEvent(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+
+ mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, stats);
+ assertEquals(i + 1, stats.countInWindow);
+ }
+ }
+
+ /**
+ * Tests that QuotaChangeListeners are notified when a UPTC reaches its count quota.
+ */
+ @Test
+ public void testTracking_OutOfQuota() {
+ spyOn(mQuotaChangeListener);
+
+ mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 10, 2 * HOUR_IN_MILLIS);
+ logEvents(9);
+
+ mQuotaTracker.noteEvent(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+
+ // Wait for some extra time to allow for processing.
+ verify(mQuotaChangeListener, timeout(3 * SECOND_IN_MILLIS).times(1))
+ .onQuotaStateChanged(eq(TEST_USER_ID), eq(TEST_PACKAGE), eq(TEST_TAG));
+ assertFalse(mQuotaTracker.isWithinQuota(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+ }
+
+ /**
+ * Tests that QuotaChangeListeners are not incorrectly notified after a UPTC event is logged
+ * quota times.
+ */
+ @Test
+ public void testTracking_InQuota() {
+ spyOn(mQuotaChangeListener);
+
+ mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 5, MINUTE_IN_MILLIS);
+
+ // Log an event once per minute. This is well below the quota, so listeners should not be
+ // notified.
+ for (int i = 0; i < 10; i++) {
+ advanceElapsedClock(MINUTE_IN_MILLIS);
+ mQuotaTracker.noteEvent(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ }
+
+ // Wait for some extra time to allow for processing.
+ verify(mQuotaChangeListener, timeout(3 * SECOND_IN_MILLIS).times(0))
+ .onQuotaStateChanged(eq(TEST_USER_ID), eq(TEST_PACKAGE), eq(TEST_TAG));
+ assertTrue(mQuotaTracker.isWithinQuota(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+ }
+}
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 015e574f2..ace15eb 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -26,6 +26,7 @@
"services.core",
"services.devicepolicy",
"services.net",
+ "services.people",
"services.usage",
"guava",
"androidx.test.core",
@@ -43,6 +44,10 @@
"servicestests-utils",
"service-appsearch",
"service-jobscheduler",
+ // TODO: remove once Android migrates to JUnit 4.12,
+ // which provides assertThrows
+ "testng",
+
],
aidl: {
diff --git a/services/tests/servicestests/src/com/android/server/CountryDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/CountryDetectorServiceTest.java
index e9c5ce7..d5483ff 100644
--- a/services/tests/servicestests/src/com/android/server/CountryDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/CountryDetectorServiceTest.java
@@ -19,8 +19,11 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
import android.content.Context;
+import android.content.res.Resources;
import android.location.Country;
import android.location.CountryListener;
import android.location.ICountryListener;
@@ -31,6 +34,10 @@
import androidx.test.core.app.ApplicationProvider;
+import com.android.internal.R;
+import com.android.server.location.ComprehensiveCountryDetector;
+import com.android.server.location.CustomCountryDetectorTestClass;
+
import com.google.common.truth.Expect;
import org.junit.Before;
@@ -38,12 +45,18 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class CountryDetectorServiceTest {
+ private static final String VALID_CUSTOM_TEST_CLASS =
+ "com.android.server.location.CustomCountryDetectorTestClass";
+ private static final String INVALID_CUSTOM_TEST_CLASS =
+ "com.android.server.location.MissingCountryDetectorTestClass";
+
private static class CountryListenerTester extends ICountryListener.Stub {
private Country mCountry;
@@ -83,12 +96,11 @@
}
}
- @Rule
- public final Expect expect = Expect.create();
- @Spy
- private Context mContext = ApplicationProvider.getApplicationContext();
- @Spy
- private Handler mHandler = new Handler(Looper.myLooper());
+ @Rule public final Expect expect = Expect.create();
+ @Spy private Context mContext = ApplicationProvider.getApplicationContext();
+ @Spy private Handler mHandler = new Handler(Looper.myLooper());
+ @Mock private Resources mResources;
+
private CountryDetectorServiceTester mCountryDetectorService;
@BeforeClass
@@ -108,10 +120,12 @@
message.getCallback().run();
return true;
}).when(mHandler).sendMessageAtTime(any(Message.class), anyLong());
+
+ doReturn(mResources).when(mContext).getResources();
}
@Test
- public void countryListener_add_successful() throws RemoteException {
+ public void addCountryListener_validListener_listenerAdded() throws RemoteException {
CountryListenerTester countryListener = new CountryListenerTester();
mCountryDetectorService.systemRunning();
@@ -122,7 +136,7 @@
}
@Test
- public void countryListener_remove_successful() throws RemoteException {
+ public void removeCountryListener_validListener_listenerRemoved() throws RemoteException {
CountryListenerTester countryListener = new CountryListenerTester();
mCountryDetectorService.systemRunning();
@@ -133,8 +147,31 @@
expect.that(mCountryDetectorService.isListenerSet()).isFalse();
}
+ @Test(expected = RemoteException.class)
+ public void addCountryListener_serviceNotReady_throwsException() throws RemoteException {
+ CountryListenerTester countryListener = new CountryListenerTester();
+
+ expect.that(mCountryDetectorService.isSystemReady()).isFalse();
+ mCountryDetectorService.addCountryListener(countryListener);
+ }
+
+ @Test(expected = RemoteException.class)
+ public void removeCountryListener_serviceNotReady_throwsException() throws RemoteException {
+ CountryListenerTester countryListener = new CountryListenerTester();
+
+ expect.that(mCountryDetectorService.isSystemReady()).isFalse();
+ mCountryDetectorService.removeCountryListener(countryListener);
+ }
+
@Test
- public void countryListener_notify_successful() throws RemoteException {
+ public void detectCountry_serviceNotReady_returnNull() {
+ expect.that(mCountryDetectorService.isSystemReady()).isFalse();
+
+ expect.that(mCountryDetectorService.detectCountry()).isNull();
+ }
+
+ @Test
+ public void notifyReceivers_twoListenersRegistered_bothNotified() throws RemoteException {
CountryListenerTester countryListenerA = new CountryListenerTester();
CountryListenerTester countryListenerB = new CountryListenerTester();
Country country = new Country("US", Country.COUNTRY_SOURCE_NETWORK);
@@ -151,4 +188,26 @@
expect.that(countryListenerA.getCountry().equalsIgnoreSource(country)).isTrue();
expect.that(countryListenerB.getCountry().equalsIgnoreSource(country)).isTrue();
}
+
+ @Test
+ public void initialize_deviceWithCustomDetector_useCustomDetectorClass() {
+ when(mResources.getString(R.string.config_customCountryDetector))
+ .thenReturn(VALID_CUSTOM_TEST_CLASS);
+
+ mCountryDetectorService.initialize();
+
+ expect.that(mCountryDetectorService.getCountryDetector())
+ .isInstanceOf(CustomCountryDetectorTestClass.class);
+ }
+
+ @Test
+ public void initialize_deviceWithInvalidCustomDetector_useDefaultDetector() {
+ when(mResources.getString(R.string.config_customCountryDetector))
+ .thenReturn(INVALID_CUSTOM_TEST_CLASS);
+
+ mCountryDetectorService.initialize();
+
+ expect.that(mCountryDetectorService.getCountryDetector())
+ .isInstanceOf(ComprehensiveCountryDetector.class);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
index f3c76b6..8871348 100644
--- a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
@@ -28,6 +28,7 @@
import android.app.admin.DevicePolicyManagerInternal;
import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetManagerInternal;
import android.appwidget.AppWidgetProviderInfo;
import android.appwidget.PendingHostUpdate;
import android.content.BroadcastReceiver;
@@ -80,6 +81,7 @@
super.setUp();
LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
LocalServices.removeServiceForTest(ShortcutServiceInternal.class);
+ LocalServices.removeServiceForTest(AppWidgetManagerInternal.class);
mTestContext = new TestContext();
mPkgName = mTestContext.getOpPackageName();
diff --git a/services/tests/servicestests/src/com/android/server/backup/internal/BackupHandlerTest.java b/services/tests/servicestests/src/com/android/server/backup/internal/BackupHandlerTest.java
new file mode 100644
index 0000000..fa35e3f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/backup/internal/BackupHandlerTest.java
@@ -0,0 +1,139 @@
+/*
+ * 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.backup.internal;
+
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertThrows;
+import static org.testng.Assert.assertTrue;
+
+import android.os.HandlerThread;
+import android.os.Message;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.backup.BackupAgentTimeoutParameters;
+import com.android.server.backup.UserBackupManagerService;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class BackupHandlerTest {
+ private static final int MESSAGE_TIMEOUT_MINUTES = 1;
+
+ @Mock private UserBackupManagerService mUserBackupManagerService;
+ @Mock private BackupAgentTimeoutParameters mTimeoutParameters;
+
+ private HandlerThread mHandlerThread;
+ private CountDownLatch mCountDownLatch;
+ private boolean mExceptionPropagated;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(/* testClass */ this);
+ when(mUserBackupManagerService.getAgentTimeoutParameters()).thenReturn(mTimeoutParameters);
+
+ mExceptionPropagated = false;
+ mCountDownLatch = new CountDownLatch(/* count */ 1);
+ mHandlerThread = new HandlerThread("BackupHandlerTestThread");
+ mHandlerThread.start();
+ }
+
+ @After
+ public void tearDown() {
+ mHandlerThread.quit();
+ }
+
+ @Test
+ public void testSendMessage_propagatesExceptions() throws Exception {
+ BackupHandler handler = new TestBackupHandler(/* shouldStop */ false);
+ handler.sendMessage(getMessage());
+ mCountDownLatch.await(MESSAGE_TIMEOUT_MINUTES, TimeUnit.MINUTES);
+
+ assertTrue(mExceptionPropagated);
+ }
+
+ @Test
+ public void testPost_propagatesExceptions() throws Exception {
+ BackupHandler handler = new TestBackupHandler(/* shouldStop */ false);
+ handler.post(() -> {});
+ mCountDownLatch.await(MESSAGE_TIMEOUT_MINUTES, TimeUnit.MINUTES);
+
+ assertTrue(mExceptionPropagated);
+ }
+
+ @Test
+ public void testSendMessage_stopping_doesntPropagateExceptions() throws Exception {
+ BackupHandler handler = new TestBackupHandler(/* shouldStop */ true);
+ handler.sendMessage(getMessage());
+ mCountDownLatch.await(MESSAGE_TIMEOUT_MINUTES, TimeUnit.MINUTES);
+
+ assertFalse(mExceptionPropagated);
+ }
+
+ @Test
+ public void testPost_stopping_doesntPropagateExceptions() throws Exception {
+ BackupHandler handler = new TestBackupHandler(/* shouldStop */ true);
+ handler.post(() -> {});
+ mCountDownLatch.await(MESSAGE_TIMEOUT_MINUTES, TimeUnit.MINUTES);
+
+ assertFalse(mExceptionPropagated);
+ }
+
+ private static Message getMessage() {
+ Message message = Message.obtain();
+ message.what = -1;
+ return message;
+ }
+
+ private class TestBackupHandler extends BackupHandler {
+ private final boolean mShouldStop;
+
+ TestBackupHandler(boolean shouldStop) {
+ super(mUserBackupManagerService, mHandlerThread);
+
+ mShouldStop = shouldStop;
+ }
+
+ @Override
+ public void dispatchMessage(Message msg) {
+ try {
+ super.dispatchMessage(msg);
+ } catch (Exception e) {
+ mExceptionPropagated = true;
+ } finally {
+ mCountDownLatch.countDown();
+ }
+ }
+
+ @Override
+ void dispatchMessageInternal(Message msg) {
+ mIsStopping = mShouldStop;
+ throw new RuntimeException();
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/compat/ApplicationInfoBuilder.java b/services/tests/servicestests/src/com/android/server/compat/ApplicationInfoBuilder.java
new file mode 100644
index 0000000..d0767cc
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/compat/ApplicationInfoBuilder.java
@@ -0,0 +1,58 @@
+/*
+ * 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.compat;
+
+import android.content.pm.ApplicationInfo;
+
+class ApplicationInfoBuilder {
+ private boolean mIsDebuggable;
+ private int mTargetSdk;
+ private String mPackageName;
+
+ private ApplicationInfoBuilder() {
+ mTargetSdk = -1;
+ }
+
+ static ApplicationInfoBuilder create() {
+ return new ApplicationInfoBuilder();
+ }
+
+ ApplicationInfoBuilder withTargetSdk(int targetSdk) {
+ mTargetSdk = targetSdk;
+ return this;
+ }
+
+ ApplicationInfoBuilder debuggable() {
+ mIsDebuggable = true;
+ return this;
+ }
+
+ ApplicationInfoBuilder withPackageName(String packageName) {
+ mPackageName = packageName;
+ return this;
+ }
+
+ ApplicationInfo build() {
+ final ApplicationInfo applicationInfo = new ApplicationInfo();
+ if (mIsDebuggable) {
+ applicationInfo.flags |= ApplicationInfo.FLAG_DEBUGGABLE;
+ }
+ applicationInfo.packageName = mPackageName;
+ applicationInfo.targetSdkVersion = mTargetSdk;
+ return applicationInfo;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
new file mode 100644
index 0000000..328c71d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
@@ -0,0 +1,99 @@
+/*
+ * 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.compat;
+
+import android.content.Context;
+
+import com.android.internal.compat.AndroidBuildClassifier;
+
+import java.util.ArrayList;
+
+/**
+ * Helper class for creating a CompatConfig.
+ */
+class CompatConfigBuilder {
+ private ArrayList<CompatChange> mChanges;
+ private AndroidBuildClassifier mBuildClassifier;
+ private Context mContext;
+
+ private CompatConfigBuilder(AndroidBuildClassifier buildClassifier, Context context) {
+ mChanges = new ArrayList<>();
+ mBuildClassifier = buildClassifier;
+ mContext = context;
+ }
+
+ static CompatConfigBuilder create(AndroidBuildClassifier buildClassifier, Context context) {
+ return new CompatConfigBuilder(buildClassifier, context);
+ }
+
+ CompatConfigBuilder addTargetSdkChangeWithId(int sdk, long id) {
+ mChanges.add(new CompatChange(id, "", sdk, false, ""));
+ return this;
+ }
+
+ CompatConfigBuilder addTargetSdkDisabledChangeWithId(int sdk, long id) {
+ mChanges.add(new CompatChange(id, "", sdk, true, ""));
+ return this;
+ }
+
+ CompatConfigBuilder addTargetSdkChangeWithIdAndName(int sdk, long id, String name) {
+ mChanges.add(new CompatChange(id, name, sdk, false, ""));
+ return this;
+ }
+
+ CompatConfigBuilder addTargetSdkChangeWithIdAndDescription(int sdk, long id,
+ String description) {
+ mChanges.add(new CompatChange(id, "", sdk, false, description));
+ return this;
+ }
+
+ CompatConfigBuilder addEnabledChangeWithId(long id) {
+ mChanges.add(new CompatChange(id, "", -1, false, ""));
+ return this;
+ }
+
+ CompatConfigBuilder addEnabledChangeWithIdAndName(long id, String name) {
+ mChanges.add(new CompatChange(id, name, -1, false, ""));
+ return this;
+ }
+ CompatConfigBuilder addEnabledChangeWithIdAndDescription(long id, String description) {
+ mChanges.add(new CompatChange(id, "", -1, false, description));
+ return this;
+ }
+
+ CompatConfigBuilder addDisabledChangeWithId(long id) {
+ mChanges.add(new CompatChange(id, "", -1, true, ""));
+ return this;
+ }
+
+ CompatConfigBuilder addDisabledChangeWithIdAndName(long id, String name) {
+ mChanges.add(new CompatChange(id, name, -1, true, ""));
+ return this;
+ }
+
+ CompatConfigBuilder addDisabledChangeWithIdAndDescription(long id, String description) {
+ mChanges.add(new CompatChange(id, "", -1, true, description));
+ return this;
+ }
+
+ CompatConfig build() {
+ CompatConfig config = new CompatConfig(mBuildClassifier, mContext);
+ for (CompatChange change : mChanges) {
+ config.addChange(change);
+ }
+ return config;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index cb99c11..407f67e 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -18,12 +18,25 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
+
+import android.content.Context;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.compat.AndroidBuildClassifier;
+
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.io.File;
import java.io.FileOutputStream;
@@ -34,12 +47,12 @@
@RunWith(AndroidJUnit4.class)
public class CompatConfigTest {
- private ApplicationInfo makeAppInfo(String pName, int targetSdkVersion) {
- ApplicationInfo ai = new ApplicationInfo();
- ai.packageName = pName;
- ai.targetSdkVersion = targetSdkVersion;
- return ai;
- }
+ @Mock
+ private Context mContext;
+ @Mock
+ PackageManager mPackageManager;
+ @Mock
+ private AndroidBuildClassifier mBuildClassifier;
private File createTempDir() {
String base = System.getProperty("java.io.tmpdir");
@@ -54,112 +67,206 @@
os.close();
}
- @Test
- public void testUnknownChangeEnabled() {
- CompatConfig pc = new CompatConfig();
- assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isTrue();
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ // Assume userdebug/eng non-final build
+ when(mBuildClassifier.isDebuggableBuild()).thenReturn(true);
+ when(mBuildClassifier.isFinalBuild()).thenReturn(false);
}
@Test
- public void testDisabledChangeDisabled() {
- CompatConfig pc = new CompatConfig();
- pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, true, ""));
- assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isFalse();
+ public void testUnknownChangeEnabled() throws Exception {
+ CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext);
+ assertThat(compatConfig.isChangeEnabled(1234L, ApplicationInfoBuilder.create().build()))
+ .isTrue();
}
@Test
- public void testTargetSdkChangeDisabled() {
- CompatConfig pc = new CompatConfig();
- pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, false, null));
- assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 2))).isFalse();
+ public void testDisabledChangeDisabled() throws Exception {
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addDisabledChangeWithId(1234L)
+ .build();
+
+ assertThat(compatConfig.isChangeEnabled(1234L, ApplicationInfoBuilder.create().build()))
+ .isFalse();
}
@Test
- public void testTargetSdkChangeEnabled() {
- CompatConfig pc = new CompatConfig();
- pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, false, ""));
- assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 3))).isTrue();
+ public void testTargetSdkChangeDisabled() throws Exception {
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addTargetSdkChangeWithId(2, 1234L)
+ .build();
+
+ assertThat(compatConfig.isChangeEnabled(1234L,
+ ApplicationInfoBuilder.create().withTargetSdk(2).build()))
+ .isFalse();
}
@Test
- public void testDisabledOverrideTargetSdkChange() {
- CompatConfig pc = new CompatConfig();
- pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, true, null));
- assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 3))).isFalse();
+ public void testTargetSdkChangeEnabled() throws Exception {
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addTargetSdkChangeWithId(2, 1234L)
+ .build();
+
+ assertThat(compatConfig.isChangeEnabled(1234L,
+ ApplicationInfoBuilder.create().withTargetSdk(3).build())).isTrue();
}
@Test
- public void testGetDisabledChanges() {
- CompatConfig pc = new CompatConfig();
- pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, true, null));
- pc.addChange(new CompatChange(2345L, "OTHER_CHANGE", -1, false, null));
- assertThat(pc.getDisabledChanges(
- makeAppInfo("com.some.package", 2))).asList().containsExactly(1234L);
+ public void testDisabledOverrideTargetSdkChange() throws Exception {
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addTargetSdkDisabledChangeWithId(2, 1234L)
+ .build();
+
+ assertThat(compatConfig.isChangeEnabled(1234L,
+ ApplicationInfoBuilder.create().withTargetSdk(3).build())).isFalse();
}
@Test
- public void testGetDisabledChangesSorted() {
- CompatConfig pc = new CompatConfig();
- pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, true, null));
- pc.addChange(new CompatChange(123L, "OTHER_CHANGE", 2, true, null));
- pc.addChange(new CompatChange(12L, "THIRD_CHANGE", 2, true, null));
- assertThat(pc.getDisabledChanges(
- makeAppInfo("com.some.package", 2))).asList().containsExactly(12L, 123L, 1234L);
+ public void testGetDisabledChanges() throws Exception {
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addDisabledChangeWithId(1234L)
+ .addEnabledChangeWithId(2345L)
+ .build();
+
+ assertThat(compatConfig.getDisabledChanges(
+ ApplicationInfoBuilder.create().build())).asList().containsExactly(1234L);
}
@Test
- public void testPackageOverrideEnabled() {
- CompatConfig pc = new CompatConfig();
- pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, true, null)); // disabled
- pc.addOverride(1234L, "com.some.package", true);
- assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 2))).isTrue();
- assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.other.package", 2))).isFalse();
+ public void testGetDisabledChangesSorted() throws Exception {
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addDisabledChangeWithId(1234L)
+ .addDisabledChangeWithId(123L)
+ .addDisabledChangeWithId(12L)
+ .build();
+
+ assertThat(compatConfig.getDisabledChanges(ApplicationInfoBuilder.create().build()))
+ .asList().containsExactly(12L, 123L, 1234L);
}
@Test
- public void testPackageOverrideDisabled() {
- CompatConfig pc = new CompatConfig();
- pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, false, null));
- pc.addOverride(1234L, "com.some.package", false);
- assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 2))).isFalse();
- assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.other.package", 2))).isTrue();
+ public void testPackageOverrideEnabled() throws Exception {
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addDisabledChangeWithId(1234L)
+ .build();
+
+ compatConfig.addOverride(1234L, "com.some.package", true);
+
+ assertThat(compatConfig.isChangeEnabled(1234L, ApplicationInfoBuilder.create()
+ .withPackageName("com.some.package").build())).isTrue();
+ assertThat(compatConfig.isChangeEnabled(1234L, ApplicationInfoBuilder.create()
+ .withPackageName("com.other.package").build())).isFalse();
}
@Test
- public void testPackageOverrideUnknownPackage() {
- CompatConfig pc = new CompatConfig();
- pc.addOverride(1234L, "com.some.package", false);
- assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 2))).isFalse();
- assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.other.package", 2))).isTrue();
+ public void testPackageOverrideDisabled() throws Exception {
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addEnabledChangeWithId(1234L)
+ .build();
+
+ compatConfig.addOverride(1234L, "com.some.package", false);
+
+ assertThat(compatConfig.isChangeEnabled(1234L, ApplicationInfoBuilder.create()
+ .withPackageName("com.some.package").build())).isFalse();
+ assertThat(compatConfig.isChangeEnabled(1234L, ApplicationInfoBuilder.create()
+ .withPackageName("com.other.package").build())).isTrue();
}
@Test
- public void testPackageOverrideUnknownChange() {
- CompatConfig pc = new CompatConfig();
- assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isTrue();
+ public void testPackageOverrideUnknownPackage() throws Exception {
+ CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext);
+
+ compatConfig.addOverride(1234L, "com.some.package", false);
+
+ assertThat(compatConfig.isChangeEnabled(1234L, ApplicationInfoBuilder.create()
+ .withPackageName("com.some.package").build())).isFalse();
+ assertThat(compatConfig.isChangeEnabled(1234L, ApplicationInfoBuilder.create()
+ .withPackageName("com.other.package").build())).isTrue();
}
@Test
- public void testRemovePackageOverride() {
- CompatConfig pc = new CompatConfig();
- pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, false, null));
- pc.addOverride(1234L, "com.some.package", false);
- pc.removeOverride(1234L, "com.some.package");
- assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 2))).isTrue();
+ public void testPreventAddOverride() throws Exception {
+ final long changeId = 1234L;
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addDisabledChangeWithId(1234L)
+ .build();
+ ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
+ .withPackageName("com.some.package")
+ .build();
+ PackageManager packageManager = mock(PackageManager.class);
+ when(mContext.getPackageManager()).thenReturn(packageManager);
+ when(packageManager.getApplicationInfo(eq("com.some.package"), anyInt()))
+ .thenReturn(applicationInfo);
+
+ // Force the validator to prevent overriding the change by using a user build.
+ when(mBuildClassifier.isDebuggableBuild()).thenReturn(false);
+ when(mBuildClassifier.isFinalBuild()).thenReturn(true);
+
+ assertThrows(SecurityException.class,
+ () -> compatConfig.addOverride(1234L, "com.some.package", true)
+ );
+ assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isFalse();
}
@Test
- public void testLookupChangeId() {
- CompatConfig pc = new CompatConfig();
- pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, false, null));
- pc.addChange(new CompatChange(2345L, "ANOTHER_CHANGE", -1, false, null));
- assertThat(pc.lookupChangeId("MY_CHANGE")).isEqualTo(1234L);
+ public void testPreventRemoveOverride() throws Exception {
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addDisabledChangeWithId(1234L)
+ .build();
+ ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
+ .withPackageName("com.some.package")
+ .build();
+ when(mPackageManager.getApplicationInfo(eq("com.some.package"), anyInt()))
+ .thenReturn(applicationInfo);
+ // Assume the override was allowed to be added.
+ compatConfig.addOverride(1234L, "com.some.package", true);
+
+ // Validator allows turning on the change.
+ assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isTrue();
+
+ // Reject all override attempts.
+ // Force the validator to prevent overriding the change by using a user build.
+ when(mBuildClassifier.isDebuggableBuild()).thenReturn(false);
+ when(mBuildClassifier.isFinalBuild()).thenReturn(true);
+ // Try to turn off change, but validator prevents it.
+ assertThrows(SecurityException.class,
+ () -> compatConfig.removeOverride(1234L, "com.some.package"));
+ assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isTrue();
}
@Test
- public void testLookupChangeIdNotPresent() {
- CompatConfig pc = new CompatConfig();
- assertThat(pc.lookupChangeId("MY_CHANGE")).isEqualTo(-1L);
+ public void testRemovePackageOverride() throws Exception {
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addEnabledChangeWithId(1234L)
+ .build();
+ ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
+ .withPackageName("com.some.package")
+ .build();
+
+ assertThat(compatConfig.addOverride(1234L, "com.some.package", false)).isTrue();
+ assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isFalse();
+
+ compatConfig.removeOverride(1234L, "com.some.package");
+ assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isTrue();
+ }
+
+ @Test
+ public void testLookupChangeId() throws Exception {
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addEnabledChangeWithIdAndName(1234L, "MY_CHANGE")
+ .addEnabledChangeWithIdAndName(2345L, "MY_OTHER_CHANGE")
+ .build();
+
+ assertThat(compatConfig.lookupChangeId("MY_CHANGE")).isEqualTo(1234L);
+ }
+
+ @Test
+ public void testLookupChangeIdNotPresent() throws Exception {
+ CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext);
+ assertThat(compatConfig.lookupChangeId("MY_CHANGE")).isEqualTo(-1L);
}
@Test
@@ -172,14 +279,17 @@
File dir = createTempDir();
writeToFile(dir, "platform_compat_config.xml", configXml);
+ CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext);
+ compatConfig.initConfigFromLib(dir);
- CompatConfig pc = new CompatConfig();
- pc.initConfigFromLib(dir);
-
- assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isFalse();
- assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 3))).isTrue();
- assertThat(pc.isChangeEnabled(1235L, makeAppInfo("com.some.package", 5))).isFalse();
- assertThat(pc.isChangeEnabled(1236L, makeAppInfo("com.some.package", 1))).isTrue();
+ assertThat(compatConfig.isChangeEnabled(1234L,
+ ApplicationInfoBuilder.create().withTargetSdk(1).build())).isFalse();
+ assertThat(compatConfig.isChangeEnabled(1234L,
+ ApplicationInfoBuilder.create().withTargetSdk(3).build())).isTrue();
+ assertThat(compatConfig.isChangeEnabled(1235L,
+ ApplicationInfoBuilder.create().withTargetSdk(5).build())).isFalse();
+ assertThat(compatConfig.isChangeEnabled(1236L,
+ ApplicationInfoBuilder.create().withTargetSdk(1).build())).isTrue();
}
@Test
@@ -195,15 +305,16 @@
File dir = createTempDir();
writeToFile(dir, "libcore_platform_compat_config.xml", configXml1);
writeToFile(dir, "frameworks_platform_compat_config.xml", configXml2);
+ CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext);
+ compatConfig.initConfigFromLib(dir);
- CompatConfig pc = new CompatConfig();
- pc.initConfigFromLib(dir);
-
- assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isFalse();
- assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 3))).isTrue();
- assertThat(pc.isChangeEnabled(1235L, makeAppInfo("com.some.package", 5))).isFalse();
- assertThat(pc.isChangeEnabled(1236L, makeAppInfo("com.some.package", 1))).isTrue();
+ assertThat(compatConfig.isChangeEnabled(1234L,
+ ApplicationInfoBuilder.create().withTargetSdk(1).build())).isFalse();
+ assertThat(compatConfig.isChangeEnabled(1234L,
+ ApplicationInfoBuilder.create().withTargetSdk(3).build())).isTrue();
+ assertThat(compatConfig.isChangeEnabled(1235L,
+ ApplicationInfoBuilder.create().withTargetSdk(5).build())).isFalse();
+ assertThat(compatConfig.isChangeEnabled(1236L,
+ ApplicationInfoBuilder.create().withTargetSdk(1).build())).isTrue();
}
}
-
-
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatibilityChangeConfigBuilder.java b/services/tests/servicestests/src/com/android/server/compat/CompatibilityChangeConfigBuilder.java
new file mode 100644
index 0000000..793296e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatibilityChangeConfigBuilder.java
@@ -0,0 +1,52 @@
+/*
+ * 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.compat;
+
+import android.compat.Compatibility;
+
+import com.android.internal.compat.CompatibilityChangeConfig;
+
+import java.util.HashSet;
+import java.util.Set;
+
+class CompatibilityChangeConfigBuilder {
+ private Set<Long> mEnabled;
+ private Set<Long> mDisabled;
+
+ private CompatibilityChangeConfigBuilder() {
+ mEnabled = new HashSet<>();
+ mDisabled = new HashSet<>();
+ }
+
+ static CompatibilityChangeConfigBuilder create() {
+ return new CompatibilityChangeConfigBuilder();
+ }
+
+ CompatibilityChangeConfigBuilder enable(Long id) {
+ mEnabled.add(id);
+ return this;
+ }
+
+ CompatibilityChangeConfigBuilder disable(Long id) {
+ mDisabled.add(id);
+ return this;
+ }
+
+ CompatibilityChangeConfig build() {
+ return new CompatibilityChangeConfig(new Compatibility.ChangeConfig(mEnabled, mDisabled));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
new file mode 100644
index 0000000..ecd07bd
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
@@ -0,0 +1,383 @@
+/*
+ * 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.compat;
+
+import static com.android.internal.compat.OverrideAllowedState.ALLOWED;
+import static com.android.internal.compat.OverrideAllowedState.DISABLED_NON_TARGET_SDK;
+import static com.android.internal.compat.OverrideAllowedState.DISABLED_NOT_DEBUGGABLE;
+import static com.android.internal.compat.OverrideAllowedState.DISABLED_TARGET_SDK_TOO_HIGH;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.compat.AndroidBuildClassifier;
+import com.android.internal.compat.IOverrideValidator;
+import com.android.internal.compat.OverrideAllowedState;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class OverrideValidatorImplTest {
+ private static final String PACKAGE_NAME = "my.package";
+ private static final int TARGET_SDK = 10;
+ private static final int TARGET_SDK_BEFORE = 9;
+ private static final int TARGET_SDK_AFTER = 11;
+
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ Context mContext;
+
+ private AndroidBuildClassifier debuggableBuild() {
+ AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class);
+ when(buildClassifier.isDebuggableBuild()).thenReturn(true);
+ return buildClassifier;
+ }
+
+ private AndroidBuildClassifier betaBuild() {
+ AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class);
+ when(buildClassifier.isDebuggableBuild()).thenReturn(false);
+ when(buildClassifier.isFinalBuild()).thenReturn(false);
+ return buildClassifier;
+ }
+
+ private AndroidBuildClassifier finalBuild() {
+ AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class);
+ when(buildClassifier.isDebuggableBuild()).thenReturn(false);
+ when(buildClassifier.isFinalBuild()).thenReturn(true);
+ return buildClassifier;
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ }
+
+ @Test
+ public void getOverrideAllowedState_debugBuildAnyChangeDebugApp_allowOverride()
+ throws Exception {
+ CompatConfig config = CompatConfigBuilder.create(debuggableBuild(), mContext)
+ .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1)
+ .addTargetSdkChangeWithId(TARGET_SDK, 2)
+ .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3)
+ .addEnabledChangeWithId(4)
+ .addDisabledChangeWithId(5).build();
+ IOverrideValidator overrideValidator = config.getOverrideValidator();
+ when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(ApplicationInfoBuilder.create()
+ .debuggable()
+ .withTargetSdk(TARGET_SDK)
+ .withPackageName(PACKAGE_NAME).build());
+
+ OverrideAllowedState stateTargetSdkLessChange =
+ overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+ OverrideAllowedState stateTargetSdkEqualChange =
+ overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME);
+ OverrideAllowedState stateTargetSdkAfterChange =
+ overrideValidator.getOverrideAllowedState(3, PACKAGE_NAME);
+ OverrideAllowedState stateEnabledChange =
+ overrideValidator.getOverrideAllowedState(4, PACKAGE_NAME);
+ OverrideAllowedState stateDisabledChange =
+ overrideValidator.getOverrideAllowedState(5, PACKAGE_NAME);
+
+ assertThat(stateTargetSdkLessChange)
+ .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1));
+ assertThat(stateTargetSdkEqualChange)
+ .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1));
+ assertThat(stateTargetSdkAfterChange)
+ .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1));
+ assertThat(stateEnabledChange)
+ .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1));
+ assertThat(stateDisabledChange)
+ .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1));
+ }
+
+ @Test
+ public void getOverrideAllowedState_debugBuildAnyChangeReleaseApp_allowOverride()
+ throws Exception {
+ CompatConfig config = CompatConfigBuilder.create(debuggableBuild(), mContext)
+ .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1)
+ .addTargetSdkChangeWithId(TARGET_SDK, 2)
+ .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3)
+ .addEnabledChangeWithId(4)
+ .addDisabledChangeWithId(5).build();
+ IOverrideValidator overrideValidator = config.getOverrideValidator();
+ when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(ApplicationInfoBuilder.create()
+ .withPackageName(PACKAGE_NAME)
+ .withTargetSdk(TARGET_SDK).build());
+
+ OverrideAllowedState stateTargetSdkLessChange =
+ overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+ OverrideAllowedState stateTargetSdkEqualChange =
+ overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME);
+ OverrideAllowedState stateTargetSdkAfterChange =
+ overrideValidator.getOverrideAllowedState(3, PACKAGE_NAME);
+ OverrideAllowedState stateEnabledChange =
+ overrideValidator.getOverrideAllowedState(4, PACKAGE_NAME);
+ OverrideAllowedState stateDisabledChange =
+ overrideValidator.getOverrideAllowedState(5, PACKAGE_NAME);
+
+ assertThat(stateTargetSdkLessChange)
+ .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1));
+ assertThat(stateTargetSdkEqualChange)
+ .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1));
+ assertThat(stateTargetSdkAfterChange)
+ .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1));
+ assertThat(stateEnabledChange)
+ .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1));
+ assertThat(stateDisabledChange)
+ .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1));
+ }
+
+ @Test
+ public void getOverrideAllowedState_betaBuildTargetSdkChangeDebugApp_allowOverride()
+ throws Exception {
+ CompatConfig config = CompatConfigBuilder.create(betaBuild(), mContext)
+ .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1)
+ .addTargetSdkChangeWithId(TARGET_SDK, 2)
+ .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3).build();
+ IOverrideValidator overrideValidator = config.getOverrideValidator();
+ when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(ApplicationInfoBuilder.create()
+ .debuggable()
+ .withTargetSdk(TARGET_SDK)
+ .withPackageName(PACKAGE_NAME).build());
+
+ OverrideAllowedState stateTargetSdkLessChange =
+ overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+ OverrideAllowedState stateTargetSdkEqualChange =
+ overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME);
+ OverrideAllowedState stateTargetSdkAfterChange =
+ overrideValidator.getOverrideAllowedState(3, PACKAGE_NAME);
+
+ assertThat(stateTargetSdkLessChange)
+ .isEqualTo(new OverrideAllowedState(ALLOWED, TARGET_SDK, TARGET_SDK_BEFORE));
+ assertThat(stateTargetSdkEqualChange)
+ .isEqualTo(new OverrideAllowedState(ALLOWED, TARGET_SDK, TARGET_SDK));
+ assertThat(stateTargetSdkAfterChange)
+ .isEqualTo(new OverrideAllowedState(ALLOWED, TARGET_SDK, TARGET_SDK_AFTER));
+ }
+
+ @Test
+ public void getOverrideAllowedState_betaBuildEnabledChangeDebugApp_rejectOverride()
+ throws Exception {
+ CompatConfig config = CompatConfigBuilder.create(betaBuild(), mContext)
+ .addEnabledChangeWithId(1).build();
+ IOverrideValidator overrideValidator = config.getOverrideValidator();
+ when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(ApplicationInfoBuilder.create()
+ .withPackageName(PACKAGE_NAME)
+ .debuggable()
+ .build());
+
+ OverrideAllowedState allowedState =
+ overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+
+ assertThat(allowedState)
+ .isEqualTo(new OverrideAllowedState(DISABLED_NON_TARGET_SDK, -1, -1));
+ }
+
+ @Test
+ public void getOverrideAllowedState_betaBuildDisabledChangeDebugApp_rejectOverride()
+ throws Exception {
+ CompatConfig config = CompatConfigBuilder.create(betaBuild(), mContext)
+ .addDisabledChangeWithId(1).build();
+ IOverrideValidator overrideValidator = config.getOverrideValidator();
+ when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(ApplicationInfoBuilder.create()
+ .debuggable()
+ .withPackageName(PACKAGE_NAME).build());
+
+ OverrideAllowedState allowedState =
+ overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+
+ assertThat(allowedState)
+ .isEqualTo(new OverrideAllowedState(DISABLED_NON_TARGET_SDK, -1, -1));
+ }
+
+ @Test
+ public void getOverrideAllowedState_betaBuildAnyChangeReleaseApp_rejectOverride()
+ throws Exception {
+ CompatConfig config = CompatConfigBuilder.create(betaBuild(), mContext)
+ .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1)
+ .addTargetSdkChangeWithId(TARGET_SDK, 2)
+ .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3)
+ .addEnabledChangeWithId(4)
+ .addDisabledChangeWithId(5).build();
+ IOverrideValidator overrideValidator = config.getOverrideValidator();
+ when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(ApplicationInfoBuilder.create()
+ .withPackageName(PACKAGE_NAME)
+ .withTargetSdk(TARGET_SDK).build());
+
+ OverrideAllowedState stateTargetSdkLessChange =
+ overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+ OverrideAllowedState stateTargetSdkEqualChange =
+ overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME);
+ OverrideAllowedState stateTargetSdkAfterChange =
+ overrideValidator.getOverrideAllowedState(3, PACKAGE_NAME);
+ OverrideAllowedState stateEnabledChange =
+ overrideValidator.getOverrideAllowedState(4, PACKAGE_NAME);
+ OverrideAllowedState stateDisabledChange =
+ overrideValidator.getOverrideAllowedState(5, PACKAGE_NAME);
+
+ assertThat(stateTargetSdkLessChange)
+ .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1));
+ assertThat(stateTargetSdkEqualChange)
+ .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1));
+ assertThat(stateTargetSdkAfterChange)
+ .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1));
+ assertThat(stateEnabledChange)
+ .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1));
+ assertThat(stateDisabledChange)
+ .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1));
+ }
+
+ @Test
+ public void getOverrideAllowedState_finalBuildTargetSdkChangeDebugAppOptin_allowOverride()
+ throws Exception {
+ CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext)
+ .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 1).build();
+ IOverrideValidator overrideValidator = config.getOverrideValidator();
+ when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(ApplicationInfoBuilder.create()
+ .debuggable()
+ .withTargetSdk(TARGET_SDK)
+ .withPackageName(PACKAGE_NAME).build());
+
+ OverrideAllowedState allowedState =
+ overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+
+ assertThat(allowedState)
+ .isEqualTo(new OverrideAllowedState(ALLOWED, TARGET_SDK, TARGET_SDK_AFTER));
+ }
+
+ @Test
+ public void getOverrideAllowedState_finalBuildTargetSdkChangeDebugAppOptout_rejectOverride()
+ throws Exception {
+ CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext)
+ .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1)
+ .addTargetSdkChangeWithId(TARGET_SDK, 2).build();
+ IOverrideValidator overrideValidator = config.getOverrideValidator();
+ when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(ApplicationInfoBuilder.create()
+ .withPackageName(PACKAGE_NAME)
+ .withTargetSdk(TARGET_SDK)
+ .debuggable()
+ .build());
+
+ OverrideAllowedState stateTargetSdkLessChange =
+ overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+ OverrideAllowedState stateTargetSdkEqualChange =
+ overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME);
+
+ assertThat(stateTargetSdkLessChange).isEqualTo(
+ new OverrideAllowedState(DISABLED_TARGET_SDK_TOO_HIGH, TARGET_SDK,
+ TARGET_SDK_BEFORE));
+ assertThat(stateTargetSdkEqualChange).isEqualTo(
+ new OverrideAllowedState(DISABLED_TARGET_SDK_TOO_HIGH, TARGET_SDK, TARGET_SDK));
+ }
+
+ @Test
+ public void getOverrideAllowedState_finalBuildEnabledChangeDebugApp_rejectOverride()
+ throws Exception {
+ CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext)
+ .addEnabledChangeWithId(1).build();
+ IOverrideValidator overrideValidator = config.getOverrideValidator();
+ when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(ApplicationInfoBuilder.create()
+ .withPackageName(PACKAGE_NAME)
+ .debuggable().build());
+
+ OverrideAllowedState allowedState =
+ overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+
+ assertThat(allowedState)
+ .isEqualTo(new OverrideAllowedState(DISABLED_NON_TARGET_SDK, -1, -1));
+ }
+
+ @Test
+ public void getOverrideAllowedState_finalBuildDisabledChangeDebugApp_rejectOverride()
+ throws Exception {
+ CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext)
+ .addDisabledChangeWithId(1).build();
+ IOverrideValidator overrideValidator = config.getOverrideValidator();
+ when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(ApplicationInfoBuilder.create()
+ .withPackageName(PACKAGE_NAME)
+ .debuggable().build());
+
+ OverrideAllowedState allowedState =
+ overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+
+ assertThat(allowedState)
+ .isEqualTo(new OverrideAllowedState(DISABLED_NON_TARGET_SDK, -1, -1));
+ }
+
+ @Test
+ public void getOverrideAllowedState_finalBuildAnyChangeReleaseApp_rejectOverride()
+ throws Exception {
+ CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext)
+ .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1)
+ .addTargetSdkChangeWithId(TARGET_SDK, 2)
+ .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3)
+ .addEnabledChangeWithId(4)
+ .addDisabledChangeWithId(5).build();
+ IOverrideValidator overrideValidator = config.getOverrideValidator();
+ when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(ApplicationInfoBuilder.create()
+ .withPackageName(PACKAGE_NAME)
+ .withTargetSdk(TARGET_SDK).build());
+
+ OverrideAllowedState stateTargetSdkLessChange =
+ overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+ OverrideAllowedState stateTargetSdkEqualChange =
+ overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME);
+ OverrideAllowedState stateTargetSdkAfterChange =
+ overrideValidator.getOverrideAllowedState(3, PACKAGE_NAME);
+ OverrideAllowedState stateEnabledChange =
+ overrideValidator.getOverrideAllowedState(4, PACKAGE_NAME);
+ OverrideAllowedState stateDisabledChange =
+ overrideValidator.getOverrideAllowedState(5, PACKAGE_NAME);
+
+ assertThat(stateTargetSdkLessChange)
+ .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1));
+ assertThat(stateTargetSdkEqualChange)
+ .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1));
+ assertThat(stateTargetSdkAfterChange)
+ .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1));
+ assertThat(stateEnabledChange)
+ .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1));
+ assertThat(stateDisabledChange)
+ .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
index c406876..ce5d6d9 100644
--- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
@@ -26,21 +26,20 @@
import static org.mockito.internal.verification.VerificationModeFactory.times;
import static org.testng.Assert.assertThrows;
-import android.compat.Compatibility;
import android.content.Context;
import android.content.pm.PackageManager;
-import com.android.internal.compat.CompatibilityChangeConfig;
+import androidx.test.runner.AndroidJUnit4;
-import com.google.common.collect.ImmutableSet;
+import com.android.internal.compat.AndroidBuildClassifier;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnitRunner;
+import org.mockito.MockitoAnnotations;
-@RunWith(MockitoJUnitRunner.class)
+@RunWith(AndroidJUnit4.class)
public class PlatformCompatTest {
private static final String PACKAGE_NAME = "my.package";
@@ -50,84 +49,77 @@
private PackageManager mPackageManager;
@Mock
CompatChange.ChangeListener mListener1, mListener2;
-
+ PlatformCompat mPlatformCompat;
+ CompatConfig mCompatConfig;
+ @Mock
+ private AndroidBuildClassifier mBuildClassifier;
@Before
public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mPackageManager.getPackageUid(eq(PACKAGE_NAME), eq(0))).thenThrow(
new PackageManager.NameNotFoundException());
- CompatConfig.get().clearChanges();
+ mCompatConfig = new CompatConfig(mBuildClassifier, mContext);
+ mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
+ // Assume userdebug/eng non-final build
+ when(mBuildClassifier.isDebuggableBuild()).thenReturn(true);
+ when(mBuildClassifier.isFinalBuild()).thenReturn(false);
}
@Test
- public void testRegisterListenerToSameIdThrows() {
- PlatformCompat pc = new PlatformCompat(mContext);
-
+ public void testRegisterListenerToSameIdThrows() throws Exception {
// Registering a listener to change 1 is successful.
- pc.registerListener(1, mListener1);
+ mPlatformCompat.registerListener(1, mListener1);
// Registering a listener to change 2 is successful.
- pc.registerListener(2, mListener1);
+ mPlatformCompat.registerListener(2, mListener1);
// Trying to register another listener to change id 1 fails.
- assertThrows(IllegalStateException.class, () -> pc.registerListener(1, mListener1));
+ assertThrows(IllegalStateException.class,
+ () -> mPlatformCompat.registerListener(1, mListener1));
}
@Test
- public void testRegisterListenerReturn() {
- PlatformCompat pc = new PlatformCompat(mContext);
-
- pc.setOverrides(
- new CompatibilityChangeConfig(
- new Compatibility.ChangeConfig(ImmutableSet.of(1L), ImmutableSet.of())),
+ public void testRegisterListenerReturn() throws Exception {
+ mPlatformCompat.setOverrides(
+ CompatibilityChangeConfigBuilder.create().enable(1L).build(),
PACKAGE_NAME);
// Change id 1 is known (added in setOverrides).
- assertThat(pc.registerListener(1, mListener1)).isTrue();
+ assertThat(mPlatformCompat.registerListener(1, mListener1)).isTrue();
// Change 2 is unknown.
- assertThat(pc.registerListener(2, mListener1)).isFalse();
+ assertThat(mPlatformCompat.registerListener(2, mListener1)).isFalse();
}
@Test
- public void testListenerCalledOnSetOverrides() {
- PlatformCompat pc = new PlatformCompat(mContext);
+ public void testListenerCalledOnSetOverrides() throws Exception {
+ mPlatformCompat.registerListener(1, mListener1);
+ mPlatformCompat.registerListener(2, mListener1);
- pc.registerListener(1, mListener1);
- pc.registerListener(2, mListener1);
-
- pc.setOverrides(
- new CompatibilityChangeConfig(
- new Compatibility.ChangeConfig(ImmutableSet.of(1L), ImmutableSet.of(2L))),
+ mPlatformCompat.setOverrides(
+ CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(),
PACKAGE_NAME);
verify(mListener1, times(2)).onCompatChange(PACKAGE_NAME);
}
@Test
- public void testListenerNotCalledOnWrongPackage() {
- PlatformCompat pc = new PlatformCompat(mContext);
+ public void testListenerNotCalledOnWrongPackage() throws Exception {
+ mPlatformCompat.registerListener(1, mListener1);
+ mPlatformCompat.registerListener(2, mListener1);
- pc.registerListener(1, mListener1);
- pc.registerListener(2, mListener1);
-
- pc.setOverridesForTest(
- new CompatibilityChangeConfig(
- new Compatibility.ChangeConfig(ImmutableSet.of(1L), ImmutableSet.of(2L))),
+ mPlatformCompat.setOverrides(
+ CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(),
PACKAGE_NAME);
verify(mListener1, never()).onCompatChange("other.package");
}
@Test
- public void testListenerCalledOnSetOverridesTwoListeners() {
- PlatformCompat pc = new PlatformCompat(mContext);
- pc.registerListener(1, mListener1);
+ public void testListenerCalledOnSetOverridesTwoListeners() throws Exception {
+ mPlatformCompat.registerListener(1, mListener1);
- final ImmutableSet<Long> enabled = ImmutableSet.of(1L);
- final ImmutableSet<Long> disabled = ImmutableSet.of(2L);
-
- pc.setOverrides(
- new CompatibilityChangeConfig(
- new Compatibility.ChangeConfig(enabled, disabled)),
+ mPlatformCompat.setOverrides(
+ CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(),
PACKAGE_NAME);
verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME);
@@ -136,11 +128,10 @@
reset(mListener1);
reset(mListener2);
- pc.registerListener(2, mListener2);
+ mPlatformCompat.registerListener(2, mListener2);
- pc.setOverrides(
- new CompatibilityChangeConfig(
- new Compatibility.ChangeConfig(enabled, disabled)),
+ mPlatformCompat.setOverrides(
+ CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(),
PACKAGE_NAME);
verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME);
@@ -148,31 +139,23 @@
}
@Test
- public void testListenerCalledOnSetOverridesForTest() {
- PlatformCompat pc = new PlatformCompat(mContext);
+ public void testListenerCalledOnSetOverridesForTest() throws Exception {
+ mPlatformCompat.registerListener(1, mListener1);
+ mPlatformCompat.registerListener(2, mListener1);
- pc.registerListener(1, mListener1);
- pc.registerListener(2, mListener1);
-
- pc.setOverridesForTest(
- new CompatibilityChangeConfig(
- new Compatibility.ChangeConfig(ImmutableSet.of(1L), ImmutableSet.of(2L))),
+ mPlatformCompat.setOverrides(
+ CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(),
PACKAGE_NAME);
verify(mListener1, times(2)).onCompatChange(PACKAGE_NAME);
}
@Test
- public void testListenerCalledOnSetOverridesTwoListenersForTest() {
- PlatformCompat pc = new PlatformCompat(mContext);
- pc.registerListener(1, mListener1);
+ public void testListenerCalledOnSetOverridesTwoListenersForTest() throws Exception {
+ mPlatformCompat.registerListener(1, mListener1);
- final ImmutableSet<Long> enabled = ImmutableSet.of(1L);
- final ImmutableSet<Long> disabled = ImmutableSet.of(2L);
-
- pc.setOverridesForTest(
- new CompatibilityChangeConfig(
- new Compatibility.ChangeConfig(enabled, disabled)),
+ mPlatformCompat.setOverrides(
+ CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(),
PACKAGE_NAME);
verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME);
@@ -181,10 +164,10 @@
reset(mListener1);
reset(mListener2);
- pc.registerListener(2, mListener2);
- pc.setOverridesForTest(
- new CompatibilityChangeConfig(
- new Compatibility.ChangeConfig(enabled, disabled)),
+ mPlatformCompat.registerListener(2, mListener2);
+
+ mPlatformCompat.setOverrides(
+ CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(),
PACKAGE_NAME);
verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME);
@@ -192,15 +175,12 @@
}
@Test
- public void testListenerCalledOnClearOverrides() {
- PlatformCompat pc = new PlatformCompat(mContext);
+ public void testListenerCalledOnClearOverrides() throws Exception {
+ mPlatformCompat.registerListener(1, mListener1);
+ mPlatformCompat.registerListener(2, mListener2);
- pc.registerListener(1, mListener1);
- pc.registerListener(2, mListener2);
-
- pc.setOverrides(
- new CompatibilityChangeConfig(
- new Compatibility.ChangeConfig(ImmutableSet.of(1L), ImmutableSet.of())),
+ mPlatformCompat.setOverrides(
+ CompatibilityChangeConfigBuilder.create().enable(1L).build(),
PACKAGE_NAME);
verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME);
verify(mListener2, never()).onCompatChange(PACKAGE_NAME);
@@ -208,21 +188,18 @@
reset(mListener1);
reset(mListener2);
- pc.clearOverrides(PACKAGE_NAME);
+ mPlatformCompat.clearOverrides(PACKAGE_NAME);
verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME);
verify(mListener2, never()).onCompatChange(PACKAGE_NAME);
}
@Test
- public void testListenerCalledOnClearOverridesMultipleOverrides() {
- PlatformCompat pc = new PlatformCompat(mContext);
+ public void testListenerCalledOnClearOverridesMultipleOverrides() throws Exception {
+ mPlatformCompat.registerListener(1, mListener1);
+ mPlatformCompat.registerListener(2, mListener2);
- pc.registerListener(1, mListener1);
- pc.registerListener(2, mListener2);
-
- pc.setOverrides(
- new CompatibilityChangeConfig(
- new Compatibility.ChangeConfig(ImmutableSet.of(1L), ImmutableSet.of(2L))),
+ mPlatformCompat.setOverrides(
+ CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(),
PACKAGE_NAME);
verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME);
verify(mListener2, times(1)).onCompatChange(PACKAGE_NAME);
@@ -230,21 +207,18 @@
reset(mListener1);
reset(mListener2);
- pc.clearOverrides(PACKAGE_NAME);
+ mPlatformCompat.clearOverrides(PACKAGE_NAME);
verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME);
verify(mListener2, times(1)).onCompatChange(PACKAGE_NAME);
}
@Test
- public void testListenerCalledOnClearOverrideExists() {
- PlatformCompat pc = new PlatformCompat(mContext);
+ public void testListenerCalledOnClearOverrideExists() throws Exception {
+ mPlatformCompat.registerListener(1, mListener1);
+ mPlatformCompat.registerListener(2, mListener2);
- pc.registerListener(1, mListener1);
- pc.registerListener(2, mListener2);
-
- pc.setOverrides(
- new CompatibilityChangeConfig(
- new Compatibility.ChangeConfig(ImmutableSet.of(1L), ImmutableSet.of())),
+ mPlatformCompat.setOverrides(
+ CompatibilityChangeConfigBuilder.create().enable(1L).build(),
PACKAGE_NAME);
verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME);
verify(mListener2, never()).onCompatChange(PACKAGE_NAME);
@@ -252,21 +226,17 @@
reset(mListener1);
reset(mListener2);
- pc.clearOverride(1, PACKAGE_NAME);
+ mPlatformCompat.clearOverride(1, PACKAGE_NAME);
verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME);
verify(mListener2, never()).onCompatChange(PACKAGE_NAME);
}
@Test
- public void testListenerCalledOnClearOverrideDoesntExist() {
- PlatformCompat pc = new PlatformCompat(mContext);
+ public void testListenerCalledOnClearOverrideDoesntExist() throws Exception {
+ mPlatformCompat.registerListener(1, mListener1);
- pc.registerListener(1, mListener1);
-
- pc.clearOverride(1, PACKAGE_NAME);
+ mPlatformCompat.clearOverride(1, PACKAGE_NAME);
// Listener not called when a non existing override is removed.
verify(mListener1, never()).onCompatChange(PACKAGE_NAME);
}
-
-
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 4fcfa32..175c756 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -104,6 +104,7 @@
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.mockito.Mockito;
+import org.mockito.internal.util.collections.Sets;
import org.mockito.stubbing.Answer;
import java.io.File;
@@ -3635,6 +3636,29 @@
verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME_ZONE, 0);
}
+ public void testIsOrganizationOwnedDevice() throws Exception {
+ setupProfileOwner();
+ // Set up the user manager to return correct user info
+ UserInfo managedProfileUserInfo = new UserInfo(DpmMockContext.CALLER_USER_HANDLE,
+ "managed profile",
+ UserInfo.FLAG_MANAGED_PROFILE);
+ when(getServices().userManager.getUsers())
+ .thenReturn(Arrays.asList(managedProfileUserInfo));
+
+ // Any caller without the MANAGE_USERS permission should get a security exception.
+ assertExpectException(SecurityException.class, null, () ->
+ dpm.isOrganizationOwnedDeviceWithManagedProfile());
+ // But when the right permission is granted, this should succeed.
+ mContext.permissions.add(android.Manifest.permission.MANAGE_USERS);
+ assertFalse(dpm.isOrganizationOwnedDeviceWithManagedProfile());
+ configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE);
+ assertTrue(dpm.isOrganizationOwnedDeviceWithManagedProfile());
+
+ // A random caller from another user should also be able to get the right result.
+ mContext.binder.callingUid = DpmMockContext.ANOTHER_UID;
+ assertTrue(dpm.isOrganizationOwnedDeviceWithManagedProfile());
+ }
+
public void testSetTime() throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setupDeviceOwner();
@@ -3659,6 +3683,25 @@
assertExpectException(SecurityException.class, null, () -> dpm.setTime(admin1, 0));
}
+ public void testSetTimeWithPOOfOrganizationOwnedDevice() throws Exception {
+ setupProfileOwner();
+ configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE);
+ dpm.setTime(admin1, 0);
+
+ BaseMatcher<ManualTimeSuggestion> hasZeroTime = new BaseMatcher<ManualTimeSuggestion>() {
+ @Override
+ public boolean matches(Object item) {
+ final ManualTimeSuggestion suggestion = (ManualTimeSuggestion) item;
+ return suggestion.getUtcTime().getValue() == 0;
+ }
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("ManualTimeSuggestion{utcTime.value=0}");
+ }
+ };
+ verify(getServices().timeDetector).suggestManualTime(argThat(hasZeroTime));
+ }
+
public void testSetTimeWithAutoTimeOn() throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setupDeviceOwner();
@@ -3682,6 +3725,15 @@
() -> dpm.setTimeZone(admin1, "Asia/Shanghai"));
}
+ public void testSetTimeZoneWithPOOfOrganizationOwnedDevice() throws Exception {
+ setupProfileOwner();
+ configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE);
+ dpm.setTimeZone(admin1, "Asia/Shanghai");
+ ManualTimeZoneSuggestion suggestion =
+ TimeZoneDetector.createManualTimeZoneSuggestion("Asia/Shanghai", "Test debug info");
+ verify(getServices().timeZoneDetector).suggestManualTimeZone(suggestion);
+ }
+
public void testSetTimeZoneWithAutoTimeZoneOn() throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setupDeviceOwner();
@@ -5496,6 +5548,36 @@
assertTrue(dpm.isPackageAllowedToAccessCalendar(testPackage));
}
+ public void testSetProtectedPackages_asDO() throws Exception {
+ final List<String> testPackages = new ArrayList<>();
+ testPackages.add("package_1");
+ testPackages.add("package_2");
+
+ mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
+ setDeviceOwner();
+
+ dpm.setProtectedPackages(admin1, testPackages);
+
+ verify(getServices().packageManagerInternal).setDeviceOwnerProtectedPackages(testPackages);
+
+ assertEquals(testPackages, dpm.getProtectedPackages(admin1));
+ }
+
+ public void testSetProtectedPackages_failingAsPO() throws Exception {
+ final List<String> testPackages = new ArrayList<>();
+ testPackages.add("package_1");
+ testPackages.add("package_2");
+
+ mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
+ setAsProfileOwner(admin1);
+
+ assertExpectException(SecurityException.class, /* messageRegex= */ null,
+ () -> dpm.setProtectedPackages(admin1, testPackages));
+
+ assertExpectException(SecurityException.class, /* messageRegex= */ null,
+ () -> dpm.getProtectedPackages(admin1));
+ }
+
private void configureProfileOwnerOfOrgOwnedDevice(ComponentName who, int userId) {
when(getServices().userManager.getProfileParent(eq(UserHandle.of(userId))))
.thenReturn(UserHandle.SYSTEM);
@@ -5542,6 +5624,57 @@
assertEquals(packages, dpm.getCrossProfilePackages(admin1));
}
+ public void testGetAllCrossProfilePackages_notSet_returnsEmpty() throws Exception {
+ addManagedProfile(admin1, mServiceContext.binder.callingUid, admin1);
+
+ setCrossProfileAppsList();
+
+ assertTrue(dpm.getAllCrossProfilePackages().isEmpty());
+ }
+
+ public void testGetAllCrossProfilePackages_notSet_dpmsReinitialized_returnsEmpty()
+ throws Exception {
+ addManagedProfile(admin1, mServiceContext.binder.callingUid, admin1);
+
+ setCrossProfileAppsList();
+ initializeDpms();
+
+ assertTrue(dpm.getAllCrossProfilePackages().isEmpty());
+ }
+
+ public void testGetAllCrossProfilePackages_whenSet_returnsCombinedSet() throws Exception {
+ addManagedProfile(admin1, mServiceContext.binder.callingUid, admin1);
+ final Set<String> packages = Sets.newSet("TEST_PACKAGE", "TEST_COMMON_PACKAGE");
+
+ dpm.setCrossProfilePackages(admin1, packages);
+ setCrossProfileAppsList("TEST_DEFAULT_PACKAGE", "TEST_COMMON_PACKAGE");
+
+ assertEquals(Sets.newSet(
+ "TEST_PACKAGE", "TEST_DEFAULT_PACKAGE", "TEST_COMMON_PACKAGE"),
+ dpm.getAllCrossProfilePackages());
+
+ }
+
+ public void testGetAllCrossProfilePackages_whenSet_dpmsReinitialized_returnsCombinedSet()
+ throws Exception {
+ addManagedProfile(admin1, mServiceContext.binder.callingUid, admin1);
+ final Set<String> packages = Sets.newSet("TEST_PACKAGE", "TEST_COMMON_PACKAGE");
+
+ dpm.setCrossProfilePackages(admin1, packages);
+ setCrossProfileAppsList("TEST_DEFAULT_PACKAGE", "TEST_COMMON_PACKAGE");
+ initializeDpms();
+
+ assertEquals(Sets.newSet(
+ "TEST_PACKAGE", "TEST_DEFAULT_PACKAGE", "TEST_COMMON_PACKAGE"),
+ dpm.getAllCrossProfilePackages());
+ }
+
+ private void setCrossProfileAppsList(String... packages) {
+ when(mContext.getResources()
+ .getStringArray(eq(R.array.cross_profile_apps)))
+ .thenReturn(packages);
+ }
+
// admin1 is the outgoing DPC, adminAnotherPakcage is the incoming one.
private void assertDeviceOwnershipRevertedWithFakeTransferMetadata() throws Exception {
writeFakeTransferMetadataFile(UserHandle.USER_SYSTEM,
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 1a67576..960f670 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -217,6 +217,8 @@
return mMockSystemServices.wifiManager;
case Context.ACCOUNT_SERVICE:
return mMockSystemServices.accountManager;
+ case Context.TELEPHONY_SERVICE:
+ return mMockSystemServices.telephonyManager;
}
throw new UnsupportedOperationException();
}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/BinaryFileOperationsTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/BinaryFileOperationsTest.java
new file mode 100644
index 0000000..94f68a5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/integrity/parser/BinaryFileOperationsTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.integrity.parser;
+
+import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
+import static com.android.server.integrity.parser.BinaryFileOperations.getBooleanValue;
+import static com.android.server.integrity.parser.BinaryFileOperations.getIntValue;
+import static com.android.server.integrity.parser.BinaryFileOperations.getStringValue;
+import static com.android.server.integrity.utils.TestUtils.getBits;
+import static com.android.server.integrity.utils.TestUtils.getBytes;
+import static com.android.server.integrity.utils.TestUtils.getValueBits;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.server.integrity.IntegrityUtils;
+import com.android.server.integrity.model.BitInputStream;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+
+@RunWith(JUnit4.class)
+public class BinaryFileOperationsTest {
+
+ private static final String IS_NOT_HASHED = "0";
+ private static final String IS_HASHED = "1";
+ private static final String PACKAGE_NAME = "com.test.app";
+ private static final String APP_CERTIFICATE = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+
+ @Test
+ public void testGetStringValue() throws IOException {
+ byte[] stringBytes =
+ getBytes(
+ IS_NOT_HASHED
+ + getBits(PACKAGE_NAME.length(), VALUE_SIZE_BITS)
+ + getValueBits(PACKAGE_NAME));
+ ByteBuffer rule = ByteBuffer.allocate(stringBytes.length);
+ rule.put(stringBytes);
+ BitInputStream inputStream = new BitInputStream(rule.array());
+
+ String resultString = getStringValue(inputStream);
+
+ assertThat(resultString).isEqualTo(PACKAGE_NAME);
+ }
+
+ @Test
+ public void testGetHashedStringValue() throws IOException {
+ byte[] ruleBytes =
+ getBytes(
+ IS_HASHED
+ + getBits(APP_CERTIFICATE.length(), VALUE_SIZE_BITS)
+ + getValueBits(APP_CERTIFICATE));
+ ByteBuffer rule = ByteBuffer.allocate(ruleBytes.length);
+ rule.put(ruleBytes);
+ BitInputStream inputStream = new BitInputStream(rule.array());
+
+ String resultString = getStringValue(inputStream);
+
+ assertThat(resultString)
+ .isEqualTo(IntegrityUtils.getHexDigest(
+ APP_CERTIFICATE.getBytes(StandardCharsets.UTF_8)));
+ }
+
+ @Test
+ public void testGetStringValue_withSizeAndHashingInfo() throws IOException {
+ byte[] ruleBytes = getBytes(getValueBits(PACKAGE_NAME));
+ ByteBuffer rule = ByteBuffer.allocate(ruleBytes.length);
+ rule.put(ruleBytes);
+ BitInputStream inputStream = new BitInputStream(rule.array());
+
+ String resultString = getStringValue(inputStream,
+ PACKAGE_NAME.length(), /* isHashedValue= */false);
+
+ assertThat(resultString).isEqualTo(PACKAGE_NAME);
+ }
+
+ @Test
+ public void testGetIntValue() throws IOException {
+ int randomValue = 15;
+ byte[] ruleBytes = getBytes(getBits(randomValue, /* numOfBits= */ 32));
+ ByteBuffer rule = ByteBuffer.allocate(ruleBytes.length);
+ rule.put(ruleBytes);
+ BitInputStream inputStream = new BitInputStream(rule.array());
+
+ assertThat(getIntValue(inputStream)).isEqualTo(randomValue);
+ }
+
+ @Test
+ public void testGetBooleanValue_true() throws IOException {
+ String booleanValue = "1";
+ byte[] ruleBytes = getBytes(booleanValue);
+ ByteBuffer rule = ByteBuffer.allocate(ruleBytes.length);
+ rule.put(ruleBytes);
+ BitInputStream inputStream = new BitInputStream(rule.array());
+
+ assertThat(getBooleanValue(inputStream)).isEqualTo(true);
+ }
+
+ @Test
+ public void testGetBooleanValue_false() throws IOException {
+ String booleanValue = "0";
+ byte[] ruleBytes = getBytes(booleanValue);
+ ByteBuffer rule = ByteBuffer.allocate(ruleBytes.length);
+ rule.put(ruleBytes);
+ BitInputStream inputStream = new BitInputStream(rule.array());
+
+ assertThat(getBooleanValue(inputStream)).isEqualTo(false);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java
new file mode 100644
index 0000000..742952e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.integrity.parser;
+
+import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
+import static com.android.server.integrity.model.IndexingFileConstants.END_INDEXING_KEY;
+import static com.android.server.integrity.model.IndexingFileConstants.START_INDEXING_KEY;
+import static com.android.server.integrity.utils.TestUtils.getBits;
+import static com.android.server.integrity.utils.TestUtils.getBytes;
+import static com.android.server.integrity.utils.TestUtils.getValueBits;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.integrity.AppInstallMetadata;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.List;
+
+@RunWith(JUnit4.class)
+public class RuleIndexingControllerTest {
+
+ @Test
+ public void verifyIndexRangeSearchIsCorrect() throws IOException {
+ InputStream inputStream = obtainDefaultIndexingMapForTest();
+
+ RuleIndexingController indexingController = new RuleIndexingController(inputStream);
+
+ AppInstallMetadata appInstallMetadata =
+ new AppInstallMetadata.Builder()
+ .setPackageName("ddd")
+ .setAppCertificate("777")
+ .build();
+
+ List<RuleIndexRange> resultingIndexes =
+ indexingController.identifyRulesToEvaluate(appInstallMetadata);
+
+ assertThat(resultingIndexes)
+ .containsExactly(
+ new RuleIndexRange(200, 300),
+ new RuleIndexRange(700, 800),
+ new RuleIndexRange(900, 945));
+ }
+
+ @Test
+ public void verifyIndexRangeSearchIsCorrect_keysInFirstAndLastBlock() throws IOException {
+ InputStream inputStream = obtainDefaultIndexingMapForTest();
+
+ RuleIndexingController indexingController = new RuleIndexingController(inputStream);
+
+ AppInstallMetadata appInstallMetadata =
+ new AppInstallMetadata.Builder()
+ .setPackageName("bbb")
+ .setAppCertificate("999")
+ .build();
+
+ List<RuleIndexRange> resultingIndexes =
+ indexingController.identifyRulesToEvaluate(appInstallMetadata);
+
+ assertThat(resultingIndexes)
+ .containsExactly(
+ new RuleIndexRange(100, 200),
+ new RuleIndexRange(800, 900),
+ new RuleIndexRange(900, 945));
+ }
+
+ @Test
+ public void verifyIndexRangeSearchIsCorrect_keysMatchWithValues() throws IOException {
+ InputStream inputStream = obtainDefaultIndexingMapForTest();
+
+ RuleIndexingController indexingController = new RuleIndexingController(inputStream);
+
+ AppInstallMetadata appInstallMetadata =
+ new AppInstallMetadata.Builder()
+ .setPackageName("ccc")
+ .setAppCertificate("444")
+ .build();
+
+ List<RuleIndexRange> resultingIndexes =
+ indexingController.identifyRulesToEvaluate(appInstallMetadata);
+
+ assertThat(resultingIndexes)
+ .containsExactly(
+ new RuleIndexRange(200, 300),
+ new RuleIndexRange(700, 800),
+ new RuleIndexRange(900, 945));
+ }
+
+ @Test
+ public void verifyIndexRangeSearchIsCorrect_noIndexesAvailable() throws IOException {
+ byte[] stringBytes =
+ getBytes(
+ getKeyValueString(START_INDEXING_KEY, 100)
+ + getKeyValueString(END_INDEXING_KEY, 500)
+ + getKeyValueString(START_INDEXING_KEY, 500)
+ + getKeyValueString(END_INDEXING_KEY, 900)
+ + getKeyValueString(START_INDEXING_KEY, 900)
+ + getKeyValueString(END_INDEXING_KEY, 945));
+ ByteBuffer rule = ByteBuffer.allocate(stringBytes.length);
+ rule.put(stringBytes);
+ InputStream inputStream = new ByteArrayInputStream(rule.array());
+
+ RuleIndexingController indexingController = new RuleIndexingController(inputStream);
+
+ AppInstallMetadata appInstallMetadata =
+ new AppInstallMetadata.Builder()
+ .setPackageName("ccc")
+ .setAppCertificate("444")
+ .build();
+
+ List<RuleIndexRange> resultingIndexes =
+ indexingController.identifyRulesToEvaluate(appInstallMetadata);
+
+ assertThat(resultingIndexes)
+ .containsExactly(
+ new RuleIndexRange(100, 500),
+ new RuleIndexRange(500, 900),
+ new RuleIndexRange(900, 945));
+ }
+
+ private static InputStream obtainDefaultIndexingMapForTest() {
+ byte[] stringBytes =
+ getBytes(
+ getKeyValueString(START_INDEXING_KEY, 100)
+ + getKeyValueString("ccc", 200)
+ + getKeyValueString("eee", 300)
+ + getKeyValueString("hhh", 400)
+ + getKeyValueString(END_INDEXING_KEY, 500)
+ + getKeyValueString(START_INDEXING_KEY, 500)
+ + getKeyValueString("111", 600)
+ + getKeyValueString("444", 700)
+ + getKeyValueString("888", 800)
+ + getKeyValueString(END_INDEXING_KEY, 900)
+ + getKeyValueString(START_INDEXING_KEY, 900)
+ + getKeyValueString(END_INDEXING_KEY, 945));
+ ByteBuffer rule = ByteBuffer.allocate(stringBytes.length);
+ rule.put(stringBytes);
+ return new ByteArrayInputStream(rule.array());
+ }
+
+ private static String getKeyValueString(String key, int value) {
+ String isNotHashed = "0";
+ return isNotHashed
+ + getBits(key.length(), VALUE_SIZE_BITS)
+ + getValueBits(key)
+ + getBits(value, /* numOfBits= */ 32);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java
index 981db6a..eb6698b 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java
@@ -27,6 +27,9 @@
import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS;
import static com.android.server.integrity.model.ComponentBitSize.SEPARATOR_BITS;
import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
+import static com.android.server.integrity.model.IndexingFileConstants.END_INDEXING_KEY;
+import static com.android.server.integrity.model.IndexingFileConstants.INDEXING_BLOCK_SIZE;
+import static com.android.server.integrity.model.IndexingFileConstants.START_INDEXING_KEY;
import static com.android.server.integrity.utils.TestUtils.getBits;
import static com.android.server.integrity.utils.TestUtils.getBytes;
import static com.android.server.integrity.utils.TestUtils.getValueBits;
@@ -94,6 +97,15 @@
private static final byte[] DEFAULT_FORMAT_VERSION_BYTES =
getBytes(getBits(DEFAULT_FORMAT_VERSION, FORMAT_VERSION_BITS));
+ private static final String SERIALIZED_START_INDEXING_KEY =
+ IS_NOT_HASHED
+ + getBits(START_INDEXING_KEY.length(), VALUE_SIZE_BITS)
+ + getValueBits(START_INDEXING_KEY);
+ private static final String SERIALIZED_END_INDEXING_KEY =
+ IS_NOT_HASHED
+ + getBits(END_INDEXING_KEY.length(), VALUE_SIZE_BITS)
+ + getValueBits(END_INDEXING_KEY);
+
@Test
public void testBinaryString_serializeNullRules() {
RuleSerializer binarySerializer = new RuleBinarySerializer();
@@ -107,15 +119,34 @@
@Test
public void testBinaryString_emptyRules() throws Exception {
- ByteArrayOutputStream expectedArrayOutputStream = new ByteArrayOutputStream();
- expectedArrayOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
-
- ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ ByteArrayOutputStream ruleOutputStream = new ByteArrayOutputStream();
+ ByteArrayOutputStream indexingOutputStream = new ByteArrayOutputStream();
RuleSerializer binarySerializer = new RuleBinarySerializer();
- binarySerializer.serialize(
- Collections.emptyList(), /* formatVersion= */ Optional.empty(), outputStream);
- assertThat(outputStream.toByteArray()).isEqualTo(expectedArrayOutputStream.toByteArray());
+ binarySerializer.serialize(
+ Collections.emptyList(),
+ /* formatVersion= */ Optional.empty(),
+ ruleOutputStream,
+ indexingOutputStream);
+
+ ByteArrayOutputStream expectedRuleOutputStream = new ByteArrayOutputStream();
+ expectedRuleOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
+ assertThat(ruleOutputStream.toByteArray())
+ .isEqualTo(expectedRuleOutputStream.toByteArray());
+
+ ByteArrayOutputStream expectedIndexingOutputStream = new ByteArrayOutputStream();
+ byte[] expectedIndexingBytes =
+ getBytes(
+ SERIALIZED_START_INDEXING_KEY
+ + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32)
+ + SERIALIZED_END_INDEXING_KEY
+ + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */
+ 32));
+ expectedIndexingOutputStream.write(expectedIndexingBytes);
+ expectedIndexingOutputStream.write(expectedIndexingBytes);
+ expectedIndexingOutputStream.write(expectedIndexingBytes);
+ assertThat(indexingOutputStream.toByteArray())
+ .isEqualTo(expectedIndexingOutputStream.toByteArray());
}
@Test
@@ -131,8 +162,16 @@
packageName,
/* isHashedValue= */ false))),
Rule.DENY);
+
+ ByteArrayOutputStream ruleOutputStream = new ByteArrayOutputStream();
+ ByteArrayOutputStream indexingOutputStream = new ByteArrayOutputStream();
RuleSerializer binarySerializer = new RuleBinarySerializer();
- ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ binarySerializer.serialize(
+ Collections.singletonList(rule),
+ /* formatVersion= */ Optional.empty(),
+ ruleOutputStream,
+ indexingOutputStream);
+
String expectedBits =
START_BIT
+ COMPOUND_FORMULA_START_BITS
@@ -146,18 +185,29 @@
+ COMPOUND_FORMULA_END_BITS
+ DENY
+ END_BIT;
- ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
- byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
- byteArrayOutputStream.write(getBytes(expectedBits));
- byte[] expectedRules = byteArrayOutputStream.toByteArray();
+ ByteArrayOutputStream expectedRuleOutputStream = new ByteArrayOutputStream();
+ expectedRuleOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
+ expectedRuleOutputStream.write(getBytes(expectedBits));
+ assertThat(ruleOutputStream.toByteArray())
+ .isEqualTo(expectedRuleOutputStream.toByteArray());
- binarySerializer.serialize(
- Collections.singletonList(rule),
- /* formatVersion= */ Optional.empty(),
- outputStream);
-
- byte[] actualRules = outputStream.toByteArray();
- assertThat(actualRules).isEqualTo(expectedRules);
+ ByteArrayOutputStream expectedIndexingOutputStream = new ByteArrayOutputStream();
+ String expectedIndexingBitsForIndexed =
+ SERIALIZED_START_INDEXING_KEY
+ + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32)
+ + SERIALIZED_END_INDEXING_KEY
+ + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32);
+ expectedIndexingOutputStream.write(getBytes(expectedIndexingBitsForIndexed));
+ expectedIndexingOutputStream.write(getBytes(expectedIndexingBitsForIndexed));
+ String expectedIndexingBitsForUnindexed =
+ SERIALIZED_START_INDEXING_KEY
+ + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32)
+ + SERIALIZED_END_INDEXING_KEY
+ + getBits(DEFAULT_FORMAT_VERSION_BYTES.length + getBytes(
+ expectedBits).length, /* numOfBits= */ 32);
+ expectedIndexingOutputStream.write(getBytes(expectedIndexingBitsForUnindexed));
+ assertThat(indexingOutputStream.toByteArray())
+ .isEqualTo(expectedIndexingOutputStream.toByteArray());
}
@Test
@@ -453,91 +503,113 @@
}
@Test
- public void testBinaryString_serializeComplexCompoundFormula_indexingOrderValid()
- throws Exception {
- String packageNameA = "aaa";
- String packageNameB = "bbb";
- String packageNameC = "ccc";
- String appCert1 = "cert1";
- String appCert2 = "cert2";
- String appCert3 = "cert3";
- Rule installerRule =
- new Rule(
- new CompoundFormula(
- CompoundFormula.AND,
- Arrays.asList(
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.INSTALLER_NAME,
- SAMPLE_INSTALLER_NAME,
- /* isHashedValue= */ false),
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.INSTALLER_CERTIFICATE,
- SAMPLE_INSTALLER_CERT,
- /* isHashedValue= */ false))),
- Rule.DENY);
+ public void testBinaryString_verifyManyRulesAreIndexedCorrectly() throws Exception {
+ int ruleCount = 225;
+ String packagePrefix = "package.name.";
+ String appCertificatePrefix = "app.cert.";
+ String installerNamePrefix = "installer.";
- RuleSerializer binarySerializer = new RuleBinarySerializer();
+ // Create the rule set with 225 package name based rules, 225 app certificate indexed rules,
+ // and 225 non-indexed rules..
List<Rule> ruleList = new ArrayList();
- ruleList.add(getRuleWithAppCertificateAndSampleInstallerName(appCert3));
- ruleList.add(getRuleWithAppCertificateAndSampleInstallerName(appCert2));
- ruleList.add(getRuleWithAppCertificateAndSampleInstallerName(appCert1));
- ruleList.add(getRuleWithPackageNameAndSampleInstallerName(packageNameB));
- ruleList.add(getRuleWithPackageNameAndSampleInstallerName(packageNameC));
- ruleList.add(getRuleWithPackageNameAndSampleInstallerName(packageNameA));
- ruleList.add(installerRule);
- byte[] actualRules =
- binarySerializer.serialize(ruleList, /* formatVersion= */ Optional.empty());
+ for (int count = 0; count < ruleCount; count++) {
+ ruleList.add(getRuleWithPackageNameAndSampleInstallerName(
+ String.format("%s%04d", packagePrefix, count)));
+ }
+ for (int count = 0; count < ruleCount; count++) {
+ ruleList.add(getRuleWithAppCertificateAndSampleInstallerName(
+ String.format("%s%04d", appCertificatePrefix, count)));
+ }
+ for (int count = 0; count < ruleCount; count++) {
+ ruleList.add(getNonIndexedRuleWithInstallerName(
+ String.format("%s%04d", installerNamePrefix, count)));
+ }
- // Note that ordering is important here and the test verifies that the rules are written
- // in this sorted order.
- ByteArrayOutputStream expectedArrayOutputStream = new ByteArrayOutputStream();
- expectedArrayOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
- expectedArrayOutputStream.write(
- getBytes(
- getSerializedCompoundRuleWithPackageNameAndSampleInstallerName(
- packageNameA)));
- expectedArrayOutputStream.write(
- getBytes(
- getSerializedCompoundRuleWithPackageNameAndSampleInstallerName(
- packageNameB)));
- expectedArrayOutputStream.write(
- getBytes(
- getSerializedCompoundRuleWithPackageNameAndSampleInstallerName(
- packageNameC)));
- expectedArrayOutputStream.write(
- getBytes(
- getSerializedCompoundRuleWithCertificateNameAndSampleInstallerName(
- appCert1)));
- expectedArrayOutputStream.write(
- getBytes(
- getSerializedCompoundRuleWithCertificateNameAndSampleInstallerName(
- appCert2)));
- expectedArrayOutputStream.write(
- getBytes(
- getSerializedCompoundRuleWithCertificateNameAndSampleInstallerName(
- appCert3)));
- String expectedBitsForInstallerRule =
- START_BIT
- + COMPOUND_FORMULA_START_BITS
- + AND
- + ATOMIC_FORMULA_START_BITS
- + INSTALLER_NAME
- + EQ
- + IS_NOT_HASHED
- + getBits(SAMPLE_INSTALLER_NAME.length(), VALUE_SIZE_BITS)
- + getValueBits(SAMPLE_INSTALLER_NAME)
- + ATOMIC_FORMULA_START_BITS
- + INSTALLER_CERTIFICATE
- + EQ
- + IS_NOT_HASHED
- + getBits(SAMPLE_INSTALLER_CERT.length(), VALUE_SIZE_BITS)
- + getValueBits(SAMPLE_INSTALLER_CERT)
- + COMPOUND_FORMULA_END_BITS
- + DENY
- + END_BIT;
- expectedArrayOutputStream.write(getBytes(expectedBitsForInstallerRule));
+ // Serialize the rules.
+ ByteArrayOutputStream ruleOutputStream = new ByteArrayOutputStream();
+ ByteArrayOutputStream indexingOutputStream = new ByteArrayOutputStream();
+ RuleSerializer binarySerializer = new RuleBinarySerializer();
+ binarySerializer.serialize(
+ ruleList,
+ /* formatVersion= */ Optional.empty(),
+ ruleOutputStream,
+ indexingOutputStream);
- assertThat(actualRules).isEqualTo(expectedArrayOutputStream.toByteArray());
+ // Verify the rules file and index files.
+ ByteArrayOutputStream expectedOrderedRuleOutputStream = new ByteArrayOutputStream();
+ ByteArrayOutputStream expectedIndexingOutputStream = new ByteArrayOutputStream();
+
+ expectedOrderedRuleOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
+ int totalBytesWritten = DEFAULT_FORMAT_VERSION_BYTES.length;
+
+ String expectedIndexingBytesForPackageNameIndexed =
+ SERIALIZED_START_INDEXING_KEY
+ + getBits(totalBytesWritten, /* numOfBits= */ 32);
+ for (int count = 0; count < ruleCount; count++) {
+ String packageName = String.format("%s%04d", packagePrefix, count);
+ if (count > 0 && count % INDEXING_BLOCK_SIZE == 0) {
+ expectedIndexingBytesForPackageNameIndexed +=
+ IS_NOT_HASHED
+ + getBits(packageName.length(), VALUE_SIZE_BITS)
+ + getValueBits(packageName)
+ + getBits(totalBytesWritten, /* numOfBits= */ 32);
+ }
+
+ byte[] bytesForPackage =
+ getBytes(getSerializedCompoundRuleWithPackageNameAndSampleInstallerName(
+ packageName));
+ expectedOrderedRuleOutputStream.write(bytesForPackage);
+ totalBytesWritten += bytesForPackage.length;
+ }
+ expectedIndexingBytesForPackageNameIndexed +=
+ SERIALIZED_END_INDEXING_KEY
+ + getBits(totalBytesWritten, /* numOfBits= */ 32);
+ expectedIndexingOutputStream.write(getBytes(expectedIndexingBytesForPackageNameIndexed));
+
+ String expectedIndexingBytesForAppCertificateIndexed =
+ SERIALIZED_START_INDEXING_KEY
+ + getBits(totalBytesWritten, /* numOfBits= */ 32);
+ for (int count = 0; count < ruleCount; count++) {
+ String appCertificate = String.format("%s%04d", appCertificatePrefix, count);
+ if (count > 0 && count % INDEXING_BLOCK_SIZE == 0) {
+ expectedIndexingBytesForAppCertificateIndexed +=
+ IS_NOT_HASHED
+ + getBits(appCertificate.length(), VALUE_SIZE_BITS)
+ + getValueBits(appCertificate)
+ + getBits(totalBytesWritten, /* numOfBits= */ 32);
+ }
+
+ byte[] bytesForPackage =
+ getBytes(getSerializedCompoundRuleWithCertificateNameAndSampleInstallerName(
+ appCertificate));
+ expectedOrderedRuleOutputStream.write(bytesForPackage);
+ totalBytesWritten += bytesForPackage.length;
+ }
+ expectedIndexingBytesForAppCertificateIndexed +=
+ SERIALIZED_END_INDEXING_KEY
+ + getBits(totalBytesWritten, /* numOfBits= */ 32);
+ expectedIndexingOutputStream.write(getBytes(expectedIndexingBytesForAppCertificateIndexed));
+
+ String expectedIndexingBytesForUnindexed =
+ SERIALIZED_START_INDEXING_KEY
+ + getBits(totalBytesWritten, /* numOfBits= */ 32);
+ for (int count = 0; count < ruleCount; count++) {
+ byte[] bytesForPackage =
+ getBytes(getSerializedCompoundRuleWithInstallerNameAndInstallerCert(
+ String.format("%s%04d", installerNamePrefix, count)));
+ expectedOrderedRuleOutputStream.write(bytesForPackage);
+ totalBytesWritten += bytesForPackage.length;
+ }
+ expectedIndexingBytesForUnindexed +=
+ SERIALIZED_END_INDEXING_KEY
+ + getBits(totalBytesWritten, /* numOfBits= */ 32);
+ expectedIndexingOutputStream.write(getBytes(expectedIndexingBytesForUnindexed));
+
+
+ assertThat(ruleOutputStream.toByteArray())
+ .isEqualTo(expectedOrderedRuleOutputStream.toByteArray());
+ assertThat(indexingOutputStream.toByteArray())
+ .isEqualTo(expectedIndexingOutputStream.toByteArray());
}
private Rule getRuleWithPackageNameAndSampleInstallerName(String packageName) {
@@ -616,6 +688,44 @@
+ END_BIT;
}
+ private Rule getNonIndexedRuleWithInstallerName(String installerName) {
+ return new Rule(
+ new CompoundFormula(
+ CompoundFormula.AND,
+ Arrays.asList(
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.INSTALLER_NAME,
+ installerName,
+ /* isHashedValue= */ false),
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.INSTALLER_CERTIFICATE,
+ SAMPLE_INSTALLER_CERT,
+ /* isHashedValue= */ false))),
+ Rule.DENY);
+ }
+
+ private String getSerializedCompoundRuleWithInstallerNameAndInstallerCert(
+ String installerName) {
+ return START_BIT
+ + COMPOUND_FORMULA_START_BITS
+ + AND
+ + ATOMIC_FORMULA_START_BITS
+ + INSTALLER_NAME
+ + EQ
+ + IS_NOT_HASHED
+ + getBits(installerName.length(), VALUE_SIZE_BITS)
+ + getValueBits(installerName)
+ + ATOMIC_FORMULA_START_BITS
+ + INSTALLER_CERTIFICATE
+ + EQ
+ + IS_NOT_HASHED
+ + getBits(SAMPLE_INSTALLER_CERT.length(), VALUE_SIZE_BITS)
+ + getValueBits(SAMPLE_INSTALLER_CERT)
+ + COMPOUND_FORMULA_END_BITS
+ + DENY
+ + END_BIT;
+ }
+
private static Formula getInvalidFormula() {
return new Formula() {
@Override
diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleXmlSerializerTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleXmlSerializerTest.java
index 0bb2d44..ff7722c 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleXmlSerializerTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleXmlSerializerTest.java
@@ -145,7 +145,8 @@
xmlSerializer.serialize(
Collections.singletonList(rule),
/* formatVersion= */ Optional.empty(),
- outputStream);
+ outputStream,
+ new ByteArrayOutputStream());
byte[] actualRules = outputStream.toString().getBytes(StandardCharsets.UTF_8);
assertEquals(expectedRules, new String(actualRules, StandardCharsets.UTF_8));
diff --git a/services/tests/servicestests/src/com/android/server/location/CustomCountryDetectorTestClass.java b/services/tests/servicestests/src/com/android/server/location/CustomCountryDetectorTestClass.java
new file mode 100644
index 0000000..e159012
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/location/CustomCountryDetectorTestClass.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import android.content.Context;
+import android.location.Country;
+
+public class CustomCountryDetectorTestClass extends CountryDetectorBase {
+ public CustomCountryDetectorTestClass(Context ctx) {
+ super(ctx);
+ }
+
+ @Override
+ public Country detectCountry() {
+ return null;
+ }
+
+ @Override
+ public void stop() {
+
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index ed70fa6..a3d15dd 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -53,7 +53,6 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.widget.ILockSettings;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockSettingsInternal;
import com.android.internal.widget.LockscreenCredential;
@@ -91,7 +90,6 @@
MockLockSettingsContext mContext;
LockSettingsStorageTestable mStorage;
- LockPatternUtils mLockPatternUtils;
FakeGateKeeperService mGateKeeperService;
NotificationManager mNotificationManager;
UserManager mUserManager;
@@ -111,7 +109,6 @@
FingerprintManager mFingerprintManager;
FaceManager mFaceManager;
PackageManager mPackageManager;
- protected boolean mHasSecureLockScreen;
FakeSettings mSettings;
@Before
@@ -153,25 +150,14 @@
storageDir.mkdirs();
}
- mHasSecureLockScreen = true;
- mLockPatternUtils = new LockPatternUtils(mContext) {
- @Override
- public ILockSettings getLockSettings() {
- return mService;
- }
-
- @Override
- public boolean hasSecureLockScreen() {
- return mHasSecureLockScreen;
- }
- };
mSpManager = new MockSyntheticPasswordManager(mContext, mStorage, mGateKeeperService,
mUserManager, mPasswordSlotManager);
mAuthSecretService = mock(IAuthSecret.class);
- mService = new LockSettingsServiceTestable(mContext, mLockPatternUtils, mStorage,
+ mService = new LockSettingsServiceTestable(mContext, mStorage,
mGateKeeperService, mKeyStore, setUpStorageManagerMock(), mActivityManager,
mSpManager, mAuthSecretService, mGsiService, mRecoverableKeyStoreManager,
mUserManagerInternal, mDeviceStateCache, mSettings);
+ mService.mHasSecureLockScreen = true;
when(mUserManager.getUserInfo(eq(PRIMARY_USER_ID))).thenReturn(PRIMARY_USER_INFO);
mPrimaryUserProfiles.add(PRIMARY_USER_INFO);
installChildProfile(MANAGED_PROFILE_USER_ID);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
index 7e7e170..271e8e2 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
@@ -32,7 +32,6 @@
import android.security.KeyStore;
import android.security.keystore.KeyPermanentlyInvalidatedException;
-import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockscreenCredential;
import com.android.server.ServiceThread;
import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;
@@ -46,7 +45,6 @@
private LockSettingsStorage mLockSettingsStorage;
private KeyStore mKeyStore;
private IActivityManager mActivityManager;
- private LockPatternUtils mLockPatternUtils;
private IStorageManager mStorageManager;
private SyntheticPasswordManager mSpManager;
private FakeGsiService mGsiService;
@@ -56,7 +54,7 @@
private FakeSettings mSettings;
public MockInjector(Context context, LockSettingsStorage storage, KeyStore keyStore,
- IActivityManager activityManager, LockPatternUtils lockPatternUtils,
+ IActivityManager activityManager,
IStorageManager storageManager, SyntheticPasswordManager spManager,
FakeGsiService gsiService, RecoverableKeyStoreManager recoverableKeyStoreManager,
UserManagerInternal userManagerInternal, DeviceStateCache deviceStateCache,
@@ -65,7 +63,6 @@
mLockSettingsStorage = storage;
mKeyStore = keyStore;
mActivityManager = activityManager;
- mLockPatternUtils = lockPatternUtils;
mStorageManager = storageManager;
mSpManager = spManager;
mGsiService = gsiService;
@@ -101,10 +98,6 @@
}
@Override
- public LockPatternUtils getLockPatternUtils() {
- return mLockPatternUtils;
- }
- @Override
public DeviceStateCache getDeviceStateCache() {
return mDeviceStateCache;
}
@@ -158,14 +151,14 @@
public MockInjector mInjector;
- protected LockSettingsServiceTestable(Context context, LockPatternUtils lockPatternUtils,
+ protected LockSettingsServiceTestable(Context context,
LockSettingsStorage storage, FakeGateKeeperService gatekeeper, KeyStore keystore,
IStorageManager storageManager, IActivityManager mActivityManager,
SyntheticPasswordManager spManager, IAuthSecret authSecretService,
FakeGsiService gsiService, RecoverableKeyStoreManager recoverableKeyStoreManager,
UserManagerInternal userManagerInternal, DeviceStateCache deviceStateCache,
FakeSettings settings) {
- super(new MockInjector(context, storage, keystore, mActivityManager, lockPatternUtils,
+ super(new MockInjector(context, storage, keystore, mActivityManager,
storageManager, spManager, gsiService,
recoverableKeyStoreManager, userManagerInternal, deviceStateCache, settings));
mGateKeeperService = gatekeeper;
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
index abfda77..2e77c9f 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
@@ -423,7 +423,7 @@
private void testCreateCredentialFailsWithoutLockScreen(
int userId, LockscreenCredential credential) throws RemoteException {
- mHasSecureLockScreen = false;
+ mService.mHasSecureLockScreen = false;
try {
mService.setLockCredential(credential, null, userId);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowDataTest.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowDataTest.java
new file mode 100644
index 0000000..54c552b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowDataTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.locksettings;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * atest FrameworksServicesTests:RebootEscrowDataTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class RebootEscrowDataTest {
+ private static byte[] getTestSp() {
+ byte[] testSp = new byte[10];
+ for (int i = 0; i < testSp.length; i++) {
+ testSp[i] = (byte) i;
+ }
+ return testSp;
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void fromEntries_failsOnNull() throws Exception {
+ RebootEscrowData.fromSyntheticPassword((byte) 2, null);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void fromEncryptedData_failsOnNullData() throws Exception {
+ byte[] testSp = getTestSp();
+ RebootEscrowData expected = RebootEscrowData.fromSyntheticPassword((byte) 2, testSp);
+ SecretKeySpec key = RebootEscrowData.fromKeyBytes(expected.getKey());
+ RebootEscrowData.fromEncryptedData(key, null);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void fromEncryptedData_failsOnNullKey() throws Exception {
+ byte[] testSp = getTestSp();
+ RebootEscrowData expected = RebootEscrowData.fromSyntheticPassword((byte) 2, testSp);
+ RebootEscrowData.fromEncryptedData(null, expected.getBlob());
+ }
+
+ @Test
+ public void fromEntries_loopback_success() throws Exception {
+ byte[] testSp = getTestSp();
+ RebootEscrowData expected = RebootEscrowData.fromSyntheticPassword((byte) 2, testSp);
+
+ SecretKeySpec key = RebootEscrowData.fromKeyBytes(expected.getKey());
+ RebootEscrowData actual = RebootEscrowData.fromEncryptedData(key, expected.getBlob());
+
+ assertThat(actual.getSpVersion(), is(expected.getSpVersion()));
+ assertThat(actual.getIv(), is(expected.getIv()));
+ assertThat(actual.getKey(), is(expected.getKey()));
+ assertThat(actual.getBlob(), is(expected.getBlob()));
+ assertThat(actual.getSyntheticPassword(), is(expected.getSyntheticPassword()));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
new file mode 100644
index 0000000..78a5a0b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings;
+
+import static android.content.pm.UserInfo.FLAG_FULL;
+import static android.content.pm.UserInfo.FLAG_PRIMARY;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.hardware.rebootescrow.IRebootEscrow;
+import android.os.RemoteException;
+import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.widget.RebootEscrowListener;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.util.ArrayList;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class RebootEscrowManagerTests {
+ protected static final int PRIMARY_USER_ID = 0;
+ protected static final int NONSECURE_USER_ID = 10;
+ private static final byte FAKE_SP_VERSION = 1;
+ private static final byte[] FAKE_AUTH_TOKEN = new byte[] {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ };
+
+ private Context mContext;
+ private UserManager mUserManager;
+ private RebootEscrowManager.Callbacks mCallbacks;
+ private IRebootEscrow mRebootEscrow;
+
+ LockSettingsStorageTestable mStorage;
+
+ private RebootEscrowManager mService;
+
+ static class MockInjector extends RebootEscrowManager.Injector {
+ private final IRebootEscrow mRebootEscrow;
+ private final UserManager mUserManager;
+
+ MockInjector(Context context, UserManager userManager, IRebootEscrow rebootEscrow) {
+ super(context);
+ mRebootEscrow = rebootEscrow;
+ mUserManager = userManager;
+ }
+
+ @Override
+ public UserManager getUserManager() {
+ return mUserManager;
+ }
+
+ @Override
+ public IRebootEscrow getRebootEscrow() {
+ return mRebootEscrow;
+ }
+ }
+
+ @Before
+ public void setUp_baseServices() throws Exception {
+ mContext = mock(Context.class);
+ mUserManager = mock(UserManager.class);
+ mCallbacks = mock(RebootEscrowManager.Callbacks.class);
+ mRebootEscrow = mock(IRebootEscrow.class);
+
+ mStorage = new LockSettingsStorageTestable(mContext,
+ new File(InstrumentationRegistry.getContext().getFilesDir(), "locksettings"));
+
+ ArrayList<UserInfo> users = new ArrayList<>();
+ users.add(new UserInfo(PRIMARY_USER_ID, "primary", FLAG_PRIMARY));
+ users.add(new UserInfo(NONSECURE_USER_ID, "non-secure", FLAG_FULL));
+ when(mUserManager.getUsers()).thenReturn(users);
+ when(mCallbacks.isUserSecure(PRIMARY_USER_ID)).thenReturn(true);
+ when(mCallbacks.isUserSecure(NONSECURE_USER_ID)).thenReturn(false);
+ mService = new RebootEscrowManager(new MockInjector(mContext, mUserManager, mRebootEscrow),
+ mCallbacks, mStorage);
+ }
+
+ @Test
+ public void prepareRebootEscrow_Success() throws Exception {
+ RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+ mService.setRebootEscrowListener(mockListener);
+ mService.prepareRebootEscrow();
+
+ clearInvocations(mRebootEscrow);
+ mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+ verify(mockListener).onPreparedForReboot(eq(true));
+ verify(mRebootEscrow, never()).storeKey(any());
+ }
+
+ @Test
+ public void prepareRebootEscrow_ClearCredentials_Success() throws Exception {
+ RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+ mService.setRebootEscrowListener(mockListener);
+ mService.prepareRebootEscrow();
+ mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+ verify(mockListener).onPreparedForReboot(eq(true));
+
+ clearInvocations(mRebootEscrow);
+ mService.clearRebootEscrow();
+ verify(mockListener).onPreparedForReboot(eq(false));
+ verify(mRebootEscrow).storeKey(eq(new byte[32]));
+ }
+
+ @Test
+ public void armService_Success() throws Exception {
+ RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+ mService.setRebootEscrowListener(mockListener);
+ mService.prepareRebootEscrow();
+
+ clearInvocations(mRebootEscrow);
+ mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+ verify(mockListener).onPreparedForReboot(eq(true));
+ verify(mRebootEscrow, never()).storeKey(any());
+
+ assertTrue(mService.armRebootEscrowIfNeeded());
+ verify(mRebootEscrow).storeKey(any());
+ }
+
+ @Test
+ public void armService_NoInitialization_Failure() throws Exception {
+ assertFalse(mService.armRebootEscrowIfNeeded());
+ verifyNoMoreInteractions(mRebootEscrow);
+ }
+
+ @Test
+ public void armService_RebootEscrowServiceException_Failure() throws Exception {
+ doThrow(RemoteException.class).when(mRebootEscrow).storeKey(any());
+ assertFalse(mService.armRebootEscrowIfNeeded());
+ verifyNoMoreInteractions(mRebootEscrow);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index 7457067..8982925 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -381,7 +381,7 @@
LockscreenCredential pattern = newPattern("123654");
byte[] token = "some-high-entropy-secure-token".getBytes();
- mHasSecureLockScreen = false;
+ mService.mHasSecureLockScreen = false;
enableSyntheticPassword();
long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 7529bc5..28e6830 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -1138,11 +1138,11 @@
new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(360), 0L, 0L, 0L, 0));
reset(mTelephonyManager, mNetworkManager, mNotifManager);
- expectMobileDefaults();
+ TelephonyManager tmSub = expectMobileDefaults();
mService.updateNetworks();
- verify(mTelephonyManager, atLeastOnce()).setPolicyDataEnabled(true, TEST_SUB_ID);
+ verify(tmSub, atLeastOnce()).setPolicyDataEnabled(true);
verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE,
DataUnit.MEGABYTES.toBytes(1800 - 360));
verify(mNotifManager, never()).notifyAsUser(any(), anyInt(), any(), any());
@@ -1155,11 +1155,11 @@
new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1799), 0L, 0L, 0L, 0));
reset(mTelephonyManager, mNetworkManager, mNotifManager);
- expectMobileDefaults();
+ TelephonyManager tmSub = expectMobileDefaults();
mService.updateNetworks();
- verify(mTelephonyManager, atLeastOnce()).setPolicyDataEnabled(true, TEST_SUB_ID);
+ verify(tmSub, atLeastOnce()).setPolicyDataEnabled(true);
verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE,
DataUnit.MEGABYTES.toBytes(1800 - 1799));
verify(mNotifManager, atLeastOnce()).notifyAsUser(any(), eq(TYPE_WARNING),
@@ -1173,12 +1173,12 @@
new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1799), 0L, 0L, 0L, 0));
reset(mTelephonyManager, mNetworkManager, mNotifManager);
- expectMobileDefaults();
+ TelephonyManager tmSub = expectMobileDefaults();
expectDefaultCarrierConfig();
mService.updateNetworks();
- verify(mTelephonyManager, atLeastOnce()).setPolicyDataEnabled(true, TEST_SUB_ID);
+ verify(tmSub, atLeastOnce()).setPolicyDataEnabled(true);
verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE,
DataUnit.MEGABYTES.toBytes(1800 - 1799));
// Since this isn't from the identified carrier, there should be no notifications
@@ -1192,11 +1192,11 @@
new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1810), 0L, 0L, 0L, 0));
reset(mTelephonyManager, mNetworkManager, mNotifManager);
- expectMobileDefaults();
+ TelephonyManager tmSub = expectMobileDefaults();
mService.updateNetworks();
- verify(mTelephonyManager, atLeastOnce()).setPolicyDataEnabled(false, TEST_SUB_ID);
+ verify(tmSub, atLeastOnce()).setPolicyDataEnabled(false);
verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE, 1);
verify(mNotifManager, atLeastOnce()).notifyAsUser(any(), eq(TYPE_LIMIT),
isA(Notification.class), eq(UserHandle.ALL));
@@ -1205,12 +1205,12 @@
// Snooze limit
{
reset(mTelephonyManager, mNetworkManager, mNotifManager);
- expectMobileDefaults();
+ TelephonyManager tmSub = expectMobileDefaults();
mService.snoozeLimit(NetworkTemplate.buildTemplateMobileAll(TEST_IMSI));
mService.updateNetworks();
- verify(mTelephonyManager, atLeastOnce()).setPolicyDataEnabled(true, TEST_SUB_ID);
+ verify(tmSub, atLeastOnce()).setPolicyDataEnabled(true);
verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE,
Long.MAX_VALUE);
verify(mNotifManager, atLeastOnce()).notifyAsUser(any(), eq(TYPE_LIMIT_SNOOZED),
@@ -1928,10 +1928,11 @@
.thenReturn(CarrierConfigManager.getDefaultConfig());
}
- private void expectMobileDefaults() throws Exception {
- setupTelephonySubscriptionManagers(TEST_SUB_ID, TEST_IMSI);
- doNothing().when(mTelephonyManager).setPolicyDataEnabled(anyBoolean(), anyInt());
+ private TelephonyManager expectMobileDefaults() throws Exception {
+ TelephonyManager tmSub = setupTelephonySubscriptionManagers(TEST_SUB_ID, TEST_IMSI);
+ doNothing().when(tmSub).setPolicyDataEnabled(anyBoolean());
expectNetworkState(false /* roaming */);
+ return tmSub;
}
private void verifyAdvisePersistThreshold() throws Exception {
@@ -2090,8 +2091,10 @@
/**
* Creates a mock {@link TelephonyManager} and {@link SubscriptionManager}.
+ *
*/
- private void setupTelephonySubscriptionManagers(int subscriptionId, String subscriberId) {
+ private TelephonyManager setupTelephonySubscriptionManagers(int subscriptionId,
+ String subscriberId) {
when(mSubscriptionManager.getActiveSubscriptionInfoList()).thenReturn(
createSubscriptionInfoList(subscriptionId));
@@ -2101,6 +2104,7 @@
when(subTelephonyManager.getSubscriberId()).thenReturn(subscriberId);
when(mTelephonyManager.createForSubscriptionId(subscriptionId))
.thenReturn(subTelephonyManager);
+ return subTelephonyManager;
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java b/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java
new file mode 100644
index 0000000..d3166b9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.people;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.prediction.AppPredictionContext;
+import android.app.prediction.AppPredictionSessionId;
+import android.app.prediction.AppTarget;
+import android.app.prediction.IPredictionCallback;
+import android.content.Context;
+import android.content.pm.ParceledListSlice;
+import android.os.Binder;
+import android.os.RemoteException;
+
+import com.android.server.LocalServices;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+@RunWith(JUnit4.class)
+public final class PeopleServiceTest {
+
+ private static final String APP_PREDICTION_SHARE_UI_SURFACE = "share";
+ private static final int APP_PREDICTION_TARGET_COUNT = 4;
+ private static final String TEST_PACKAGE_NAME = "com.example";
+
+ private PeopleServiceInternal mServiceInternal;
+ private PeopleService.LocalService mLocalService;
+ private AppPredictionSessionId mSessionId;
+ private AppPredictionContext mPredictionContext;
+
+ @Mock private Context mContext;
+ @Mock private IPredictionCallback mCallback;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mContext.getPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ when(mCallback.asBinder()).thenReturn(new Binder());
+
+ PeopleService service = new PeopleService(mContext);
+ service.onStart();
+
+ mServiceInternal = LocalServices.getService(PeopleServiceInternal.class);
+ mLocalService = (PeopleService.LocalService) mServiceInternal;
+
+ mSessionId = new AppPredictionSessionId("abc");
+ mPredictionContext = new AppPredictionContext.Builder(mContext)
+ .setUiSurface(APP_PREDICTION_SHARE_UI_SURFACE)
+ .setPredictedTargetCount(APP_PREDICTION_TARGET_COUNT)
+ .build();
+ }
+
+ @After
+ public void tearDown() {
+ LocalServices.removeServiceForTest(PeopleServiceInternal.class);
+ }
+
+ @Test
+ public void testRegisterCallbacks() throws RemoteException {
+ mServiceInternal.onCreatePredictionSession(mPredictionContext, mSessionId);
+
+ SessionInfo sessionInfo = mLocalService.getSessionInfo(mSessionId);
+
+ mServiceInternal.registerPredictionUpdates(mSessionId, mCallback);
+
+ Consumer<List<AppTarget>> updatePredictionMethod =
+ sessionInfo.getPredictor().getUpdatePredictionsMethod();
+ updatePredictionMethod.accept(new ArrayList<>());
+ updatePredictionMethod.accept(new ArrayList<>());
+
+ verify(mCallback, times(2)).onResult(any(ParceledListSlice.class));
+
+ mServiceInternal.unregisterPredictionUpdates(mSessionId, mCallback);
+
+ updatePredictionMethod.accept(new ArrayList<>());
+
+ // After the un-registration, the callback should no longer be called.
+ verify(mCallback, times(2)).onResult(any(ParceledListSlice.class));
+
+ mServiceInternal.onDestroyPredictionSession(mSessionId);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
index 1e760cc..ac27a08 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
@@ -139,18 +139,18 @@
assertEquals("A Name", mUserManagerService.getUserInfo(TEST_ID).name);
}
- /** Test UMS.getUserTypeForUser(). */
+ /** Test UMS.isUserOfType(). */
@Test
- public void testGetUserTypeForUser() throws Exception {
- final String typeSys = mUserManagerService.getUserTypeForUser(UserHandle.USER_SYSTEM);
- assertTrue("System user was of invalid type " + typeSys,
- typeSys.equals(USER_TYPE_SYSTEM_HEADLESS) || typeSys.equals(USER_TYPE_FULL_SYSTEM));
+ public void testIsUserOfType() throws Exception {
+ assertTrue("System user was of invalid type",
+ mUserManagerService.isUserOfType(UserHandle.USER_SYSTEM, USER_TYPE_SYSTEM_HEADLESS)
+ || mUserManagerService.isUserOfType(UserHandle.USER_SYSTEM, USER_TYPE_FULL_SYSTEM));
final int testId = 100;
final String typeName = "A type";
UserInfo userInfo = createUser(testId, 0, typeName);
mUserManagerService.putUserInfo(userInfo);
- assertEquals(typeName, mUserManagerService.getUserTypeForUser(testId));
+ assertTrue(mUserManagerService.isUserOfType(testId, typeName));
}
/** Tests upgradeIfNecessaryLP (but without locking) for upgrading from version 8 to 9+. */
@@ -169,22 +169,22 @@
mUserManagerService.upgradeIfNecessaryLP(null, versionToTest - 1);
- assertEquals(USER_TYPE_PROFILE_MANAGED, mUserManagerService.getUserTypeForUser(100));
+ assertTrue(mUserManagerService.isUserOfType(100, USER_TYPE_PROFILE_MANAGED));
assertTrue((mUserManagerService.getUserInfo(100).flags & FLAG_PROFILE) != 0);
- assertEquals(USER_TYPE_FULL_GUEST, mUserManagerService.getUserTypeForUser(101));
+ assertTrue(mUserManagerService.isUserOfType(101, USER_TYPE_FULL_GUEST));
- assertEquals(USER_TYPE_FULL_RESTRICTED, mUserManagerService.getUserTypeForUser(102));
+ assertTrue(mUserManagerService.isUserOfType(102, USER_TYPE_FULL_RESTRICTED));
assertTrue((mUserManagerService.getUserInfo(102).flags & FLAG_PROFILE) == 0);
- assertEquals(USER_TYPE_FULL_SECONDARY, mUserManagerService.getUserTypeForUser(103));
+ assertTrue(mUserManagerService.isUserOfType(103, USER_TYPE_FULL_SECONDARY));
assertTrue((mUserManagerService.getUserInfo(103).flags & FLAG_PROFILE) == 0);
- assertEquals(USER_TYPE_SYSTEM_HEADLESS, mUserManagerService.getUserTypeForUser(104));
+ assertTrue(mUserManagerService.isUserOfType(104, USER_TYPE_SYSTEM_HEADLESS));
- assertEquals(USER_TYPE_FULL_SYSTEM, mUserManagerService.getUserTypeForUser(105));
+ assertTrue(mUserManagerService.isUserOfType(105, USER_TYPE_FULL_SYSTEM));
- assertEquals(USER_TYPE_FULL_DEMO, mUserManagerService.getUserTypeForUser(106));
+ assertTrue(mUserManagerService.isUserOfType(106, USER_TYPE_FULL_DEMO));
}
/** Creates a UserInfo with the given flags and userType. */
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 06b3dc1..77376f0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
+import static org.testng.Assert.assertThrows;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
@@ -140,20 +141,15 @@
assertThat(userInfo).isNotNull();
List<UserInfo> list = mUserManager.getUsers();
- boolean found = false;
for (UserInfo user : list) {
if (user.id == userInfo.id && user.name.equals("Guest 1")
&& user.isGuest()
&& !user.isAdmin()
&& !user.isPrimary()) {
- found = true;
- Bundle restrictions = mUserManager.getUserRestrictions(user.getUserHandle());
- assertWithMessage("Guest user should have DISALLOW_CONFIG_WIFI=true by default")
- .that(restrictions.getBoolean(UserManager.DISALLOW_CONFIG_WIFI))
- .isTrue();
+ return;
}
}
- assertThat(found).isTrue();
+ fail("Didn't find a guest: " + list);
}
@MediumTest
@@ -206,14 +202,7 @@
@MediumTest
@Test
public void testRemoveUserByHandle_ThrowsException() {
- synchronized (mUserRemoveLock) {
- try {
- mUserManager.removeUser(null);
- fail("Expected IllegalArgumentException on passing in a null UserHandle.");
- } catch (IllegalArgumentException expected) {
- // Do nothing - exception is expected.
- }
- }
+ assertThrows(IllegalArgumentException.class, () -> mUserManager.removeUser(null));
}
/** Tests creating a FULL user via specifying userType. */
@@ -343,7 +332,6 @@
UserManager.USER_TYPE_PROFILE_MANAGED, primaryUserId);
assertThat(userInfo).isNotNull();
final int userId = userInfo.id;
- final UserHandle userHandle = new UserHandle(userId);
assertThat(mUserManager.hasBadge(userId)).isEqualTo(userTypeDetails.hasBadge());
assertThat(mUserManager.getUserIconBadgeResId(userId))
@@ -353,13 +341,13 @@
assertThat(mUserManager.getUserBadgeNoBackgroundResId(userId))
.isEqualTo(userTypeDetails.getBadgeNoBackground());
assertThat(mUserManager.isProfile(userId)).isEqualTo(userTypeDetails.isProfile());
- assertThat(mUserManager.getUserTypeForUser(userHandle))
- .isEqualTo(userTypeDetails.getName());
+ assertThat(mUserManager.isUserOfType(asHandle(userId), userTypeDetails.getName()))
+ .isTrue();
final int badgeIndex = userInfo.profileBadge;
assertThat(mUserManager.getUserBadgeColor(userId)).isEqualTo(
Resources.getSystem().getColor(userTypeDetails.getBadgeColor(badgeIndex), null));
- assertThat(mUserManager.getBadgedLabelForUser("Test", userHandle)).isEqualTo(
+ assertThat(mUserManager.getBadgedLabelForUser("Test", asHandle(userId))).isEqualTo(
Resources.getSystem().getString(userTypeDetails.getBadgeLabel(badgeIndex), "Test"));
}
@@ -438,9 +426,8 @@
@MediumTest
@Test
public void testCreateUser_disallowAddUser() throws Exception {
- final int creatorId = isAutomotive() ? ActivityManager.getCurrentUser()
- : mUserManager.getPrimaryUser().id;
- final UserHandle creatorHandle = new UserHandle(creatorId);
+ final int creatorId = ActivityManager.getCurrentUser();
+ final UserHandle creatorHandle = asHandle(creatorId);
mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, true, creatorHandle);
try {
UserInfo createadInfo = createUser("SecondaryUser", /*flags=*/ 0);
@@ -457,7 +444,7 @@
public void testCreateProfileForUser_disallowAddManagedProfile() throws Exception {
assumeManagedUsersSupported();
final int primaryUserId = mUserManager.getPrimaryUser().id;
- final UserHandle primaryUserHandle = new UserHandle(primaryUserId);
+ final UserHandle primaryUserHandle = asHandle(primaryUserId);
mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, true,
primaryUserHandle);
try {
@@ -476,7 +463,7 @@
public void testCreateProfileForUserEvenWhenDisallowed() throws Exception {
assumeManagedUsersSupported();
final int primaryUserId = mUserManager.getPrimaryUser().id;
- final UserHandle primaryUserHandle = new UserHandle(primaryUserId);
+ final UserHandle primaryUserHandle = asHandle(primaryUserId);
mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, true,
primaryUserHandle);
try {
@@ -495,7 +482,7 @@
public void testCreateProfileForUser_disallowAddUser() throws Exception {
assumeManagedUsersSupported();
final int primaryUserId = mUserManager.getPrimaryUser().id;
- final UserHandle primaryUserHandle = new UserHandle(primaryUserId);
+ final UserHandle primaryUserHandle = asHandle(primaryUserId);
mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, true, primaryUserHandle);
try {
UserInfo userInfo = createProfileForUser("Managed",
@@ -540,8 +527,7 @@
@MediumTest
@Test
- public void testGetUserCreationTime() throws Exception {
- // TODO: should add a regular user instead of a profile, so it can be tested everywhere
+ public void testGetManagedProfileCreationTime() throws Exception {
assumeManagedUsersSupported();
final int primaryUserId = mUserManager.getPrimaryUser().id;
final long startTime = System.currentTimeMillis();
@@ -556,38 +542,40 @@
assertWithMessage("creationTime must be 0 if the time is not > EPOCH_PLUS_30_years")
.that(profile.creationTime).isEqualTo(0);
}
- assertThat(mUserManager.getUserCreationTime(
- new UserHandle(profile.id))).isEqualTo(profile.creationTime);
+ assertThat(mUserManager.getUserCreationTime(asHandle(profile.id)))
+ .isEqualTo(profile.creationTime);
long ownerCreationTime = mUserManager.getUserInfo(primaryUserId).creationTime;
- assertThat(mUserManager.getUserCreationTime(
- new UserHandle(primaryUserId))).isEqualTo(ownerCreationTime);
+ assertThat(mUserManager.getUserCreationTime(asHandle(primaryUserId)))
+ .isEqualTo(ownerCreationTime);
+ }
+
+ @MediumTest
+ @Test
+ public void testGetUserCreationTime() throws Exception {
+ long startTime = System.currentTimeMillis();
+ UserInfo user = createUser("User", /* flags= */ 0);
+ long endTime = System.currentTimeMillis();
+ assertThat(user).isNotNull();
+ assertWithMessage("creationTime must be set when the user is created")
+ .that(user.creationTime).isIn(Range.closed(startTime, endTime));
}
@SmallTest
@Test
public void testGetUserCreationTime_nonExistentUser() throws Exception {
- try {
- int noSuchUserId = 100500;
- mUserManager.getUserCreationTime(new UserHandle(noSuchUserId));
- fail("SecurityException should be thrown for nonexistent user");
- } catch (Exception e) {
- assertWithMessage("SecurityException should be thrown for nonexistent user").that(e)
- .isInstanceOf(SecurityException.class);
- }
+ int noSuchUserId = 100500;
+ assertThrows(SecurityException.class,
+ () -> mUserManager.getUserCreationTime(asHandle(noSuchUserId)));
}
@SmallTest
@Test
public void testGetUserCreationTime_otherUser() throws Exception {
UserInfo user = createUser("User 1", 0);
- try {
- mUserManager.getUserCreationTime(new UserHandle(user.id));
- fail("SecurityException should be thrown for other user");
- } catch (Exception e) {
- assertWithMessage("SecurityException should be thrown for other user").that(e)
- .isInstanceOf(SecurityException.class);
- }
+ assertThat(user).isNotNull();
+ assertThrows(SecurityException.class,
+ () -> mUserManager.getUserCreationTime(asHandle(user.id)));
}
private boolean findUser(int id) {
@@ -658,11 +646,11 @@
UserInfo testUser = createUser("User 1", 0);
mUserManager.setUserRestriction(
- UserManager.DISALLOW_INSTALL_APPS, true, new UserHandle(testUser.id));
+ UserManager.DISALLOW_INSTALL_APPS, true, asHandle(testUser.id));
mUserManager.setUserRestriction(
- UserManager.DISALLOW_CONFIG_WIFI, false, new UserHandle(testUser.id));
+ UserManager.DISALLOW_CONFIG_WIFI, false, asHandle(testUser.id));
- Bundle stored = mUserManager.getUserRestrictions(new UserHandle(testUser.id));
+ Bundle stored = mUserManager.getUserRestrictions(asHandle(testUser.id));
// Note this will fail if DO already sets those restrictions.
assertThat(stored.getBoolean(UserManager.DISALLOW_CONFIG_WIFI)).isFalse();
assertThat(stored.getBoolean(UserManager.DISALLOW_UNINSTALL_APPS)).isFalse();
@@ -738,15 +726,8 @@
@Test
public void testSwitchUserByHandle_ThrowsException() {
- synchronized (mUserSwitchLock) {
- try {
- ActivityManager am = mContext.getSystemService(ActivityManager.class);
- am.switchUser(null);
- fail("Expected IllegalArgumentException on passing in a null UserHandle.");
- } catch (IllegalArgumentException expected) {
- // Do nothing - exception is expected.
- }
- }
+ ActivityManager am = mContext.getSystemService(ActivityManager.class);
+ assertThrows(IllegalArgumentException.class, () -> am.switchUser(null));
}
@MediumTest
@@ -903,4 +884,8 @@
private boolean isAutomotive() {
return mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
}
+
+ private static UserHandle asHandle(int userId) {
+ return new UserHandle(userId);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/power/NotifierTest.java b/services/tests/servicestests/src/com/android/server/power/NotifierTest.java
new file mode 100644
index 0000000..7666ab9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/power/NotifierTest.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.hardware.SensorManager;
+import android.hardware.display.AmbientDisplayConfiguration;
+import android.os.BatteryStats;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.ServiceManager;
+import android.os.Vibrator;
+import android.os.test.TestLooper;
+import android.provider.Settings;
+import android.testing.TestableContext;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.os.BatteryStatsImpl;
+import com.android.server.LocalServices;
+import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.power.batterysaver.BatterySaverPolicy;
+import com.android.server.power.batterysaver.BatterySavingStats;
+import com.android.server.statusbar.StatusBarManagerInternal;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link com.android.server.power.Notifier}
+ */
+public class NotifierTest {
+ private static final String SYSTEM_PROPERTY_QUIESCENT = "ro.boot.quiescent";
+ private static final int USER_ID = 0;
+
+ @Mock private BatterySaverPolicy mBatterySaverPolicyMock;
+ @Mock private PowerManagerService.NativeWrapper mNativeWrapperMock;
+ @Mock private Notifier mNotifierMock;
+ @Mock private WirelessChargerDetector mWirelessChargerDetectorMock;
+ @Mock private AmbientDisplayConfiguration mAmbientDisplayConfigurationMock;
+ @Mock private SystemPropertiesWrapper mSystemPropertiesMock;
+ @Mock private InattentiveSleepWarningController mInattentiveSleepWarningControllerMock;
+ @Mock private BatteryStatsImpl mBatteryStats;
+ @Mock private Vibrator mVibrator;
+ @Mock private StatusBarManagerInternal mStatusBarManagerInternal;
+
+ private PowerManagerService mService;
+ private Context mContextSpy;
+ private Resources mResourcesSpy;
+ private TestLooper mTestLooper = new TestLooper();
+ private Notifier mNotifier;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
+ LocalServices.addService(StatusBarManagerInternal.class, mStatusBarManagerInternal);
+
+ mContextSpy = spy(new TestableContext(InstrumentationRegistry.getContext()));
+ mResourcesSpy = spy(mContextSpy.getResources());
+ when(mContextSpy.getResources()).thenReturn(mResourcesSpy);
+ when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), anyString())).thenReturn("");
+ when(mContextSpy.getSystemService(Vibrator.class)).thenReturn(mVibrator);
+
+ mService = new PowerManagerService(mContextSpy, mInjector);
+ }
+
+ @Test
+ public void testVibrateEnabled_wiredCharging() {
+ createNotifier();
+
+ // GIVEN the charging vibration is enabled
+ enableChargingVibration(true);
+
+ // WHEN wired charging starts
+ mNotifier.onWiredChargingStarted(USER_ID);
+ mTestLooper.dispatchAll();
+
+ // THEN the device vibrates once
+ verify(mVibrator, times(1)).vibrate(any(), any());
+ }
+
+ @Test
+ public void testVibrateDisabled_wiredCharging() {
+ createNotifier();
+
+ // GIVEN the charging vibration is disabled
+ enableChargingVibration(false);
+
+ // WHEN wired charging starts
+ mNotifier.onWiredChargingStarted(USER_ID);
+ mTestLooper.dispatchAll();
+
+ // THEN the device doesn't vibrate
+ verify(mVibrator, never()).vibrate(any(), any());
+ }
+
+ @Test
+ public void testVibrateEnabled_wirelessCharging() {
+ createNotifier();
+
+ // GIVEN the charging vibration is enabled
+ enableChargingVibration(true);
+
+ // WHEN wireless charging starts
+ mNotifier.onWirelessChargingStarted(5, USER_ID);
+ mTestLooper.dispatchAll();
+
+ // THEN the device vibrates once
+ verify(mVibrator, times(1)).vibrate(any(), any());
+ }
+
+ @Test
+ public void testVibrateDisabled_wirelessCharging() {
+ createNotifier();
+
+ // GIVEN the charging vibration is disabeld
+ enableChargingVibration(false);
+
+ // WHEN wireless charging starts
+ mNotifier.onWirelessChargingStarted(5, USER_ID);
+ mTestLooper.dispatchAll();
+
+ // THEN the device doesn't vibrate
+ verify(mVibrator, never()).vibrate(any(), any());
+ }
+
+ @Test
+ public void testVibrateEnabled_dndOn() {
+ createNotifier();
+
+ // GIVEN the charging vibration is enabled but dnd is on
+ enableChargingVibration(true);
+ enableChargingFeedback(
+ /* chargingFeedbackEnabled */ true,
+ /* dndOn */ true);
+
+ // WHEN wired charging starts
+ mNotifier.onWiredChargingStarted(USER_ID);
+ mTestLooper.dispatchAll();
+
+ // THEN the device doesn't vibrate
+ verify(mVibrator, never()).vibrate(any(), any());
+ }
+
+ @Test
+ public void testWirelessAnimationEnabled() {
+ // GIVEN the wireless charging animation is enabled
+ when(mResourcesSpy.getBoolean(
+ com.android.internal.R.bool.config_showBuiltinWirelessChargingAnim))
+ .thenReturn(true);
+ createNotifier();
+
+ // WHEN wireless charging starts
+ mNotifier.onWirelessChargingStarted(5, USER_ID);
+ mTestLooper.dispatchAll();
+
+ // THEN the charging animation is triggered
+ verify(mStatusBarManagerInternal, times(1)).showChargingAnimation(5);
+ }
+
+ @Test
+ public void testWirelessAnimationDisabled() {
+ // GIVEN the wireless charging animation is disabled
+ when(mResourcesSpy.getBoolean(
+ com.android.internal.R.bool.config_showBuiltinWirelessChargingAnim))
+ .thenReturn(false);
+ createNotifier();
+
+ // WHEN wireless charging starts
+ mNotifier.onWirelessChargingStarted(5, USER_ID);
+ mTestLooper.dispatchAll();
+
+ // THEN the charging animation never gets called
+ verify(mStatusBarManagerInternal, never()).showChargingAnimation(anyInt());
+ }
+
+ private final PowerManagerService.Injector mInjector = new PowerManagerService.Injector() {
+ @Override
+ Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
+ SuspendBlocker suspendBlocker, WindowManagerPolicy policy) {
+ return mNotifierMock;
+ }
+
+ @Override
+ SuspendBlocker createSuspendBlocker(PowerManagerService service, String name) {
+ return super.createSuspendBlocker(service, name);
+ }
+
+ @Override
+ BatterySaverPolicy createBatterySaverPolicy(
+ Object lock, Context context, BatterySavingStats batterySavingStats) {
+ return mBatterySaverPolicyMock;
+ }
+
+ @Override
+ PowerManagerService.NativeWrapper createNativeWrapper() {
+ return mNativeWrapperMock;
+ }
+
+ @Override
+ WirelessChargerDetector createWirelessChargerDetector(
+ SensorManager sensorManager, SuspendBlocker suspendBlocker, Handler handler) {
+ return mWirelessChargerDetectorMock;
+ }
+
+ @Override
+ AmbientDisplayConfiguration createAmbientDisplayConfiguration(Context context) {
+ return mAmbientDisplayConfigurationMock;
+ }
+
+ @Override
+ InattentiveSleepWarningController createInattentiveSleepWarningController() {
+ return mInattentiveSleepWarningControllerMock;
+ }
+
+ @Override
+ public SystemPropertiesWrapper createSystemPropertiesWrapper() {
+ return mSystemPropertiesMock;
+ }
+ };
+
+ private void enableChargingFeedback(boolean chargingFeedbackEnabled, boolean dndOn) {
+ // enable/disable charging feedback
+ Settings.Secure.putIntForUser(
+ mContextSpy.getContentResolver(),
+ Settings.Secure.CHARGING_SOUNDS_ENABLED,
+ chargingFeedbackEnabled ? 1 : 0,
+ USER_ID);
+
+ // toggle on/off dnd
+ Settings.Global.putInt(
+ mContextSpy.getContentResolver(),
+ Settings.Global.ZEN_MODE,
+ dndOn ? Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+ : Settings.Global.ZEN_MODE_OFF);
+ }
+
+ private void enableChargingVibration(boolean enable) {
+ enableChargingFeedback(true, false);
+
+ Settings.Secure.putIntForUser(
+ mContextSpy.getContentResolver(),
+ Settings.Secure.CHARGING_VIBRATION_ENABLED,
+ enable ? 1 : 0,
+ USER_ID);
+ }
+
+ private void createNotifier() {
+ mNotifier = new Notifier(
+ mTestLooper.getLooper(),
+ mContextSpy,
+ IBatteryStats.Stub.asInterface(ServiceManager.getService(
+ BatteryStats.SERVICE_NAME)),
+ mInjector.createSuspendBlocker(mService, "testBlocker"),
+ null);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
index 1f312bf..d5cdbeb 100644
--- a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
@@ -20,16 +20,19 @@
import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.content.IntentSender;
import android.os.Handler;
import android.os.IPowerManager;
import android.os.IRecoverySystemProgressListener;
@@ -40,6 +43,8 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.widget.LockSettingsInternal;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -58,6 +63,7 @@
private Context mContext;
private IPowerManager mIPowerManager;
private FileWriter mUncryptUpdateFileWriter;
+ private LockSettingsInternal mLockSettingsInternal;
@Before
public void setup() {
@@ -65,6 +71,9 @@
mSystemProperties = new RecoverySystemServiceTestable.FakeSystemProperties();
mUncryptSocket = mock(RecoverySystemService.UncryptSocket.class);
mUncryptUpdateFileWriter = mock(FileWriter.class);
+ mLockSettingsInternal = mock(LockSettingsInternal.class);
+
+ when(mLockSettingsInternal.armRebootEscrow()).thenReturn(true);
Looper looper = InstrumentationRegistry.getContext().getMainLooper();
mIPowerManager = mock(IPowerManager.class);
@@ -72,7 +81,7 @@
new Handler(looper));
mRecoverySystemService = new RecoverySystemServiceTestable(mContext, mSystemProperties,
- powerManager, mUncryptUpdateFileWriter, mUncryptSocket);
+ powerManager, mUncryptUpdateFileWriter, mUncryptSocket, mLockSettingsInternal);
}
@Test
@@ -194,4 +203,100 @@
verify(mUncryptSocket).sendAck();
verify(mUncryptSocket).close();
}
+
+ @Test(expected = SecurityException.class)
+ public void requestLskf_protected() {
+ doThrow(SecurityException.class).when(mContext).enforceCallingOrSelfPermission(
+ eq(android.Manifest.permission.RECOVERY), any());
+ mRecoverySystemService.requestLskf("test", null);
+ }
+
+
+ @Test
+ public void requestLskf_nullToken_failure() {
+ assertThat(mRecoverySystemService.requestLskf(null, null), is(false));
+ }
+
+ @Test
+ public void requestLskf_success() throws Exception {
+ IntentSender intentSender = mock(IntentSender.class);
+ assertThat(mRecoverySystemService.requestLskf("test", intentSender), is(true));
+ mRecoverySystemService.onPreparedForReboot(true);
+ verify(intentSender).sendIntent(any(), anyInt(), any(), any(), any());
+ }
+
+ @Test
+ public void requestLskf_subsequentRequestClearsPrepared() throws Exception {
+ IntentSender intentSender = mock(IntentSender.class);
+ assertThat(mRecoverySystemService.requestLskf("test", intentSender), is(true));
+ mRecoverySystemService.onPreparedForReboot(true);
+ verify(intentSender).sendIntent(any(), anyInt(), any(), any(), any());
+
+ assertThat(mRecoverySystemService.requestLskf("test2", null), is(true));
+ assertThat(mRecoverySystemService.rebootWithLskf("test", null), is(false));
+ assertThat(mRecoverySystemService.rebootWithLskf("test2", "foobar"), is(false));
+
+ mRecoverySystemService.onPreparedForReboot(true);
+ assertThat(mRecoverySystemService.rebootWithLskf("test2", "foobar"), is(true));
+ verify(intentSender).sendIntent(any(), anyInt(), any(), any(), any());
+ verify(mIPowerManager).reboot(anyBoolean(), eq("foobar"), anyBoolean());
+ }
+
+
+ @Test
+ public void requestLskf_requestedButNotPrepared() throws Exception {
+ IntentSender intentSender = mock(IntentSender.class);
+ assertThat(mRecoverySystemService.requestLskf("test", intentSender), is(true));
+ verify(intentSender, never()).sendIntent(any(), anyInt(), any(), any(), any());
+ }
+
+ @Test(expected = SecurityException.class)
+ public void clearLskf_protected() {
+ doThrow(SecurityException.class).when(mContext).enforceCallingOrSelfPermission(
+ eq(android.Manifest.permission.RECOVERY), any());
+ mRecoverySystemService.clearLskf();
+ }
+
+ @Test
+ public void clearLskf_requestedThenCleared() throws Exception {
+ IntentSender intentSender = mock(IntentSender.class);
+ assertThat(mRecoverySystemService.requestLskf("test", intentSender), is(true));
+ mRecoverySystemService.onPreparedForReboot(true);
+ verify(intentSender).sendIntent(any(), anyInt(), any(), any(), any());
+
+ assertThat(mRecoverySystemService.clearLskf(), is(true));
+ verify(mLockSettingsInternal).clearRebootEscrow();
+ }
+
+ @Test
+ public void startup_setRebootEscrowListener() throws Exception {
+ mRecoverySystemService.onSystemServicesReady();
+ verify(mLockSettingsInternal).setRebootEscrowListener(any());
+ }
+
+ @Test(expected = SecurityException.class)
+ public void rebootWithLskf_protected() {
+ doThrow(SecurityException.class).when(mContext).enforceCallingOrSelfPermission(
+ eq(android.Manifest.permission.RECOVERY), any());
+ mRecoverySystemService.rebootWithLskf("test1", null);
+ }
+
+ @Test
+ public void rebootWithLskf_Success() throws Exception {
+ assertThat(mRecoverySystemService.requestLskf("test", null), is(true));
+ mRecoverySystemService.onPreparedForReboot(true);
+ assertThat(mRecoverySystemService.rebootWithLskf("test", "ab-update"), is(true));
+ verify(mIPowerManager).reboot(anyBoolean(), eq("ab-update"), anyBoolean());
+ }
+
+ @Test
+ public void rebootWithLskf_withoutPrepare_Failure() throws Exception {
+ assertThat(mRecoverySystemService.rebootWithLskf("test1", null), is(false));
+ }
+
+ @Test
+ public void rebootWithLskf_withNullUpdateToken_Failure() throws Exception {
+ assertThat(mRecoverySystemService.rebootWithLskf(null, null), is(false));
+ verifyNoMoreInteractions(mIPowerManager);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java
index a986b71..131e4f3 100644
--- a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java
@@ -19,6 +19,8 @@
import android.content.Context;
import android.os.PowerManager;
+import com.android.internal.widget.LockSettingsInternal;
+
import java.io.FileWriter;
public class RecoverySystemServiceTestable extends RecoverySystemService {
@@ -27,15 +29,17 @@
private final PowerManager mPowerManager;
private final FileWriter mUncryptPackageFileWriter;
private final UncryptSocket mUncryptSocket;
+ private final LockSettingsInternal mLockSettingsInternal;
MockInjector(Context context, FakeSystemProperties systemProperties,
PowerManager powerManager, FileWriter uncryptPackageFileWriter,
- UncryptSocket uncryptSocket) {
+ UncryptSocket uncryptSocket, LockSettingsInternal lockSettingsInternal) {
super(context);
mSystemProperties = systemProperties;
mPowerManager = powerManager;
mUncryptPackageFileWriter = uncryptPackageFileWriter;
mUncryptSocket = uncryptSocket;
+ mLockSettingsInternal = lockSettingsInternal;
}
@Override
@@ -76,13 +80,18 @@
@Override
public void threadSleep(long millis) {
}
+
+ @Override
+ public LockSettingsInternal getLockSettingsService() {
+ return mLockSettingsInternal;
+ }
}
RecoverySystemServiceTestable(Context context, FakeSystemProperties systemProperties,
PowerManager powerManager, FileWriter uncryptPackageFileWriter,
- UncryptSocket uncryptSocket) {
+ UncryptSocket uncryptSocket, LockSettingsInternal lockSettingsInternal) {
super(new MockInjector(context, systemProperties, powerManager, uncryptPackageFileWriter,
- uncryptSocket));
+ uncryptSocket, lockSettingsInternal));
}
public static class FakeSystemProperties {
diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
index aec489c..757a884 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
@@ -53,8 +53,7 @@
if (a == null || b == null) {
return a == b;
}
- return a.getLongVersionCode() == b.getLongVersionCode()
- && Objects.equals(a.getPackageName(), b.getPackageName());
+ return a.equals(b);
}
@Override
@@ -298,15 +297,8 @@
private void assertPackageRollbacksAreEquivalent(PackageRollbackInfo b, PackageRollbackInfo a) {
assertThat(b.getPackageName()).isEqualTo(a.getPackageName());
- assertThat(b.getVersionRolledBackFrom().getLongVersionCode())
- .isEqualTo(a.getVersionRolledBackFrom().getLongVersionCode());
- assertThat(b.getVersionRolledBackFrom().getPackageName())
- .isEqualTo(a.getVersionRolledBackFrom().getPackageName());
-
- assertThat(b.getVersionRolledBackTo().getLongVersionCode())
- .isEqualTo(a.getVersionRolledBackTo().getLongVersionCode());
- assertThat(b.getVersionRolledBackTo().getPackageName())
- .isEqualTo(a.getVersionRolledBackTo().getPackageName());
+ assertThat(b.getVersionRolledBackFrom()).isEqualTo(a.getVersionRolledBackFrom());
+ assertThat(b.getVersionRolledBackTo()).isEqualTo(a.getVersionRolledBackTo());
assertThat(b.getPendingBackups().toArray()).isEqualTo(a.getPendingBackups().toArray());
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
index 82f32f8..f8915c0 100644
--- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
@@ -60,8 +60,6 @@
import android.os.IHwInterface;
import android.os.RemoteException;
-import androidx.test.runner.AndroidJUnit4;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -144,7 +142,11 @@
properties.maxSoundModels = 456;
properties.maxKeyPhrases = 567;
properties.maxUsers = 678;
- properties.recognitionModes = 789;
+ properties.recognitionModes =
+ android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER
+ | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_IDENTIFICATION
+ | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_AUTHENTICATION
+ | android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER;
properties.captureTransition = true;
properties.maxBufferMs = 321;
properties.concurrentCapture = supportConcurrentCapture;
@@ -162,7 +164,10 @@
assertEquals(456, properties.maxSoundModels);
assertEquals(567, properties.maxKeyPhrases);
assertEquals(678, properties.maxUsers);
- assertEquals(789, properties.recognitionModes);
+ assertEquals(RecognitionMode.GENERIC_TRIGGER
+ | RecognitionMode.USER_AUTHENTICATION
+ | RecognitionMode.USER_IDENTIFICATION
+ | RecognitionMode.VOICE_TRIGGER, properties.recognitionModes);
assertTrue(properties.captureTransition);
assertEquals(321, properties.maxBufferMs);
assertEquals(supportConcurrentCapture, properties.concurrentCapture);
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
index 72a7f50..ae53692 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -18,15 +18,18 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.timedetector.ManualTimeSuggestion;
+import android.app.timedetector.NetworkTimeSuggestion;
import android.app.timedetector.PhoneTimeSuggestion;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -34,7 +37,7 @@
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
-import android.util.TimestampedValue;
+import android.os.TimestampedValue;
import androidx.test.runner.AndroidJUnit4;
@@ -77,6 +80,22 @@
mHandlerThread.join();
}
+ @Test(expected = SecurityException.class)
+ public void testSuggestPhoneTime_withoutPermission() {
+ doThrow(new SecurityException("Mock"))
+ .when(mMockContext).enforceCallingPermission(anyString(), any());
+ PhoneTimeSuggestion phoneTimeSuggestion = createPhoneTimeSuggestion();
+
+ try {
+ mTimeDetectorService.suggestPhoneTime(phoneTimeSuggestion);
+ fail();
+ } finally {
+ verify(mMockContext).enforceCallingPermission(
+ eq(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE),
+ anyString());
+ }
+ }
+
@Test
public void testSuggestPhoneTime() throws Exception {
doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
@@ -86,13 +105,29 @@
mTestHandler.assertTotalMessagesEnqueued(1);
verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.SET_TIME),
+ eq(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE),
anyString());
mTestHandler.waitForEmptyQueue();
mStubbedTimeDetectorStrategy.verifySuggestPhoneTimeCalled(phoneTimeSuggestion);
}
+ @Test(expected = SecurityException.class)
+ public void testSuggestManualTime_withoutPermission() {
+ doThrow(new SecurityException("Mock"))
+ .when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
+ ManualTimeSuggestion manualTimeSuggestion = createManualTimeSuggestion();
+
+ try {
+ mTimeDetectorService.suggestManualTime(manualTimeSuggestion);
+ fail();
+ } finally {
+ verify(mMockContext).enforceCallingOrSelfPermission(
+ eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE),
+ anyString());
+ }
+ }
+
@Test
public void testSuggestManualTime() throws Exception {
doNothing().when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
@@ -102,13 +137,43 @@
mTestHandler.assertTotalMessagesEnqueued(1);
verify(mMockContext).enforceCallingOrSelfPermission(
- eq(android.Manifest.permission.SET_TIME),
+ eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE),
anyString());
mTestHandler.waitForEmptyQueue();
mStubbedTimeDetectorStrategy.verifySuggestManualTimeCalled(manualTimeSuggestion);
}
+ @Test(expected = SecurityException.class)
+ public void testSuggestNetworkTime_withoutPermission() {
+ doThrow(new SecurityException("Mock"))
+ .when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
+ NetworkTimeSuggestion NetworkTimeSuggestion = createNetworkTimeSuggestion();
+
+ try {
+ mTimeDetectorService.suggestNetworkTime(NetworkTimeSuggestion);
+ fail();
+ } finally {
+ verify(mMockContext).enforceCallingOrSelfPermission(
+ eq(android.Manifest.permission.SET_TIME), anyString());
+ }
+ }
+
+ @Test
+ public void testSuggestNetworkTime() throws Exception {
+ doNothing().when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
+
+ NetworkTimeSuggestion NetworkTimeSuggestion = createNetworkTimeSuggestion();
+ mTimeDetectorService.suggestNetworkTime(NetworkTimeSuggestion);
+ mTestHandler.assertTotalMessagesEnqueued(1);
+
+ verify(mMockContext).enforceCallingOrSelfPermission(
+ eq(android.Manifest.permission.SET_TIME), anyString());
+
+ mTestHandler.waitForEmptyQueue();
+ mStubbedTimeDetectorStrategy.verifySuggestNetworkTimeCalled(NetworkTimeSuggestion);
+ }
+
@Test
public void testDump() {
when(mMockContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP))
@@ -146,11 +211,17 @@
return new ManualTimeSuggestion(timeValue);
}
+ private static NetworkTimeSuggestion createNetworkTimeSuggestion() {
+ TimestampedValue<Long> timeValue = new TimestampedValue<>(100L, 1_000_000L);
+ return new NetworkTimeSuggestion(timeValue);
+ }
+
private static class StubbedTimeDetectorStrategy implements TimeDetectorStrategy {
// Call tracking.
private PhoneTimeSuggestion mLastPhoneSuggestion;
private ManualTimeSuggestion mLastManualSuggestion;
+ private NetworkTimeSuggestion mLastNetworkSuggestion;
private boolean mLastAutoTimeDetectionToggleCalled;
private boolean mDumpCalled;
@@ -171,6 +242,12 @@
}
@Override
+ public void suggestNetworkTime(NetworkTimeSuggestion timeSuggestion) {
+ resetCallTracking();
+ mLastNetworkSuggestion = timeSuggestion;
+ }
+
+ @Override
public void handleAutoTimeDetectionChanged() {
resetCallTracking();
mLastAutoTimeDetectionToggleCalled = true;
@@ -185,6 +262,7 @@
void resetCallTracking() {
mLastPhoneSuggestion = null;
mLastManualSuggestion = null;
+ mLastNetworkSuggestion = null;
mLastAutoTimeDetectionToggleCalled = false;
mDumpCalled = false;
}
@@ -197,6 +275,10 @@
assertEquals(expectedSuggestion, mLastManualSuggestion);
}
+ public void verifySuggestNetworkTimeCalled(NetworkTimeSuggestion expectedSuggestion) {
+ assertEquals(expectedSuggestion, mLastNetworkSuggestion);
+ }
+
void verifyHandleAutoTimeDetectionToggleCalled() {
assertTrue(mLastAutoTimeDetectionToggleCalled);
}
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
index 1aa3d8f..aaf9799 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
@@ -24,12 +24,13 @@
import static org.junit.Assert.fail;
import android.app.timedetector.ManualTimeSuggestion;
+import android.app.timedetector.NetworkTimeSuggestion;
import android.app.timedetector.PhoneTimeSuggestion;
import android.content.Intent;
import android.icu.util.Calendar;
import android.icu.util.GregorianCalendar;
import android.icu.util.TimeZone;
-import android.util.TimestampedValue;
+import android.os.TimestampedValue;
import androidx.test.runner.AndroidJUnit4;
@@ -45,14 +46,16 @@
private static final TimestampedValue<Long> ARBITRARY_CLOCK_INITIALIZATION_INFO =
new TimestampedValue<>(
123456789L /* realtimeClockMillis */,
- createUtcTime(1977, 1, 1, 12, 0, 0));
+ createUtcTime(2008, 5, 23, 12, 0, 0));
+ /**
+ * An arbitrary time, very different from the {@link #ARBITRARY_CLOCK_INITIALIZATION_INFO}
+ * time. Can be used as the basis for time suggestions.
+ */
private static final long ARBITRARY_TEST_TIME_MILLIS = createUtcTime(2018, 1, 1, 12, 0, 0);
private static final int ARBITRARY_PHONE_ID = 123456;
- private static final long ONE_DAY_MILLIS = Duration.ofDays(1).toMillis();
-
private Script mScript;
@Before
@@ -67,15 +70,16 @@
int phoneId = ARBITRARY_PHONE_ID;
long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
+
PhoneTimeSuggestion timeSuggestion =
mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
- int clockIncrement = 1000;
- long expectedSystemClockMillis = testTimeMillis + clockIncrement;
+ mScript.simulateTimePassing()
+ .simulatePhoneTimeSuggestion(timeSuggestion);
- mScript.simulateTimePassing(clockIncrement)
- .simulatePhoneTimeSuggestion(timeSuggestion)
- .verifySystemClockWasSetAndResetCallTracking(
- expectedSystemClockMillis, true /* expectNetworkBroadcast */)
+ long expectedSystemClockMillis =
+ mScript.calculateTimeInMillisForNow(timeSuggestion.getUtcTime());
+ mScript.verifySystemClockWasSetAndResetCallTracking(
+ expectedSystemClockMillis, true /* expectNetworkBroadcast */)
.assertLatestPhoneSuggestion(phoneId, timeSuggestion);
}
@@ -94,26 +98,24 @@
@Test
public void testSuggestPhoneTime_systemClockThreshold() {
- int systemClockUpdateThresholdMillis = 1000;
+ final int systemClockUpdateThresholdMillis = 1000;
+ final int clockIncrementMillis = 100;
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
.pokeThresholds(systemClockUpdateThresholdMillis)
.pokeAutoTimeDetectionEnabled(true);
- final int clockIncrement = 100;
int phoneId = ARBITRARY_PHONE_ID;
// Send the first time signal. It should be used.
{
- long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
PhoneTimeSuggestion timeSuggestion1 =
- mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
- TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime();
+ mScript.generatePhoneTimeSuggestion(phoneId, ARBITRARY_TEST_TIME_MILLIS);
// Increment the the device clocks to simulate the passage of time.
- mScript.simulateTimePassing(clockIncrement);
+ mScript.simulateTimePassing(clockIncrementMillis);
long expectedSystemClockMillis1 =
- TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
+ mScript.calculateTimeInMillisForNow(timeSuggestion1.getUtcTime());
mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
.verifySystemClockWasSetAndResetCallTracking(
@@ -127,7 +129,7 @@
int underThresholdMillis = systemClockUpdateThresholdMillis - 1;
PhoneTimeSuggestion timeSuggestion2 = mScript.generatePhoneTimeSuggestion(
phoneId, mScript.peekSystemClockMillis() + underThresholdMillis);
- mScript.simulateTimePassing(clockIncrement)
+ mScript.simulateTimePassing(clockIncrementMillis)
.simulatePhoneTimeSuggestion(timeSuggestion2)
.verifySystemClockWasNotSetAndResetCallTracking()
.assertLatestPhoneSuggestion(phoneId, timeSuggestion2);
@@ -138,11 +140,10 @@
PhoneTimeSuggestion timeSuggestion3 = mScript.generatePhoneTimeSuggestion(
phoneId,
mScript.peekSystemClockMillis() + systemClockUpdateThresholdMillis);
- mScript.simulateTimePassing(clockIncrement);
+ mScript.simulateTimePassing(clockIncrementMillis);
long expectedSystemClockMillis3 =
- TimeDetectorStrategy.getTimeAt(timeSuggestion3.getUtcTime(),
- mScript.peekElapsedRealtimeMillis());
+ mScript.calculateTimeInMillisForNow(timeSuggestion3.getUtcTime());
mScript.simulatePhoneTimeSuggestion(timeSuggestion3)
.verifySystemClockWasSetAndResetCallTracking(
@@ -162,17 +163,16 @@
int phone1Id = ARBITRARY_PHONE_ID;
int phone2Id = ARBITRARY_PHONE_ID + 1;
long phone1TimeMillis = ARBITRARY_TEST_TIME_MILLIS;
- long phone2TimeMillis = phone1TimeMillis + 60000;
-
- final int clockIncrement = 999;
+ long phone2TimeMillis = ARBITRARY_TEST_TIME_MILLIS + Duration.ofDays(1).toMillis();
// Make a suggestion with phone2Id.
{
PhoneTimeSuggestion phone2TimeSuggestion =
mScript.generatePhoneTimeSuggestion(phone2Id, phone2TimeMillis);
- mScript.simulateTimePassing(clockIncrement);
+ mScript.simulateTimePassing();
- long expectedSystemClockMillis = phone2TimeMillis + clockIncrement;
+ long expectedSystemClockMillis =
+ mScript.calculateTimeInMillisForNow(phone2TimeSuggestion.getUtcTime());
mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion)
.verifySystemClockWasSetAndResetCallTracking(
@@ -181,15 +181,16 @@
.assertLatestPhoneSuggestion(phone2Id, phone2TimeSuggestion);
}
- mScript.simulateTimePassing(clockIncrement);
+ mScript.simulateTimePassing();
// Now make a different suggestion with phone1Id.
{
PhoneTimeSuggestion phone1TimeSuggestion =
mScript.generatePhoneTimeSuggestion(phone1Id, phone1TimeMillis);
- mScript.simulateTimePassing(clockIncrement);
+ mScript.simulateTimePassing();
- long expectedSystemClockMillis = phone1TimeMillis + clockIncrement;
+ long expectedSystemClockMillis =
+ mScript.calculateTimeInMillisForNow(phone1TimeSuggestion.getUtcTime());
mScript.simulatePhoneTimeSuggestion(phone1TimeSuggestion)
.verifySystemClockWasSetAndResetCallTracking(
@@ -198,14 +199,14 @@
}
- mScript.simulateTimePassing(clockIncrement);
+ mScript.simulateTimePassing();
// Make another suggestion with phone2Id. It should be stored but not used because the
// phone1Id suggestion will still "win".
{
PhoneTimeSuggestion phone2TimeSuggestion =
mScript.generatePhoneTimeSuggestion(phone2Id, phone2TimeMillis);
- mScript.simulateTimePassing(clockIncrement);
+ mScript.simulateTimePassing();
mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion)
.verifySystemClockWasNotSetAndResetCallTracking()
@@ -220,9 +221,10 @@
{
PhoneTimeSuggestion phone2TimeSuggestion =
mScript.generatePhoneTimeSuggestion(phone2Id, phone2TimeMillis);
- mScript.simulateTimePassing(clockIncrement);
+ mScript.simulateTimePassing();
- long expectedSystemClockMillis = phone2TimeMillis + clockIncrement;
+ long expectedSystemClockMillis =
+ mScript.calculateTimeInMillisForNow(phone2TimeSuggestion.getUtcTime());
mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion)
.verifySystemClockWasSetAndResetCallTracking(
@@ -239,7 +241,7 @@
int phoneId = ARBITRARY_PHONE_ID;
PhoneTimeSuggestion timeSuggestion =
mScript.generatePhoneTimeSuggestion(phoneId, ARBITRARY_TEST_TIME_MILLIS);
- mScript.simulateTimePassing(1000)
+ mScript.simulateTimePassing()
.simulatePhoneTimeSuggestion(timeSuggestion)
.verifySystemClockWasNotSetAndResetCallTracking()
.assertLatestPhoneSuggestion(phoneId, timeSuggestion);
@@ -260,9 +262,8 @@
TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime();
// Initialize the strategy / device with a time set from a phone suggestion.
- mScript.simulateTimePassing(100);
- long expectedSystemClockMillis1 =
- TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
+ mScript.simulateTimePassing();
+ long expectedSystemClockMillis1 = mScript.calculateTimeInMillisForNow(utcTime1);
mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
.verifySystemClockWasSetAndResetCallTracking(
expectedSystemClockMillis1, true /* expectNetworkBroadcast */)
@@ -299,8 +300,7 @@
long validReferenceTimeMillis = utcTime1.getReferenceTimeMillis() + 100;
TimestampedValue<Long> utcTime4 = new TimestampedValue<>(
validReferenceTimeMillis, validUtcTimeMillis);
- long expectedSystemClockMillis4 =
- TimeDetectorStrategy.getTimeAt(utcTime4, mScript.peekElapsedRealtimeMillis());
+ long expectedSystemClockMillis4 = mScript.calculateTimeInMillisForNow(utcTime4);
PhoneTimeSuggestion timeSuggestion4 =
createPhoneTimeSuggestion(phoneId, utcTime4);
mScript.simulatePhoneTimeSuggestion(timeSuggestion4)
@@ -335,8 +335,7 @@
// Simulate more time passing.
mScript.simulateTimePassing(clockIncrementMillis);
- long expectedSystemClockMillis1 =
- TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
+ long expectedSystemClockMillis1 = mScript.calculateTimeInMillisForNow(utcTime1);
// Turn on auto time detection.
mScript.simulateAutoTimeDetectionToggle()
@@ -357,8 +356,8 @@
// Simulate more time passing.
mScript.simulateTimePassing(clockIncrementMillis);
- long expectedSystemClockMillis2 = TimeDetectorStrategy.getTimeAt(
- timeSuggestion2.getUtcTime(), mScript.peekElapsedRealtimeMillis());
+ long expectedSystemClockMillis2 =
+ mScript.calculateTimeInMillisForNow(timeSuggestion2.getUtcTime());
// The new time, though valid, should not be set in the system clock because auto time is
// disabled.
@@ -382,19 +381,21 @@
long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
PhoneTimeSuggestion phoneSuggestion =
mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
- int clockIncrementMillis = 1000;
- mScript.simulateTimePassing(clockIncrementMillis)
- .simulatePhoneTimeSuggestion(phoneSuggestion)
+ mScript.simulateTimePassing();
+
+ long expectedSystemClockMillis =
+ mScript.calculateTimeInMillisForNow(phoneSuggestion.getUtcTime());
+ mScript.simulatePhoneTimeSuggestion(phoneSuggestion)
.verifySystemClockWasSetAndResetCallTracking(
- testTimeMillis + clockIncrementMillis, true /* expectedNetworkBroadcast */)
+ expectedSystemClockMillis, true /* expectedNetworkBroadcast */)
.assertLatestPhoneSuggestion(phoneId, phoneSuggestion);
// Look inside and check what the strategy considers the current best phone suggestion.
assertEquals(phoneSuggestion, mScript.peekBestPhoneSuggestion());
// Simulate time passing, long enough that phoneSuggestion is now too old.
- mScript.simulateTimePassing(TimeDetectorStrategyImpl.PHONE_MAX_AGE_MILLIS);
+ mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_UTC_TIME_AGE_MILLIS);
// Look inside and check what the strategy considers the current best phone suggestion. It
// should still be the, it's just no longer used.
@@ -407,13 +408,14 @@
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
.pokeAutoTimeDetectionEnabled(false);
- long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
- ManualTimeSuggestion timeSuggestion = mScript.generateManualTimeSuggestion(testTimeMillis);
- final int clockIncrement = 1000;
- long expectedSystemClockMillis = testTimeMillis + clockIncrement;
+ ManualTimeSuggestion timeSuggestion =
+ mScript.generateManualTimeSuggestion(ARBITRARY_TEST_TIME_MILLIS);
- mScript.simulateTimePassing(clockIncrement)
- .simulateManualTimeSuggestion(timeSuggestion)
+ mScript.simulateTimePassing();
+
+ long expectedSystemClockMillis =
+ mScript.calculateTimeInMillisForNow(timeSuggestion.getUtcTime());
+ mScript.simulateManualTimeSuggestion(timeSuggestion)
.verifySystemClockWasSetAndResetCallTracking(
expectedSystemClockMillis, false /* expectNetworkBroadcast */);
}
@@ -430,21 +432,19 @@
long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
PhoneTimeSuggestion phoneTimeSuggestion =
mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
- long expectedAutoClockMillis = phoneTimeSuggestion.getUtcTime().getValue();
- final int clockIncrement = 1000;
// Simulate the passage of time.
- mScript.simulateTimePassing(clockIncrement);
- expectedAutoClockMillis += clockIncrement;
+ mScript.simulateTimePassing();
+ long expectedAutoClockMillis =
+ mScript.calculateTimeInMillisForNow(phoneTimeSuggestion.getUtcTime());
mScript.simulatePhoneTimeSuggestion(phoneTimeSuggestion)
.verifySystemClockWasSetAndResetCallTracking(
expectedAutoClockMillis, true /* expectNetworkBroadcast */)
.assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
// Simulate the passage of time.
- mScript.simulateTimePassing(clockIncrement);
- expectedAutoClockMillis += clockIncrement;
+ mScript.simulateTimePassing();
// Switch to manual.
mScript.simulateAutoTimeDetectionToggle()
@@ -452,26 +452,29 @@
.assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
// Simulate the passage of time.
- mScript.simulateTimePassing(clockIncrement);
- expectedAutoClockMillis += clockIncrement;
+ mScript.simulateTimePassing();
// Simulate a manual suggestion 1 day different from the auto suggestion.
- long manualTimeMillis = testTimeMillis + ONE_DAY_MILLIS;
- long expectedManualClockMillis = manualTimeMillis;
+ long manualTimeMillis = testTimeMillis + Duration.ofDays(1).toMillis();
ManualTimeSuggestion manualTimeSuggestion =
mScript.generateManualTimeSuggestion(manualTimeMillis);
+ mScript.simulateTimePassing();
+
+ long expectedManualClockMillis =
+ mScript.calculateTimeInMillisForNow(manualTimeSuggestion.getUtcTime());
mScript.simulateManualTimeSuggestion(manualTimeSuggestion)
.verifySystemClockWasSetAndResetCallTracking(
expectedManualClockMillis, false /* expectNetworkBroadcast */)
.assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
// Simulate the passage of time.
- mScript.simulateTimePassing(clockIncrement);
- expectedAutoClockMillis += clockIncrement;
+ mScript.simulateTimePassing();
// Switch back to auto.
mScript.simulateAutoTimeDetectionToggle();
+ expectedAutoClockMillis =
+ mScript.calculateTimeInMillisForNow(phoneTimeSuggestion.getUtcTime());
mScript.verifySystemClockWasSetAndResetCallTracking(
expectedAutoClockMillis, true /* expectNetworkBroadcast */)
.assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
@@ -492,13 +495,143 @@
ManualTimeSuggestion timeSuggestion =
mScript.generateManualTimeSuggestion(ARBITRARY_TEST_TIME_MILLIS);
- final int clockIncrement = 1000;
- mScript.simulateTimePassing(clockIncrement)
+ mScript.simulateTimePassing()
.simulateManualTimeSuggestion(timeSuggestion)
.verifySystemClockWasNotSetAndResetCallTracking();
}
+ @Test
+ public void testSuggestNetworkTime_autoTimeEnabled() {
+ mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+ .pokeAutoTimeDetectionEnabled(true);
+
+ NetworkTimeSuggestion timeSuggestion =
+ mScript.generateNetworkTimeSuggestion(ARBITRARY_TEST_TIME_MILLIS);
+
+ mScript.simulateTimePassing();
+
+ long expectedSystemClockMillis =
+ mScript.calculateTimeInMillisForNow(timeSuggestion.getUtcTime());
+ mScript.simulateNetworkTimeSuggestion(timeSuggestion)
+ .verifySystemClockWasSetAndResetCallTracking(
+ expectedSystemClockMillis, false /* expectNetworkBroadcast */);
+ }
+
+ @Test
+ public void testSuggestNetworkTime_autoTimeDisabled() {
+ mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+ .pokeAutoTimeDetectionEnabled(false);
+
+ NetworkTimeSuggestion timeSuggestion =
+ mScript.generateNetworkTimeSuggestion(ARBITRARY_TEST_TIME_MILLIS);
+
+ mScript.simulateTimePassing()
+ .simulateNetworkTimeSuggestion(timeSuggestion)
+ .verifySystemClockWasNotSetAndResetCallTracking();
+ }
+
+ @Test
+ public void testSuggestNetworkTime_phoneSuggestionsBeatNetworkSuggestions() {
+ mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+ .pokeAutoTimeDetectionEnabled(true);
+
+ // Three obviously different times that could not be mistaken for each other.
+ long networkTimeMillis1 = ARBITRARY_TEST_TIME_MILLIS;
+ long networkTimeMillis2 = ARBITRARY_TEST_TIME_MILLIS + Duration.ofDays(30).toMillis();
+ long phoneTimeMillis = ARBITRARY_TEST_TIME_MILLIS + Duration.ofDays(60).toMillis();
+ // A small increment used to simulate the passage of time, but not enough to interfere with
+ // macro-level time changes associated with suggestion age.
+ final long smallTimeIncrementMillis = 101;
+
+ // A network suggestion is made. It should be used because there is no phone suggestion.
+ NetworkTimeSuggestion networkTimeSuggestion1 =
+ mScript.generateNetworkTimeSuggestion(networkTimeMillis1);
+ mScript.simulateTimePassing(smallTimeIncrementMillis)
+ .simulateNetworkTimeSuggestion(networkTimeSuggestion1)
+ .verifySystemClockWasSetAndResetCallTracking(
+ mScript.calculateTimeInMillisForNow(networkTimeSuggestion1.getUtcTime()),
+ false /* expectNetworkBroadcast */);
+
+ // Check internal state.
+ mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, null)
+ .assertLatestNetworkSuggestion(networkTimeSuggestion1);
+ assertEquals(networkTimeSuggestion1, mScript.peekLatestValidNetworkSuggestion());
+ assertNull(mScript.peekBestPhoneSuggestion());
+
+ // Simulate a little time passing.
+ mScript.simulateTimePassing(smallTimeIncrementMillis)
+ .verifySystemClockWasNotSetAndResetCallTracking();
+
+ // Now a phone suggestion is made. Phone suggestions are prioritized over network
+ // suggestions so it should "win".
+ PhoneTimeSuggestion phoneTimeSuggestion =
+ mScript.generatePhoneTimeSuggestion(ARBITRARY_PHONE_ID, phoneTimeMillis);
+ mScript.simulateTimePassing(smallTimeIncrementMillis)
+ .simulatePhoneTimeSuggestion(phoneTimeSuggestion)
+ .verifySystemClockWasSetAndResetCallTracking(
+ mScript.calculateTimeInMillisForNow(phoneTimeSuggestion.getUtcTime()),
+ true /* expectNetworkBroadcast */);
+
+ // Check internal state.
+ mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, phoneTimeSuggestion)
+ .assertLatestNetworkSuggestion(networkTimeSuggestion1);
+ assertEquals(networkTimeSuggestion1, mScript.peekLatestValidNetworkSuggestion());
+ assertEquals(phoneTimeSuggestion, mScript.peekBestPhoneSuggestion());
+
+ // Simulate some significant time passing: half the time allowed before a time signal
+ // becomes "too old to use".
+ mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_UTC_TIME_AGE_MILLIS / 2)
+ .verifySystemClockWasNotSetAndResetCallTracking();
+
+ // Now another network suggestion is made. Phone suggestions are prioritized over network
+ // suggestions so the latest phone suggestion should still "win".
+ NetworkTimeSuggestion networkTimeSuggestion2 =
+ mScript.generateNetworkTimeSuggestion(networkTimeMillis2);
+ mScript.simulateTimePassing(smallTimeIncrementMillis)
+ .simulateNetworkTimeSuggestion(networkTimeSuggestion2)
+ .verifySystemClockWasNotSetAndResetCallTracking();
+
+ // Check internal state.
+ mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, phoneTimeSuggestion)
+ .assertLatestNetworkSuggestion(networkTimeSuggestion2);
+ assertEquals(networkTimeSuggestion2, mScript.peekLatestValidNetworkSuggestion());
+ assertEquals(phoneTimeSuggestion, mScript.peekBestPhoneSuggestion());
+
+ // Simulate some significant time passing: half the time allowed before a time signal
+ // becomes "too old to use". This should mean that phoneTimeSuggestion is now too old to be
+ // used but networkTimeSuggestion2 is not.
+ mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_UTC_TIME_AGE_MILLIS / 2);
+
+ // NOTE: The TimeDetectorStrategyImpl doesn't set an alarm for the point when the last
+ // suggestion it used becomes too old: it requires a new suggestion or an auto-time toggle
+ // to re-run the detection logic. This may change in future but until then we rely on a
+ // steady stream of suggestions to re-evaluate.
+ mScript.verifySystemClockWasNotSetAndResetCallTracking();
+
+ // Check internal state.
+ mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, phoneTimeSuggestion)
+ .assertLatestNetworkSuggestion(networkTimeSuggestion2);
+ assertEquals(networkTimeSuggestion2, mScript.peekLatestValidNetworkSuggestion());
+ assertNull(mScript.peekBestPhoneSuggestion());
+
+ // Toggle auto-time off and on to force the detection logic to run.
+ mScript.simulateAutoTimeDetectionToggle()
+ .simulateTimePassing(smallTimeIncrementMillis)
+ .simulateAutoTimeDetectionToggle();
+
+ // Verify the latest network time now wins.
+ mScript.verifySystemClockWasSetAndResetCallTracking(
+ mScript.calculateTimeInMillisForNow(networkTimeSuggestion2.getUtcTime()),
+ false /* expectNetworkTimeBroadcast */);
+
+ // Check internal state.
+ mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, phoneTimeSuggestion)
+ .assertLatestNetworkSuggestion(networkTimeSuggestion2);
+ assertEquals(networkTimeSuggestion2, mScript.peekLatestValidNetworkSuggestion());
+ assertNull(mScript.peekBestPhoneSuggestion());
+ }
+
/**
* A fake implementation of TimeDetectorStrategy.Callback. Besides tracking changes and behaving
* like the real thing should, it also asserts preconditions.
@@ -674,6 +807,11 @@
return this;
}
+ Script simulateNetworkTimeSuggestion(NetworkTimeSuggestion timeSuggestion) {
+ mTimeDetectorStrategy.suggestNetworkTime(timeSuggestion);
+ return this;
+ }
+
Script simulateAutoTimeDetectionToggle() {
mFakeCallback.simulateAutoTimeZoneDetectionToggle();
mTimeDetectorStrategy.handleAutoTimeDetectionChanged();
@@ -685,6 +823,13 @@
return this;
}
+ /**
+ * Simulates time passing by an arbitrary (but relatively small) amount.
+ */
+ Script simulateTimePassing() {
+ return simulateTimePassing(999);
+ }
+
Script verifySystemClockWasNotSetAndResetCallTracking() {
mFakeCallback.verifySystemClockNotSet();
mFakeCallback.verifyIntentWasNotBroadcast();
@@ -711,14 +856,30 @@
}
/**
+ * White box test info: Asserts the latest network suggestion is as expected.
+ */
+ Script assertLatestNetworkSuggestion(NetworkTimeSuggestion expected) {
+ assertEquals(expected, mTimeDetectorStrategy.getLatestNetworkSuggestion());
+ return this;
+ }
+
+ /**
* White box test info: Returns the phone suggestion that would be used, if any, given the
- * current elapsed real time clock.
+ * current elapsed real time clock and regardless of origin prioritization.
*/
PhoneTimeSuggestion peekBestPhoneSuggestion() {
return mTimeDetectorStrategy.findBestPhoneSuggestionForTests();
}
/**
+ * White box test info: Returns the network suggestion that would be used, if any, given the
+ * current elapsed real time clock and regardless of origin prioritization.
+ */
+ NetworkTimeSuggestion peekLatestValidNetworkSuggestion() {
+ return mTimeDetectorStrategy.findLatestValidNetworkSuggestionForTests();
+ }
+
+ /**
* Generates a ManualTimeSuggestion using the current elapsed realtime clock for the
* reference time.
*/
@@ -739,6 +900,24 @@
}
return createPhoneTimeSuggestion(phoneId, time);
}
+
+ /**
+ * Generates a NetworkTimeSuggestion using the current elapsed realtime clock for the
+ * reference time.
+ */
+ NetworkTimeSuggestion generateNetworkTimeSuggestion(long timeMillis) {
+ TimestampedValue<Long> utcTime =
+ new TimestampedValue<>(mFakeCallback.peekElapsedRealtimeMillis(), timeMillis);
+ return new NetworkTimeSuggestion(utcTime);
+ }
+
+ /**
+ * Calculates what the supplied time would be when adjusted for the movement of the fake
+ * elapsed realtime clock.
+ */
+ long calculateTimeInMillisForNow(TimestampedValue<Long> utcTime) {
+ return TimeDetectorStrategy.getTimeAt(utcTime, peekElapsedRealtimeMillis());
+ }
}
private static PhoneTimeSuggestion createPhoneTimeSuggestion(int phoneId,
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyTest.java
index 239d413..f1e9191 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyTest.java
@@ -18,7 +18,7 @@
import static org.junit.Assert.assertEquals;
-import android.util.TimestampedValue;
+import android.os.TimestampedValue;
import androidx.test.runner.AndroidJUnit4;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 95617b1..172df99 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -492,23 +492,13 @@
return sbn;
}
-
private NotificationRecord generateNotificationRecord(NotificationChannel channel, int id,
String groupKey, boolean isSummary) {
- return generateNotificationRecord(channel, id, groupKey, isSummary, false /* isBubble */);
- }
-
- private NotificationRecord generateNotificationRecord(NotificationChannel channel, int id,
- String groupKey, boolean isSummary, boolean isBubble) {
Notification.Builder nb = new Notification.Builder(mContext, channel.getId())
.setContentTitle("foo")
.setSmallIcon(android.R.drawable.sym_def_app_icon)
.setGroup(groupKey)
.setGroupSummary(isSummary);
- if (isBubble) {
- nb.setBubbleMetadata(getBasicBubbleMetadataBuilder().build());
- }
-
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, id,
"tag" + System.currentTimeMillis(), mUid, 0,
nb.build(), new UserHandle(mUid), null, 0);
@@ -521,11 +511,6 @@
private NotificationRecord generateNotificationRecord(NotificationChannel channel,
Notification.TvExtender extender) {
- return generateNotificationRecord(channel, extender, false /* isBubble */);
- }
-
- private NotificationRecord generateNotificationRecord(NotificationChannel channel,
- Notification.TvExtender extender, boolean isBubble) {
if (channel == null) {
channel = mTestNotificationChannel;
}
@@ -535,9 +520,6 @@
if (extender != null) {
nb.extend(extender);
}
- if (isBubble) {
- nb.setBubbleMetadata(getBasicBubbleMetadataBuilder().build());
- }
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
nb.build(), new UserHandle(mUid), null, 0);
return new NotificationRecord(mContext, sbn, channel);
@@ -555,6 +537,26 @@
return new NotificationRecord(mContext, sbn, channel);
}
+ private NotificationRecord generateMessageBubbleNotifRecord(NotificationChannel channel,
+ String tag) {
+ return generateMessageBubbleNotifRecord(true, channel, 1, tag, null, false);
+ }
+
+ private NotificationRecord generateMessageBubbleNotifRecord(boolean addMetadata,
+ NotificationChannel channel, int id, String tag, String groupKey, boolean isSummary) {
+ if (channel == null) {
+ channel = mTestNotificationChannel;
+ }
+ if (tag == null) {
+ tag = "tag";
+ }
+ Notification.Builder nb = getMessageStyleNotifBuilder(addMetadata, groupKey, isSummary);
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, id,
+ tag, mUid, 0,
+ nb.build(), new UserHandle(mUid), null, 0);
+ return new NotificationRecord(mContext, sbn, channel);
+ }
+
private Map<String, Answer> getSignalExtractorSideEffects() {
Map<String, Answer> answers = new ArrayMap<>();
@@ -610,23 +612,57 @@
false);
}
- private Notification.BubbleMetadata.Builder getBasicBubbleMetadataBuilder() {
+ private Notification.BubbleMetadata.Builder getBubbleMetadataBuilder() {
PendingIntent pi = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
return new Notification.BubbleMetadata.Builder()
.setIntent(pi)
.setIcon(Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon));
}
+ private Notification.Builder getMessageStyleNotifBuilder(boolean addBubbleMetadata,
+ String groupKey, boolean isSummary) {
+ // Give it a person
+ Person person = new Person.Builder()
+ .setName("bubblebot")
+ .build();
+ // It needs remote input to be bubble-able
+ RemoteInput remoteInput = new RemoteInput.Builder("reply_key").setLabel("reply").build();
+ PendingIntent inputIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+ Icon icon = Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
+ Notification.Action replyAction = new Notification.Action.Builder(icon, "Reply",
+ inputIntent).addRemoteInput(remoteInput)
+ .build();
+ // Make it messaging style
+ Notification.Builder nb = new Notification.Builder(mContext,
+ mTestNotificationChannel.getId())
+ .setContentTitle("foo")
+ .setStyle(new Notification.MessagingStyle(person)
+ .setConversationTitle("Bubble Chat")
+ .addMessage("Hello?",
+ SystemClock.currentThreadTimeMillis() - 300000, person)
+ .addMessage("Is it me you're looking for?",
+ SystemClock.currentThreadTimeMillis(), person)
+ )
+ .setActions(replyAction)
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setGroupSummary(isSummary);
+ if (groupKey != null) {
+ nb.setGroup(groupKey);
+ }
+ if (addBubbleMetadata) {
+ nb.setBubbleMetadata(getBubbleMetadataBuilder().build());
+ }
+ return nb;
+ }
+
private NotificationRecord addGroupWithBubblesAndValidateAdded(boolean summaryAutoCancel)
throws RemoteException {
- // Notification that has bubble metadata
- NotificationRecord nrBubble = generateNotificationRecord(mTestNotificationChannel, 1,
- "BUBBLE_GROUP", false /* isSummary */, true /* isBubble */);
+ String groupKey = "BUBBLE_GROUP";
- // Make the package foreground so that we're allowed to be a bubble
- when(mActivityManager.getPackageImportance(nrBubble.sbn.getPackageName())).thenReturn(
- IMPORTANCE_FOREGROUND);
+ // Notification that has bubble metadata
+ NotificationRecord nrBubble = generateMessageBubbleNotifRecord(true /* addMetadata */,
+ mTestNotificationChannel, 1 /* id */, "tag", groupKey, false /* isSummary */);
mBinderService.enqueueNotificationWithTag(PKG, PKG, nrBubble.sbn.getTag(),
nrBubble.sbn.getId(), nrBubble.sbn.getNotification(), nrBubble.sbn.getUserId());
@@ -637,9 +673,10 @@
assertEquals(1, notifsAfter.length);
assertTrue((notifsAfter[0].getNotification().flags & FLAG_BUBBLE) != 0);
- // Plain notification without bubble metadata
- NotificationRecord nrPlain = generateNotificationRecord(mTestNotificationChannel, 2,
- "BUBBLE_GROUP", false /* isSummary */, false /* isBubble */);
+ // Notification without bubble metadata
+ NotificationRecord nrPlain = generateMessageBubbleNotifRecord(false /* addMetadata */,
+ mTestNotificationChannel, 2 /* id */, "tag", groupKey, false /* isSummary */);
+
mBinderService.enqueueNotificationWithTag(PKG, PKG, nrPlain.sbn.getTag(),
nrPlain.sbn.getId(), nrPlain.sbn.getNotification(), nrPlain.sbn.getUserId());
waitForIdle();
@@ -648,8 +685,9 @@
assertEquals(2, notifsAfter.length);
// Summary notification for both of those
- NotificationRecord nrSummary = generateNotificationRecord(mTestNotificationChannel, 3,
- "BUBBLE_GROUP", true /* isSummary */, false /* isBubble */);
+ NotificationRecord nrSummary = generateMessageBubbleNotifRecord(false /* addMetadata */,
+ mTestNotificationChannel, 3 /* id */, "tag", groupKey, true /* isSummary */);
+
if (summaryAutoCancel) {
nrSummary.getNotification().flags |= FLAG_AUTO_CANCEL;
}
@@ -4708,13 +4746,8 @@
// Bubbles are allowed!
setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
- // Notif with bubble metadata but not our other misc requirements
- NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
- null /* tvExtender */, true /* isBubble */);
-
- // Say we're foreground
- when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
- IMPORTANCE_FOREGROUND);
+ NotificationRecord nr =
+ generateMessageBubbleNotifRecord(mTestNotificationChannel, "testFlagBubble");
mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
@@ -4732,13 +4765,8 @@
// Bubbles are allowed!
setUpPrefsForBubbles(true /* global */, false /* app */, true /* channel */);
- // Notif with bubble metadata but not our other misc requirements
- NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
- null /* tvExtender */, true /* isBubble */);
-
- // Say we're foreground
- when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
- IMPORTANCE_FOREGROUND);
+ NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
+ "testFlagBubble_noFlag_appNotAllowed");
mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
@@ -4752,174 +4780,40 @@
}
@Test
- public void testFlagBubbleNotifs_flag_appForeground() throws RemoteException {
+ public void testFlagBubbleNotifs_noFlag_whenAppForeground() throws RemoteException {
// Bubbles are allowed!
setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
// Notif with bubble metadata but not our other misc requirements
- NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
- null /* tvExtender */, true /* isBubble */);
+ Notification.Builder nb = new Notification.Builder(mContext,
+ mTestNotificationChannel.getId())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setBubbleMetadata(getBubbleMetadataBuilder().build());
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, "tag", mUid, 0,
+ nb.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
// Say we're foreground
when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
IMPORTANCE_FOREGROUND);
-
- mBinderService.enqueueNotificationWithTag(PKG, PKG,
- nr.sbn.getTag(), nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
- waitForIdle();
-
- // yes allowed, yes foreground, yes bubble
- assertTrue(mService.getNotificationRecord(
- nr.sbn.getKey()).getNotification().isBubbleNotification());
- }
-
- @Test
- public void testFlagBubbleNotifs_noFlag_appNotForeground() throws RemoteException {
- // Bubbles are allowed!
- setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
-
- // Notif with bubble metadata but not our other misc requirements
- NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
- null /* tvExtender */, true /* isBubble */);
-
- // Make sure we're NOT foreground
- when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
- IMPORTANCE_VISIBLE);
-
mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
waitForIdle();
- // yes allowed but NOT foreground, no bubble
+ // if notif isn't configured properly it doesn't get to bubble just because app is
+ // foreground.
assertFalse(mService.getNotificationRecord(
nr.sbn.getKey()).getNotification().isBubbleNotification());
}
@Test
- public void testFlagBubbleNotifs_flag_previousForegroundFlag() throws RemoteException {
- // Bubbles are allowed!
- setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
-
- // Notif with bubble metadata but not our other misc requirements
- NotificationRecord nr1 = generateNotificationRecord(mTestNotificationChannel,
- null /* tvExtender */, true /* isBubble */);
-
- // Send notif when we're foreground
- when(mActivityManager.getPackageImportance(nr1.sbn.getPackageName())).thenReturn(
- IMPORTANCE_FOREGROUND);
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nr1.sbn.getTag(),
- nr1.sbn.getId(), nr1.sbn.getNotification(), nr1.sbn.getUserId());
- waitForIdle();
-
- // yes allowed, yes foreground, yes bubble
- assertTrue(mService.getNotificationRecord(
- nr1.sbn.getKey()).getNotification().isBubbleNotification());
-
- // Send a new update when we're not foreground
- NotificationRecord nr2 = generateNotificationRecord(mTestNotificationChannel,
- null /* tvExtender */, true /* isBubble */);
-
- when(mActivityManager.getPackageImportance(nr2.sbn.getPackageName())).thenReturn(
- IMPORTANCE_VISIBLE);
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nr2.sbn.getTag(),
- nr2.sbn.getId(), nr2.sbn.getNotification(), nr2.sbn.getUserId());
- waitForIdle();
-
- // yes allowed, previously foreground / flagged, yes bubble
- assertTrue(mService.getNotificationRecord(
- nr2.sbn.getKey()).getNotification().isBubbleNotification());
-
- StatusBarNotification[] notifs2 = mBinderService.getActiveNotifications(PKG);
- assertEquals(1, notifs2.length);
- assertEquals(1, mService.getNotificationRecordCount());
- }
-
- @Test
- public void testFlagBubbleNotifs_noFlag_previousForegroundFlag_afterRemoval()
- throws RemoteException {
- // Bubbles are allowed!
- setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
-
- // Notif with bubble metadata but not our other misc requirements
- NotificationRecord nr1 = generateNotificationRecord(mTestNotificationChannel,
- null /* tvExtender */, true /* isBubble */);
-
- // Send notif when we're foreground
- when(mActivityManager.getPackageImportance(nr1.sbn.getPackageName())).thenReturn(
- IMPORTANCE_FOREGROUND);
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nr1.sbn.getTag(),
- nr1.sbn.getId(), nr1.sbn.getNotification(), nr1.sbn.getUserId());
- waitForIdle();
-
- // yes allowed, yes foreground, yes bubble
- assertTrue(mService.getNotificationRecord(
- nr1.sbn.getKey()).getNotification().isBubbleNotification());
-
- // Remove the bubble
- mBinderService.cancelNotificationWithTag(PKG, PKG, nr1.sbn.getTag(), nr1.sbn.getId(),
- nr1.sbn.getUserId());
- waitForIdle();
-
- StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
- assertEquals(0, notifs.length);
- assertEquals(0, mService.getNotificationRecordCount());
-
- // Send a new update when we're not foreground
- NotificationRecord nr2 = generateNotificationRecord(mTestNotificationChannel,
- null /* tvExtender */, true /* isBubble */);
-
- when(mActivityManager.getPackageImportance(nr2.sbn.getPackageName())).thenReturn(
- IMPORTANCE_VISIBLE);
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nr2.sbn.getTag(),
- nr2.sbn.getId(), nr2.sbn.getNotification(), nr2.sbn.getUserId());
- waitForIdle();
-
- // yes allowed, but was removed & no foreground, so no bubble
- assertFalse(mService.getNotificationRecord(
- nr2.sbn.getKey()).getNotification().isBubbleNotification());
-
- StatusBarNotification[] notifs2 = mBinderService.getActiveNotifications(PKG);
- assertEquals(1, notifs2.length);
- assertEquals(1, mService.getNotificationRecordCount());
- }
-
- @Test
public void testFlagBubbleNotifs_flag_messaging() throws RemoteException {
// Bubbles are allowed!
setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
- // Give it bubble metadata
- Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
- // Give it a person
- Person person = new Person.Builder()
- .setName("bubblebot")
- .build();
- // It needs remote input to be bubble-able
- RemoteInput remoteInput = new RemoteInput.Builder("reply_key").setLabel("reply").build();
- PendingIntent inputIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
- Icon icon = Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
- Notification.Action replyAction = new Notification.Action.Builder(icon, "Reply",
- inputIntent).addRemoteInput(remoteInput)
- .build();
- // Make it messaging style
- Notification.Builder nb = new Notification.Builder(mContext,
- mTestNotificationChannel.getId())
- .setContentTitle("foo")
- .setBubbleMetadata(data)
- .setStyle(new Notification.MessagingStyle(person)
- .setConversationTitle("Bubble Chat")
- .addMessage("Hello?",
- SystemClock.currentThreadTimeMillis() - 300000, person)
- .addMessage("Is it me you're looking for?",
- SystemClock.currentThreadTimeMillis(), person)
- )
- .setActions(replyAction)
- .setSmallIcon(android.R.drawable.sym_def_app_icon);
-
- StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
- "testFlagBubbleNotifs_flag_messaging", mUid, 0,
- nb.build(), new UserHandle(mUid), null, 0);
- NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+ NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
+ "testFlagBubbleNotifs_flag_messaging");
mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
@@ -4927,7 +4821,7 @@
// yes allowed, yes messaging, yes bubble
assertTrue(mService.getNotificationRecord(
- sbn.getKey()).getNotification().isBubbleNotification());
+ nr.sbn.getKey()).getNotification().isBubbleNotification());
}
@Test
@@ -4936,7 +4830,7 @@
setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
// Give it bubble metadata
- Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
+ Notification.BubbleMetadata data = getBubbleMetadataBuilder().build();
// Give it a person
Person person = new Person.Builder()
.setName("bubblebot")
@@ -4972,7 +4866,7 @@
setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
// Give it bubble metadata
- Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
+ Notification.BubbleMetadata data = getBubbleMetadataBuilder().build();
// Give it a person
Person person = new Person.Builder()
.setName("bubblebot")
@@ -5005,7 +4899,7 @@
setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
// Give it bubble metadata
- Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
+ Notification.BubbleMetadata data = getBubbleMetadataBuilder().build();
// Make it a phone call
Notification.Builder nb = new Notification.Builder(mContext,
mTestNotificationChannel.getId())
@@ -5036,7 +4930,7 @@
setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
// Give it bubble metadata
- Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
+ Notification.BubbleMetadata data = getBubbleMetadataBuilder().build();
// Give it a person
Person person = new Person.Builder()
.setName("bubblebot")
@@ -5070,30 +4964,8 @@
// Bubbles are NOT allowed!
setUpPrefsForBubbles(false /* global */, true /* app */, true /* channel */);
- // Give it bubble metadata
- Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
- // Give it a person
- Person person = new Person.Builder()
- .setName("bubblebot")
- .build();
- // Make it messaging style
- Notification.Builder nb = new Notification.Builder(mContext,
- mTestNotificationChannel.getId())
- .setContentTitle("foo")
- .setBubbleMetadata(data)
- .setStyle(new Notification.MessagingStyle(person)
- .setConversationTitle("Bubble Chat")
- .addMessage("Hello?",
- SystemClock.currentThreadTimeMillis() - 300000, person)
- .addMessage("Is it me you're looking for?",
- SystemClock.currentThreadTimeMillis(), person)
- )
- .setSmallIcon(android.R.drawable.sym_def_app_icon);
-
- StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
- "testFlagBubbleNotifs_noFlag_messaging_appNotAllowed", mUid, 0,
- nb.build(), new UserHandle(mUid), null, 0);
- NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+ NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
+ "testFlagBubbleNotifs_noFlag_messaging_appNotAllowed");
// Post the notification
mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
@@ -5102,7 +4974,7 @@
// not allowed, no bubble
assertFalse(mService.getNotificationRecord(
- sbn.getKey()).getNotification().isBubbleNotification());
+ nr.sbn.getKey()).getNotification().isBubbleNotification());
}
@Test
@@ -5110,8 +4982,14 @@
// Bubbles are allowed!
setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
- // Notif WITHOUT bubble metadata
- NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel);
+ // Messaging notif WITHOUT bubble metadata
+ Notification.Builder nb = getMessageStyleNotifBuilder(false /* addBubbleMetadata */,
+ null /* groupKey */, false /* isSummary */);
+
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
+ "testFlagBubbleNotifs_noFlag_notBubble", mUid, 0,
+ nb.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
// Post the notification
mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
@@ -5128,39 +5006,17 @@
// Bubbles are allowed except on this channel
setUpPrefsForBubbles(true /* global */, true /* app */, false /* channel */);
- // Give it bubble metadata
- Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
- // Give it a person
- Person person = new Person.Builder()
- .setName("bubblebot")
- .build();
- // Make it messaging style
- Notification.Builder nb = new Notification.Builder(mContext,
- mTestNotificationChannel.getId())
- .setContentTitle("foo")
- .setBubbleMetadata(data)
- .setStyle(new Notification.MessagingStyle(person)
- .setConversationTitle("Bubble Chat")
- .addMessage("Hello?",
- SystemClock.currentThreadTimeMillis() - 300000, person)
- .addMessage("Is it me you're looking for?",
- SystemClock.currentThreadTimeMillis(), person)
- )
- .setSmallIcon(android.R.drawable.sym_def_app_icon);
-
- StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
- "testFlagBubbleNotifs_noFlag_messaging_channelNotAllowed", mUid, 0,
- nb.build(), new UserHandle(mUid), null, 0);
- NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+ NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
+ "testFlagBubbleNotifs_noFlag_messaging_channelNotAllowed");
// Post the notification
- mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
waitForIdle();
// channel not allowed, no bubble
assertFalse(mService.getNotificationRecord(
- sbn.getKey()).getNotification().isBubbleNotification());
+ nr.sbn.getKey()).getNotification().isBubbleNotification());
}
@Test
@@ -5169,7 +5025,7 @@
setUpPrefsForBubbles(false /* global */, true /* app */, true /* channel */);
// Give it bubble metadata
- Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
+ Notification.BubbleMetadata data = getBubbleMetadataBuilder().build();
// Give it a person
Person person = new Person.Builder()
.setName("bubblebot")
@@ -5205,7 +5061,7 @@
setUpPrefsForBubbles(true /* global */, true /* app */, false /* channel */);
// Give it bubble metadata
- Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
+ Notification.BubbleMetadata data = getBubbleMetadataBuilder().build();
// Give it a person
Person person = new Person.Builder()
.setName("bubblebot")
@@ -5412,13 +5268,9 @@
// Bubbles are allowed!
setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
- // Notif with bubble metadata but not our other misc requirements
- NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
- null /* tvExtender */, true /* isBubble */);
-
- // Say we're foreground
- when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
- IMPORTANCE_FOREGROUND);
+ // Notif with bubble metadata
+ NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
+ "testNotificationBubbleChanged_false");
mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
@@ -5447,9 +5299,9 @@
// Bubbles are allowed!
setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
- // Plain notification that has bubble metadata
+ // Notif that is not a bubble
NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
- null /* tvExtender */, true /* isBubble */);
+ 1, null, false);
mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
waitForIdle();
@@ -5459,9 +5311,12 @@
assertEquals(1, notifsBefore.length);
assertEquals((notifsBefore[0].getNotification().flags & FLAG_BUBBLE), 0);
- // Make the package foreground so that we're allowed to be a bubble
- when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
- IMPORTANCE_FOREGROUND);
+ // Update the notification to be message style / meet bubble requirements
+ NotificationRecord nr2 = generateMessageBubbleNotifRecord(mTestNotificationChannel,
+ nr.sbn.getTag());
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nr2.sbn.getTag(),
+ nr2.sbn.getId(), nr2.sbn.getNotification(), nr2.sbn.getUserId());
+ waitForIdle();
// Reset as this is called when the notif is first sent
reset(mListeners);
@@ -5482,8 +5337,7 @@
setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
// Notif that is not a bubble
- NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
- null /* tvExtender */, true /* isBubble */);
+ NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel);
mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
waitForIdle();
@@ -5681,26 +5535,17 @@
// Bubbles are allowed!
setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
- // Plain notification that has bubble metadata
- NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
- null /* tvExtender */, true /* isBubble */);
+ // And we are low ram
+ when(mActivityManager.isLowRamDevice()).thenReturn(true);
+
+ // Notification that would typically bubble
+ NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
+ "testNotificationBubbles_disabled_lowRamDevice");
mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
waitForIdle();
- // Would be a normal notification because wouldn't have met requirements to bubble
- StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG);
- assertEquals(1, notifsBefore.length);
- assertEquals((notifsBefore[0].getNotification().flags & FLAG_BUBBLE), 0);
-
- // Make the package foreground so that we're allowed to be a bubble
- when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
- IMPORTANCE_FOREGROUND);
-
- // And we are low ram
- when(mActivityManager.isLowRamDevice()).thenReturn(true);
-
- // We wouldn't be a bubble because the notification didn't meet requirements (low ram)
+ // But we wouldn't be a bubble because the device is low ram & all bubbles are disabled.
StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG);
assertEquals(1, notifsAfter.length);
assertEquals((notifsAfter[0].getNotification().flags & FLAG_BUBBLE), 0);
@@ -5774,50 +5619,23 @@
// Bubbles are allowed!
setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
- // Give it bubble metadata
- Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder()
- .setSuppressNotification(true)
- .setAutoExpandBubble(true).build();
- // Give it a person
- Person person = new Person.Builder()
- .setName("bubblebot")
- .build();
- // It needs remote input to be bubble-able
- RemoteInput remoteInput = new RemoteInput.Builder("reply_key").setLabel("reply").build();
- PendingIntent inputIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
- Icon icon = Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
- Notification.Action replyAction = new Notification.Action.Builder(icon, "Reply",
- inputIntent).addRemoteInput(remoteInput)
- .build();
- // Make it messaging style
- Notification.Builder nb = new Notification.Builder(mContext,
- mTestNotificationChannel.getId())
- .setContentTitle("foo")
- .setBubbleMetadata(data)
- .setStyle(new Notification.MessagingStyle(person)
- .setConversationTitle("Bubble Chat")
- .addMessage("Hello?",
- SystemClock.currentThreadTimeMillis() - 300000, person)
- .addMessage("Is it me you're looking for?",
- SystemClock.currentThreadTimeMillis(), person)
- )
- .setActions(replyAction)
- .setSmallIcon(android.R.drawable.sym_def_app_icon);
-
- StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0,
- nb.build(), new UserHandle(mUid), null, 0);
- NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+ NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
+ "testNotificationBubbles_flagAutoExpandForeground_fails_notForeground");
+ // Modify metadata flags
+ nr.sbn.getNotification().getBubbleMetadata().setFlags(
+ Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE
+ | Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION);
// Ensure we're not foreground
when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
IMPORTANCE_VISIBLE);
- mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
waitForIdle();
// yes allowed, yes messaging, yes bubble
- Notification notif = mService.getNotificationRecord(sbn.getKey()).getNotification();
+ Notification notif = mService.getNotificationRecord(nr.sbn.getKey()).getNotification();
assertTrue(notif.isBubbleNotification());
// Our flags should have failed since we're not foreground
@@ -5831,53 +5649,26 @@
// Bubbles are allowed!
setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
- // Give it bubble metadata
- Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder()
- .setSuppressNotification(true)
- .setAutoExpandBubble(true).build();
- // Give it a person
- Person person = new Person.Builder()
- .setName("bubblebot")
- .build();
- // It needs remote input to be bubble-able
- RemoteInput remoteInput = new RemoteInput.Builder("reply_key").setLabel("reply").build();
- PendingIntent inputIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
- Icon icon = Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
- Notification.Action replyAction = new Notification.Action.Builder(icon, "Reply",
- inputIntent).addRemoteInput(remoteInput)
- .build();
- // Make it messaging style
- Notification.Builder nb = new Notification.Builder(mContext,
- mTestNotificationChannel.getId())
- .setContentTitle("foo")
- .setBubbleMetadata(data)
- .setStyle(new Notification.MessagingStyle(person)
- .setConversationTitle("Bubble Chat")
- .addMessage("Hello?",
- SystemClock.currentThreadTimeMillis() - 300000, person)
- .addMessage("Is it me you're looking for?",
- SystemClock.currentThreadTimeMillis(), person)
- )
- .setActions(replyAction)
- .setSmallIcon(android.R.drawable.sym_def_app_icon);
-
- StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0,
- nb.build(), new UserHandle(mUid), null, 0);
- NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+ NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
+ "testNotificationBubbles_flagAutoExpandForeground_succeeds_foreground");
+ // Modify metadata flags
+ nr.sbn.getNotification().getBubbleMetadata().setFlags(
+ Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE
+ | Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION);
// Ensure we are in the foreground
when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
IMPORTANCE_FOREGROUND);
- mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
waitForIdle();
// yes allowed, yes messaging, yes bubble
- Notification notif = mService.getNotificationRecord(sbn.getKey()).getNotification();
+ Notification notif = mService.getNotificationRecord(nr.sbn.getKey()).getNotification();
assertTrue(notif.isBubbleNotification());
- // Our flags should have failed since we are foreground
+ // Our flags should have passed since we are foreground
assertTrue(notif.getBubbleMetadata().getAutoExpandBubble());
assertTrue(notif.getBubbleMetadata().isNotificationSuppressed());
}
@@ -6004,7 +5795,7 @@
@Test
public void testNotificationHistory_addNoisyNotification() throws Exception {
NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
- null /* tvExtender */, false);
+ null /* tvExtender */);
mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
waitForIdle();
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 d6bd1d0..73b2f6b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -902,7 +902,7 @@
topActivity.mVisibleRequested = false;
topActivity.nowVisible = false;
topActivity.finishing = true;
- topActivity.setState(PAUSED, "true");
+ topActivity.setState(STOPPED, "true");
// Mark the bottom activity as not visible, so that we would wait for it before removing
// the top one.
mActivity.mVisibleRequested = false;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index 4e4020b..a5157fe9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -574,6 +574,27 @@
}
@Test
+ public void testShouldBeVisible_FullscreenBehindTranslucentInHomeStack() {
+ final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
+
+ final ActivityRecord firstActivity = new ActivityBuilder(mService)
+ .setStack(homeStack)
+ .setCreateTask(true)
+ .build();
+ final Task task = firstActivity.getTask();
+ final ActivityRecord secondActivity = new ActivityBuilder(mService)
+ .setTask(task)
+ .build();
+
+ doReturn(false).when(secondActivity).occludesParent();
+ homeStack.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
+ false /* preserveWindows */);
+
+ assertTrue(firstActivity.shouldBeVisible());
+ }
+
+ @Test
public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindFullscreen() {
mDefaultDisplay.removeStack(mStack);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index b1132f6..9e54f40 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -33,8 +33,10 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
+import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.clearInvocations;
@@ -433,32 +435,28 @@
}
/**
- * This test ensures that if the intent is being delivered to a
+ * This test ensures that if the intent is being delivered to a split-screen unfocused task
+ * while it already on top, reports it as delivering to top.
*/
@Test
public void testSplitScreenDeliverToTop() {
- final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-
- final ActivityRecord focusActivity = new ActivityBuilder(mService)
- .setCreateTask(true)
- .build();
-
- focusActivity.getActivityStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
-
- final ActivityRecord reusableActivity = new ActivityBuilder(mService)
- .setCreateTask(true)
- .build();
-
- // Create reusable activity after entering split-screen so that it is the top secondary
- // stack.
- reusableActivity.getActivityStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ final ActivityStarter starter = prepareStarter(
+ FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | FLAG_ACTIVITY_SINGLE_TOP, false);
+ final ActivityRecord splitPrimaryFocusActivity =
+ new ActivityBuilder(mService).setCreateTask(true).build();
+ final ActivityRecord splitSecondReusableActivity =
+ new ActivityBuilder(mService).setCreateTask(true).build();
+ splitPrimaryFocusActivity.getActivityStack()
+ .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ splitSecondReusableActivity.getActivityStack()
+ .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
// Set focus back to primary.
- final ActivityStack focusStack = focusActivity.getActivityStack();
- focusStack.moveToFront("testSplitScreenDeliverToTop");
+ splitPrimaryFocusActivity.getActivityStack().moveToFront("testSplitScreenDeliverToTop");
- doReturn(reusableActivity).when(mRootWindowContainer).findTask(any(), anyInt());
-
+ // Start activity and delivered new intent.
+ starter.getIntent().setComponent(splitSecondReusableActivity.mActivityComponent);
+ doReturn(splitSecondReusableActivity).when(mRootWindowContainer).findTask(any(), anyInt());
final int result = starter.setReason("testSplitScreenDeliverToTop").execute();
// Ensure result is delivering intent to top.
@@ -471,26 +469,30 @@
*/
@Test
public void testSplitScreenTaskToFront() {
- final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ final ActivityStarter starter = prepareStarter(
+ FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | FLAG_ACTIVITY_SINGLE_TOP, false);
+ final ActivityRecord splitSecondReusableActivity =
+ new ActivityBuilder(mService).setCreateTask(true).build();
+ final ActivityRecord splitSecondTopActivity =
+ new ActivityBuilder(mService).setCreateTask(true).build();
+ final ActivityRecord splitPrimaryFocusActivity =
+ new ActivityBuilder(mService).setCreateTask(true).build();
+ splitPrimaryFocusActivity.getActivityStack()
+ .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ splitSecondReusableActivity.getActivityStack()
+ .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ splitSecondTopActivity.getActivityStack()
+ .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
- // Create reusable activity here first. Setting the windowing mode of the primary stack
- // will move the existing standard full screen stack to secondary, putting this one on the
- // bottom.
- final ActivityRecord reusableActivity = new ActivityBuilder(mService)
- .setCreateTask(true)
- .build();
+ // Make it on top of split-screen-secondary.
+ splitSecondTopActivity.getActivityStack().moveToFront("testSplitScreenTaskToFront");
- reusableActivity.getActivityStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ // Let primary stack has focus.
+ splitPrimaryFocusActivity.getActivityStack().moveToFront("testSplitScreenTaskToFront");
- final ActivityRecord focusActivity = new ActivityBuilder(mService)
- .setCreateTask(true)
- .build();
-
- // Enter split-screen. Primary stack should have focus.
- focusActivity.getActivityStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
-
- doReturn(reusableActivity).when(mRootWindowContainer).findTask(any(), anyInt());
-
+ // Start activity and delivered new intent.
+ starter.getIntent().setComponent(splitSecondReusableActivity.mActivityComponent);
+ doReturn(splitSecondReusableActivity).when(mRootWindowContainer).findTask(any(), anyInt());
final int result = starter.setReason("testSplitScreenMoveToFront").execute();
// Ensure result is moving task to front.
@@ -979,4 +981,29 @@
assertThat(result == START_SUCCESS).isTrue();
assertThat(starter.mAddingToTask).isTrue();
}
+
+ @Test
+ public void testTargetStackInSplitScreen() {
+ final ActivityStarter starter =
+ prepareStarter(FLAG_ACTIVITY_LAUNCH_ADJACENT, false /* mockGetLaunchStack */);
+ final ActivityRecord top = new ActivityBuilder(mService).setCreateTask(true).build();
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ final ActivityRecord[] outActivity = new ActivityRecord[1];
+
+ // Activity must not land on split-screen stack if currently not in split-screen mode.
+ starter.setActivityOptions(options.toBundle())
+ .setReason("testWindowingModeOptionsLaunchAdjacent")
+ .setOutActivity(outActivity).execute();
+ assertThat(outActivity[0].inSplitScreenWindowingMode()).isFalse();
+
+ // Move activity to split-screen-primary stack and make sure it has the focus.
+ top.getActivityStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ top.getActivityStack().moveToFront("testWindowingModeOptionsLaunchAdjacent");
+
+ // Activity must landed on split-screen-secondary when launch adjacent.
+ starter.setActivityOptions(options.toBundle())
+ .setReason("testWindowingModeOptionsLaunchAdjacent")
+ .setOutActivity(outActivity).execute();
+ assertThat(outActivity[0].inSplitScreenSecondaryWindowingMode()).isTrue();
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java b/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java
index a39be56..574517a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java
@@ -28,11 +28,11 @@
import android.platform.test.annotations.Presubmit;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -44,6 +44,7 @@
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class AnimatingActivityRegistryTest extends WindowTestsBase {
@Mock
@@ -60,7 +61,6 @@
}
@Test
- @FlakyTest(bugId = 144611135)
public void testDeferring() {
final ActivityRecord activity1 = createActivityRecord(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
@@ -83,7 +83,6 @@
}
@Test
- @FlakyTest(bugId = 131005232)
public void testContainerRemoved() {
final ActivityRecord window1 = createActivityRecord(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 73dd2df..f2ba97c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -57,6 +57,8 @@
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -86,6 +88,7 @@
import android.view.MotionEvent;
import android.view.Surface;
import android.view.ViewRootImpl;
+import android.view.WindowManager;
import android.view.test.InsetsModeSession;
import androidx.test.filters.SmallTest;
@@ -561,8 +564,7 @@
final DisplayContent dc = createNewDisplay();
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
- dc.setLayoutNeeded();
- dc.performLayout(true /* initial */, false /* updateImeWindows */);
+ performLayout(dc);
assertThat(win.mLayoutSeq, is(dc.mLayoutSeq));
}
@@ -829,8 +831,7 @@
win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
win.setSystemGestureExclusion(Collections.singletonList(new Rect(10, 20, 30, 40)));
- dc.setLayoutNeeded();
- dc.performLayout(true /* initial */, false /* updateImeWindows */);
+ performLayout(dc);
win.setHasSurface(true);
dc.updateSystemGestureExclusion();
@@ -866,8 +867,7 @@
win2.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
win2.setSystemGestureExclusion(Collections.singletonList(new Rect(20, 30, 40, 50)));
- dc.setLayoutNeeded();
- dc.performLayout(true /* initial */, false /* updateImeWindows */);
+ performLayout(dc);
win.setHasSurface(true);
win2.setHasSurface(true);
@@ -898,8 +898,7 @@
win2.getAttrs().height = 10;
win2.setSystemGestureExclusion(Collections.emptyList());
- dc.setLayoutNeeded();
- dc.performLayout(true /* initial */, false /* updateImeWindows */);
+ performLayout(dc);
win.setHasSurface(true);
win2.setHasSurface(true);
@@ -922,8 +921,7 @@
| SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
win.mActivityRecord.mTargetSdk = P;
- dc.setLayoutNeeded();
- dc.performLayout(true /* initial */, false /* updateImeWindows */);
+ performLayout(dc);
win.setHasSurface(true);
@@ -935,6 +933,22 @@
}
@Test
+ public void testRequestResizeForEmptyFrames() {
+ final WindowState win = mChildAppWindowAbove;
+ makeWindowVisible(win, win.getParentWindow());
+ win.setRequestedSize(mDisplayContent.mBaseDisplayWidth, 0 /* height */);
+ win.mAttrs.width = win.mAttrs.height = WindowManager.LayoutParams.WRAP_CONTENT;
+ win.mAttrs.gravity = Gravity.CENTER;
+ performLayout(mDisplayContent);
+
+ // The frame is empty because the requested height is zero.
+ assertTrue(win.getFrameLw().isEmpty());
+ // The window should be scheduled to resize then the client may report a new non-empty size.
+ win.updateResizingWindowIfNeeded();
+ assertThat(mWm.mResizingWindows).contains(win);
+ }
+
+ @Test
public void testOrientationChangeLogging() {
MetricsLogger mockLogger = mock(MetricsLogger.class);
Configuration oldConfig = new Configuration();
@@ -1011,6 +1025,11 @@
mWm.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateInputWindows */);
}
+ private void performLayout(DisplayContent dc) {
+ dc.setLayoutNeeded();
+ dc.performLayout(true /* initial */, false /* updateImeWindows */);
+ }
+
/**
* Create DisplayContent that does not update display base/initial values from device to keep
* the values set by test.
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 5aece45..0527561 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -594,6 +594,8 @@
@Test
public void layoutWindowLw_withForwardInset_SoftInputAdjustResize() {
+ assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_NONE);
+
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.softInputMode = SOFT_INPUT_ADJUST_RESIZE;
diff --git a/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java b/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java
index 103c3ab..f007149 100644
--- a/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java
@@ -24,12 +24,13 @@
import android.platform.test.annotations.Presubmit;
import android.view.RemoteAnimationAdapter;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
+import com.android.server.AnimationThread;
import com.android.server.testutils.OffsettableClock;
import com.android.server.testutils.TestHandler;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
@@ -41,7 +42,7 @@
*/
@SmallTest
@Presubmit
-public class PendingRemoteAnimationRegistryTest extends ActivityTestsBase {
+public class PendingRemoteAnimationRegistryTest {
@Mock RemoteAnimationAdapter mAdapter;
private PendingRemoteAnimationRegistry mRegistry;
@@ -51,10 +52,15 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mService.mH.runWithScissors(() -> {
+ AnimationThread.getHandler().runWithScissors(() -> {
mHandler = new TestHandler(null, mClock);
}, 0);
- mRegistry = new PendingRemoteAnimationRegistry(mService, mHandler);
+ mRegistry = new PendingRemoteAnimationRegistry(new WindowManagerGlobalLock(), mHandler);
+ }
+
+ @After
+ public void teadDown() {
+ AnimationThread.dispose();
}
@Test
@@ -74,7 +80,6 @@
}
@Test
- @FlakyTest(bugId = 131005232)
public void testTimeout() {
mRegistry.addPendingAnimation("com.android.test", mAdapter);
mClock.fastForward(5000);
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 e27c724..e507508 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -408,6 +408,16 @@
}
}
+ /**
+ * Throws if caller doesn't hold the given lock.
+ * @param lock the lock
+ */
+ static void checkHoldsLock(Object lock) {
+ if (!Thread.holdsLock(lock)) {
+ throw new IllegalStateException("Caller doesn't hold global lock.");
+ }
+ }
+
protected class TestActivityTaskManagerService extends ActivityTaskManagerService {
// ActivityStackSupervisor may be created more than once while setting up AMS and ATMS.
// We keep the reference in order to prevent creating it twice.
@@ -484,8 +494,8 @@
spyOn(this);
// Do not schedule idle that may touch methods outside the scope of the test.
- doNothing().when(this).scheduleIdleLocked();
- doNothing().when(this).scheduleIdleTimeoutLocked(any());
+ doNothing().when(this).scheduleIdle();
+ doNothing().when(this).scheduleIdleTimeout(any());
// unit test version does not handle launch wake lock
doNothing().when(this).acquireLaunchWakelock();
doReturn(mock(KeyguardController.class)).when(this).getKeyguardController();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
index 8e32dad..ca84932 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
@@ -84,7 +84,7 @@
mTarget.finishTaskPositioning();
// Wait until the looper processes finishTaskPositioning.
- assertTrue(waitHandlerIdle(mWm.mH, TIMEOUT_MS));
+ assertTrue(waitHandlerIdle(mWm.mAnimationHandler, TIMEOUT_MS));
assertFalse(mTarget.isPositioningLocked());
assertNull(mTarget.getDragWindowHandleLocked());
@@ -103,7 +103,7 @@
mTarget.finishTaskPositioning(mWindow.mClient);
// Wait until the looper processes finishTaskPositioning.
- assertTrue(waitHandlerIdle(mWm.mH, TIMEOUT_MS));
+ assertTrue(waitHandlerIdle(mWm.mAnimationHandler, TIMEOUT_MS));
assertFalse(mTarget.isPositioningLocked());
assertNull(mTarget.getDragWindowHandleLocked());
@@ -119,7 +119,7 @@
assertNotNull(mWindow.getTask().getTopVisibleAppMainWindow());
mTarget.handleTapOutsideTask(content, 0, 0);
- // Wait until the looper processes finishTaskPositioning.
+ // Wait until the looper processes handleTapOutsideTask.
assertTrue(waitHandlerIdle(mWm.mH, TIMEOUT_MS));
assertTrue(mTarget.isPositioningLocked());
@@ -127,7 +127,7 @@
mTarget.finishTaskPositioning();
// Wait until the looper processes finishTaskPositioning.
- assertTrue(waitHandlerIdle(mWm.mH, TIMEOUT_MS));
+ assertTrue(waitHandlerIdle(mWm.mAnimationHandler, TIMEOUT_MS));
assertFalse(mTarget.isPositioningLocked());
assertNull(mTarget.getDragWindowHandleLocked());
@@ -145,7 +145,7 @@
mWindow.getTask().setResizeMode(RESIZE_MODE_UNRESIZEABLE);
mTarget.handleTapOutsideTask(content, 0, 0);
- // Wait until the looper processes finishTaskPositioning.
+ // Wait until the looper processes handleTapOutsideTask.
assertTrue(waitHandlerIdle(mWm.mH, TIMEOUT_MS));
assertFalse(mTarget.isPositioningLocked());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index e5121b9..77af5ee 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -72,7 +72,7 @@
private final DisplayInfo mInfo;
private boolean mCanRotate = true;
private int mWindowingMode = WINDOWING_MODE_FULLSCREEN;
- private int mPosition = POSITION_TOP;
+ private int mPosition = POSITION_BOTTOM;
private final ActivityTaskManagerService mService;
private boolean mSystemDecorations = false;
@@ -127,13 +127,13 @@
return this;
}
TestDisplayContent build() {
+ SystemServicesTestRule.checkHoldsLock(mService.mGlobalLock);
+
final int displayId = SystemServicesTestRule.sNextDisplayId++;
final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
mInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
- final TestDisplayContent newDisplay;
- synchronized (mService.mGlobalLock) {
- newDisplay = new TestDisplayContent(mService.mStackSupervisor, display);
- }
+ final TestDisplayContent newDisplay =
+ new TestDisplayContent(mService.mStackSupervisor, display);
// disable the normal system decorations
final DisplayPolicy displayPolicy = newDisplay.mDisplayContent.getDisplayPolicy();
spyOn(displayPolicy);
@@ -153,6 +153,10 @@
doReturn(false).when(newDisplay.mDisplayContent)
.handlesOrientationChangeFromDescendant();
}
+ // Please add stubbing before this line. Services will start using this display in other
+ // threads immediately after adding it to hierarchy. Calling doAnswer() type of stubbing
+ // reduces chance of races, but still doesn't eliminate race conditions.
+ mService.mRootWindowContainer.addChild(newDisplay, mPosition);
return newDisplay;
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 634d2f0..08ee0eb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -80,11 +80,6 @@
}
@Override
- public boolean checkShowToOwnerOnly(WindowManager.LayoutParams attrs) {
- return false;
- }
-
- @Override
public void adjustConfigurationLw(Configuration config, int keyboardPresence,
int navigationPresence) {
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 248b06b..d3cd3cb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -21,6 +21,7 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.view.WindowManager.TRANSIT_TASK_OPEN;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyFloat;
@@ -49,7 +50,12 @@
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.IBinder;
+import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationRunner;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
@@ -782,6 +788,63 @@
assertEquals(newDc, activity.mDisplayContent);
}
+ @Test
+ public void testTaskCanApplyAnimation() {
+ final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
+ final Task task = createTaskInStack(stack, 0 /* userId */);
+ final ActivityRecord activity =
+ WindowTestUtils.createActivityRecordInTask(mDisplayContent, task);
+ verifyWindowContainerApplyAnimation(task, activity);
+ }
+
+ @Test
+ public void testStackCanApplyAnimation() {
+ final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
+ final ActivityRecord activity = WindowTestUtils.createActivityRecordInTask(mDisplayContent,
+ createTaskInStack(stack, 0 /* userId */));
+ verifyWindowContainerApplyAnimation(stack, activity);
+ }
+
+ private void verifyWindowContainerApplyAnimation(WindowContainer wc, ActivityRecord act) {
+ // Initial remote animation for app transition.
+ final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
+ new IRemoteAnimationRunner.Stub() {
+ @Override
+ public void onAnimationStart(RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
+ IRemoteAnimationFinishedCallback finishedCallback) {
+ try {
+ finishedCallback.onAnimationFinished();
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void onAnimationCancelled() {
+ }
+ }, 0, 0, false);
+ adapter.setCallingPidUid(123, 456);
+ wc.getDisplayContent().prepareAppTransition(TRANSIT_TASK_OPEN, false);
+ wc.getDisplayContent().mAppTransition.overridePendingAppTransitionRemote(adapter);
+ spyOn(wc);
+ doReturn(true).when(wc).okToAnimate();
+
+ // Make sure animating state is as expected after applied animation.
+ assertTrue(wc.applyAnimation(null, TRANSIT_TASK_OPEN, true, false));
+ assertEquals(wc.getTopMostActivity(), act);
+ assertTrue(wc.isAnimating());
+ assertTrue(act.isAnimating(PARENTS));
+
+ // Make sure animation finish callback will be received and reset animating state after
+ // animation finish.
+ wc.getDisplayContent().mAppTransition.goodToGo(TRANSIT_TASK_OPEN, act,
+ mDisplayContent.mOpeningApps);
+ verify(wc).onAnimationFinished();
+ assertFalse(wc.isAnimating());
+ assertFalse(act.isAnimating(PARENTS));
+ }
+
/* Used so we can gain access to some protected members of the {@link WindowContainer} class */
private static class TestWindowContainer extends WindowContainer<TestWindowContainer> {
private final int mLayer;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 6d23b2e..e081ca3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -421,8 +421,10 @@
.setWindow(statusBar, null /* frameProvider */);
mDisplayContent.getInsetsStateController().onBarControlTargetChanged(
app, null /* fakeTopControlling */, app, null /* fakeNavControlling */);
+ final InsetsSource source = new InsetsSource(ITYPE_STATUS_BAR);
+ source.setVisible(false);
mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_STATUS_BAR)
- .onInsetsModified(app, new InsetsSource(ITYPE_STATUS_BAR));
+ .onInsetsModified(app, source);
waitUntilHandlersIdle();
assertFalse(statusBar.isVisible());
}
@@ -522,7 +524,8 @@
public void testVisibilityChangeSwitchUser() {
final WindowState window = createWindow(null, TYPE_APPLICATION, "app");
window.mHasSurface = true;
- window.setShowToOwnerOnlyLocked(true);
+ spyOn(window);
+ doReturn(false).when(window).showForAllUsers();
mWm.mCurrentUserId = 1;
window.switchUser(mWm.mCurrentUserId);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index ea88315..31a7f24 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -44,6 +44,7 @@
import android.view.DisplayInfo;
import android.view.IWindow;
import android.view.SurfaceControl.Transaction;
+import android.view.View;
import android.view.WindowManager;
import com.android.server.AttributeCache;
@@ -312,6 +313,16 @@
}
}
+ static void makeWindowVisible(WindowState... windows) {
+ for (WindowState win : windows) {
+ win.mViewVisibility = View.VISIBLE;
+ win.mRelayoutCalled = true;
+ win.mHasSurface = true;
+ win.mHidden = false;
+ win.showLw(false /* doAnimation */, false /* requestAnim */);
+ }
+ }
+
/** Creates a {@link ActivityStack} and adds it to the specified {@link DisplayContent}. */
ActivityStack createTaskStackOnDisplay(DisplayContent dc) {
return createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, dc);
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/RotationAnimationUtilsTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/RotationAnimationUtilsTest.java
new file mode 100644
index 0000000..9cda084
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/RotationAnimationUtilsTest.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.utils;
+
+import static android.graphics.Bitmap.Config.ARGB_8888;
+
+import static org.junit.Assert.assertEquals;
+
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.ColorSpace;
+import android.graphics.GraphicBuffer;
+import android.graphics.Matrix;
+import android.graphics.PointF;
+import android.view.Surface;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class RotationAnimationUtilsTest {
+
+ private static final int BITMAP_HEIGHT = 100;
+ private static final int BITMAP_WIDTH = 100;
+ private static final int POINT_WIDTH = 1000;
+ private static final int POINT_HEIGHT = 2000;
+
+ private ColorSpace mColorSpace = ColorSpace.get(ColorSpace.Named.DISPLAY_P3);
+ private Matrix mMatrix;
+
+ @Before
+ public void setup() {
+ mMatrix = new Matrix();
+ }
+
+ @Test
+ public void blackLuma() {
+ Bitmap swBitmap = createBitmap(0);
+ GraphicBuffer gb = swBitmapToGraphicsBuffer(swBitmap);
+ float borderLuma = RotationAnimationUtils.getAvgBorderLuma(gb, mColorSpace);
+ assertEquals(0, borderLuma, 0);
+ }
+
+ @Test
+ public void whiteLuma() {
+ Bitmap swBitmap = createBitmap(1);
+ GraphicBuffer gb = swBitmapToGraphicsBuffer(swBitmap);
+ float borderLuma = RotationAnimationUtils.getAvgBorderLuma(gb, mColorSpace);
+ assertEquals(1, borderLuma, 0);
+ }
+
+ @Test
+ public void whiteImageBlackBorderLuma() {
+ Bitmap swBitmap = createBitmap(1);
+ setBorderLuma(swBitmap, 0);
+ GraphicBuffer gb = swBitmapToGraphicsBuffer(swBitmap);
+ float borderLuma = RotationAnimationUtils.getAvgBorderLuma(gb, mColorSpace);
+ assertEquals(0, borderLuma, 0);
+ }
+
+ @Test
+ public void blackImageWhiteBorderLuma() {
+ Bitmap swBitmap = createBitmap(0);
+ setBorderLuma(swBitmap, 1);
+ GraphicBuffer gb = swBitmapToGraphicsBuffer(swBitmap);
+ float borderLuma = RotationAnimationUtils.getAvgBorderLuma(gb, mColorSpace);
+ assertEquals(1, borderLuma, 0);
+ }
+
+ @Test
+ public void rotate_0_bottomRight() {
+ RotationAnimationUtils.createRotationMatrix(Surface.ROTATION_0,
+ POINT_WIDTH, POINT_HEIGHT, mMatrix);
+ PointF newPoints = checkMappedPoints(POINT_WIDTH, POINT_HEIGHT);
+ assertEquals(POINT_WIDTH, newPoints.x, 0);
+ assertEquals(POINT_HEIGHT, newPoints.y, 0);
+ }
+
+ @Test
+ public void rotate_90_bottomRight() {
+ RotationAnimationUtils.createRotationMatrix(Surface.ROTATION_90,
+ POINT_WIDTH, POINT_HEIGHT, mMatrix);
+ PointF newPoints = checkMappedPoints(POINT_WIDTH, POINT_HEIGHT);
+ assertEquals(0, newPoints.x, 0);
+ assertEquals(POINT_WIDTH, newPoints.y, 0);
+ }
+
+ @Test
+ public void rotate_180_bottomRight() {
+ RotationAnimationUtils.createRotationMatrix(Surface.ROTATION_180,
+ POINT_WIDTH, POINT_HEIGHT, mMatrix);
+ PointF newPoints = checkMappedPoints(POINT_WIDTH, POINT_HEIGHT);
+ assertEquals(0, newPoints.x, 0);
+ assertEquals(0, newPoints.y, 0);
+ }
+
+ @Test
+ public void rotate_270_bottomRight() {
+ RotationAnimationUtils.createRotationMatrix(Surface.ROTATION_270,
+ POINT_WIDTH, POINT_HEIGHT, mMatrix);
+ PointF newPoints = checkMappedPoints(POINT_WIDTH, POINT_HEIGHT);
+ assertEquals(POINT_HEIGHT, newPoints.x, 0);
+ assertEquals(0, newPoints.y, 0);
+ }
+
+ private PointF checkMappedPoints(int x, int y) {
+ final float[] fs = new float[] {x, y};
+ mMatrix.mapPoints(fs);
+ return new PointF(fs[0], fs[1]);
+ }
+
+ private Bitmap createBitmap(float luma) {
+ Bitmap bitmap = Bitmap.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, ARGB_8888);
+ for (int i = 0; i < BITMAP_WIDTH; i++) {
+ for (int j = 0; j < BITMAP_HEIGHT; j++) {
+ bitmap.setPixel(i, j, Color.argb(1, luma, luma, luma));
+ }
+ }
+ return bitmap;
+ }
+
+ private GraphicBuffer swBitmapToGraphicsBuffer(Bitmap swBitmap) {
+ Bitmap hwBitmap = swBitmap.copy(Bitmap.Config.HARDWARE, false);
+ return hwBitmap.createGraphicBufferHandle();
+ }
+
+ private void setBorderLuma(Bitmap swBitmap, float luma) {
+ int i;
+ int width = swBitmap.getWidth();
+ int height = swBitmap.getHeight();
+ for (i = 0; i < width; i++) {
+ swBitmap.setPixel(i, 0, Color.argb(1, luma, luma, luma));
+ swBitmap.setPixel(i, height - 1, Color.argb(1, luma, luma, luma));
+ }
+ for (i = 0; i < height; i++) {
+ swBitmap.setPixel(0, i, Color.argb(1, luma, luma, luma));
+ swBitmap.setPixel(width - 1, i, Color.argb(1, luma, luma, luma));
+ }
+ }
+}
diff --git a/services/usage/OWNERS b/services/usage/OWNERS
new file mode 100644
index 0000000..9daa093
--- /dev/null
+++ b/services/usage/OWNERS
@@ -0,0 +1,4 @@
+mwachens@google.com
+varunshah@google.com
+huiyu@google.com
+yamasani@google.com
diff --git a/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java b/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java
index b75510b..488ee78 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java
@@ -83,7 +83,7 @@
* could transition to INTENT_STARTED.
*
* <p> If any bad transition happened, the state becomse UNKNOWN. The UNKNOWN state
- * could be * accumulated, because during the UNKNOWN state more IntentStarted may
+ * could be accumulated, because during the UNKNOWN state more IntentStarted may
* be triggered. To recover from UNKNOWN to INIT, all the accumualted IntentStarted
* should termniate.
*
@@ -100,7 +100,7 @@
@Override
public void onIntentStarted(@NonNull Intent intent, long timestampNs) {
if (state == State.UNKNOWN) {
- Log.e(TAG, "IntentStarted during UNKNOWN." + intent);
+ Log.wtf(TAG, "IntentStarted during UNKNOWN." + intent);
incAccIntentStartedEvents();
return;
}
@@ -110,32 +110,32 @@
state != State.ACTIVITY_CANCELLED &&
state != State.ACTIVITY_FINISHED &&
state != State.REPORT_FULLY_DRAWN) {
- Log.e(TAG,
+ Log.wtf(TAG,
String.format("Cannot transition from %s to %s", state, State.INTENT_STARTED));
incAccIntentStartedEvents();
incAccIntentStartedEvents();
return;
}
- Log.i(TAG, String.format("Tansition from %s to %s", state, State.INTENT_STARTED));
+ Log.i(TAG, String.format("Transition from %s to %s", state, State.INTENT_STARTED));
state = State.INTENT_STARTED;
}
@Override
public void onIntentFailed() {
if (state == State.UNKNOWN) {
- Log.e(TAG, "IntentFailed during UNKNOWN.");
+ Log.wtf(TAG, "IntentFailed during UNKNOWN.");
decAccIntentStartedEvents();
return;
}
if (state != State.INTENT_STARTED) {
- Log.e(TAG,
+ Log.wtf(TAG,
String.format("Cannot transition from %s to %s", state, State.INTENT_FAILED));
incAccIntentStartedEvents();
return;
}
- Log.i(TAG, String.format("Tansition from %s to %s", state, State.INTENT_FAILED));
+ Log.i(TAG, String.format("Transition from %s to %s", state, State.INTENT_FAILED));
state = State.INTENT_FAILED;
}
@@ -143,11 +143,11 @@
public void onActivityLaunched(@NonNull @ActivityRecordProto byte[] activity,
@Temperature int temperature) {
if (state == State.UNKNOWN) {
- Log.e(TAG, "onActivityLaunched during UNKNOWN.");
+ Log.wtf(TAG, "onActivityLaunched during UNKNOWN.");
return;
}
if (state != State.INTENT_STARTED) {
- Log.e(TAG,
+ Log.wtf(TAG,
String.format("Cannot transition from %s to %s", state, State.ACTIVITY_LAUNCHED));
incAccIntentStartedEvents();
return;
@@ -160,12 +160,12 @@
@Override
public void onActivityLaunchCancelled(@Nullable @ActivityRecordProto byte[] activity) {
if (state == State.UNKNOWN) {
- Log.e(TAG, "onActivityLaunchCancelled during UNKNOWN.");
+ Log.wtf(TAG, "onActivityLaunchCancelled during UNKNOWN.");
decAccIntentStartedEvents();
return;
}
if (state != State.ACTIVITY_LAUNCHED) {
- Log.e(TAG,
+ Log.wtf(TAG,
String.format("Cannot transition from %s to %s", state, State.ACTIVITY_CANCELLED));
incAccIntentStartedEvents();
return;
@@ -179,13 +179,13 @@
public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] activity,
long timestampNs) {
if (state == State.UNKNOWN) {
- Log.e(TAG, "onActivityLaunchFinished during UNKNOWN.");
+ Log.wtf(TAG, "onActivityLaunchFinished during UNKNOWN.");
decAccIntentStartedEvents();
return;
}
if (state != State.ACTIVITY_LAUNCHED) {
- Log.e(TAG,
+ Log.wtf(TAG,
String.format("Cannot transition from %s to %s", state, State.ACTIVITY_FINISHED));
incAccIntentStartedEvents();
return;
@@ -199,7 +199,7 @@
public void onReportFullyDrawn(@NonNull @ActivityRecordProto byte[] activity,
long timestampNs) {
if (state == State.UNKNOWN) {
- Log.e(TAG, "onReportFullyDrawn during UNKNOWN.");
+ Log.wtf(TAG, "onReportFullyDrawn during UNKNOWN.");
return;
}
if (state == State.INIT) {
@@ -207,7 +207,7 @@
}
if (state != State.ACTIVITY_FINISHED) {
- Log.e(TAG,
+ Log.wtf(TAG,
String.format("Cannot transition from %s to %s", state, State.REPORT_FULLY_DRAWN));
return;
}
@@ -230,7 +230,7 @@
private void incAccIntentStartedEvents() {
if (accIntentStartedEvents < 0) {
throw new AssertionError(
- String.format("The number of unknows cannot be negative"));
+ String.format("The number of unknowns cannot be negative"));
}
if (accIntentStartedEvents == 0) {
state = State.UNKNOWN;
@@ -243,7 +243,7 @@
private void decAccIntentStartedEvents() {
if (accIntentStartedEvents <= 0) {
throw new AssertionError(
- String.format("The number of unknows cannot be negative"));
+ String.format("The number of unknowns cannot be negative"));
}
if(accIntentStartedEvents == 1) {
state = State.INIT;
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 0becaf2..8808339 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -502,51 +502,116 @@
//**********************************************************************************************
/**
- * Define IMS Audio Codec
+ * Indicates that the audio codec is currently not specified or is unknown.
*/
- // Current audio codec is NONE
public static final int AUDIO_CODEC_NONE = ImsStreamMediaProfile.AUDIO_QUALITY_NONE; // 0
- // Current audio codec is AMR
+ /**
+ * Adaptive Multi-rate audio codec.
+ */
public static final int AUDIO_CODEC_AMR = ImsStreamMediaProfile.AUDIO_QUALITY_AMR; // 1
- // Current audio codec is AMR_WB
+ /**
+ * Adaptive Multi-rate wideband audio codec.
+ */
public static final int AUDIO_CODEC_AMR_WB = ImsStreamMediaProfile.AUDIO_QUALITY_AMR_WB; // 2
- // Current audio codec is QCELP13K
+ /**
+ * Qualcomm code-excited linear prediction 13 kilobit audio codec.
+ */
public static final int AUDIO_CODEC_QCELP13K = ImsStreamMediaProfile.AUDIO_QUALITY_QCELP13K; //3
- // Current audio codec is EVRC
+ /**
+ * Enhanced Variable Rate Codec. See 3GPP2 C.S0014-A.
+ */
public static final int AUDIO_CODEC_EVRC = ImsStreamMediaProfile.AUDIO_QUALITY_EVRC; // 4
- // Current audio codec is EVRC_B
+ /**
+ * Enhanced Variable Rate Codec B. Commonly used on CDMA networks.
+ */
public static final int AUDIO_CODEC_EVRC_B = ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_B; // 5
- // Current audio codec is EVRC_WB
+ /**
+ * Enhanced Variable Rate Wideband Codec. See RFC5188.
+ */
public static final int AUDIO_CODEC_EVRC_WB = ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_WB; // 6
- // Current audio codec is EVRC_NW
+ /**
+ * Enhanced Variable Rate Narrowband-Wideband Codec.
+ */
public static final int AUDIO_CODEC_EVRC_NW = ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_NW; // 7
- // Current audio codec is GSM_EFR
+ /**
+ * GSM Enhanced Full-Rate audio codec, also known as GSM-EFR, GSM 06.60, or simply EFR.
+ */
public static final int AUDIO_CODEC_GSM_EFR = ImsStreamMediaProfile.AUDIO_QUALITY_GSM_EFR; // 8
- // Current audio codec is GSM_FR
+ /**
+ * GSM Full-Rate audio codec, also known as GSM-FR, GSM 06.10, GSM, or simply FR.
+ */
public static final int AUDIO_CODEC_GSM_FR = ImsStreamMediaProfile.AUDIO_QUALITY_GSM_FR; // 9
- // Current audio codec is GSM_HR
+ /**
+ * GSM Half Rate audio codec.
+ */
public static final int AUDIO_CODEC_GSM_HR = ImsStreamMediaProfile.AUDIO_QUALITY_GSM_HR; // 10
- // Current audio codec is G711U
+ /**
+ * ITU-T G711U audio codec.
+ */
public static final int AUDIO_CODEC_G711U = ImsStreamMediaProfile.AUDIO_QUALITY_G711U; // 11
- // Current audio codec is G723
+ /**
+ * ITU-T G723 audio codec.
+ */
public static final int AUDIO_CODEC_G723 = ImsStreamMediaProfile.AUDIO_QUALITY_G723; // 12
- // Current audio codec is G711A
+ /**
+ * ITU-T G711A audio codec.
+ */
public static final int AUDIO_CODEC_G711A = ImsStreamMediaProfile.AUDIO_QUALITY_G711A; // 13
- // Current audio codec is G722
+ /**
+ * ITU-T G722 audio codec.
+ */
public static final int AUDIO_CODEC_G722 = ImsStreamMediaProfile.AUDIO_QUALITY_G722; // 14
- // Current audio codec is G711AB
+ /**
+ * ITU-T G711AB audio codec.
+ */
public static final int AUDIO_CODEC_G711AB = ImsStreamMediaProfile.AUDIO_QUALITY_G711AB; // 15
- // Current audio codec is G729
+ /**
+ * ITU-T G729 audio codec.
+ */
public static final int AUDIO_CODEC_G729 = ImsStreamMediaProfile.AUDIO_QUALITY_G729; // 16
- // Current audio codec is EVS_NB
+ /**
+ * Enhanced Voice Services Narrowband audio codec. See 3GPP TS 26.441.
+ */
public static final int AUDIO_CODEC_EVS_NB = ImsStreamMediaProfile.AUDIO_QUALITY_EVS_NB; // 17
- // Current audio codec is EVS_WB
+ /**
+ * Enhanced Voice Services Wideband audio codec. See 3GPP TS 26.441.
+ */
public static final int AUDIO_CODEC_EVS_WB = ImsStreamMediaProfile.AUDIO_QUALITY_EVS_WB; // 18
- // Current audio codec is EVS_SWB
+ /**
+ * Enhanced Voice Services Super-Wideband audio codec. See 3GPP TS 26.441.
+ */
public static final int AUDIO_CODEC_EVS_SWB = ImsStreamMediaProfile.AUDIO_QUALITY_EVS_SWB; // 19
- // Current audio codec is EVS_FB
+ /**
+ * Enhanced Voice Services Fullband audio codec. See 3GPP TS 26.441.
+ */
public static final int AUDIO_CODEC_EVS_FB = ImsStreamMediaProfile.AUDIO_QUALITY_EVS_FB; // 20
+ /**@hide*/
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "AUDIO_CODEC_", value = {
+ AUDIO_CODEC_NONE,
+ AUDIO_CODEC_AMR,
+ AUDIO_CODEC_AMR_WB,
+ AUDIO_CODEC_QCELP13K,
+ AUDIO_CODEC_EVRC,
+ AUDIO_CODEC_EVRC_B,
+ AUDIO_CODEC_EVRC_WB,
+ AUDIO_CODEC_EVRC_NW,
+ AUDIO_CODEC_GSM_EFR,
+ AUDIO_CODEC_GSM_FR,
+ AUDIO_CODEC_GSM_HR,
+ AUDIO_CODEC_G711U,
+ AUDIO_CODEC_G723,
+ AUDIO_CODEC_G711A,
+ AUDIO_CODEC_G722,
+ AUDIO_CODEC_G711AB,
+ AUDIO_CODEC_G729,
+ AUDIO_CODEC_EVS_NB,
+ AUDIO_CODEC_EVS_SWB,
+ AUDIO_CODEC_EVS_FB
+ })
+ public @interface AudioCodec {}
+
/**
* Connection extra key used to store the last forwarded number associated with the current
* connection. Used to communicate to the user interface that the connection was forwarded via
@@ -640,10 +705,10 @@
"android.telecom.extra.IS_RTT_AUDIO_PRESENT";
/**
- * The audio codec in use for the current {@link Connection}, if known. Valid values include
- * {@link #AUDIO_CODEC_AMR_WB} and {@link #AUDIO_CODEC_EVS_WB}.
+ * The audio codec in use for the current {@link Connection}, if known. Examples of valid
+ * values include {@link #AUDIO_CODEC_AMR_WB} and {@link #AUDIO_CODEC_EVS_WB}.
*/
- public static final String EXTRA_AUDIO_CODEC =
+ public static final @AudioCodec String EXTRA_AUDIO_CODEC =
"android.telecom.extra.AUDIO_CODEC";
/**
diff --git a/telephony/OWNERS b/telephony/OWNERS
index 2a6e8de..58a7ea0 100644
--- a/telephony/OWNERS
+++ b/telephony/OWNERS
@@ -14,4 +14,5 @@
refuhoo@google.com
paulye@google.com
nazaninb@google.com
-sarahchin@google.com
\ No newline at end of file
+sarahchin@google.com
+dbright@google.com
diff --git a/telephony/java/android/telephony/LocationAccessPolicy.java b/telephony/common/android/telephony/LocationAccessPolicy.java
similarity index 97%
rename from telephony/java/android/telephony/LocationAccessPolicy.java
rename to telephony/common/android/telephony/LocationAccessPolicy.java
index 95aa101..f39981f 100644
--- a/telephony/java/android/telephony/LocationAccessPolicy.java
+++ b/telephony/common/android/telephony/LocationAccessPolicy.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -11,7 +11,7 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
*/
package android.telephony;
@@ -60,6 +60,7 @@
DENIED_HARD,
}
+ /** Data structure for location permission query */
public static class LocationPermissionQuery {
public final String callingPackage;
public final String callingFeatureId;
@@ -83,6 +84,7 @@
this.method = method;
}
+ /** Builder for LocationPermissionQuery */
public static class Builder {
private String mCallingPackage;
private String mCallingFeatureId;
@@ -161,6 +163,7 @@
return this;
}
+ /** build LocationPermissionQuery */
public LocationPermissionQuery build() {
return new LocationPermissionQuery(mCallingPackage, mCallingFeatureId,
mCallingUid, mCallingPid, mMinSdkVersionForCoarse, mMinSdkVersionForFine,
@@ -258,6 +261,7 @@
}
}
+ /** Check if location permissions have been granted */
public static LocationPermissionResult checkLocationPermission(
Context context, LocationPermissionQuery query) {
// Always allow the phone process and system server to access location. This avoid
diff --git a/telephony/java/com/android/internal/telephony/CarrierAppUtils.java b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
similarity index 94%
rename from telephony/java/com/android/internal/telephony/CarrierAppUtils.java
rename to telephony/common/com/android/internal/telephony/CarrierAppUtils.java
index 2dc6ae3..eb02ea6f 100644
--- a/telephony/java/com/android/internal/telephony/CarrierAppUtils.java
+++ b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -11,7 +11,7 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
*/
package com.android.internal.telephony;
@@ -74,7 +74,7 @@
* system startup prior to any application running, as well as any time the set of carrier
* privileged apps may have changed.
*/
- public synchronized static void disableCarrierAppsUntilPrivileged(String callingPackage,
+ public static synchronized void disableCarrierAppsUntilPrivileged(String callingPackage,
IPackageManager packageManager, IPermissionManager permissionManager,
TelephonyManager telephonyManager, ContentResolver contentResolver, int userId) {
if (DEBUG) {
@@ -101,7 +101,7 @@
* broadcasts. The app will continue to run (briefly) after being disabled, before the Package
* Manager can kill it, and this can lead to crashes as the app is in an unexpected state.
*/
- public synchronized static void disableCarrierAppsUntilPrivileged(String callingPackage,
+ public static synchronized void disableCarrierAppsUntilPrivileged(String callingPackage,
IPackageManager packageManager, IPermissionManager permissionManager,
ContentResolver contentResolver, int userId) {
if (DEBUG) {
@@ -119,7 +119,10 @@
systemCarrierAppsDisabledUntilUsed, systemCarrierAssociatedAppsDisabledUntilUsed);
}
- // Must be public b/c framework unit tests can't access package-private methods.
+ /**
+ * Disable carrier apps until they are privileged
+ * Must be public b/c framework unit tests can't access package-private methods.
+ */
@VisibleForTesting
public static void disableCarrierAppsUntilPrivileged(String callingPackage,
IPackageManager packageManager, IPermissionManager permissionManager,
@@ -169,10 +172,10 @@
// Only update enabled state for the app on /system. Once it has been
// updated we shouldn't touch it.
if (!ai.isUpdatedSystemApp()
- && (ai.enabledSetting ==
- PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
- || ai.enabledSetting ==
- PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
+ && (ai.enabledSetting
+ == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
+ || ai.enabledSetting
+ == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
|| (ai.flags & ApplicationInfo.FLAG_INSTALLED) == 0)) {
Rlog.i(TAG, "Update state(" + packageName + "): ENABLED for user "
+ userId);
@@ -191,10 +194,10 @@
// Also enable any associated apps for this carrier app.
if (associatedAppList != null) {
for (ApplicationInfo associatedApp : associatedAppList) {
- if (associatedApp.enabledSetting ==
- PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
- || associatedApp.enabledSetting ==
- PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
+ if (associatedApp.enabledSetting
+ == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
+ || associatedApp.enabledSetting
+ == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
|| (associatedApp.flags
& ApplicationInfo.FLAG_INSTALLED) == 0) {
Rlog.i(TAG, "Update associated state(" + associatedApp.packageName
@@ -219,8 +222,8 @@
// Only update enabled state for the app on /system. Once it has been
// updated we shouldn't touch it.
if (!ai.isUpdatedSystemApp()
- && ai.enabledSetting ==
- PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
+ && ai.enabledSetting
+ == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
&& (ai.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
Rlog.i(TAG, "Update state(" + packageName
+ "): DISABLED_UNTIL_USED for user " + userId);
@@ -294,8 +297,8 @@
ApplicationInfo ai = candidates.get(i);
String packageName = ai.packageName;
boolean hasPrivileges =
- telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName) ==
- TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+ telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName)
+ == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
if (!hasPrivileges) {
candidates.remove(i);
}
diff --git a/telephony/common/com/android/internal/telephony/EncodeException.java b/telephony/common/com/android/internal/telephony/EncodeException.java
index cdc853e..bb723a0 100644
--- a/telephony/common/com/android/internal/telephony/EncodeException.java
+++ b/telephony/common/com/android/internal/telephony/EncodeException.java
@@ -16,7 +16,7 @@
package com.android.internal.telephony;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
/**
* {@hide}
diff --git a/telephony/common/com/android/internal/telephony/PackageChangeReceiver.java b/telephony/common/com/android/internal/telephony/PackageChangeReceiver.java
index 922af12..0b47547 100644
--- a/telephony/common/com/android/internal/telephony/PackageChangeReceiver.java
+++ b/telephony/common/com/android/internal/telephony/PackageChangeReceiver.java
@@ -24,17 +24,17 @@
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Looper;
import android.os.UserHandle;
-import com.android.internal.os.BackgroundThread;
-
/**
* Helper class for monitoring the state of packages: adding, removing,
* updating, and disappearing and reappearing on the SD card.
*/
public abstract class PackageChangeReceiver extends BroadcastReceiver {
static final IntentFilter sPackageIntentFilter = new IntentFilter();
+ private static HandlerThread sHandlerThread;
static {
sPackageIntentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
sPackageIntentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
@@ -43,28 +43,24 @@
sPackageIntentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
sPackageIntentFilter.addDataScheme("package");
}
+ // Keep an instance of Context around as long as we still want the receiver:
+ // if the instance of Context gets garbage-collected, it'll unregister the receiver, so only
+ // unset when we want to unregister.
Context mRegisteredContext;
/**
- * To register the intents that needed for monitoring the state of packages
+ * To register the intents that needed for monitoring the state of packages. Once this method
+ * has been called on an instance of {@link PackageChangeReceiver}, all subsequent calls must
+ * have the same {@code user} argument.
*/
public void register(@NonNull Context context, @Nullable Looper thread,
@Nullable UserHandle user) {
if (mRegisteredContext != null) {
throw new IllegalStateException("Already registered");
}
- Handler handler = (thread == null) ? BackgroundThread.getHandler() : new Handler(thread);
- mRegisteredContext = context;
- if (handler != null) {
- if (user != null) {
- context.registerReceiverAsUser(this, user, sPackageIntentFilter, null, handler);
- } else {
- context.registerReceiver(this, sPackageIntentFilter,
- null, handler);
- }
- } else {
- throw new NullPointerException();
- }
+ Handler handler = new Handler(thread == null ? getStaticLooper() : thread);
+ mRegisteredContext = user == null ? context : context.createContextAsUser(user, 0);
+ mRegisteredContext.registerReceiver(this, sPackageIntentFilter, null, handler);
}
/**
@@ -78,6 +74,14 @@
mRegisteredContext = null;
}
+ private static synchronized Looper getStaticLooper() {
+ if (sHandlerThread == null) {
+ sHandlerThread = new HandlerThread(PackageChangeReceiver.class.getSimpleName());
+ sHandlerThread.start();
+ }
+ return sHandlerThread.getLooper();
+ }
+
/**
* This method is invoked when receive the Intent.ACTION_PACKAGE_ADDED
*/
diff --git a/telephony/common/com/android/internal/telephony/SmsApplication.java b/telephony/common/com/android/internal/telephony/SmsApplication.java
index 70ce1bf..b302589 100644
--- a/telephony/common/com/android/internal/telephony/SmsApplication.java
+++ b/telephony/common/com/android/internal/telephony/SmsApplication.java
@@ -44,6 +44,7 @@
import android.telephony.TelephonyManager;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -57,6 +58,7 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
+import java.util.stream.Collectors;
/**
* Class for managing the primary application that we will deliver SMS/MMS messages to
@@ -65,10 +67,10 @@
*/
public final class SmsApplication {
static final String LOG_TAG = "SmsApplication";
- private static final String PHONE_PACKAGE_NAME = "com.android.phone";
- private static final String BLUETOOTH_PACKAGE_NAME = "com.android.bluetooth";
- private static final String MMS_SERVICE_PACKAGE_NAME = "com.android.mms.service";
- private static final String TELEPHONY_PROVIDER_PACKAGE_NAME = "com.android.providers.telephony";
+ public static final String PHONE_PACKAGE_NAME = "com.android.phone";
+ public static final String BLUETOOTH_PACKAGE_NAME = "com.android.bluetooth";
+ public static final String MMS_SERVICE_PACKAGE_NAME = "com.android.mms.service";
+ public static final String TELEPHONY_PROVIDER_PACKAGE_NAME = "com.android.providers.telephony";
private static final String SCHEME_SMS = "sms";
private static final String SCHEME_SMSTO = "smsto";
@@ -77,13 +79,13 @@
private static final boolean DEBUG = false;
private static final boolean DEBUG_MULTIUSER = false;
- private static final int[] DEFAULT_APP_EXCLUSIVE_APPOPS = {
- AppOpsManager.OP_READ_SMS,
- AppOpsManager.OP_WRITE_SMS,
- AppOpsManager.OP_RECEIVE_SMS,
- AppOpsManager.OP_RECEIVE_WAP_PUSH,
- AppOpsManager.OP_SEND_SMS,
- AppOpsManager.OP_READ_CELL_BROADCASTS
+ private static final String[] DEFAULT_APP_EXCLUSIVE_APPOPS = {
+ AppOpsManager.OPSTR_READ_SMS,
+ AppOpsManager.OPSTR_WRITE_SMS,
+ AppOpsManager.OPSTR_RECEIVE_SMS,
+ AppOpsManager.OPSTR_RECEIVE_WAP_PUSH,
+ AppOpsManager.OPSTR_SEND_SMS,
+ AppOpsManager.OPSTR_READ_CELL_BROADCASTS
};
private static SmsPackageMonitor sSmsPackageMonitor = null;
@@ -247,6 +249,7 @@
private static Collection<SmsApplicationData> getApplicationCollectionInternal(
Context context, int userId) {
PackageManager packageManager = context.getPackageManager();
+ UserHandle userHandle = UserHandle.of(userId);
// Get the list of apps registered for SMS
Intent intent = new Intent(Intents.SMS_DELIVER_ACTION);
@@ -255,7 +258,7 @@
}
List<ResolveInfo> smsReceivers = packageManager.queryBroadcastReceiversAsUser(intent,
PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- userId);
+ userHandle);
HashMap<String, SmsApplicationData> receivers = new HashMap<String, SmsApplicationData>();
@@ -282,7 +285,7 @@
intent.setDataAndType(null, "application/vnd.wap.mms-message");
List<ResolveInfo> mmsReceivers = packageManager.queryBroadcastReceiversAsUser(intent,
PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- userId);
+ userHandle);
for (ResolveInfo resolveInfo : mmsReceivers) {
final ActivityInfo activityInfo = resolveInfo.activityInfo;
if (activityInfo == null) {
@@ -324,7 +327,7 @@
Uri.fromParts(SCHEME_SMSTO, "", null));
List<ResolveInfo> sendToActivities = packageManager.queryIntentActivitiesAsUser(intent,
PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- userId);
+ userHandle);
for (ResolveInfo resolveInfo : sendToActivities) {
final ActivityInfo activityInfo = resolveInfo.activityInfo;
if (activityInfo == null) {
@@ -342,7 +345,7 @@
List<ResolveInfo> smsAppChangedReceivers =
packageManager.queryBroadcastReceiversAsUser(intent,
PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userHandle);
if (DEBUG_MULTIUSER) {
Log.i(LOG_TAG, "getApplicationCollectionInternal smsAppChangedActivities=" +
smsAppChangedReceivers);
@@ -369,7 +372,7 @@
List<ResolveInfo> providerChangedReceivers =
packageManager.queryBroadcastReceiversAsUser(intent,
PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userHandle);
if (DEBUG_MULTIUSER) {
Log.i(LOG_TAG, "getApplicationCollectionInternal providerChangedActivities=" +
providerChangedReceivers);
@@ -396,7 +399,7 @@
List<ResolveInfo> simFullReceivers =
packageManager.queryBroadcastReceiversAsUser(intent,
PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userHandle);
if (DEBUG_MULTIUSER) {
Log.i(LOG_TAG, "getApplicationCollectionInternal simFullReceivers="
+ simFullReceivers);
@@ -496,7 +499,7 @@
// If we found a package, make sure AppOps permissions are set up correctly
if (applicationData != null) {
- // We can only call checkOp if we are privileged (updateIfNeeded) or if the app we
+ // We can only call unsafeCheckOp if we are privileged (updateIfNeeded) or if the app we
// are checking is for our current uid. Doing this check from the unprivileged current
// SMS app allows us to tell the current SMS app that it is not in a good state and
// needs to ask to be the current SMS app again to work properly.
@@ -550,23 +553,23 @@
// apps, all of them should be able to write to telephony provider.
// This is to allow the proxy package permission check in telephony provider
// to pass.
- for (int appop : DEFAULT_APP_EXCLUSIVE_APPOPS) {
- appOps.setUidMode(appop, Process.PHONE_UID, AppOpsManager.MODE_ALLOWED);
+ for (String opStr : DEFAULT_APP_EXCLUSIVE_APPOPS) {
+ appOps.setUidMode(opStr, Process.PHONE_UID, AppOpsManager.MODE_ALLOWED);
}
}
private static boolean tryFixExclusiveSmsAppops(Context context,
SmsApplicationData applicationData, boolean updateIfNeeded) {
AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
- for (int appOp : DEFAULT_APP_EXCLUSIVE_APPOPS) {
- int mode = appOps.checkOp(appOp, applicationData.mUid,
+ for (String opStr : DEFAULT_APP_EXCLUSIVE_APPOPS) {
+ int mode = appOps.unsafeCheckOp(opStr, applicationData.mUid,
applicationData.mPackageName);
if (mode != AppOpsManager.MODE_ALLOWED) {
Rlog.e(LOG_TAG, applicationData.mPackageName + " lost "
- + AppOpsManager.modeToName(appOp) + ": "
+ + opStr + ": "
+ (updateIfNeeded ? " (fixing)" : " (no permission to fix)"));
if (updateIfNeeded) {
- appOps.setUidMode(appOp, applicationData.mUid, AppOpsManager.MODE_ALLOWED);
+ appOps.setUidMode(opStr, applicationData.mUid, AppOpsManager.MODE_ALLOWED);
} else {
return false;
}
@@ -625,7 +628,8 @@
}
// We only make the change if the new package is valid
- PackageManager packageManager = context.getPackageManager();
+ PackageManager packageManager =
+ context.createContextAsUser(userHandle, 0).getPackageManager();
Collection<SmsApplicationData> applications = getApplicationCollectionInternal(
context, userId);
SmsApplicationData oldAppData = oldPackageName != null ?
@@ -636,8 +640,7 @@
AppOpsManager appOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
if (oldPackageName != null) {
try {
- int uid = packageManager.getPackageInfoAsUser(
- oldPackageName, 0, userId).applicationInfo.uid;
+ int uid = packageManager.getPackageInfo(oldPackageName, 0).applicationInfo.uid;
setExclusiveAppops(oldPackageName, appOps, uid, AppOpsManager.MODE_DEFAULT);
} catch (NameNotFoundException e) {
Rlog.w(LOG_TAG, "Old SMS package not found: " + oldPackageName);
@@ -752,7 +755,7 @@
}
try {
PackageInfo info = packageManager.getPackageInfo(packageName, 0);
- int mode = appOps.checkOp(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
+ int mode = appOps.unsafeCheckOp(AppOpsManager.OPSTR_WRITE_SMS, info.applicationInfo.uid,
packageName);
if (mode != AppOpsManager.MODE_ALLOWED) {
Rlog.w(LOG_TAG, packageName + " does not have OP_WRITE_SMS: (fixing)");
@@ -768,8 +771,8 @@
private static void setExclusiveAppops(String pkg, AppOpsManager appOpsManager, int uid,
int mode) {
- for (int appop : DEFAULT_APP_EXCLUSIVE_APPOPS) {
- appOpsManager.setUidMode(appop, uid, mode);
+ for (String opStr : DEFAULT_APP_EXCLUSIVE_APPOPS) {
+ appOpsManager.setUidMode(opStr, uid, mode);
}
}
@@ -801,9 +804,16 @@
}
private void onPackageChanged() {
- PackageManager packageManager = mContext.getPackageManager();
+ int userId;
+ try {
+ userId = getSendingUser().getIdentifier();
+ } catch (NullPointerException e) {
+ // This should never happen in prod -- unit tests will put the receiver into a
+ // unusual state where the pending result is null, which produces a NPE when calling
+ // getSendingUserId. Just pretend like it's the system user for testing.
+ userId = UserHandle.USER_SYSTEM;
+ }
Context userContext = mContext;
- final int userId = getSendingUserId();
if (userId != UserHandle.USER_SYSTEM) {
try {
userContext = mContext.createPackageContextAsUser(mContext.getPackageName(), 0,
@@ -814,10 +824,11 @@
}
}
}
+ PackageManager packageManager = userContext.getPackageManager();
// Ensure this component is still configured as the preferred activity
ComponentName componentName = getDefaultSendToApplication(userContext, true);
if (componentName != null) {
- configurePreferredActivity(packageManager, componentName, userId);
+ configurePreferredActivity(packageManager, componentName);
}
}
}
@@ -829,41 +840,36 @@
@UnsupportedAppUsage
private static void configurePreferredActivity(PackageManager packageManager,
- ComponentName componentName, int userId) {
+ ComponentName componentName) {
// Add the four activity preferences we want to direct to this app.
- replacePreferredActivity(packageManager, componentName, userId, SCHEME_SMS);
- replacePreferredActivity(packageManager, componentName, userId, SCHEME_SMSTO);
- replacePreferredActivity(packageManager, componentName, userId, SCHEME_MMS);
- replacePreferredActivity(packageManager, componentName, userId, SCHEME_MMSTO);
+ replacePreferredActivity(packageManager, componentName, SCHEME_SMS);
+ replacePreferredActivity(packageManager, componentName, SCHEME_SMSTO);
+ replacePreferredActivity(packageManager, componentName, SCHEME_MMS);
+ replacePreferredActivity(packageManager, componentName, SCHEME_MMSTO);
}
/**
* Updates the ACTION_SENDTO activity to the specified component for the specified scheme.
*/
private static void replacePreferredActivity(PackageManager packageManager,
- ComponentName componentName, int userId, String scheme) {
+ ComponentName componentName, String scheme) {
// Build the set of existing activities that handle this scheme
Intent intent = new Intent(Intent.ACTION_SENDTO, Uri.fromParts(scheme, "", null));
- List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivitiesAsUser(
- intent, PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_RESOLVED_FILTER,
- userId);
+ List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivities(
+ intent, PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_RESOLVED_FILTER);
- // Build the set of ComponentNames for these activities
- final int n = resolveInfoList.size();
- ComponentName[] set = new ComponentName[n];
- for (int i = 0; i < n; i++) {
- ResolveInfo info = resolveInfoList.get(i);
- set[i] = new ComponentName(info.activityInfo.packageName, info.activityInfo.name);
- }
+ List<ComponentName> components = resolveInfoList.stream().map(info ->
+ new ComponentName(info.activityInfo.packageName, info.activityInfo.name))
+ .collect(Collectors.toList());
// Update the preferred SENDTO activity for the specified scheme
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_SENDTO);
intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
intentFilter.addDataScheme(scheme);
- packageManager.replacePreferredActivityAsUser(intentFilter,
+ packageManager.replacePreferredActivity(intentFilter,
IntentFilter.MATCH_CATEGORY_SCHEME + IntentFilter.MATCH_ADJUSTMENT_NORMAL,
- set, componentName, userId);
+ components, componentName);
}
/**
@@ -894,6 +900,7 @@
* @param userId target user ID.
* @return component name of the app and class to deliver SMS messages to
*/
+ @VisibleForTesting
public static ComponentName getDefaultSmsApplicationAsUser(Context context,
boolean updateIfNeeded, int userId) {
final long token = Binder.clearCallingIdentity();
diff --git a/telephony/java/com/android/internal/telephony/util/ArrayUtils.java b/telephony/common/com/android/internal/telephony/util/ArrayUtils.java
similarity index 86%
rename from telephony/java/com/android/internal/telephony/util/ArrayUtils.java
rename to telephony/common/com/android/internal/telephony/util/ArrayUtils.java
index 2905125..28401a6 100644
--- a/telephony/java/com/android/internal/telephony/util/ArrayUtils.java
+++ b/telephony/common/com/android/internal/telephony/util/ArrayUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,17 +29,30 @@
/**
* Adds value to given array if not already present, providing set-like behavior.
+ *
+ * @param kind The class of the array elements.
+ * @param array The array to append to.
+ * @param element The array element to append.
+ * @return The array containing the appended element.
*/
@SuppressWarnings("unchecked")
- public static @NonNull <T> T[] appendElement(Class<T> kind, @Nullable T[] array, T element) {
+ @NonNull
+ public static <T> T[] appendElement(Class<T> kind, @Nullable T[] array, T element) {
return appendElement(kind, array, element, false);
}
/**
* Adds value to given array.
+ *
+ * @param kind The class of the array elements.
+ * @param array The array to append to.
+ * @param element The array element to append.
+ * @param allowDuplicates Whether to allow duplicated elements in array.
+ * @return The array containing the appended element.
*/
@SuppressWarnings("unchecked")
- public static @NonNull <T> T[] appendElement(Class<T> kind, @Nullable T[] array, T element,
+ @NonNull
+ public static <T> T[] appendElement(Class<T> kind, @Nullable T[] array, T element,
boolean allowDuplicates) {
final T[] result;
final int end;
@@ -59,13 +72,14 @@
/**
* Combine multiple arrays into a single array.
*
- * @param kind The class of the array elements
+ * @param kind The class of the array elements
* @param arrays The arrays to combine
- * @param <T> The class of the array elements (inferred from kind).
+ * @param <T> The class of the array elements (inferred from kind).
* @return A single array containing all the elements of the parameter arrays.
*/
@SuppressWarnings("unchecked")
- public static @NonNull <T> T[] concatElements(Class<T> kind, @Nullable T[]... arrays) {
+ @NonNull
+ public static <T> T[] concatElements(Class<T> kind, @Nullable T[]... arrays) {
if (arrays == null || arrays.length == 0) {
return createEmptyArray(kind);
}
diff --git a/telephony/java/com/android/internal/telephony/util/TelephonyUtils.java b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
similarity index 100%
rename from telephony/java/com/android/internal/telephony/util/TelephonyUtils.java
rename to telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
diff --git a/telephony/common/com/google/android/mms/pdu/PduPersister.java b/telephony/common/com/google/android/mms/pdu/PduPersister.java
index b237705..8efca0e 100755
--- a/telephony/common/com/google/android/mms/pdu/PduPersister.java
+++ b/telephony/common/com/google/android/mms/pdu/PduPersister.java
@@ -34,6 +34,7 @@
import android.provider.Telephony.MmsSms.PendingMessages;
import android.provider.Telephony.Threads;
import android.telephony.PhoneNumberUtils;
+import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -1448,9 +1449,9 @@
final Set<String> myPhoneNumbers = new HashSet<String>();
if (excludeMyNumber) {
// Build a list of my phone numbers from the various sims.
- for (int subid : subscriptionManager.getActiveSubscriptionIdList()) {
+ for (SubscriptionInfo subInfo : subscriptionManager.getActiveSubscriptionInfoList()) {
final String myNumber = mContext.getSystemService(TelephonyManager.class).
- createForSubscriptionId(subid).getLine1Number();
+ createForSubscriptionId(subInfo.getSubscriptionId()).getLine1Number();
if (myNumber != null) {
myPhoneNumbers.add(myNumber);
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index eea08dc..5baddef 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -29,7 +29,6 @@
import android.content.Context;
import android.os.PersistableBundle;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.service.carrier.CarrierService;
import android.telecom.TelecomManager;
import android.telephony.ims.ImsReasonInfo;
@@ -2481,7 +2480,6 @@
*/
public static final String IMSI_KEY_AVAILABILITY_INT = "imsi_key_availability_int";
-
/**
* Key identifying if the CDMA Caller ID presentation and suppression MMI codes
* should be converted to 3GPP CLIR codes when a multimode (CDMA+UMTS+LTE) device is roaming
@@ -3120,6 +3118,16 @@
"data_switch_validation_timeout_long";
/**
+ * Specifies whether the system should prefix the EAP method to the anonymous identity.
+ * The following prefix will be added if this key is set to TRUE:
+ * EAP-AKA: "0"
+ * EAP-SIM: "1"
+ * EAP-AKA_PRIME: "6"
+ * @hide
+ */
+ public static final String ENABLE_EAP_METHOD_PREFIX_BOOL = "enable_eap_method_prefix_bool";
+
+ /**
* GPS configs. See the GNSS HAL documentation for more details.
*/
public static final class Gps {
@@ -3934,6 +3942,7 @@
CellSignalStrengthLte.USE_RSRP | CellSignalStrengthLte.USE_RSSNR);
// Default wifi configurations.
sDefaults.putAll(Wifi.getDefaults());
+ sDefaults.putBoolean(ENABLE_EAP_METHOD_PREFIX_BOOL, false);
}
/**
@@ -4195,8 +4204,11 @@
/** @hide */
@Nullable
private ICarrierConfigLoader getICarrierConfigLoader() {
- return ICarrierConfigLoader.Stub
- .asInterface(ServiceManager.getService(Context.CARRIER_CONFIG_SERVICE));
+ return ICarrierConfigLoader.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getCarrierConfigServiceRegisterer()
+ .get());
}
/**
diff --git a/telephony/java/android/telephony/CellIdentity.java b/telephony/java/android/telephony/CellIdentity.java
index e523fba..5345469 100644
--- a/telephony/java/android/telephony/CellIdentity.java
+++ b/telephony/java/android/telephony/CellIdentity.java
@@ -185,6 +185,15 @@
@SystemApi
public abstract @NonNull CellLocation asCellLocation();
+ /**
+ * Create and a return a new instance of CellIdentity with location-identifying information
+ * removed.
+ *
+ * @hide
+ */
+ @SystemApi
+ public abstract @NonNull CellIdentity sanitizeLocationInfo();
+
@Override
public boolean equals(Object other) {
if (!(other instanceof CellIdentity)) {
@@ -312,4 +321,23 @@
return true;
}
+ /** @hide */
+ public static CellIdentity create(android.hardware.radio.V1_5.CellIdentity ci) {
+ if (ci == null) return null;
+ switch (ci.getDiscriminator()) {
+ case android.hardware.radio.V1_5.CellIdentity.hidl_discriminator.gsm:
+ return new CellIdentityGsm(ci.gsm());
+ case android.hardware.radio.V1_5.CellIdentity.hidl_discriminator.cdma:
+ return new CellIdentityCdma(ci.cdma());
+ case android.hardware.radio.V1_5.CellIdentity.hidl_discriminator.lte:
+ return new CellIdentityLte(ci.lte());
+ case android.hardware.radio.V1_5.CellIdentity.hidl_discriminator.wcdma:
+ return new CellIdentityWcdma(ci.wcdma());
+ case android.hardware.radio.V1_5.CellIdentity.hidl_discriminator.tdscdma:
+ return new CellIdentityTdscdma(ci.tdscdma());
+ case android.hardware.radio.V1_5.CellIdentity.hidl_discriminator.nr:
+ return new CellIdentityNr(ci.nr());
+ default: return null;
+ }
+ }
}
diff --git a/telephony/java/android/telephony/CellIdentityCdma.java b/telephony/java/android/telephony/CellIdentityCdma.java
index 54236b42..1a6bf33 100644
--- a/telephony/java/android/telephony/CellIdentityCdma.java
+++ b/telephony/java/android/telephony/CellIdentityCdma.java
@@ -128,7 +128,8 @@
}
/** @hide */
- public CellIdentityCdma sanitizeLocationInfo() {
+ @Override
+ public @NonNull CellIdentityCdma sanitizeLocationInfo() {
return new CellIdentityCdma(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
mAlphaLong, mAlphaShort);
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index 4e4454d..2ecdfce 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -104,7 +104,8 @@
}
/** @hide */
- public CellIdentityGsm sanitizeLocationInfo() {
+ @Override
+ public @NonNull CellIdentityGsm sanitizeLocationInfo() {
return new CellIdentityGsm(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
CellInfo.UNAVAILABLE, mMccStr, mMncStr, mAlphaLong, mAlphaShort);
}
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index c3fc73b..15c9175 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -121,7 +121,8 @@
}
/** @hide */
- public CellIdentityLte sanitizeLocationInfo() {
+ @Override
+ public @NonNull CellIdentityLte sanitizeLocationInfo() {
return new CellIdentityLte(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
mMccStr, mMncStr, mAlphaLong, mAlphaShort);
diff --git a/telephony/java/android/telephony/CellIdentityNr.java b/telephony/java/android/telephony/CellIdentityNr.java
index e3fec7b..f08a580 100644
--- a/telephony/java/android/telephony/CellIdentityNr.java
+++ b/telephony/java/android/telephony/CellIdentityNr.java
@@ -69,7 +69,8 @@
}
/** @hide */
- public CellIdentityNr sanitizeLocationInfo() {
+ @Override
+ public @NonNull CellIdentityNr sanitizeLocationInfo() {
return new CellIdentityNr(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
mMccStr, mMncStr, CellInfo.UNAVAILABLE, mAlphaLong, mAlphaShort);
}
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index 8f812b6..4bb3a95 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -97,7 +97,8 @@
}
/** @hide */
- public CellIdentityTdscdma sanitizeLocationInfo() {
+ @Override
+ public @NonNull CellIdentityTdscdma sanitizeLocationInfo() {
return new CellIdentityTdscdma(mMccStr, mMncStr, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, mAlphaLong, mAlphaShort);
}
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index 556bc32..4ecd134 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -18,7 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.telephony.gsm.GsmCellLocation;
import android.text.TextUtils;
@@ -98,7 +98,8 @@
}
/** @hide */
- public CellIdentityWcdma sanitizeLocationInfo() {
+ @Override
+ public @NonNull CellIdentityWcdma sanitizeLocationInfo() {
return new CellIdentityWcdma(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, mMccStr, mMncStr,
mAlphaLong, mAlphaShort);
diff --git a/telephony/java/android/telephony/CellInfo.java b/telephony/java/android/telephony/CellInfo.java
index ae45307..475c99b 100644
--- a/telephony/java/android/telephony/CellInfo.java
+++ b/telephony/java/android/telephony/CellInfo.java
@@ -18,7 +18,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.hardware.radio.V1_4.CellInfo.Info;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/telephony/java/android/telephony/CellInfoCdma.java b/telephony/java/android/telephony/CellInfoCdma.java
index 30b131f..2b1387c 100644
--- a/telephony/java/android/telephony/CellInfoCdma.java
+++ b/telephony/java/android/telephony/CellInfoCdma.java
@@ -17,11 +17,10 @@
package android.telephony;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
-import android.telephony.Rlog;
/**
* A {@link CellInfo} representing a CDMA cell that provides identity and measurement info.
diff --git a/telephony/java/android/telephony/CellInfoGsm.java b/telephony/java/android/telephony/CellInfoGsm.java
index 137f97e..4f7c7a9 100644
--- a/telephony/java/android/telephony/CellInfoGsm.java
+++ b/telephony/java/android/telephony/CellInfoGsm.java
@@ -17,10 +17,9 @@
package android.telephony;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
-import android.telephony.Rlog;
/**
* A {@link CellInfo} representing a GSM cell that provides identity and measurement info.
diff --git a/telephony/java/android/telephony/CellInfoLte.java b/telephony/java/android/telephony/CellInfoLte.java
index da7b7ab..6d19261 100644
--- a/telephony/java/android/telephony/CellInfoLte.java
+++ b/telephony/java/android/telephony/CellInfoLte.java
@@ -17,7 +17,7 @@
package android.telephony;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/telephony/java/android/telephony/CellLocation.java b/telephony/java/android/telephony/CellLocation.java
index 0133153..1193199 100644
--- a/telephony/java/android/telephony/CellLocation.java
+++ b/telephony/java/android/telephony/CellLocation.java
@@ -16,13 +16,12 @@
package android.telephony;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Bundle;
import android.os.RemoteException;
-import android.os.ServiceManager;
-
-import android.annotation.UnsupportedAppUsage;
import android.telephony.cdma.CdmaCellLocation;
import android.telephony.gsm.GsmCellLocation;
+
import com.android.internal.telephony.ITelephony;
import com.android.internal.telephony.PhoneConstants;
@@ -38,7 +37,11 @@
*/
public static void requestLocationUpdate() {
try {
- ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.getService("phone"));
+ ITelephony phone = ITelephony.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getTelephonyServiceRegisterer()
+ .get());
if (phone != null) {
phone.updateServiceLocation();
}
diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java
index fb16d54..a9f3487 100644
--- a/telephony/java/android/telephony/CellSignalStrengthGsm.java
+++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java
@@ -17,12 +17,11 @@
package android.telephony;
import android.annotation.IntRange;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
-import android.telephony.Rlog;
import java.util.Objects;
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index 8df9d23..a6ba9c2 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -17,7 +17,7 @@
package android.telephony;
import android.annotation.IntRange;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java
index f31fafe..d28d750 100644
--- a/telephony/java/android/telephony/CellSignalStrengthNr.java
+++ b/telephony/java/android/telephony/CellSignalStrengthNr.java
@@ -155,7 +155,17 @@
* @param ss signal strength from modem.
*/
public CellSignalStrengthNr(android.hardware.radio.V1_4.NrSignalStrength ss) {
- this(ss.csiRsrp, ss.csiRsrq, ss.csiSinr, ss.ssRsrp, ss.ssRsrq, ss.ssSinr);
+ this(flip(ss.csiRsrp), flip(ss.csiRsrq), ss.csiSinr, flip(ss.ssRsrp), flip(ss.ssRsrq),
+ ss.ssSinr);
+ }
+
+ /**
+ * Flip sign cell strength value when taking in the value from hal
+ * @param val cell strength value
+ * @return flipped value
+ */
+ private static int flip(int val) {
+ return val != CellInfo.UNAVAILABLE ? -val : val;
}
/**
diff --git a/telephony/java/android/telephony/CellSignalStrengthWcdma.java b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
index 4dc54f0..34b1385 100644
--- a/telephony/java/android/telephony/CellSignalStrengthWcdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
@@ -18,11 +18,10 @@
import android.annotation.IntRange;
import android.annotation.StringDef;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
-import android.telephony.Rlog;
import android.text.TextUtils;
import java.lang.annotation.Retention;
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index 04ec4b6..be85b30 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -18,7 +18,7 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
/**
* Describes the cause of a disconnected call. Those disconnect causes can be converted into a more
@@ -360,6 +360,12 @@
*/
public static final int OUTGOING_EMERGENCY_CALL_PLACED = 80;
+ /**
+ * Indicates that incoming call was rejected by the modem before the call went in ringing
+ */
+ public static final int INCOMING_AUTO_REJECTED = 81;
+
+
//*********************************************************************************************
// When adding a disconnect type:
// 1) Update toString() with the newly added disconnect type.
@@ -536,6 +542,8 @@
return "WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION";
case OUTGOING_EMERGENCY_CALL_PLACED:
return "OUTGOING_EMERGENCY_CALL_PLACED";
+ case INCOMING_AUTO_REJECTED:
+ return "INCOMING_AUTO_REJECTED";
default:
return "INVALID: " + cause;
}
diff --git a/telephony/java/android/telephony/JapanesePhoneNumberFormatter.java b/telephony/java/android/telephony/JapanesePhoneNumberFormatter.java
index 92a674c..1c31368 100644
--- a/telephony/java/android/telephony/JapanesePhoneNumberFormatter.java
+++ b/telephony/java/android/telephony/JapanesePhoneNumberFormatter.java
@@ -16,7 +16,7 @@
package android.telephony;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.text.Editable;
/*
diff --git a/telephony/java/android/telephony/NeighboringCellInfo.java b/telephony/java/android/telephony/NeighboringCellInfo.java
index 023ed30..5f46799 100644
--- a/telephony/java/android/telephony/NeighboringCellInfo.java
+++ b/telephony/java/android/telephony/NeighboringCellInfo.java
@@ -24,7 +24,7 @@
import static android.telephony.TelephonyManager.NETWORK_TYPE_UMTS;
import static android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index fc717e7..cbd5ed6 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -46,13 +46,17 @@
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = "DOMAIN_", value = {DOMAIN_CS, DOMAIN_PS})
+ @IntDef(prefix = "DOMAIN_", value = {DOMAIN_UNKNOWN, DOMAIN_CS, DOMAIN_PS, DOMAIN_CS_PS})
public @interface Domain {}
+ /** Unknown / Unspecified domain */
+ public static final int DOMAIN_UNKNOWN = 0;
/** Circuit switching domain */
- public static final int DOMAIN_CS = 1;
+ public static final int DOMAIN_CS = android.hardware.radio.V1_5.Domain.CS;
/** Packet switching domain */
- public static final int DOMAIN_PS = 2;
+ public static final int DOMAIN_PS = android.hardware.radio.V1_5.Domain.PS;
+ /** Applicable to both CS and PS Domain */
+ public static final int DOMAIN_CS_PS = DOMAIN_CS | DOMAIN_PS;
/**
* Network registration state
@@ -504,11 +508,21 @@
}
}
+ /** @hide */
+ static @NonNull String domainToString(@Domain int domain) {
+ switch (domain) {
+ case DOMAIN_CS: return "CS";
+ case DOMAIN_PS: return "PS";
+ case DOMAIN_CS_PS: return "CS_PS";
+ default: return "UNKNOWN";
+ }
+ }
+
@NonNull
@Override
public String toString() {
return new StringBuilder("NetworkRegistrationInfo{")
- .append(" domain=").append((mDomain == DOMAIN_CS) ? "CS" : "PS")
+ .append(" domain=").append(domainToString(mDomain))
.append(" transportType=").append(
AccessNetworkConstants.transportTypeToString(mTransportType))
.append(" registrationState=").append(registrationStateToString(mRegistrationState))
@@ -646,7 +660,7 @@
* .build();
* </code></pre>
*/
- public static final class Builder{
+ public static final class Builder {
@Domain
private int mDomain;
diff --git a/telephony/java/android/telephony/NetworkScan.java b/telephony/java/android/telephony/NetworkScan.java
index 202da68..b10649c 100644
--- a/telephony/java/android/telephony/NetworkScan.java
+++ b/telephony/java/android/telephony/NetworkScan.java
@@ -17,9 +17,7 @@
package android.telephony;
import android.annotation.IntDef;
-import android.content.Context;
import android.os.RemoteException;
-import android.os.ServiceManager;
import com.android.internal.telephony.ITelephony;
@@ -148,6 +146,9 @@
private ITelephony getITelephony() {
return ITelephony.Stub.asInterface(
- ServiceManager.getService(Context.TELEPHONY_SERVICE));
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getTelephonyServiceRegisterer()
+ .get());
}
}
diff --git a/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java b/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java
index ac6bcaa..d4ed860 100644
--- a/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java
+++ b/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java
@@ -16,15 +16,14 @@
package android.telephony;
-import com.android.i18n.phonenumbers.AsYouTypeFormatter;
-import com.android.i18n.phonenumbers.PhoneNumberUtil;
-
-import android.annotation.UnsupportedAppUsage;
-import android.telephony.PhoneNumberUtils;
+import android.compat.annotation.UnsupportedAppUsage;
import android.text.Editable;
import android.text.Selection;
import android.text.TextWatcher;
+import com.android.i18n.phonenumbers.AsYouTypeFormatter;
+import com.android.i18n.phonenumbers.PhoneNumberUtil;
+
import java.util.Locale;
/**
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 67afa7d..6e86a42 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -21,7 +21,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
diff --git a/telephony/java/android/telephony/PreciseCallState.java b/telephony/java/android/telephony/PreciseCallState.java
index bfa6326..98eeacf 100644
--- a/telephony/java/android/telephony/PreciseCallState.java
+++ b/telephony/java/android/telephony/PreciseCallState.java
@@ -19,14 +19,12 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.Annotation.DisconnectCauses;
import android.telephony.Annotation.PreciseCallStates;
import android.telephony.Annotation.PreciseDisconnectCauses;
-import android.telephony.DisconnectCause;
-import android.telephony.PreciseDisconnectCause;
import java.util.Objects;
diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java
index 0610796..31434c1 100644
--- a/telephony/java/android/telephony/PreciseDataConnectionState.java
+++ b/telephony/java/android/telephony/PreciseDataConnectionState.java
@@ -20,7 +20,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.net.LinkProperties;
import android.os.Build;
import android.os.Parcel;
diff --git a/telephony/java/android/telephony/RadioAccessFamily.java b/telephony/java/android/telephony/RadioAccessFamily.java
index 28f6515..bc84738 100644
--- a/telephony/java/android/telephony/RadioAccessFamily.java
+++ b/telephony/java/android/telephony/RadioAccessFamily.java
@@ -16,9 +16,7 @@
package android.telephony;
-import android.annotation.UnsupportedAppUsage;
-import android.hardware.radio.V1_0.RadioTechnology;
-import android.hardware.radio.V1_4.CellInfo.Info;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 599cd9f..5b12aed 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -21,7 +21,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
@@ -545,7 +545,7 @@
* @see #STATE_EMERGENCY_ONLY
* @see #STATE_POWER_OFF
*
- * @return current data registration state {@link RegState}
+ * @return current data registration state
*
* @hide
*/
@@ -562,7 +562,7 @@
* @see #STATE_EMERGENCY_ONLY
* @see #STATE_POWER_OFF
*
- * @return current data registration state {@link RegState}
+ * @return current data registration state
*
* @hide
*/
@@ -1886,7 +1886,7 @@
synchronized (mNetworkRegistrationInfos) {
for (NetworkRegistrationInfo networkRegistrationInfo : mNetworkRegistrationInfos) {
- if (networkRegistrationInfo.getDomain() == domain) {
+ if ((networkRegistrationInfo.getDomain() & domain) != 0) {
list.add(new NetworkRegistrationInfo(networkRegistrationInfo));
}
}
@@ -1902,7 +1902,6 @@
* @param transportType The transport type
* @return The matching {@link NetworkRegistrationInfo}
* @hide
- *
*/
@Nullable
@SystemApi
@@ -1911,7 +1910,7 @@
synchronized (mNetworkRegistrationInfos) {
for (NetworkRegistrationInfo networkRegistrationInfo : mNetworkRegistrationInfos) {
if (networkRegistrationInfo.getTransportType() == transportType
- && networkRegistrationInfo.getDomain() == domain) {
+ && (networkRegistrationInfo.getDomain() & domain) != 0) {
return new NetworkRegistrationInfo(networkRegistrationInfo);
}
}
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index 9aafc1b..3350a33 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -17,7 +17,8 @@
package android.telephony;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.annotation.SystemApi;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
@@ -275,8 +276,8 @@
*
* @hide
*/
- @UnsupportedAppUsage
- public SignalStrength(SignalStrength s) {
+ @SystemApi
+ public SignalStrength(@NonNull SignalStrength s) {
copyFrom(s);
}
@@ -332,17 +333,16 @@
/**
* {@link Parcelable.Creator}
*
- * @hide
*/
- @UnsupportedAppUsage
- public static final @android.annotation.NonNull Parcelable.Creator<SignalStrength> CREATOR = new Parcelable.Creator() {
- public SignalStrength createFromParcel(Parcel in) {
- return new SignalStrength(in);
- }
+ public static final @android.annotation.NonNull Parcelable.Creator<SignalStrength> CREATOR =
+ new Parcelable.Creator<SignalStrength>() {
+ public SignalStrength createFromParcel(Parcel in) {
+ return new SignalStrength(in);
+ }
- public SignalStrength[] newArray(int size) {
- return new SignalStrength[size];
- }
+ public SignalStrength[] newArray(int size) {
+ return new SignalStrength[size];
+ }
};
/**
diff --git a/telephony/java/android/telephony/SmsCbMessage.java b/telephony/java/android/telephony/SmsCbMessage.java
index 045d1eb..3c67094 100644
--- a/telephony/java/android/telephony/SmsCbMessage.java
+++ b/telephony/java/android/telephony/SmsCbMessage.java
@@ -557,7 +557,7 @@
public ContentValues getContentValues() {
ContentValues cv = new ContentValues(16);
cv.put(CellBroadcasts.SLOT_INDEX, mSlotIndex);
- cv.put(CellBroadcasts.SUB_ID, mSubId);
+ cv.put(CellBroadcasts.SUBSCRIPTION_ID, mSubId);
cv.put(CellBroadcasts.GEOGRAPHICAL_SCOPE, mGeographicalScope);
if (mLocation.getPlmn() != null) {
cv.put(CellBroadcasts.PLMN, mLocation.getPlmn());
@@ -621,7 +621,7 @@
int format = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.MESSAGE_FORMAT));
int priority = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.MESSAGE_PRIORITY));
int slotIndex = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.SLOT_INDEX));
- int subId = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.SUB_ID));
+ int subId = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.SUBSCRIPTION_ID));
String plmn;
int plmnColumn = cursor.getColumnIndex(CellBroadcasts.PLMN);
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 7215ef8..eb328a7 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -25,9 +25,9 @@
import android.annotation.SuppressAutoDoc;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
import android.app.ActivityThread;
import android.app.PendingIntent;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.pm.PackageManager;
import android.database.CursorWindow;
@@ -35,7 +35,6 @@
import android.os.Build;
import android.os.Bundle;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -745,7 +744,11 @@
"Invalid pdu format. format must be either 3gpp or 3gpp2");
}
try {
- ISms iSms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+ ISms iSms = ISms.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSmsServiceRegisterer()
+ .get());
if (iSms != null) {
iSms.injectSmsPduForSubscriber(
getSubscriptionId(), pdu, format, receivedIntent);
@@ -1535,7 +1538,10 @@
private static ITelephony getITelephony() {
ITelephony binder = ITelephony.Stub.asInterface(
- ServiceManager.getService(Context.TELEPHONY_SERVICE));
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getTelephonyServiceRegisterer()
+ .get());
if (binder == null) {
throw new RuntimeException("Could not find Telephony Service.");
}
@@ -1573,7 +1579,11 @@
}
private static ISms getISmsService() {
- return ISms.Stub.asInterface(ServiceManager.getService("isms"));
+ return ISms.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSmsServiceRegisterer()
+ .get());
}
/**
@@ -2007,7 +2017,11 @@
public boolean isSMSPromptEnabled() {
ISms iSms = null;
try {
- iSms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+ iSms = ISms.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSmsServiceRegisterer()
+ .get());
return iSms.isSMSPromptEnabled();
} catch (RemoteException ex) {
return false;
@@ -2504,22 +2518,31 @@
public static final String MESSAGE_STATUS_READ = "read";
/**
- * Get carrier-dependent configuration values.
+ * Get carrier-dependent MMS configuration values.
*
* <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
- * applications or the Telephony framework and will never trigger an SMS disambiguation
- * dialog. If this method is called on a device that has multiple active subscriptions, this
- * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
- * default subscription is defined, the subscription ID associated with this message will be
- * INVALID, which will result in the operation being completed on the subscription associated
- * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
- * operation is performed on the correct subscription.
+ * applications or the Telephony framework and will never trigger an SMS disambiguation dialog.
+ * If this method is called on a device that has multiple active subscriptions, this {@link
+ * SmsManager} instance has been created with {@link #getDefault()}, and no user-defined default
+ * subscription is defined, the subscription ID associated with this message will be INVALID,
+ * which will result in the operation being completed on the subscription associated with
+ * logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the operation is
+ * performed on the correct subscription.
* </p>
*
- * @return bundle key/values pairs of configuration values
+ * @return the bundle key/values pairs that contains MMS configuration values
*/
+ @Nullable
public Bundle getCarrierConfigValues() {
- return MmsManager.getInstance().getCarrierConfigValues(getSubscriptionId());
+ try {
+ ISms iSms = getISmsService();
+ if (iSms != null) {
+ return iSms.getCarrierConfigValuesForSubscriber(getSubscriptionId());
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ return null;
}
/**
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index 2897358..c0bc29d 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -20,7 +20,7 @@
import android.annotation.Nullable;
import android.annotation.StringDef;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.Resources;
import android.os.Binder;
import android.text.TextUtils;
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 94085e9..2c0a1c9 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -18,7 +18,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 7380b31..88501a7 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -33,9 +33,9 @@
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
import android.app.BroadcastOptions;
import android.app.PendingIntent;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
@@ -53,7 +53,6 @@
import android.os.ParcelUuid;
import android.os.Process;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.provider.Telephony.SimInfo;
import android.telephony.euicc.EuiccManager;
import android.telephony.ims.ImsMmTelManager;
@@ -265,7 +264,7 @@
* <P>Type: TEXT (String)</P>
*/
/** @hide */
- public static final String UNIQUE_KEY_SUBSCRIPTION_ID = "_id";
+ public static final String UNIQUE_KEY_SUBSCRIPTION_ID = SimInfo.UNIQUE_KEY_SUBSCRIPTION_ID;
/**
* TelephonyProvider column name for a unique identifier for the subscription within the
@@ -274,18 +273,18 @@
* <P>Type: TEXT (String)</P>
*/
/** @hide */
- public static final String ICC_ID = "icc_id";
+ public static final String ICC_ID = SimInfo.ICC_ID;
/**
* TelephonyProvider column name for user SIM_SlOT_INDEX
* <P>Type: INTEGER (int)</P>
*/
/** @hide */
- public static final String SIM_SLOT_INDEX = "sim_id";
+ public static final String SIM_SLOT_INDEX = SimInfo.SIM_SLOT_INDEX;
/** SIM is not inserted */
/** @hide */
- public static final int SIM_NOT_INSERTED = -1;
+ public static final int SIM_NOT_INSERTED = SimInfo.SIM_NOT_INSERTED;
/**
* The slot-index for Bluetooth Remote-SIM subscriptions
@@ -300,23 +299,7 @@
* Default value is 0.
*/
/** @hide */
- public static final String SUBSCRIPTION_TYPE = "subscription_type";
-
- /**
- * TelephonyProvider column name white_listed_apn_data.
- * It's a bitmask of APN types that will be allowed on this subscription even if it's metered
- * and mobile data is turned off by the user.
- * <P>Type: INTEGER (int)</P> For example, if TYPE_MMS is is true, Telephony will allow MMS
- * data connection to setup even if MMS is metered and mobile_data is turned off on that
- * subscription.
- *
- * Default value is 0.
- *
- * @deprecated Replaced by {@link #DATA_ENABLED_OVERRIDE_RULES}
- * @hide
- */
- @Deprecated
- public static final String WHITE_LISTED_APN_DATA = "white_listed_apn_data";
+ public static final String SUBSCRIPTION_TYPE = SimInfo.SUBSCRIPTION_TYPE;
/**
* TelephonyProvider column name data_enabled_override_rules.
@@ -329,7 +312,15 @@
*
* @hide
*/
- public static final String DATA_ENABLED_OVERRIDE_RULES = "data_enabled_override_rules";
+ public static final String DATA_ENABLED_OVERRIDE_RULES = SimInfo.DATA_ENABLED_OVERRIDE_RULES;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"SUBSCRIPTION_TYPE_"},
+ value = {
+ SUBSCRIPTION_TYPE_LOCAL_SIM,
+ SUBSCRIPTION_TYPE_REMOTE_SIM})
+ public @interface SubscriptionType {}
/**
* This constant is to designate a subscription as a Local-SIM Subscription.
@@ -337,7 +328,7 @@
* device.
* </p>
*/
- public static final int SUBSCRIPTION_TYPE_LOCAL_SIM = 0;
+ public static final int SUBSCRIPTION_TYPE_LOCAL_SIM = SimInfo.SUBSCRIPTION_TYPE_LOCAL_SIM;
/**
* This constant is to designate a subscription as a Remote-SIM Subscription.
@@ -363,29 +354,21 @@
* was never seen before.
* </p>
*/
- public static final int SUBSCRIPTION_TYPE_REMOTE_SIM = 1;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = {"SUBSCRIPTION_TYPE_"},
- value = {
- SUBSCRIPTION_TYPE_LOCAL_SIM,
- SUBSCRIPTION_TYPE_REMOTE_SIM})
- public @interface SubscriptionType {}
+ public static final int SUBSCRIPTION_TYPE_REMOTE_SIM = SimInfo.SUBSCRIPTION_TYPE_REMOTE_SIM;
/**
* TelephonyProvider column name for user displayed name.
* <P>Type: TEXT (String)</P>
*/
/** @hide */
- public static final String DISPLAY_NAME = "display_name";
+ public static final String DISPLAY_NAME = SimInfo.DISPLAY_NAME;
/**
* TelephonyProvider column name for the service provider name for the SIM.
* <P>Type: TEXT (String)</P>
*/
/** @hide */
- public static final String CARRIER_NAME = "carrier_name";
+ public static final String CARRIER_NAME = SimInfo.CARRIER_NAME;
/**
* Default name resource
@@ -399,44 +382,44 @@
*
* @hide
*/
- public static final String NAME_SOURCE = "name_source";
+ public static final String NAME_SOURCE = SimInfo.NAME_SOURCE;
/**
* The name_source is the default, which is from the carrier id.
* @hide
*/
- public static final int NAME_SOURCE_DEFAULT_SOURCE = 0;
+ public static final int NAME_SOURCE_DEFAULT = SimInfo.NAME_SOURCE_DEFAULT;
/**
* The name_source is from SIM EF_SPN.
* @hide
*/
- public static final int NAME_SOURCE_SIM_SPN = 1;
+ public static final int NAME_SOURCE_SIM_SPN = SimInfo.NAME_SOURCE_SIM_SPN;
/**
* The name_source is from user input
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- public static final int NAME_SOURCE_USER_INPUT = 2;
+ public static final int NAME_SOURCE_USER_INPUT = SimInfo.NAME_SOURCE_USER_INPUT;
/**
* The name_source is carrier (carrier app, carrier config, etc.)
* @hide
*/
- public static final int NAME_SOURCE_CARRIER = 3;
+ public static final int NAME_SOURCE_CARRIER = SimInfo.NAME_SOURCE_CARRIER;
/**
* The name_source is from SIM EF_PNN.
* @hide
*/
- public static final int NAME_SOURCE_SIM_PNN = 4;
+ public static final int NAME_SOURCE_SIM_PNN = SimInfo.NAME_SOURCE_SIM_PNN;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"NAME_SOURCE_"},
value = {
- NAME_SOURCE_DEFAULT_SOURCE,
+ NAME_SOURCE_DEFAULT,
NAME_SOURCE_SIM_SPN,
NAME_SOURCE_USER_INPUT,
NAME_SOURCE_CARRIER,
@@ -449,67 +432,30 @@
* <P>Type: INTEGER (int)</P>
*/
/** @hide */
- public static final String COLOR = "color";
-
- /** @hide */
- public static final int COLOR_1 = 0;
-
- /** @hide */
- public static final int COLOR_2 = 1;
-
- /** @hide */
- public static final int COLOR_3 = 2;
-
- /** @hide */
- public static final int COLOR_4 = 3;
-
- /** @hide */
- public static final int COLOR_DEFAULT = COLOR_1;
+ public static final String COLOR = SimInfo.COLOR;
/**
* TelephonyProvider column name for the phone number of a SIM.
* <P>Type: TEXT (String)</P>
*/
/** @hide */
- public static final String NUMBER = "number";
+ public static final String NUMBER = SimInfo.NUMBER;
/**
- * TelephonyProvider column name for the number display format of a SIM.
+ * TelephonyProvider column name for whether data roaming is enabled.
* <P>Type: INTEGER (int)</P>
*/
/** @hide */
- public static final String DISPLAY_NUMBER_FORMAT = "display_number_format";
-
- /** @hide */
- public static final int DISPLAY_NUMBER_NONE = 0;
-
- /** @hide */
- public static final int DISPLAY_NUMBER_FIRST = 1;
-
- /** @hide */
- public static final int DISPLAY_NUMBER_LAST = 2;
-
- /** @hide */
- public static final int DISPLAY_NUMBER_DEFAULT = DISPLAY_NUMBER_FIRST;
-
- /**
- * TelephonyProvider column name for permission for data roaming of a SIM.
- * <P>Type: INTEGER (int)</P>
- */
- /** @hide */
- public static final String DATA_ROAMING = "data_roaming";
+ public static final String DATA_ROAMING = SimInfo.DATA_ROAMING;
/** Indicates that data roaming is enabled for a subscription */
- public static final int DATA_ROAMING_ENABLE = 1;
+ public static final int DATA_ROAMING_ENABLE = SimInfo.DATA_ROAMING_ENABLE;
/** Indicates that data roaming is disabled for a subscription */
- public static final int DATA_ROAMING_DISABLE = 0;
+ public static final int DATA_ROAMING_DISABLE = SimInfo.DATA_ROAMING_DISABLE;
/** @hide */
- public static final int DATA_ROAMING_DEFAULT = DATA_ROAMING_DISABLE;
-
- /** @hide */
- public static final int SIM_PROVISIONED = 0;
+ public static final int DATA_ROAMING_DEFAULT = SimInfo.DATA_ROAMING_DEFAULT;
/**
* TelephonyProvider column name for subscription carrier id.
@@ -517,61 +463,61 @@
* <p>Type: INTEGER (int) </p>
* @hide
*/
- public static final String CARRIER_ID = "carrier_id";
+ public static final String CARRIER_ID = SimInfo.CARRIER_ID;
/**
* @hide A comma-separated list of EHPLMNs associated with the subscription
* <P>Type: TEXT (String)</P>
*/
- public static final String EHPLMNS = "ehplmns";
+ public static final String EHPLMNS = SimInfo.EHPLMNS;
/**
* @hide A comma-separated list of HPLMNs associated with the subscription
* <P>Type: TEXT (String)</P>
*/
- public static final String HPLMNS = "hplmns";
+ public static final String HPLMNS = SimInfo.HPLMNS;
/**
* TelephonyProvider column name for the MCC associated with a SIM, stored as a string.
* <P>Type: TEXT (String)</P>
* @hide
*/
- public static final String MCC_STRING = "mcc_string";
+ public static final String MCC_STRING = SimInfo.MCC_STRING;
/**
* TelephonyProvider column name for the MNC associated with a SIM, stored as a string.
* <P>Type: TEXT (String)</P>
* @hide
*/
- public static final String MNC_STRING = "mnc_string";
+ public static final String MNC_STRING = SimInfo.MNC_STRING;
/**
* TelephonyProvider column name for the MCC associated with a SIM.
* <P>Type: INTEGER (int)</P>
* @hide
*/
- public static final String MCC = "mcc";
+ public static final String MCC = SimInfo.MCC;
/**
* TelephonyProvider column name for the MNC associated with a SIM.
* <P>Type: INTEGER (int)</P>
* @hide
*/
- public static final String MNC = "mnc";
+ public static final String MNC = SimInfo.MNC;
/**
* TelephonyProvider column name for the iso country code associated with a SIM.
* <P>Type: TEXT (String)</P>
* @hide
*/
- public static final String ISO_COUNTRY_CODE = "iso_country_code";
+ public static final String ISO_COUNTRY_CODE = SimInfo.ISO_COUNTRY_CODE;
/**
* TelephonyProvider column name for the sim provisioning status associated with a SIM.
* <P>Type: INTEGER (int)</P>
* @hide
*/
- public static final String SIM_PROVISIONING_STATUS = "sim_provisioning_status";
+ public static final String SIM_PROVISIONING_STATUS = SimInfo.SIM_PROVISIONING_STATUS;
/**
* TelephonyProvider column name for whether a subscription is embedded (that is, present on an
@@ -579,7 +525,7 @@
* <p>Type: INTEGER (int), 1 for embedded or 0 for non-embedded.
* @hide
*/
- public static final String IS_EMBEDDED = "is_embedded";
+ public static final String IS_EMBEDDED = SimInfo.IS_EMBEDDED;
/**
* TelephonyProvider column name for SIM card identifier. For UICC card it is the ICCID of the
@@ -587,7 +533,7 @@
* <P>Type: TEXT (String)</P>
* @hide
*/
- public static final String CARD_ID = "card_id";
+ public static final String CARD_ID = SimInfo.CARD_ID;
/**
* TelephonyProvider column name for the encoded {@link UiccAccessRule}s from
@@ -595,7 +541,7 @@
* <p>TYPE: BLOB
* @hide
*/
- public static final String ACCESS_RULES = "access_rules";
+ public static final String ACCESS_RULES = SimInfo.ACCESS_RULES;
/**
* TelephonyProvider column name for the encoded {@link UiccAccessRule}s from
@@ -605,7 +551,7 @@
* @hide
*/
public static final String ACCESS_RULES_FROM_CARRIER_CONFIGS =
- "access_rules_from_carrier_configs";
+ SimInfo.ACCESS_RULES_FROM_CARRIER_CONFIGS;
/**
* TelephonyProvider column name identifying whether an embedded subscription is on a removable
@@ -615,79 +561,79 @@
* <p>TYPE: INTEGER (int), 1 for removable or 0 for non-removable.
* @hide
*/
- public static final String IS_REMOVABLE = "is_removable";
+ public static final String IS_REMOVABLE = SimInfo.IS_REMOVABLE;
/**
* TelephonyProvider column name for extreme threat in CB settings
* @hide
*/
- public static final String CB_EXTREME_THREAT_ALERT = "enable_cmas_extreme_threat_alerts";
+ public static final String CB_EXTREME_THREAT_ALERT = SimInfo.CB_EXTREME_THREAT_ALERT;
/**
* TelephonyProvider column name for severe threat in CB settings
*@hide
*/
- public static final String CB_SEVERE_THREAT_ALERT = "enable_cmas_severe_threat_alerts";
+ public static final String CB_SEVERE_THREAT_ALERT = SimInfo.CB_SEVERE_THREAT_ALERT;
/**
* TelephonyProvider column name for amber alert in CB settings
*@hide
*/
- public static final String CB_AMBER_ALERT = "enable_cmas_amber_alerts";
+ public static final String CB_AMBER_ALERT = SimInfo.CB_AMBER_ALERT;
/**
* TelephonyProvider column name for emergency alert in CB settings
*@hide
*/
- public static final String CB_EMERGENCY_ALERT = "enable_emergency_alerts";
+ public static final String CB_EMERGENCY_ALERT = SimInfo.CB_EMERGENCY_ALERT;
/**
* TelephonyProvider column name for alert sound duration in CB settings
*@hide
*/
- public static final String CB_ALERT_SOUND_DURATION = "alert_sound_duration";
+ public static final String CB_ALERT_SOUND_DURATION = SimInfo.CB_ALERT_SOUND_DURATION;
/**
* TelephonyProvider column name for alert reminder interval in CB settings
*@hide
*/
- public static final String CB_ALERT_REMINDER_INTERVAL = "alert_reminder_interval";
+ public static final String CB_ALERT_REMINDER_INTERVAL = SimInfo.CB_ALERT_REMINDER_INTERVAL;
/**
* TelephonyProvider column name for enabling vibrate in CB settings
*@hide
*/
- public static final String CB_ALERT_VIBRATE = "enable_alert_vibrate";
+ public static final String CB_ALERT_VIBRATE = SimInfo.CB_ALERT_VIBRATE;
/**
* TelephonyProvider column name for enabling alert speech in CB settings
*@hide
*/
- public static final String CB_ALERT_SPEECH = "enable_alert_speech";
+ public static final String CB_ALERT_SPEECH = SimInfo.CB_ALERT_SPEECH;
/**
* TelephonyProvider column name for ETWS test alert in CB settings
*@hide
*/
- public static final String CB_ETWS_TEST_ALERT = "enable_etws_test_alerts";
+ public static final String CB_ETWS_TEST_ALERT = SimInfo.CB_ETWS_TEST_ALERT;
/**
* TelephonyProvider column name for enable channel50 alert in CB settings
*@hide
*/
- public static final String CB_CHANNEL_50_ALERT = "enable_channel_50_alerts";
+ public static final String CB_CHANNEL_50_ALERT = SimInfo.CB_CHANNEL_50_ALERT;
/**
* TelephonyProvider column name for CMAS test alert in CB settings
*@hide
*/
- public static final String CB_CMAS_TEST_ALERT= "enable_cmas_test_alerts";
+ public static final String CB_CMAS_TEST_ALERT = SimInfo.CB_CMAS_TEST_ALERT;
/**
* TelephonyProvider column name for Opt out dialog in CB settings
*@hide
*/
- public static final String CB_OPT_OUT_DIALOG = "show_cmas_opt_out_dialog";
+ public static final String CB_OPT_OUT_DIALOG = SimInfo.CB_OPT_OUT_DIALOG;
/**
* TelephonyProvider column name for enable Volte.
@@ -696,37 +642,37 @@
* {@link CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL}.
*@hide
*/
- public static final String ENHANCED_4G_MODE_ENABLED = "volte_vt_enabled";
+ public static final String ENHANCED_4G_MODE_ENABLED = SimInfo.ENHANCED_4G_MODE_ENABLED;
/**
* TelephonyProvider column name for enable VT (Video Telephony over IMS)
*@hide
*/
- public static final String VT_IMS_ENABLED = "vt_ims_enabled";
+ public static final String VT_IMS_ENABLED = SimInfo.VT_IMS_ENABLED;
/**
* TelephonyProvider column name for enable Wifi calling
*@hide
*/
- public static final String WFC_IMS_ENABLED = "wfc_ims_enabled";
+ public static final String WFC_IMS_ENABLED = SimInfo.WFC_IMS_ENABLED;
/**
* TelephonyProvider column name for Wifi calling mode
*@hide
*/
- public static final String WFC_IMS_MODE = "wfc_ims_mode";
+ public static final String WFC_IMS_MODE = SimInfo.WFC_IMS_MODE;
/**
* TelephonyProvider column name for Wifi calling mode in roaming
*@hide
*/
- public static final String WFC_IMS_ROAMING_MODE = "wfc_ims_roaming_mode";
+ public static final String WFC_IMS_ROAMING_MODE = SimInfo.WFC_IMS_ROAMING_MODE;
/**
* TelephonyProvider column name for enable Wifi calling in roaming
*@hide
*/
- public static final String WFC_IMS_ROAMING_ENABLED = "wfc_ims_roaming_enabled";
+ public static final String WFC_IMS_ROAMING_ENABLED = SimInfo.WFC_IMS_ROAMING_ENABLED;
/**
* TelephonyProvider column name for whether a subscription is opportunistic, that is,
@@ -735,7 +681,7 @@
* <p>Type: INTEGER (int), 1 for opportunistic or 0 for non-opportunistic.
* @hide
*/
- public static final String IS_OPPORTUNISTIC = "is_opportunistic";
+ public static final String IS_OPPORTUNISTIC = SimInfo.IS_OPPORTUNISTIC;
/**
* TelephonyProvider column name for group ID. Subscriptions with same group ID
@@ -744,7 +690,7 @@
*
* @hide
*/
- public static final String GROUP_UUID = "group_uuid";
+ public static final String GROUP_UUID = SimInfo.GROUP_UUID;
/**
* TelephonyProvider column name for group owner. It's the package name who created
@@ -752,14 +698,7 @@
*
* @hide
*/
- public static final String GROUP_OWNER = "group_owner";
-
- /**
- * TelephonyProvider column name for whether a subscription is metered or not, that is, whether
- * the network it connects to charges for subscription or not. For example, paid CBRS or unpaid.
- * @hide
- */
- public static final String IS_METERED = "is_metered";
+ public static final String GROUP_OWNER = SimInfo.GROUP_OWNER;
/**
* TelephonyProvider column name for the profile class of a subscription
@@ -767,7 +706,7 @@
* <P>Type: INTEGER (int)</P>
* @hide
*/
- public static final String PROFILE_CLASS = "profile_class";
+ public static final String PROFILE_CLASS = SimInfo.PROFILE_CLASS;
/**
* Profile class of the subscription
@@ -775,11 +714,11 @@
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = { "PROFILE_CLASS_" }, value = {
- PROFILE_CLASS_TESTING,
- PROFILE_CLASS_PROVISIONING,
- PROFILE_CLASS_OPERATIONAL,
- PROFILE_CLASS_UNSET,
- PROFILE_CLASS_DEFAULT
+ SimInfo.PROFILE_CLASS_TESTING,
+ SimInfo.PROFILE_CLASS_PROVISIONING,
+ SimInfo.PROFILE_CLASS_OPERATIONAL,
+ SimInfo.PROFILE_CLASS_UNSET,
+ SimInfo.PROFILE_CLASS_DEFAULT
})
public @interface ProfileClass {}
@@ -791,7 +730,7 @@
* @hide
*/
@SystemApi
- public static final int PROFILE_CLASS_TESTING = 0;
+ public static final int PROFILE_CLASS_TESTING = SimInfo.PROFILE_CLASS_TESTING;
/**
* A provisioning profile is pre-loaded onto the eUICC and
@@ -800,7 +739,7 @@
* @hide
*/
@SystemApi
- public static final int PROFILE_CLASS_PROVISIONING = 1;
+ public static final int PROFILE_CLASS_PROVISIONING = SimInfo.PROFILE_CLASS_PROVISIONING;
/**
* An operational profile can be pre-loaded or downloaded
@@ -809,7 +748,7 @@
* @hide
*/
@SystemApi
- public static final int PROFILE_CLASS_OPERATIONAL = 2;
+ public static final int PROFILE_CLASS_OPERATIONAL = SimInfo.PROFILE_CLASS_OPERATIONAL;
/**
* The profile class is unset. This occurs when profile class
@@ -818,14 +757,14 @@
* @hide
*/
@SystemApi
- public static final int PROFILE_CLASS_UNSET = -1;
+ public static final int PROFILE_CLASS_UNSET = SimInfo.PROFILE_CLASS_UNSET;
/**
* Default profile class
* @hide
*/
@SystemApi
- public static final int PROFILE_CLASS_DEFAULT = PROFILE_CLASS_UNSET;
+ public static final int PROFILE_CLASS_DEFAULT = SimInfo.PROFILE_CLASS_DEFAULT;
/**
* IMSI (International Mobile Subscriber Identity).
@@ -833,13 +772,13 @@
* @hide
*/
//TODO: add @SystemApi
- public static final String IMSI = "imsi";
+ public static final String IMSI = SimInfo.IMSI;
/**
* Whether uicc applications is set to be enabled or disabled. By default it's enabled.
* @hide
*/
- public static final String UICC_APPLICATIONS_ENABLED = "uicc_applications_enabled";
+ public static final String UICC_APPLICATIONS_ENABLED = SimInfo.UICC_APPLICATIONS_ENABLED;
/**
* Broadcast Action: The user has changed one of the default subs related to
@@ -1023,8 +962,11 @@
private final INetworkPolicyManager getNetworkPolicy() {
if (mNetworkPolicy == null) {
- mNetworkPolicy = INetworkPolicyManager.Stub
- .asInterface(ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
+ mNetworkPolicy = INetworkPolicyManager.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getNetworkPolicyServiceRegisterer()
+ .get());
}
return mNetworkPolicy;
}
@@ -1185,7 +1127,11 @@
SubscriptionInfo subInfo = null;
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
subInfo = iSub.getActiveSubscriptionInfo(subId, mContext.getOpPackageName(),
mContext.getFeatureId());
@@ -1199,12 +1145,17 @@
}
/**
- * Get the active SubscriptionInfo associated with the iccId
+ * Gets an active SubscriptionInfo {@link SubscriptionInfo} associated with the Sim IccId.
+ *
* @param iccId the IccId of SIM card
* @return SubscriptionInfo, maybe null if its not active
+ *
* @hide
*/
- public SubscriptionInfo getActiveSubscriptionInfoForIccIndex(String iccId) {
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @Nullable
+ @SystemApi
+ public SubscriptionInfo getActiveSubscriptionInfoForIcc(@NonNull String iccId) {
if (VDBG) logd("[getActiveSubscriptionInfoForIccIndex]+ iccId=" + iccId);
if (iccId == null) {
logd("[getActiveSubscriptionInfoForIccIndex]- null iccid");
@@ -1214,7 +1165,11 @@
SubscriptionInfo result = null;
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
result = iSub.getActiveSubscriptionInfoForIccId(iccId, mContext.getOpPackageName(),
mContext.getFeatureId());
@@ -1248,7 +1203,11 @@
SubscriptionInfo result = null;
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
result = iSub.getActiveSubscriptionInfoForSimSlotIndex(slotIndex,
mContext.getOpPackageName(), mContext.getFeatureId());
@@ -1272,7 +1231,11 @@
List<SubscriptionInfo> result = null;
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
result = iSub.getAllSubInfoList(mContext.getOpPackageName(),
mContext.getFeatureId());
@@ -1323,6 +1286,11 @@
* The records will be sorted by {@link SubscriptionInfo#getSimSlotIndex}
* then by {@link SubscriptionInfo#getSubscriptionId}.
*
+ * Hidden subscriptions refer to those are not meant visible to the users.
+ * For example, an opportunistic subscription that is grouped with other
+ * subscriptions should remain invisible to users as they are only functionally
+ * supplementary to primary ones.
+ *
* <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
* or that the calling app has carrier privileges (see
* {@link TelephonyManager#hasCarrierPrivileges}). In the latter case, only records accessible
@@ -1348,7 +1316,11 @@
List<SubscriptionInfo> activeList = null;
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
activeList = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName(),
mContext.getFeatureId());
@@ -1399,7 +1371,11 @@
List<SubscriptionInfo> result = null;
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
result = iSub.getAvailableSubscriptionInfoList(mContext.getOpPackageName(),
mContext.getFeatureId());
@@ -1438,7 +1414,11 @@
List<SubscriptionInfo> result = null;
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
result = iSub.getAccessibleSubscriptionInfoList(mContext.getOpPackageName());
}
@@ -1467,7 +1447,11 @@
public void requestEmbeddedSubscriptionInfoListRefresh() {
int cardId = TelephonyManager.from(mContext).getCardIdForDefaultEuicc();
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
iSub.requestEmbeddedSubscriptionInfoListRefresh(cardId);
}
@@ -1496,7 +1480,11 @@
@SystemApi
public void requestEmbeddedSubscriptionInfoListRefresh(int cardId) {
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
iSub.requestEmbeddedSubscriptionInfoListRefresh(cardId);
}
@@ -1517,7 +1505,11 @@
int result = 0;
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
result = iSub.getAllSubInfoCount(mContext.getOpPackageName(),
mContext.getFeatureId());
@@ -1546,7 +1538,11 @@
int result = 0;
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
result = iSub.getActiveSubInfoCount(mContext.getOpPackageName(),
mContext.getFeatureId());
@@ -1567,7 +1563,11 @@
int result = 0;
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
result = iSub.getActiveSubInfoCountMax();
}
@@ -1624,7 +1624,11 @@
}
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub == null) {
Log.e(LOG_TAG, "[addSubscriptionInfoRecord]- ISub service is null");
return;
@@ -1658,7 +1662,11 @@
}
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub == null) {
Log.e(LOG_TAG, "[removeSubscriptionInfoRecord]- ISub service is null");
return;
@@ -1761,7 +1769,11 @@
int result = INVALID_SIM_SLOT_INDEX;
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
result = iSub.getSlotIndex(subscriptionId);
}
@@ -1795,7 +1807,11 @@
int[] subId = null;
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
subId = iSub.getSubId(slotIndex);
}
@@ -1819,7 +1835,11 @@
int result = INVALID_PHONE_INDEX;
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
result = iSub.getPhoneId(subId);
}
@@ -1853,7 +1873,11 @@
int subId = INVALID_SUBSCRIPTION_ID;
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
subId = iSub.getDefaultSubId();
}
@@ -1876,7 +1900,11 @@
int subId = INVALID_SUBSCRIPTION_ID;
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
subId = iSub.getDefaultVoiceSubId();
}
@@ -1906,7 +1934,11 @@
public void setDefaultVoiceSubscriptionId(int subscriptionId) {
if (VDBG) logd("setDefaultVoiceSubId sub id = " + subscriptionId);
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
iSub.setDefaultVoiceSubId(subscriptionId);
}
@@ -1954,7 +1986,11 @@
int subId = INVALID_SUBSCRIPTION_ID;
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
subId = iSub.getDefaultSmsSubId();
}
@@ -1980,7 +2016,11 @@
public void setDefaultSmsSubId(int subscriptionId) {
if (VDBG) logd("setDefaultSmsSubId sub id = " + subscriptionId);
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
iSub.setDefaultSmsSubId(subscriptionId);
}
@@ -2018,7 +2058,11 @@
int subId = INVALID_SUBSCRIPTION_ID;
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
subId = iSub.getDefaultDataSubId();
}
@@ -2044,7 +2088,11 @@
public void setDefaultDataSubId(int subscriptionId) {
if (VDBG) logd("setDataSubscription sub id = " + subscriptionId);
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
iSub.setDefaultDataSubId(subscriptionId);
}
@@ -2075,7 +2123,11 @@
/** @hide */
public void clearSubscriptionInfo() {
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
iSub.clearSubInfo();
}
@@ -2176,27 +2228,46 @@
}
/**
- * TODO(b/137102918) Make this static, tests use this as an instance method currently.
+ * Get visible subscription Id(s) of the currently active SIM(s).
*
* @return the list of subId's that are active,
- * is never null but the length maybe 0.
+ * is never null but the length may be 0.
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public @NonNull int[] getActiveSubscriptionIdList() {
return getActiveSubscriptionIdList(/* visibleOnly */ true);
}
/**
- * TODO(b/137102918) Make this static, tests use this as an instance method currently.
+ * Get both hidden and visible subscription Id(s) of the currently active SIM(s).
*
+ * Hidden subscriptions refer to those are not meant visible to the users.
+ * For example, an opportunistic subscription that is grouped with other
+ * subscriptions should remain invisible to users as they are only functionally
+ * supplementary to primary ones.
+ *
+ * @return the list of subId's that are active,
+ * is never null but the length may be 0.
+ * @hide
+ */
+ @SystemApi
+ public @NonNull int[] getActiveAndHiddenSubscriptionIdList() {
+ return getActiveSubscriptionIdList(/* visibleOnly */false);
+ }
+
+ /**
* @return a non-null list of subId's that are active.
*
* @hide
*/
public @NonNull int[] getActiveSubscriptionIdList(boolean visibleOnly) {
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
int[] subId = iSub.getActiveSubIdList(visibleOnly);
if (subId != null) return subId;
@@ -2247,7 +2318,11 @@
int simState = TelephonyManager.SIM_STATE_UNKNOWN;
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
simState = iSub.getSimStateForSlotIndex(slotIndex);
}
@@ -2266,7 +2341,11 @@
*/
public static void setSubscriptionProperty(int subId, String propKey, String propValue) {
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
iSub.setSubscriptionProperty(subId, propKey, propValue);
}
@@ -2286,7 +2365,11 @@
Context context) {
String resultValue = null;
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
resultValue = iSub.getSubscriptionProperty(subId, propKey,
context.getOpPackageName(), context.getFeatureId());
@@ -2428,7 +2511,11 @@
@UnsupportedAppUsage
public boolean isActiveSubId(int subId) {
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
return iSub.isActiveSubId(subId, mContext.getOpPackageName(),
mContext.getFeatureId());
@@ -2731,7 +2818,11 @@
@TelephonyManager.SetOpportunisticSubscriptionResult Consumer<Integer> callback) {
if (VDBG) logd("[setPreferredDataSubscriptionId]+ subId:" + subId);
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub == null) return;
ISetOpportunisticDataCallback callbackStub = new ISetOpportunisticDataCallback.Stub() {
@@ -2774,7 +2865,11 @@
public int getPreferredDataSubscriptionId() {
int preferredSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
preferredSubId = iSub.getPreferredDataSubscriptionId();
}
@@ -2805,7 +2900,11 @@
List<SubscriptionInfo> subInfoList = null;
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
subInfoList = iSub.getOpportunisticSubscriptions(contextPkg, contextFeature);
}
@@ -2906,7 +3005,11 @@
ParcelUuid groupUuid = null;
int[] subIdArray = subIdList.stream().mapToInt(i->i).toArray();
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
groupUuid = iSub.createSubscriptionGroup(subIdArray, pkgForDebug);
} else {
@@ -2956,7 +3059,11 @@
int[] subIdArray = subIdList.stream().mapToInt(i->i).toArray();
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
iSub.addSubscriptionsIntoGroup(subIdArray, groupUuid, pkgForDebug);
} else {
@@ -3008,7 +3115,11 @@
int[] subIdArray = subIdList.stream().mapToInt(i->i).toArray();
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
iSub.removeSubscriptionsFromGroup(subIdArray, groupUuid, pkgForDebug);
} else {
@@ -3053,7 +3164,11 @@
List<SubscriptionInfo> result = null;
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
result = iSub.getSubscriptionsInGroup(groupUuid, contextPkg, contextFeature);
} else {
@@ -3166,7 +3281,11 @@
logd("setSubscriptionActivated subId= " + subscriptionId + " enable " + enable);
}
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
return iSub.setSubscriptionEnabled(enable, subscriptionId);
}
@@ -3196,7 +3315,11 @@
logd("setUiccApplicationsEnabled subId= " + subscriptionId + " enable " + enabled);
}
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
iSub.setUiccApplicationsEnabled(enabled, subscriptionId);
}
@@ -3226,7 +3349,11 @@
logd("canDisablePhysicalSubscription");
}
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
return iSub.canDisablePhysicalSubscription();
}
@@ -3247,7 +3374,11 @@
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isSubscriptionEnabled(int subscriptionId) {
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
return iSub.isSubscriptionEnabled(subscriptionId);
}
@@ -3270,7 +3401,11 @@
int subId = INVALID_SUBSCRIPTION_ID;
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
subId = iSub.getEnabledSubscriptionId(slotIndex);
}
@@ -3282,31 +3417,6 @@
return subId;
}
- /**
- * Set whether a subscription always allows MMS connection. If true, MMS network
- * request will be accepted by telephony even if user turns "mobile data" off
- * on this subscription.
- *
- * @param subId which subscription it's setting to.
- * @param alwaysAllow whether Mms data is always allowed.
- * @return whether operation is successful.
- *
- * @hide
- */
- public boolean setAlwaysAllowMmsData(int subId, boolean alwaysAllow) {
- try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
- if (iSub != null) {
- return iSub.setAlwaysAllowMmsData(subId, alwaysAllow);
- }
- } catch (RemoteException ex) {
- if (!isSystemProcess()) {
- ex.rethrowAsRuntimeException();
- }
- }
- return false;
- }
-
private interface CallISubMethodHelper {
int callMethod(ISub iSub) throws RemoteException;
}
@@ -3321,7 +3431,11 @@
int result = 0;
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
result = helper.callMethod(iSub);
}
@@ -3344,7 +3458,11 @@
*/
public static int getActiveDataSubscriptionId() {
try {
- ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ ISub iSub = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
if (iSub != null) {
return iSub.getActiveDataSubscriptionId();
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index ab44e8e..607450b 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -17,6 +17,8 @@
package android.telephony;
import static android.content.Context.TELECOM_SERVICE;
+import static android.provider.Telephony.Carriers.DPC_URI;
+import static android.provider.Telephony.Carriers.INVALID_APN_ID;
import static com.android.internal.util.Preconditions.checkNotNull;
@@ -34,16 +36,17 @@
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
import android.annotation.WorkerThread;
import android.app.ActivityThread;
import android.app.PendingIntent;
import android.compat.Compatibility;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.database.Cursor;
import android.net.ConnectivityManager;
import android.net.NetworkStats;
import android.net.Uri;
@@ -57,7 +60,6 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
-import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.WorkSource;
import android.provider.Settings.SettingNotFoundException;
@@ -75,6 +77,7 @@
import android.telephony.Annotation.SimActivationState;
import android.telephony.Annotation.UiccAppType;
import android.telephony.VisualVoicemailService.VisualVoicemailTask;
+import android.telephony.data.ApnSetting;
import android.telephony.data.ApnSetting.MvnoType;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories;
@@ -103,6 +106,7 @@
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.RILConstants;
import com.android.internal.telephony.SmsApplication;
+import com.android.telephony.Rlog;
import dalvik.system.VMRuntime;
@@ -781,30 +785,6 @@
public static final String EXTRA_PRECISE_DISCONNECT_CAUSE = "precise_disconnect_cause";
/**
- * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
- * for an String containing the data APN type.
- *
- * <p class="note">
- * Retrieve with
- * {@link android.content.Intent#getStringExtra(String name)}.
- *
- * @hide
- */
- public static final String EXTRA_DATA_APN_TYPE = PhoneConstants.DATA_APN_TYPE_KEY;
-
- /**
- * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
- * for an String containing the data APN.
- *
- * <p class="note">
- * Retrieve with
- * {@link android.content.Intent#getStringExtra(String name)}.
- *
- * @hide
- */
- public static final String EXTRA_DATA_APN = PhoneConstants.DATA_APN_KEY;
-
- /**
* Broadcast intent action for letting the default dialer to know to show voicemail
* notification.
*
@@ -1539,6 +1519,48 @@
public static final String ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS
= "android.telephony.action.SHOW_NOTICE_ECM_BLOCK_OTHERS";
+ /**
+ * Broadcast Action: The default data subscription has changed in a multi-SIM device.
+ * This has the following extra values:</p>
+ * <ul>
+ * <li><em>subscription</em> - A int, the current data default subscription.</li>
+ * </ul>
+ *
+ * @hide
+ */
+ @SystemApi
+ @SuppressLint("ActionValue")
+ public static final String ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED
+ = "android.intent.action.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED";
+
+ /**
+ * Broadcast Action: The default voice subscription has changed in a mult-SIm device.
+ * This has the following extra values:</p>
+ * <ul>
+ * <li><em>subscription</em> - A int, the current voice default subscription.</li>
+ * </ul>
+ *
+ * @hide
+ */
+ @SystemApi
+ @SuppressLint("ActionValue")
+ public static final String ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED
+ = "android.intent.action.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED";
+
+ /**
+ * Broadcast Action: This triggers a client initiated OMA-DM session to the OMA server.
+ * <p class="note">
+ * Open Mobile Alliance (OMA) Device Management (DM).
+ *
+ * This intent is used by the system components to trigger OMA-DM
+ *
+ * @hide
+ */
+ @SystemApi
+ @SuppressLint("ActionValue")
+ public static final String ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE
+ = "com.android.omadm.service.CONFIGURATION_UPDATE";
+
//
//
// Device Info
@@ -5061,7 +5083,11 @@
@UnsupportedAppUsage
private IPhoneSubInfo getSubscriberInfo() {
// get it each time because that process crashes a lot
- return IPhoneSubInfo.Stub.asInterface(ServiceManager.getService("iphonesubinfo"));
+ return IPhoneSubInfo.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getPhoneSubServiceRegisterer()
+ .get());
}
/**
@@ -5274,11 +5300,19 @@
}
private ITelephonyRegistry getTelephonyRegistry() {
- return ITelephonyRegistry.Stub.asInterface(ServiceManager.getService("telephony.registry"));
+ return ITelephonyRegistry.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getTelephonyRegistryServiceRegisterer()
+ .get());
}
private IOns getIOns() {
- return IOns.Stub.asInterface(ServiceManager.getService("ions"));
+ return IOns.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getOpportunisticNetworkServiceRegisterer()
+ .get());
}
//
@@ -7727,12 +7761,12 @@
/**
* Check whether DUN APN is required for tethering.
* <p>
- * Requires Permission: READ_PRIVILEGED_PHONE_STATE.
+ * Requires Permission: MODIFY_PHONE_STATE.
*
* @return {@code true} if DUN APN is required for tethering.
* @hide
*/
- @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@SystemApi
public boolean isTetheringApnRequired() {
return isTetheringApnRequired(getSubId(SubscriptionManager.getActiveDataSubscriptionId()));
@@ -10474,14 +10508,15 @@
/**
* Policy control of data connection. Usually used when data limit is passed.
* @param enabled True if enabling the data, otherwise disabling.
- * @param subId sub id
* @hide
*/
- public void setPolicyDataEnabled(boolean enabled, int subId) {
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setPolicyDataEnabled(boolean enabled) {
try {
ITelephony service = getITelephony();
if (service != null) {
- service.setPolicyDataEnabled(enabled, subId);
+ service.setPolicyDataEnabled(enabled, getSubId());
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelephony#setPolicyDataEnabled", e);
@@ -10554,7 +10589,8 @@
*
* @hide
*/
- @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isManualNetworkSelectionAllowed() {
try {
ITelephony telephony = getITelephony();
@@ -11860,6 +11896,88 @@
}
/**
+ * Returns a list of APNs set as overrides by the device policy manager via
+ * {@link #addDevicePolicyOverrideApn}.
+ * This method must only be called from the system or phone processes.
+ *
+ * @param context Context to use.
+ * @return {@link List} of APNs that have been set as overrides.
+ * @throws {@link SecurityException} if the caller is not the system or phone process.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ // TODO: add new permission tag indicating that this is system-only.
+ public @NonNull List<ApnSetting> getDevicePolicyOverrideApns(@NonNull Context context) {
+ try (Cursor cursor = context.getContentResolver().query(DPC_URI, null, null, null, null)) {
+ if (cursor == null) {
+ return Collections.emptyList();
+ }
+ List<ApnSetting> apnList = new ArrayList<ApnSetting>();
+ cursor.moveToPosition(-1);
+ while (cursor.moveToNext()) {
+ ApnSetting apn = ApnSetting.makeApnSetting(cursor);
+ apnList.add(apn);
+ }
+ return apnList;
+ }
+ }
+
+ /**
+ * Used by the device policy manager to add a new override APN.
+ * This method must only be called from the system or phone processes.
+ *
+ * @param context Context to use.
+ * @param apnSetting The {@link ApnSetting} describing the new APN.
+ * @return An integer, corresponding to a primary key in a database, that allows the caller to
+ * modify the APN in the future via {@link #modifyDevicePolicyOverrideApn}, or
+ * {@link android.provider.Telephony.Carriers.INVALID_APN_ID} if the override operation
+ * failed.
+ * @throws {@link SecurityException} if the caller is not the system or phone process.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ // TODO: add new permission tag indicating that this is system-only.
+ public int addDevicePolicyOverrideApn(@NonNull Context context,
+ @NonNull ApnSetting apnSetting) {
+ Uri resultUri = context.getContentResolver().insert(DPC_URI, apnSetting.toContentValues());
+
+ int resultId = INVALID_APN_ID;
+ if (resultUri != null) {
+ try {
+ resultId = Integer.parseInt(resultUri.getLastPathSegment());
+ } catch (NumberFormatException e) {
+ Rlog.e(TAG, "Failed to parse inserted override APN id: "
+ + resultUri.getLastPathSegment());
+ }
+ }
+ return resultId;
+ }
+
+ /**
+ * Used by the device policy manager to modify an override APN.
+ * This method must only be called from the system or phone processes.
+ *
+ * @param context Context to use.
+ * @param apnId The integer key of the APN to modify, as returned by
+ * {@link #addDevicePolicyOverrideApn}
+ * @param apnSetting The {@link ApnSetting} describing the updated APN.
+ * @return {@code true} if successful, {@code false} otherwise.
+ * @throws {@link SecurityException} if the caller is not the system or phone process.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ // TODO: add new permission tag indicating that this is system-only.
+ public boolean modifyDevicePolicyOverrideApn(@NonNull Context context, int apnId,
+ @NonNull ApnSetting apnSetting) {
+ return context.getContentResolver().update(
+ Uri.withAppendedPath(DPC_URI, Integer.toString(apnId)),
+ apnSetting.toContentValues(), null, null) > 0;
+ }
+
+ /**
* Return whether data is enabled for certain APN type. This will tell if framework will accept
* corresponding network requests on a subId.
*
@@ -11872,7 +11990,7 @@
* 1) User data is turned on, or
* 2) APN is un-metered for this subscription, or
* 3) APN type is whitelisted. E.g. MMS is whitelisted if
- * {@link SubscriptionManager#setAlwaysAllowMmsData} is turned on.
+ * {@link #setAlwaysAllowMmsData(boolean)} is turned on.
*
* @param apnType Value indicating the apn type. Apn types are defined in {@link ApnSetting}.
* @return whether data is enabled for a apn type.
@@ -11996,4 +12114,30 @@
}
return false;
}
+
+ /**
+ * Set whether the specific sim card always allows MMS connection. If true, MMS network
+ * request will be accepted by telephony even if user turns "mobile data" off
+ * on this specific sim card.
+ *
+ * @param alwaysAllow whether Mms data is always allowed.
+ * @return whether operation is successful.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public boolean setAlwaysAllowMmsData(boolean alwaysAllow) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.setAlwaysAllowMmsData(getSubId(), alwaysAllow);
+ }
+ } catch (RemoteException ex) {
+ if (!isSystemProcess()) {
+ ex.rethrowAsRuntimeException();
+ }
+ }
+ return false;
+ }
}
diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java
index 96b6db7..8e6c170 100644
--- a/telephony/java/android/telephony/TelephonyScanManager.java
+++ b/telephony/java/android/telephony/TelephonyScanManager.java
@@ -19,7 +19,6 @@
import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.Nullable;
-import android.content.Context;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -29,7 +28,6 @@
import android.os.Messenger;
import android.os.Parcelable;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.util.SparseArray;
import com.android.internal.telephony.ITelephony;
@@ -234,6 +232,9 @@
private ITelephony getITelephony() {
return ITelephony.Stub.asInterface(
- ServiceManager.getService(Context.TELEPHONY_SERVICE));
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getTelephonyServiceRegisterer()
+ .get());
}
}
diff --git a/telephony/java/android/telephony/VoLteServiceState.java b/telephony/java/android/telephony/VoLteServiceState.java
index f65c7b0..414b999 100644
--- a/telephony/java/android/telephony/VoLteServiceState.java
+++ b/telephony/java/android/telephony/VoLteServiceState.java
@@ -16,12 +16,11 @@
package android.telephony;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
-import android.telephony.Rlog;
/**
* Contains LTE network state related information.
diff --git a/telephony/java/android/telephony/cdma/CdmaCellLocation.java b/telephony/java/android/telephony/cdma/CdmaCellLocation.java
index 45b1e47..9bc39a0 100644
--- a/telephony/java/android/telephony/cdma/CdmaCellLocation.java
+++ b/telephony/java/android/telephony/cdma/CdmaCellLocation.java
@@ -16,7 +16,7 @@
package android.telephony.cdma;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Bundle;
import android.telephony.CellLocation;
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 034fc22..dbfb6a2 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -1056,6 +1056,11 @@
}
/** @hide */
+ public boolean isEmergencyApn() {
+ return hasApnType(TYPE_EMERGENCY);
+ }
+
+ /** @hide */
public boolean canHandleType(@ApnType int type) {
if (!mCarrierEnabled) {
return false;
diff --git a/telephony/java/android/telephony/euicc/DownloadableSubscription.java b/telephony/java/android/telephony/euicc/DownloadableSubscription.java
index cb27f64..23d46ba 100644
--- a/telephony/java/android/telephony/euicc/DownloadableSubscription.java
+++ b/telephony/java/android/telephony/euicc/DownloadableSubscription.java
@@ -17,17 +17,18 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
import android.app.PendingIntent;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.UiccAccessRule;
+
+import com.android.internal.util.Preconditions;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import com.android.internal.util.Preconditions;
-
/**
* Information about a subscription which is downloadable to an eUICC using
* {@link EuiccManager#downloadSubscription(DownloadableSubscription, boolean, PendingIntent).
diff --git a/telephony/java/android/telephony/euicc/EuiccCardManager.java b/telephony/java/android/telephony/euicc/EuiccCardManager.java
index 994c49c..e16fffa 100644
--- a/telephony/java/android/telephony/euicc/EuiccCardManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccCardManager.java
@@ -21,8 +21,8 @@
import android.annotation.SystemApi;
import android.content.Context;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.service.euicc.EuiccProfileInfo;
+import android.telephony.TelephonyFrameworkInitializer;
import android.util.Log;
import com.android.internal.telephony.euicc.IAuthenticateServerCallback;
@@ -148,7 +148,10 @@
private IEuiccCardController getIEuiccCardController() {
return IEuiccCardController.Stub.asInterface(
- ServiceManager.getService("euicc_card_controller"));
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getEuiccCardControllerServiceRegisterer()
+ .get());
}
/**
diff --git a/telephony/java/android/telephony/euicc/EuiccInfo.java b/telephony/java/android/telephony/euicc/EuiccInfo.java
index 91ebb6c..467d268 100644
--- a/telephony/java/android/telephony/euicc/EuiccInfo.java
+++ b/telephony/java/android/telephony/euicc/EuiccInfo.java
@@ -16,7 +16,7 @@
package android.telephony.euicc;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index cb66a96..d5a48df 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -30,7 +30,7 @@
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.RemoteException;
-import android.os.ServiceManager;
+import android.telephony.TelephonyFrameworkInitializer;
import android.telephony.TelephonyManager;
import android.telephony.euicc.EuiccCardManager.ResetOption;
@@ -968,6 +968,10 @@
}
private static IEuiccController getIEuiccController() {
- return IEuiccController.Stub.asInterface(ServiceManager.getService("econtroller"));
+ return IEuiccController.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getEuiccControllerService()
+ .get());
}
}
diff --git a/telephony/java/android/telephony/gsm/GsmCellLocation.java b/telephony/java/android/telephony/gsm/GsmCellLocation.java
index d6780ce..30cea0e 100644
--- a/telephony/java/android/telephony/gsm/GsmCellLocation.java
+++ b/telephony/java/android/telephony/gsm/GsmCellLocation.java
@@ -16,7 +16,7 @@
package android.telephony.gsm;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Bundle;
import android.telephony.CellLocation;
diff --git a/telephony/java/android/telephony/ims/ImsCallForwardInfo.java b/telephony/java/android/telephony/ims/ImsCallForwardInfo.java
index 9f09d7a..d53a2e6 100644
--- a/telephony/java/android/telephony/ims/ImsCallForwardInfo.java
+++ b/telephony/java/android/telephony/ims/ImsCallForwardInfo.java
@@ -20,7 +20,7 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index 87e5391..bc60d81 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -20,7 +20,7 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -279,6 +279,14 @@
"android.telephony.ims.extra.ADDITIONAL_SIP_INVITE_FIELDS";
/**
+ * CallDisconnectCause: Specify call disconnect cause. This extra should be a code
+ * corresponding to ImsReasonInfo and should only be populated in the case that the
+ * call has already been missed
+ */
+ public static final String EXTRA_CALL_DISCONNECT_CAUSE =
+ "android.telephony.ims.extra.CALL_DISCONNECT_CAUSE";
+
+ /**
* Extra key which the RIL can use to indicate the radio technology used for a call.
* Valid values are:
* {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE},
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index 057d22c..91514e9 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -25,14 +25,13 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.content.Context;
import android.os.Binder;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.telephony.AccessNetworkConstants;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyFrameworkInitializer;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.MmTelFeature;
@@ -1018,7 +1017,10 @@
private static ITelephony getITelephony() {
ITelephony binder = ITelephony.Stub.asInterface(
- ServiceManager.getService(Context.TELEPHONY_SERVICE));
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getTelephonyServiceRegisterer()
+ .get());
if (binder == null) {
throw new RuntimeException("Could not find Telephony Service.");
}
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index d3fb37f..4f0f089 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -26,8 +26,8 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.telephony.AccessNetworkConstants;
+import android.telephony.TelephonyFrameworkInitializer;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsRcsController;
import android.telephony.ims.feature.ImsFeature;
@@ -362,7 +362,10 @@
}
private IImsRcsController getIImsRcsController() {
- IBinder binder = ServiceManager.getService(Context.TELEPHONY_IMS_SERVICE);
+ IBinder binder = TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getTelephonyImsServiceRegisterer()
+ .get();
return IImsRcsController.Stub.asInterface(binder);
}
}
diff --git a/telephony/java/android/telephony/ims/ImsReasonInfo.java b/telephony/java/android/telephony/ims/ImsReasonInfo.java
index f4b2cef..0d6b31d 100644
--- a/telephony/java/android/telephony/ims/ImsReasonInfo.java
+++ b/telephony/java/android/telephony/ims/ImsReasonInfo.java
@@ -20,7 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/telephony/java/android/telephony/ims/ImsSsInfo.java b/telephony/java/android/telephony/ims/ImsSsInfo.java
index 77bd984..9cce95f 100644
--- a/telephony/java/android/telephony/ims/ImsSsInfo.java
+++ b/telephony/java/android/telephony/ims/ImsSsInfo.java
@@ -21,7 +21,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java b/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java
index b7ab0a0..b70fd64 100644
--- a/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java
+++ b/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java
@@ -19,7 +19,7 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/telephony/java/android/telephony/ims/ImsVideoCallProvider.java b/telephony/java/android/telephony/ims/ImsVideoCallProvider.java
index 270e693..569c6d5 100644
--- a/telephony/java/android/telephony/ims/ImsVideoCallProvider.java
+++ b/telephony/java/android/telephony/ims/ImsVideoCallProvider.java
@@ -18,7 +18,7 @@
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index e4d6335..f052180 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -25,14 +25,13 @@
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.WorkerThread;
-import android.content.Context;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyFrameworkInitializer;
import android.telephony.ims.aidl.IImsConfigCallback;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsConfigImplBase;
@@ -415,7 +414,11 @@
}
private static boolean isImsAvailableOnDevice() {
- IPackageManager pm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
+ IPackageManager pm = IPackageManager.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getPackageManagerServiceRegisterer()
+ .get());
if (pm == null) {
// For some reason package manger is not available.. This will fail internally anyways,
// so do not throw error and allow.
@@ -432,7 +435,10 @@
private static ITelephony getITelephony() {
ITelephony binder = ITelephony.Stub.asInterface(
- ServiceManager.getService(Context.TELEPHONY_SERVICE));
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getTelephonyServiceRegisterer()
+ .get());
if (binder == null) {
throw new RuntimeException("Could not find Telephony Service.");
}
diff --git a/telephony/java/android/telephony/ims/RcsControllerCall.java b/telephony/java/android/telephony/ims/RcsControllerCall.java
index ce03c3c..1e93437 100644
--- a/telephony/java/android/telephony/ims/RcsControllerCall.java
+++ b/telephony/java/android/telephony/ims/RcsControllerCall.java
@@ -18,7 +18,7 @@
import android.content.Context;
import android.os.RemoteException;
-import android.os.ServiceManager;
+import android.telephony.TelephonyFrameworkInitializer;
import android.telephony.ims.aidl.IRcsMessage;
/**
@@ -35,8 +35,11 @@
}
<R> R call(RcsServiceCall<R> serviceCall) throws RcsMessageStoreException {
- IRcsMessage iRcsMessage = IRcsMessage.Stub.asInterface(ServiceManager.getService(
- Context.TELEPHONY_RCS_MESSAGE_SERVICE));
+ IRcsMessage iRcsMessage = IRcsMessage.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getTelephonyRcsMessageServiceRegisterer()
+ .get());
if (iRcsMessage == null) {
throw new RcsMessageStoreException("Could not connect to RCS storage service");
}
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
index 75e3f0a..2e3f59a 100644
--- a/telephony/java/android/telephony/ims/RcsUceAdapter.java
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -21,12 +21,11 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
-import android.content.Context;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.ServiceManager;
+import android.telephony.TelephonyFrameworkInitializer;
import android.telephony.ims.aidl.IImsRcsController;
import android.telephony.ims.aidl.IRcsUceControllerCallback;
import android.util.Log;
@@ -365,7 +364,10 @@
}
private IImsRcsController getIImsRcsController() {
- IBinder binder = ServiceManager.getService(Context.TELEPHONY_IMS_SERVICE);
+ IBinder binder = TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getTelephonyImsServiceRegisterer()
+ .get();
return IImsRcsController.Stub.asInterface(binder);
}
}
diff --git a/telephony/java/android/telephony/ims/compat/ImsService.java b/telephony/java/android/telephony/ims/compat/ImsService.java
index 97a8517..eafbb14 100644
--- a/telephony/java/android/telephony/ims/compat/ImsService.java
+++ b/telephony/java/android/telephony/ims/compat/ImsService.java
@@ -17,8 +17,8 @@
package android.telephony.ims.compat;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
import android.app.Service;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
diff --git a/telephony/java/android/telephony/ims/compat/feature/ImsFeature.java b/telephony/java/android/telephony/ims/compat/feature/ImsFeature.java
index de4f174..5a9e8e2 100644
--- a/telephony/java/android/telephony/ims/compat/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/compat/feature/ImsFeature.java
@@ -17,7 +17,7 @@
package android.telephony.ims.compat.feature;
import android.annotation.IntDef;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.IInterface;
import android.os.RemoteException;
diff --git a/telephony/java/android/telephony/ims/compat/feature/MMTelFeature.java b/telephony/java/android/telephony/ims/compat/feature/MMTelFeature.java
index 3fd356a..b52c371 100644
--- a/telephony/java/android/telephony/ims/compat/feature/MMTelFeature.java
+++ b/telephony/java/android/telephony/ims/compat/feature/MMTelFeature.java
@@ -16,8 +16,8 @@
package android.telephony.ims.compat.feature;
-import android.annotation.UnsupportedAppUsage;
import android.app.PendingIntent;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Message;
import android.os.RemoteException;
import android.telephony.ims.ImsCallProfile;
diff --git a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
index d77f78e..acab738 100644
--- a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
+++ b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
@@ -16,7 +16,7 @@
package android.telephony.ims.compat.stub;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Message;
import android.os.RemoteException;
import android.telephony.CallQuality;
diff --git a/telephony/java/android/telephony/ims/compat/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/compat/stub/ImsConfigImplBase.java
index e55c3d0..aae6f92 100644
--- a/telephony/java/android/telephony/ims/compat/stub/ImsConfigImplBase.java
+++ b/telephony/java/android/telephony/ims/compat/stub/ImsConfigImplBase.java
@@ -16,7 +16,7 @@
package android.telephony.ims.compat.stub;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
import android.os.RemoteException;
diff --git a/telephony/java/android/telephony/ims/compat/stub/ImsUtListenerImplBase.java b/telephony/java/android/telephony/ims/compat/stub/ImsUtListenerImplBase.java
index e2024742..ce291d4 100644
--- a/telephony/java/android/telephony/ims/compat/stub/ImsUtListenerImplBase.java
+++ b/telephony/java/android/telephony/ims/compat/stub/ImsUtListenerImplBase.java
@@ -16,7 +16,7 @@
package android.telephony.ims.compat.stub;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Bundle;
import android.os.RemoteException;
import android.telephony.ims.ImsCallForwardInfo;
diff --git a/telephony/java/com/android/ims/ImsUtInterface.java b/telephony/java/com/android/ims/ImsUtInterface.java
index e80087d..50b63bd 100644
--- a/telephony/java/com/android/ims/ImsUtInterface.java
+++ b/telephony/java/com/android/ims/ImsUtInterface.java
@@ -16,13 +16,12 @@
package com.android.ims;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Handler;
import android.os.Message;
import android.telephony.ims.ImsCallForwardInfo;
import android.telephony.ims.ImsSsInfo;
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
/**
* Provides APIs for the supplementary service settings using IMS (Ut interface).
* It is created from 3GPP TS 24.623 (XCAP(XML Configuration Access Protocol)
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index 9e786ce..1449a62 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -15,9 +15,9 @@
*/
package com.android.internal.telephony;
-import com.android.internal.util.Protocol;
+import android.compat.annotation.UnsupportedAppUsage;
-import dalvik.annotation.compat.UnsupportedAppUsage;
+import com.android.internal.util.Protocol;
/**
* @hide
diff --git a/telephony/java/com/android/internal/telephony/ISms.aidl b/telephony/java/com/android/internal/telephony/ISms.aidl
index 9f4d066..c07a171 100644
--- a/telephony/java/com/android/internal/telephony/ISms.aidl
+++ b/telephony/java/com/android/internal/telephony/ISms.aidl
@@ -1,18 +1,18 @@
/*
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT 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 2007, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
@@ -22,7 +22,7 @@
import com.android.internal.telephony.SmsRawData;
/**
- * Interface for applications to access the ICC phone book.
+ * Service interface to handle SMS API requests
*
* See also SmsManager.java.
*/
@@ -542,6 +542,13 @@
in List<PendingIntent> deliveryIntents);
/**
+ * Get carrier-dependent configuration values.
+ *
+ * @param subId the subscription Id
+ */
+ Bundle getCarrierConfigValuesForSubscriber(int subId);
+
+ /**
* Create an app-only incoming SMS request for the calling package.
*
* If an incoming text contains the token returned by this method the provided
diff --git a/telephony/java/com/android/internal/telephony/ISmsImplBase.java b/telephony/java/com/android/internal/telephony/ISmsImplBase.java
index 2430d82..ddd3457 100644
--- a/telephony/java/com/android/internal/telephony/ISmsImplBase.java
+++ b/telephony/java/com/android/internal/telephony/ISmsImplBase.java
@@ -18,6 +18,7 @@
import android.app.PendingIntent;
import android.net.Uri;
+import android.os.Bundle;
import java.util.List;
@@ -185,6 +186,11 @@
}
@Override
+ public Bundle getCarrierConfigValuesForSubscriber(int subId) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public String createAppSpecificSmsToken(int subId, String callingPkg, PendingIntent intent) {
throw new UnsupportedOperationException();
}
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index cc02a40..571efce 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -295,8 +295,6 @@
boolean isActiveSubId(int subId, String callingPackage, String callingFeatureId);
- boolean setAlwaysAllowMmsData(int subId, boolean alwaysAllow);
-
int getActiveDataSubscriptionId();
boolean canDisablePhysicalSubscription();
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 9d721fc..b846a10 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2118,6 +2118,11 @@
boolean isDataAllowedInVoiceCall(int subId);
/**
+ * Set whether a subscription always allows MMS connection.
+ */
+ boolean setAlwaysAllowMmsData(int subId, boolean allow);
+
+ /**
* Command line command to enable or disable handling of CEP data for test purposes.
*/
oneway void setCepEnabled(boolean isCepEnabled);
diff --git a/telephony/java/com/android/internal/telephony/IccCardConstants.java b/telephony/java/com/android/internal/telephony/IccCardConstants.java
index 25f03c2..94dfae8 100644
--- a/telephony/java/com/android/internal/telephony/IccCardConstants.java
+++ b/telephony/java/com/android/internal/telephony/IccCardConstants.java
@@ -15,11 +15,10 @@
*/
package com.android.internal.telephony;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Intent;
import android.telephony.TelephonyManager;
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
/**
* {@hide}
*/
diff --git a/telephony/java/com/android/internal/telephony/OperatorInfo.java b/telephony/java/com/android/internal/telephony/OperatorInfo.java
index 59c39b1..64d7863 100644
--- a/telephony/java/com/android/internal/telephony/OperatorInfo.java
+++ b/telephony/java/com/android/internal/telephony/OperatorInfo.java
@@ -16,7 +16,7 @@
package com.android.internal.telephony;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index fadb573..51701eb 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.telephony;
-import dalvik.annotation.compat.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
/**
* @hide
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 9cbcd7f..284544b 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -16,10 +16,9 @@
package com.android.internal.telephony;
+import android.compat.annotation.UnsupportedAppUsage;
import android.sysprop.TelephonyProperties;
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
import java.util.Optional;
/**
@@ -555,4 +554,5 @@
int RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG = 1101;
int RIL_UNSOL_EMERGENCY_NUMBER_LIST = 1102;
int RIL_UNSOL_UICC_APPLICATIONS_ENABLEMENT_CHANGED = 1103;
+ int RIL_UNSOL_REGISTRATION_FAILED = 1104;
}
diff --git a/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java b/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java
index d672642..1d6ec2d 100644
--- a/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java
+++ b/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java
@@ -16,6 +16,7 @@
package com.android.internal.telephony;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.telephony.Rlog;
@@ -25,8 +26,6 @@
import com.android.internal.telephony.util.TelephonyUtils;
import com.android.internal.telephony.util.XmlUtils;
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
public class Sms7BitEncodingTranslator {
private static final String TAG = "Sms7BitEncodingTranslator";
@UnsupportedAppUsage
diff --git a/telephony/java/com/android/internal/telephony/SmsAddress.java b/telephony/java/com/android/internal/telephony/SmsAddress.java
index 2a8de8c..f18256a 100644
--- a/telephony/java/com/android/internal/telephony/SmsAddress.java
+++ b/telephony/java/com/android/internal/telephony/SmsAddress.java
@@ -16,7 +16,7 @@
package com.android.internal.telephony;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
public abstract class SmsAddress {
// From TS 23.040 9.1.2.5 and TS 24.008 table 10.5.118
diff --git a/telephony/java/com/android/internal/telephony/SmsHeader.java b/telephony/java/com/android/internal/telephony/SmsHeader.java
index dd77b01..ab3fdf4 100644
--- a/telephony/java/com/android/internal/telephony/SmsHeader.java
+++ b/telephony/java/com/android/internal/telephony/SmsHeader.java
@@ -16,7 +16,7 @@
package com.android.internal.telephony;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import com.android.internal.util.HexDump;
diff --git a/telephony/java/com/android/internal/telephony/SmsMessageBase.java b/telephony/java/com/android/internal/telephony/SmsMessageBase.java
index d6632f3..084882b 100644
--- a/telephony/java/com/android/internal/telephony/SmsMessageBase.java
+++ b/telephony/java/com/android/internal/telephony/SmsMessageBase.java
@@ -16,7 +16,7 @@
package com.android.internal.telephony;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.telephony.SmsMessage;
import android.text.TextUtils;
diff --git a/telephony/java/com/android/internal/telephony/SmsRawData.java b/telephony/java/com/android/internal/telephony/SmsRawData.java
index 18727f3..9776c8f 100644
--- a/telephony/java/com/android/internal/telephony/SmsRawData.java
+++ b/telephony/java/com/android/internal/telephony/SmsRawData.java
@@ -17,7 +17,7 @@
package com.android.internal.telephony;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index 08c536b..f78c65f 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -304,7 +304,7 @@
* </ul>
*/
public static final String ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED
- = "android.intent.action.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED";
+ = TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED;
/**
* Broadcast Action: The default voice subscription has changed. This has the following
@@ -314,7 +314,7 @@
* </ul>
*/
public static final String ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED
- = "android.intent.action.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED";
+ = TelephonyManager.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED;
/**
* Broadcast Action: The default sms subscription has changed. This has the following
@@ -433,7 +433,7 @@
* Broadcast action to trigger CI OMA-DM Session.
*/
public static final String ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE =
- "com.android.omadm.service.CONFIGURATION_UPDATE";
+ TelephonyManager.ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE;
/**
* Broadcast action to trigger the Carrier Certificate download.
diff --git a/telephony/java/com/android/internal/telephony/TelephonyProperties.java b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
index 4e42c20..ff70f8b 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyProperties.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
@@ -16,7 +16,7 @@
package com.android.internal.telephony;
-import dalvik.annotation.compat.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
/**
* Contains a list of string constants used to get or set telephone properties
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 1f7715b..e75c593 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -16,6 +16,7 @@
package com.android.internal.telephony.cdma;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.Resources;
import android.sysprop.TelephonyProperties;
import android.telephony.PhoneNumberUtils;
@@ -41,8 +42,6 @@
import com.android.internal.util.BitwiseInputStream;
import com.android.internal.util.HexDump;
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
diff --git a/telephony/java/com/android/internal/telephony/cdma/UserData.java b/telephony/java/com/android/internal/telephony/cdma/UserData.java
index 7187ae4..524cb0c 100644
--- a/telephony/java/com/android/internal/telephony/cdma/UserData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/UserData.java
@@ -16,13 +16,12 @@
package com.android.internal.telephony.cdma.sms;
+import android.compat.annotation.UnsupportedAppUsage;
import android.util.SparseIntArray;
import com.android.internal.telephony.SmsHeader;
import com.android.internal.util.HexDump;
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
public class UserData {
/**
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
index d9be548..b5af646 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -16,6 +16,7 @@
package com.android.internal.telephony.cdma.sms;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.Resources;
import android.telephony.Rlog;
import android.telephony.SmsCbCmasInfo;
@@ -31,8 +32,6 @@
import com.android.internal.util.BitwiseInputStream;
import com.android.internal.util.BitwiseOutputStream;
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
index a82b975..6f0de34 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
@@ -16,7 +16,7 @@
package com.android.internal.telephony.cdma.sms;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.util.SparseBooleanArray;
import com.android.internal.annotations.VisibleForTesting;
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java b/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
index c924ab3..9e2d29c 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
@@ -16,8 +16,7 @@
package com.android.internal.telephony.cdma.sms;
-
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.telephony.cdma.CdmaSmsCbProgramData;
public final class SmsEnvelope {
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSmsAddress.java b/telephony/java/com/android/internal/telephony/gsm/GsmSmsAddress.java
index 17f69b3..5409c09 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSmsAddress.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSmsAddress.java
@@ -16,13 +16,12 @@
package com.android.internal.telephony.gsm;
+import android.compat.annotation.UnsupportedAppUsage;
import android.telephony.PhoneNumberUtils;
import com.android.internal.telephony.GsmAlphabet;
import com.android.internal.telephony.SmsAddress;
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
import java.text.ParseException;
public class GsmSmsAddress extends SmsAddress {
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
index 216f616..d190345 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
@@ -16,13 +16,12 @@
package com.android.internal.telephony.gsm;
+import android.compat.annotation.UnsupportedAppUsage;
import android.telephony.SmsCbCmasInfo;
import android.telephony.SmsCbEtwsInfo;
import com.android.internal.telephony.SmsConstants;
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
import java.util.Arrays;
import java.util.Locale;
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index da32c8c..0681dc1 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -25,6 +25,7 @@
import static com.android.internal.telephony.SmsConstants.MAX_USER_DATA_SEPTETS;
import static com.android.internal.telephony.SmsConstants.MessageClass;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.Resources;
import android.telephony.PhoneNumberUtils;
import android.telephony.Rlog;
@@ -38,8 +39,6 @@
import com.android.internal.telephony.SmsMessageBase;
import com.android.internal.telephony.uicc.IccUtils;
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
diff --git a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
index f2d4624..eed9a86 100644
--- a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
@@ -16,6 +16,7 @@
package com.android.internal.telephony.uicc;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
import android.graphics.Bitmap;
@@ -25,8 +26,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.GsmAlphabet;
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
import java.io.UnsupportedEncodingException;
import java.util.List;
diff --git a/telephony/java/com/android/telephony/Rlog.java b/telephony/java/com/android/telephony/Rlog.java
new file mode 100644
index 0000000..9d6c930
--- /dev/null
+++ b/telephony/java/com/android/telephony/Rlog.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.telephony;
+
+import android.text.TextUtils;
+import android.util.Base64;
+import android.util.Log;
+
+import com.android.internal.telephony.util.TelephonyUtils;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * A copy of {@link android.telephony.Rlog} to be used within the telephony mainline module.
+ *
+ * @hide
+ */
+public final class Rlog {
+
+ private static final boolean USER_BUILD = TelephonyUtils.IS_USER;
+
+ private Rlog() {
+ }
+
+ private static int log(int priority, String tag, String msg) {
+ return Log.logToRadioBuffer(priority, tag, msg);
+ }
+
+ public static int v(String tag, String msg) {
+ return log(Log.VERBOSE, tag, msg);
+ }
+
+ public static int v(String tag, String msg, Throwable tr) {
+ return log(Log.VERBOSE, tag,
+ msg + '\n' + Log.getStackTraceString(tr));
+ }
+
+ public static int d(String tag, String msg) {
+ return log(Log.DEBUG, tag, msg);
+ }
+
+ public static int d(String tag, String msg, Throwable tr) {
+ return log(Log.DEBUG, tag,
+ msg + '\n' + Log.getStackTraceString(tr));
+ }
+
+ public static int i(String tag, String msg) {
+ return log(Log.INFO, tag, msg);
+ }
+
+ public static int i(String tag, String msg, Throwable tr) {
+ return log(Log.INFO, tag,
+ msg + '\n' + Log.getStackTraceString(tr));
+ }
+
+ public static int w(String tag, String msg) {
+ return log(Log.WARN, tag, msg);
+ }
+
+ public static int w(String tag, String msg, Throwable tr) {
+ return log(Log.WARN, tag,
+ msg + '\n' + Log.getStackTraceString(tr));
+ }
+
+ public static int w(String tag, Throwable tr) {
+ return log(Log.WARN, tag, Log.getStackTraceString(tr));
+ }
+
+ public static int e(String tag, String msg) {
+ return log(Log.ERROR, tag, msg);
+ }
+
+ public static int e(String tag, String msg, Throwable tr) {
+ return log(Log.ERROR, tag,
+ msg + '\n' + Log.getStackTraceString(tr));
+ }
+
+ public static int println(int priority, String tag, String msg) {
+ return log(priority, tag, msg);
+ }
+
+ public static boolean isLoggable(String tag, int level) {
+ return Log.isLoggable(tag, level);
+ }
+
+ /**
+ * Redact personally identifiable information for production users.
+ * @param tag used to identify the source of a log message
+ * @param pii the personally identifiable information we want to apply secure hash on.
+ * @return If tag is loggable in verbose mode or pii is null, return the original input.
+ * otherwise return a secure Hash of input pii
+ */
+ public static String pii(String tag, Object pii) {
+ String val = String.valueOf(pii);
+ if (pii == null || TextUtils.isEmpty(val) || isLoggable(tag, Log.VERBOSE)) {
+ return val;
+ }
+ return "[" + secureHash(val.getBytes()) + "]";
+ }
+
+ /**
+ * Redact personally identifiable information for production users.
+ * @param enablePiiLogging set when caller explicitly want to enable sensitive logging.
+ * @param pii the personally identifiable information we want to apply secure hash on.
+ * @return If enablePiiLogging is set to true or pii is null, return the original input.
+ * otherwise return a secure Hash of input pii
+ */
+ public static String pii(boolean enablePiiLogging, Object pii) {
+ String val = String.valueOf(pii);
+ if (pii == null || TextUtils.isEmpty(val) || enablePiiLogging) {
+ return val;
+ }
+ return "[" + secureHash(val.getBytes()) + "]";
+ }
+
+ /**
+ * Returns a secure hash (using the SHA1 algorithm) of the provided input.
+ *
+ * @return "****" if the build type is user, otherwise the hash
+ * @param input the bytes for which the secure hash should be computed.
+ */
+ private static String secureHash(byte[] input) {
+ // Refrain from logging user personal information in user build.
+ if (USER_BUILD) {
+ return "****";
+ }
+
+ MessageDigest messageDigest;
+
+ try {
+ messageDigest = MessageDigest.getInstance("SHA-1");
+ } catch (NoSuchAlgorithmException e) {
+ return "####";
+ }
+
+ byte[] result = messageDigest.digest(input);
+ return Base64.encodeToString(
+ result, Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP);
+ }
+}
diff --git a/test-base/hiddenapi/Android.bp b/test-base/hiddenapi/Android.bp
index c4e0fab..c202467 100644
--- a/test-base/hiddenapi/Android.bp
+++ b/test-base/hiddenapi/Android.bp
@@ -25,5 +25,8 @@
srcs: ["src/**/*.java"],
- libs: ["android.test.base"],
+ libs: [
+ "android.test.base",
+ "unsupportedappusage",
+ ],
}
diff --git a/test-base/hiddenapi/src/android/test/AndroidTestCase.java b/test-base/hiddenapi/src/android/test/AndroidTestCase.java
index 2b9beb1..fcb8d43 100644
--- a/test-base/hiddenapi/src/android/test/AndroidTestCase.java
+++ b/test-base/hiddenapi/src/android/test/AndroidTestCase.java
@@ -16,7 +16,7 @@
package android.test;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import junit.framework.TestCase;
diff --git a/test-base/hiddenapi/src/android/test/InstrumentationTestCase.java b/test-base/hiddenapi/src/android/test/InstrumentationTestCase.java
index 139cd18..a48a56c 100644
--- a/test-base/hiddenapi/src/android/test/InstrumentationTestCase.java
+++ b/test-base/hiddenapi/src/android/test/InstrumentationTestCase.java
@@ -16,7 +16,7 @@
package android.test;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import junit.framework.TestCase;
diff --git a/test-base/hiddenapi/src/junit/framework/TestCase.java b/test-base/hiddenapi/src/junit/framework/TestCase.java
index 5a54861..59fbe2b 100644
--- a/test-base/hiddenapi/src/junit/framework/TestCase.java
+++ b/test-base/hiddenapi/src/junit/framework/TestCase.java
@@ -16,7 +16,7 @@
package junit.framework;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
/**
* Stub only
diff --git a/test-base/hiddenapi/src/junit/framework/TestSuite.java b/test-base/hiddenapi/src/junit/framework/TestSuite.java
index 368c661..32305c9 100644
--- a/test-base/hiddenapi/src/junit/framework/TestSuite.java
+++ b/test-base/hiddenapi/src/junit/framework/TestSuite.java
@@ -16,7 +16,7 @@
package junit.framework;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import java.lang.reflect.Method;
diff --git a/tests/ApkVerityTest/TEST_MAPPING b/tests/ApkVerityTest/TEST_MAPPING
index a660839..72d9614 100644
--- a/tests/ApkVerityTest/TEST_MAPPING
+++ b/tests/ApkVerityTest/TEST_MAPPING
@@ -1,15 +1,12 @@
{
"presubmit": [
+ {
+ "name": "ApkVerityTest"
+ },
// 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/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
index 2445a6a..20d0e96 100644
--- a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
+++ b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
@@ -27,6 +27,7 @@
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import com.android.tradefed.util.CommandResult;
@@ -412,6 +413,7 @@
break;
}
try {
+ CLog.d("lsof: " + expectRemoteCommandToSucceed("lsof " + apkPath));
Thread.sleep(1000);
String pid = expectRemoteCommandToSucceed("pidof system_server");
mDevice.executeShellV2Command("kill -10 " + pid); // force GC
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 40169b8..9e490f7 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -324,6 +324,7 @@
@Test
public void testNetworkPassedDoesNotRollback_Phase1() throws Exception {
+ // Remove available rollbacks and uninstall NetworkStack on /data/
RollbackManager rm = RollbackUtils.getRollbackManager();
String networkStack = getNetworkStackPackageName();
@@ -332,6 +333,15 @@
assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
networkStack)).isNull();
+
+ // Reduce health check deadline, here unlike the network failed case, we use
+ // a longer deadline because joining a network can take a much longer time for
+ // reasons external to the device than 'not joining'
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
+ PROPERTY_WATCHDOG_REQUEST_TIMEOUT_MILLIS,
+ Integer.toString(300000), false);
+ // Simulate re-installation of new NetworkStack with rollbacks enabled
+ installNetworkStackPackage();
}
@Test
@@ -343,6 +353,9 @@
@Test
public void testNetworkPassedDoesNotRollback_Phase3() throws Exception {
+ // Sleep for > health check deadline. We expect no rollback should happen during sleeping.
+ // If the device reboots for rollback, this device test will fail as well as the host test.
+ Thread.sleep(TimeUnit.SECONDS.toMillis(310));
RollbackManager rm = RollbackUtils.getRollbackManager();
assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(),
getNetworkStackPackageName())).isNull();
@@ -380,11 +393,6 @@
}
@Test
- public void testRollbackWhitelistedApp_cleanUp() throws Exception {
- RollbackUtils.getRollbackManager().expireRollbackForPackage(getModuleMetadataPackageName());
- }
-
- @Test
public void testRollbackDataPolicy_Phase1() throws Exception {
Uninstall.packages(TestApp.A, TestApp.B);
Install.multi(TestApp.A1, TestApp.B1).commit();
@@ -422,6 +430,16 @@
assertThat(InstallUtils.getUserDataVersion(TestApp.B)).isEqualTo(1);
}
+ @Test
+ public void testCleanUp() throws Exception {
+ // testNativeWatchdogTriggersRollback will fail if multiple staged sessions are
+ // committed on a device which doesn't support checkpoint. Let's clean up all rollbacks
+ // so there is only one rollback to commit when testing native crashes.
+ RollbackManager rm = RollbackUtils.getRollbackManager();
+ rm.getAvailableRollbacks().stream().flatMap(info -> info.getPackages().stream())
+ .map(info -> info.getPackageName()).forEach(rm::expireRollbackForPackage);
+ }
+
private static void runShellCommand(String cmd) {
ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation().getUiAutomation()
.executeShellCommand(cmd);
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index 4644d8a..91577c2 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -22,8 +22,8 @@
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -53,6 +53,11 @@
getDevice().reboot();
}
+ @After
+ public void tearDown() throws Exception {
+ runPhase("testCleanUp");
+ }
+
/**
* Tests watchdog triggered staged rollbacks involving only apks.
*/
@@ -128,21 +133,8 @@
* Tests passed network health check does not trigger watchdog staged rollbacks.
*/
@Test
- @Ignore("b/143514090")
public void testNetworkPassedDoesNotRollback() throws Exception {
- // Remove available rollbacks and uninstall NetworkStack on /data/
runPhase("testNetworkPassedDoesNotRollback_Phase1");
- // Reduce health check deadline, here unlike the network failed case, we use
- // a longer deadline because joining a network can take a much longer time for
- // reasons external to the device than 'not joining'
- getDevice().executeShellCommand("device_config put rollback "
- + "watchdog_request_timeout_millis 300000");
- // Simulate re-installation of new NetworkStack with rollbacks enabled
- getDevice().executeShellCommand("pm install -r --staged --enable-rollback "
- + getNetworkStackPath());
-
- // Sleep to allow writes to disk before reboot
- Thread.sleep(5000);
// Reboot device to activate staged package
getDevice().reboot();
@@ -157,8 +149,6 @@
// on mobile data
getDevice().waitForDeviceAvailable();
- // Sleep for > health check deadline
- Thread.sleep(310000);
// Verify rollback was not executed after health check deadline
runPhase("testNetworkPassedDoesNotRollback_Phase3");
}
@@ -180,16 +170,9 @@
*/
@Test
public void testRollbackWhitelistedApp() throws Exception {
- try {
- runPhase("testRollbackWhitelistedApp_Phase1");
- getDevice().reboot();
- runPhase("testRollbackWhitelistedApp_Phase2");
- } finally {
- // testNativeWatchdogTriggersRollback will fail if multiple staged sessions are
- // committed on a device which doesn't support checkpoint. Let's clean up the rollback
- // so there is only one rollback to commit when testing native crashes.
- runPhase("testRollbackWhitelistedApp_cleanUp");
- }
+ runPhase("testRollbackWhitelistedApp_Phase1");
+ getDevice().reboot();
+ runPhase("testRollbackWhitelistedApp_Phase2");
}
@Test
diff --git a/tests/TelephonyCommonTests/Android.bp b/tests/TelephonyCommonTests/Android.bp
new file mode 100644
index 0000000..4f7569d
--- /dev/null
+++ b/tests/TelephonyCommonTests/Android.bp
@@ -0,0 +1,49 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+android_test {
+ name: "TelephonyCommonTests",
+ srcs: [
+ ":framework-telephony-common-sources",
+ "**/*.java",
+ ],
+ static_libs: [
+ "mockito-target-extended",
+ "androidx.test.rules",
+ "truth-prebuilt",
+ "platform-test-annotations",
+ "androidx.core_core",
+ "androidx.fragment_fragment",
+ "androidx.test.ext.junit"
+ ],
+
+ jni_libs: ["libdexmakerjvmtiagent"],
+
+ // We need to rename SmsApplication to the test package or else it'll get clobbered by the
+ // hidden api checker
+ jarjar_rules: "jarjar-rules.txt",
+
+ // Uncomment this and comment out the jarjar rule if you want to attach a debugger and step
+ // through the renamed classes.
+ // platform_apis: true,
+
+ libs: [
+ "android.test.runner",
+ "android.test.mock",
+ "android.test.base",
+ "unsupportedappusage",
+ ],
+}
diff --git a/tests/TelephonyCommonTests/AndroidManifest.xml b/tests/TelephonyCommonTests/AndroidManifest.xml
new file mode 100644
index 0000000..63f38c6
--- /dev/null
+++ b/tests/TelephonyCommonTests/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.internal.telephony.tests"
+ android:debuggable="true">
+
+ <application android:label="TelephonyCommonTests"
+ android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.internal.telephony.tests"
+ android:label="Telephony common tests"
+ android:debuggable="true"/>
+</manifest>
diff --git a/tests/TelephonyCommonTests/AndroidTest.xml b/tests/TelephonyCommonTests/AndroidTest.xml
new file mode 100644
index 0000000..e9fdabc
--- /dev/null
+++ b/tests/TelephonyCommonTests/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<configuration description="Runs Telephony Common Test Cases.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-instrumentation" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="TelephonyCommonTests.apk" />
+ </target_preparer>
+
+ <option name="test-tag" value="TelephonyCommonTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.internal.telephony.tests" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ </test>
+</configuration>
diff --git a/tests/TelephonyCommonTests/jarjar-rules.txt b/tests/TelephonyCommonTests/jarjar-rules.txt
new file mode 100644
index 0000000..4d1115f
--- /dev/null
+++ b/tests/TelephonyCommonTests/jarjar-rules.txt
@@ -0,0 +1,3 @@
+rule com.android.internal.telephony.SmsApplication* com.android.internal.telephony.tests.SmsApplication@1
+rule android.telephony.PackageChangeReceiver* com.android.internal.telephony.tests.PackageChangeReceiver@1
+rule com.android.internal.os.BackgroundThread* com.android.internal.telephony.tests.BackgroundThread@1
diff --git a/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/SmsApplicationTest.java b/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/SmsApplicationTest.java
new file mode 100644
index 0000000..83fd208
--- /dev/null
+++ b/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/SmsApplicationTest.java
@@ -0,0 +1,291 @@
+/*
+ * 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.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNotNull;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.app.AppOpsManager;
+import android.app.role.RoleManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Telephony;
+import android.telephony.TelephonyManager;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.telephony.SmsApplication;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Unit tests for the {@link SmsApplication} utility class
+ */
+@RunWith(AndroidJUnit4.class)
+public class SmsApplicationTest {
+ private static final ComponentName TEST_COMPONENT_NAME =
+ ComponentName.unflattenFromString("com.android.test/.TestSmsApp");
+ private static final String MMS_RECEIVER_NAME = "TestMmsReceiver";
+ private static final String RESPOND_VIA_SMS_NAME = "TestRespondViaSmsHandler";
+ private static final String SEND_TO_NAME = "TestSendTo";
+ private static final int SMS_APP_UID = 10001;
+
+ private static final int FAKE_PHONE_UID = 10002;
+ private static final int FAKE_MMS_UID = 10003;
+ private static final int FAKE_BT_UID = 10004;
+ private static final int FAKE_TELEPHONY_PROVIDER_UID = 10005;
+
+ private static final String[] APP_OPS_TO_CHECK = {
+ AppOpsManager.OPSTR_READ_SMS,
+ AppOpsManager.OPSTR_WRITE_SMS,
+ AppOpsManager.OPSTR_RECEIVE_SMS,
+ AppOpsManager.OPSTR_RECEIVE_WAP_PUSH,
+ AppOpsManager.OPSTR_SEND_SMS,
+ AppOpsManager.OPSTR_READ_CELL_BROADCASTS
+ };
+
+ private static final Set<String> SCHEMES_FOR_PREFERRED_APP = Arrays.stream(new String[]{
+ "mms",
+ "mmsto",
+ "sms",
+ "smsto"
+ }).collect(Collectors.toSet());
+
+ @Mock private Context mContext;
+ @Mock private TelephonyManager mTelephonyManager;
+ @Mock private RoleManager mRoleManager;
+ @Mock private PackageManager mPackageManager;
+ @Mock private AppOpsManager mAppOpsManager;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
+ when(mContext.getSystemService(Context.ROLE_SERVICE)).thenReturn(mRoleManager);
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mContext.getSystemService(RoleManager.class)).thenReturn(mRoleManager);
+ when(mContext.getSystemService(AppOpsManager.class)).thenReturn(mAppOpsManager);
+ when(mContext.createContextAsUser(isNotNull(), anyInt())).thenReturn(mContext);
+
+ doAnswer(invocation -> getResolveInfosForIntent(invocation.getArgument(0)))
+ .when(mPackageManager)
+ .queryBroadcastReceiversAsUser(nullable(Intent.class), anyInt(),
+ nullable(UserHandle.class));
+ doAnswer(invocation -> getResolveInfosForIntent(invocation.getArgument(0)))
+ .when(mPackageManager)
+ .queryIntentActivitiesAsUser(nullable(Intent.class), anyInt(),
+ nullable(UserHandle.class));
+ doAnswer(invocation -> getResolveInfosForIntent(invocation.getArgument(0)))
+ .when(mPackageManager)
+ .queryIntentServicesAsUser(nullable(Intent.class), anyInt(),
+ nullable(UserHandle.class));
+
+ when(mTelephonyManager.isSmsCapable()).thenReturn(true);
+ when(mRoleManager.isRoleAvailable(RoleManager.ROLE_SMS)).thenReturn(true);
+ when(mRoleManager.getDefaultSmsPackage(anyInt()))
+ .thenReturn(TEST_COMPONENT_NAME.getPackageName());
+
+ for (String opStr : APP_OPS_TO_CHECK) {
+ when(mAppOpsManager.unsafeCheckOp(
+ opStr, SMS_APP_UID, TEST_COMPONENT_NAME.getPackageName()))
+ .thenReturn(AppOpsManager.MODE_ALLOWED);
+ }
+ }
+
+ @Test
+ public void testGetDefaultSmsApplication() {
+ assertEquals(TEST_COMPONENT_NAME,
+ SmsApplication.getDefaultSmsApplicationAsUser(mContext, false, 0));
+ }
+
+ @Test
+ public void testGetDefaultSmsApplicationWithAppOpsFix() throws Exception {
+ when(mAppOpsManager.unsafeCheckOp(AppOpsManager.OPSTR_READ_SMS, SMS_APP_UID,
+ TEST_COMPONENT_NAME.getPackageName()))
+ .thenReturn(AppOpsManager.MODE_IGNORED);
+ setupPackageInfosForCoreApps();
+
+ assertEquals(TEST_COMPONENT_NAME,
+ SmsApplication.getDefaultSmsApplicationAsUser(mContext, true, 0));
+ verify(mAppOpsManager, atLeastOnce()).setUidMode(AppOpsManager.OPSTR_READ_SMS, SMS_APP_UID,
+ AppOpsManager.MODE_ALLOWED);
+ }
+
+ @Test
+ public void testPackageChanged() throws Exception {
+ setupPackageInfosForCoreApps();
+ SmsApplication.initSmsPackageMonitor(mContext);
+ verify(mContext).createContextAsUser(eq(UserHandle.ALL), anyInt());
+ ArgumentCaptor<BroadcastReceiver> captor = ArgumentCaptor.forClass(BroadcastReceiver.class);
+ verify(mContext).registerReceiver(captor.capture(), isNotNull(),
+ isNull(), nullable(Handler.class));
+ BroadcastReceiver smsPackageMonitor = captor.getValue();
+
+ Intent packageChangedIntent = new Intent(Intent.ACTION_PACKAGE_CHANGED);
+ packageChangedIntent.setData(
+ Uri.fromParts("package", TEST_COMPONENT_NAME.getPackageName(), null));
+ smsPackageMonitor.onReceive(mContext, packageChangedIntent);
+
+ ArgumentCaptor<IntentFilter> intentFilterCaptor =
+ ArgumentCaptor.forClass(IntentFilter.class);
+ verify(mPackageManager, times(SCHEMES_FOR_PREFERRED_APP.size()))
+ .replacePreferredActivity(intentFilterCaptor.capture(),
+ eq(IntentFilter.MATCH_CATEGORY_SCHEME
+ | IntentFilter.MATCH_ADJUSTMENT_NORMAL),
+ isNotNull(List.class),
+ eq(new ComponentName(TEST_COMPONENT_NAME.getPackageName(), SEND_TO_NAME)));
+
+ Set<String> capturedSchemes = intentFilterCaptor.getAllValues().stream()
+ .map(intentFilter -> intentFilter.getDataScheme(0))
+ .collect(Collectors.toSet());
+ assertEquals(SCHEMES_FOR_PREFERRED_APP.size(), capturedSchemes.size());
+ assertTrue(SCHEMES_FOR_PREFERRED_APP.containsAll(capturedSchemes));
+ }
+
+ private void setupPackageInfosForCoreApps() throws Exception {
+ PackageInfo phonePackageInfo = new PackageInfo();
+ ApplicationInfo phoneApplicationInfo = new ApplicationInfo();
+ phoneApplicationInfo.uid = FAKE_PHONE_UID;
+ phonePackageInfo.applicationInfo = phoneApplicationInfo;
+ when(mPackageManager.getPackageInfo(eq(SmsApplication.PHONE_PACKAGE_NAME), anyInt()))
+ .thenReturn(phonePackageInfo);
+
+ PackageInfo mmsPackageInfo = new PackageInfo();
+ ApplicationInfo mmsApplicationInfo = new ApplicationInfo();
+ mmsApplicationInfo.uid = FAKE_MMS_UID;
+ mmsPackageInfo.applicationInfo = mmsApplicationInfo;
+ when(mPackageManager.getPackageInfo(eq(SmsApplication.MMS_SERVICE_PACKAGE_NAME), anyInt()))
+ .thenReturn(mmsPackageInfo);
+
+ PackageInfo bluetoothPackageInfo = new PackageInfo();
+ ApplicationInfo bluetoothApplicationInfo = new ApplicationInfo();
+ bluetoothApplicationInfo.uid = FAKE_BT_UID;
+ bluetoothPackageInfo.applicationInfo = bluetoothApplicationInfo;
+ when(mPackageManager.getPackageInfo(eq(SmsApplication.BLUETOOTH_PACKAGE_NAME), anyInt()))
+ .thenReturn(bluetoothPackageInfo);
+
+ PackageInfo telephonyProviderPackageInfo = new PackageInfo();
+ ApplicationInfo telephonyProviderApplicationInfo = new ApplicationInfo();
+ telephonyProviderApplicationInfo.uid = FAKE_TELEPHONY_PROVIDER_UID;
+ telephonyProviderPackageInfo.applicationInfo = telephonyProviderApplicationInfo;
+ when(mPackageManager.getPackageInfo(
+ eq(SmsApplication.TELEPHONY_PROVIDER_PACKAGE_NAME), anyInt()))
+ .thenReturn(telephonyProviderPackageInfo);
+ }
+
+ private List<ResolveInfo> getResolveInfosForIntent(Intent intent) {
+ switch (intent.getAction()) {
+ case Telephony.Sms.Intents.SMS_DELIVER_ACTION:
+ return Collections.singletonList(makeSmsDeliverResolveInfo());
+ case Telephony.Sms.Intents.WAP_PUSH_DELIVER_ACTION:
+ return Collections.singletonList(makeWapPushResolveInfo());
+ case TelephonyManager.ACTION_RESPOND_VIA_MESSAGE:
+ return Collections.singletonList(makeRespondViaMessageResolveInfo());
+ case Intent.ACTION_SENDTO:
+ return Collections.singletonList(makeSendToResolveInfo());
+ }
+ return Collections.emptyList();
+ }
+
+ private ApplicationInfo makeSmsApplicationInfo() {
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.uid = SMS_APP_UID;
+ return applicationInfo;
+ }
+
+ private ResolveInfo makeSmsDeliverResolveInfo() {
+ ResolveInfo info = new ResolveInfo();
+ ActivityInfo activityInfo = new ActivityInfo();
+ activityInfo.applicationInfo = makeSmsApplicationInfo();
+
+ activityInfo.permission = Manifest.permission.BROADCAST_SMS;
+ activityInfo.packageName = TEST_COMPONENT_NAME.getPackageName();
+ activityInfo.name = TEST_COMPONENT_NAME.getClassName();
+
+ info.activityInfo = activityInfo;
+ return info;
+ }
+
+ private ResolveInfo makeWapPushResolveInfo() {
+ ResolveInfo info = new ResolveInfo();
+ ActivityInfo activityInfo = new ActivityInfo();
+
+ activityInfo.permission = Manifest.permission.BROADCAST_WAP_PUSH;
+ activityInfo.packageName = TEST_COMPONENT_NAME.getPackageName();
+ activityInfo.name = MMS_RECEIVER_NAME;
+
+ info.activityInfo = activityInfo;
+ return info;
+ }
+
+ private ResolveInfo makeRespondViaMessageResolveInfo() {
+ ResolveInfo info = new ResolveInfo();
+ ServiceInfo serviceInfo = new ServiceInfo();
+
+ serviceInfo.permission = Manifest.permission.SEND_RESPOND_VIA_MESSAGE;
+ serviceInfo.packageName = TEST_COMPONENT_NAME.getPackageName();
+ serviceInfo.name = RESPOND_VIA_SMS_NAME;
+
+ info.serviceInfo = serviceInfo;
+ return info;
+ }
+
+ private ResolveInfo makeSendToResolveInfo() {
+ ResolveInfo info = new ResolveInfo();
+ ActivityInfo activityInfo = new ActivityInfo();
+
+ activityInfo.packageName = TEST_COMPONENT_NAME.getPackageName();
+ activityInfo.name = SEND_TO_NAME;
+
+ info.activityInfo = activityInfo;
+ return info;
+ }
+}
diff --git a/tests/net/TEST_MAPPING b/tests/net/TEST_MAPPING
new file mode 100644
index 0000000..a7853b6
--- /dev/null
+++ b/tests/net/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "postsubmit": [
+ {
+ "name": "FrameworksNetIntegrationTests"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java
index a7eef05..a7328ac 100644
--- a/tests/net/common/java/android/net/LinkPropertiesTest.java
+++ b/tests/net/common/java/android/net/LinkPropertiesTest.java
@@ -38,6 +38,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.net.Inet4Address;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
@@ -65,6 +66,7 @@
private static final InetAddress GATEWAY62 = address("fe80::6:22%lo");
private static final InetAddress TESTIPV4ADDR = address("192.168.47.42");
private static final InetAddress TESTIPV6ADDR = address("fe80::7:33%43");
+ private static final Inet4Address DHCPSERVER = (Inet4Address) address("192.0.2.1");
private static final String NAME = "qmi0";
private static final String DOMAINS = "google.com";
private static final String PRIV_DNS_SERVER_NAME = "private.dns.com";
@@ -93,6 +95,7 @@
assertNull(lp.getHttpProxy());
assertNull(lp.getTcpBufferSizes());
assertNull(lp.getNat64Prefix());
+ assertNull(lp.getDhcpServerAddress());
assertFalse(lp.isProvisioned());
assertFalse(lp.isIpv4Provisioned());
assertFalse(lp.isIpv6Provisioned());
@@ -119,6 +122,7 @@
lp.setMtu(MTU);
lp.setTcpBufferSizes(TCP_BUFFER_SIZES);
lp.setNat64Prefix(new IpPrefix("2001:db8:0:64::/96"));
+ lp.setDhcpServerAddress(DHCPSERVER);
lp.setWakeOnLanSupported(true);
return lp;
}
@@ -960,11 +964,13 @@
source.setWakeOnLanSupported(true);
+ source.setDhcpServerAddress((Inet4Address) GATEWAY1);
+
final LinkProperties stacked = new LinkProperties();
stacked.setInterfaceName("test-stacked");
source.addStackedLink(stacked);
- assertParcelSane(source, 15 /* fieldCount */);
+ assertParcelSane(source, 16 /* fieldCount */);
}
@Test
@@ -1091,6 +1097,15 @@
}
@Test
+ public void testDhcpServerAddress() {
+ final LinkProperties lp = makeTestObject();
+ assertEquals(DHCPSERVER, lp.getDhcpServerAddress());
+
+ lp.clear();
+ assertNull(lp.getDhcpServerAddress());
+ }
+
+ @Test
public void testWakeOnLanSupported() {
final LinkProperties lp = makeTestObject();
assertTrue(lp.isWakeOnLanSupported());
diff --git a/tests/net/integration/Android.bp b/tests/net/integration/Android.bp
index 7d9b7b7..874bd4b 100644
--- a/tests/net/integration/Android.bp
+++ b/tests/net/integration/Android.bp
@@ -36,6 +36,7 @@
"services.net",
"testables",
],
+ test_suites: ["device-tests"],
use_embedded_native_libs: true,
jni_libs: [
// For mockito extended
diff --git a/tests/net/integration/AndroidManifest.xml b/tests/net/integration/AndroidManifest.xml
index 4dd3b5a..09c0e48 100644
--- a/tests/net/integration/AndroidManifest.xml
+++ b/tests/net/integration/AndroidManifest.xml
@@ -28,7 +28,9 @@
<!-- Reading network status -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
- <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
+ <uses-permission android:name="android.permission.NETWORK_FACTORY" />
+ <uses-permission android:name="android.permission.NETWORK_STACK" />
+ <uses-permission android:name="android.permission.OBSERVE_NETWORK_POLICY" />
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
<!-- Reading DeviceConfig flags -->
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
diff --git a/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java b/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java
index c50229a..d1d6a26 100644
--- a/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java
+++ b/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java
@@ -16,12 +16,12 @@
package com.android.framework.permission.tests;
-import android.media.AudioAttributes;
import android.os.Binder;
import android.os.IVibratorService;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.test.suitebuilder.annotation.SmallTest;
@@ -52,8 +52,8 @@
try {
final VibrationEffect effect =
VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE);
- final AudioAttributes attrs = new AudioAttributes.Builder()
- .setUsage(AudioAttributes.USAGE_ALARM)
+ final VibrationAttributes attrs = new VibrationAttributes.Builder()
+ .setUsage(VibrationAttributes.USAGE_ALARM)
.build();
mVibratorService.vibrate(Process.myUid(), null, effect, attrs,
"testVibrate", new Binder());
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index c557656..74e2a098 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -1641,8 +1641,14 @@
ParsedResource* out_resource) {
out_resource->name.type = ResourceType::kStyleable;
- // Declare-styleable is kPrivate by default, because it technically only exists in R.java.
- out_resource->visibility_level = Visibility::Level::kPublic;
+ if (!options_.preserve_visibility_of_styleables) {
+ // This was added in change Idd21b5de4d20be06c6f8c8eb5a22ccd68afc4927 to mimic aapt1, but no one
+ // knows exactly what for.
+ //
+ // FWIW, styleables only appear in generated R classes. For custom views these should always be
+ // package-private (to be used only by the view class); themes are a different story.
+ out_resource->visibility_level = Visibility::Level::kPublic;
+ }
// Declare-styleable only ends up in default config;
if (out_resource->config != ConfigDescription::DefaultConfig()) {
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index 06bb0c9..9d3ecc8 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -46,6 +46,12 @@
*/
bool error_on_positional_arguments = true;
+ /**
+ * If true, apply the same visibility rules for styleables as are used for
+ * all other resources. Otherwise, all styleables will be made public.
+ */
+ bool preserve_visibility_of_styleables = false;
+
// If visibility was forced, we need to use it when creating a new resource and also error if we
// try to parse the <public>, <public-group>, <java-symbol> or <symbol> tags.
Maybe<Visibility::Level> visibility;
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 4237469..24531bc 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -614,6 +614,32 @@
EXPECT_THAT(styleable->entries[2].name, Eq(make_value(test::ParseNameOrDie("attr/baz"))));
}
+TEST_F(ResourceParserTest, ParseDeclareStyleablePreservingVisibility) {
+ StringInputStream input(R"(
+ <resources>
+ <declare-styleable name="foo">
+ <attr name="myattr" />
+ </declare-styleable>
+ <declare-styleable name="bar">
+ <attr name="myattr" />
+ </declare-styleable>
+ <public type="styleable" name="bar" />
+ </resources>)");
+ ResourceParser parser(context_->GetDiagnostics(), &table_, Source{"test"},
+ ConfigDescription::DefaultConfig(),
+ ResourceParserOptions{.preserve_visibility_of_styleables = true});
+
+ xml::XmlPullParser xml_parser(&input);
+ ASSERT_TRUE(parser.Parse(&xml_parser));
+
+ EXPECT_EQ(
+ table_.FindResource(test::ParseNameOrDie("styleable/foo")).value().entry->visibility.level,
+ Visibility::Level::kUndefined);
+ EXPECT_EQ(
+ table_.FindResource(test::ParseNameOrDie("styleable/bar")).value().entry->visibility.level,
+ Visibility::Level::kPublic);
+}
+
TEST_F(ResourceParserTest, ParsePrivateAttributesDeclareStyleable) {
std::string input = R"(
<declare-styleable xmlns:privAndroid="http://schemas.android.com/apk/prv/res/android"
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index d50b1de..3268653 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -159,6 +159,7 @@
ResourceParserOptions parser_options;
parser_options.error_on_positional_arguments = !options.legacy_mode;
+ parser_options.preserve_visibility_of_styleables = options.preserve_visibility_of_styleables;
parser_options.translatable = translatable_file;
// If visibility was forced, we need to use it when creating a new resource and also error if
diff --git a/tools/aapt2/cmd/Compile.h b/tools/aapt2/cmd/Compile.h
index d3456b2..1752a1a 100644
--- a/tools/aapt2/cmd/Compile.h
+++ b/tools/aapt2/cmd/Compile.h
@@ -35,6 +35,8 @@
bool pseudolocalize = false;
bool no_png_crunch = false;
bool legacy_mode = false;
+ // See comments on aapt::ResourceParserOptions.
+ bool preserve_visibility_of_styleables = false;
bool verbose = false;
};
@@ -56,6 +58,11 @@
AddOptionalSwitch("--no-crunch", "Disables PNG processing", &options_.no_png_crunch);
AddOptionalSwitch("--legacy", "Treat errors that used to be valid in AAPT as warnings",
&options_.legacy_mode);
+ AddOptionalSwitch("--preserve-visibility-of-styleables",
+ "If specified, apply the same visibility rules for\n"
+ "styleables as are used for all other resources.\n"
+ "Otherwise, all stylesables will be made public.",
+ &options_.preserve_visibility_of_styleables);
AddOptionalFlag("--visibility",
"Sets the visibility of the compiled resources to the specified\n"
"level. Accepted levels: public, private, default", &visibility_);
diff --git a/wifi/Android.bp b/wifi/Android.bp
index 35a892a..09d5386 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -48,6 +48,7 @@
"//frameworks/opt/net/wifi/tests/wifitests:__subpackages__",
"//frameworks/opt/net/wifi/libs/WifiTrackerLib/tests",
+ "//external/robolectric-shadows:__subpackages__",
]
java_library {
@@ -57,6 +58,7 @@
libs: [
// TODO(b/140299412) should be framework-system-stubs once we fix all @hide dependencies
"framework-minus-apex",
+ "unsupportedappusage",
],
srcs: [
":framework-wifi-updatable-sources",
@@ -104,8 +106,10 @@
name: "framework-wifi-test-defaults",
sdk_version: "core_platform", // tests can use @CorePlatformApi's
libs: [
+ // order matters: classes in framework-wifi are resolved before framework, meaning
+ // @hide APIs in framework-wifi are resolved before @SystemApi stubs in framework
"framework-wifi",
- "framework-minus-apex",
+ "framework",
// if sdk_version="" this gets automatically included, but here we need to add manually.
"framework-res",
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 1678d5a..71942f0 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -90,6 +90,8 @@
void allowAutojoin(int netId, boolean choice);
+ void allowAutojoinPasspoint(String fqdn, boolean enableAutoJoin);
+
boolean startScan(String packageName, String featureId);
List<ScanResult> getScanResults(String callingPackage, String callingFeatureId);
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index 83a1800..c6aca07 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -19,7 +19,7 @@
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -82,16 +82,19 @@
* @hide
* No security protocol.
*/
+ @SystemApi
public static final int PROTOCOL_NONE = 0;
/**
* @hide
* Security protocol type: WPA version 1.
*/
+ @SystemApi
public static final int PROTOCOL_WPA = 1;
/**
* @hide
* Security protocol type: RSN, for WPA version 2, and version 3.
*/
+ @SystemApi
public static final int PROTOCOL_RSN = 2;
/**
* @hide
@@ -99,79 +102,94 @@
* OSU Server-only authenticated layer 2 Encryption Network.
* Used for Hotspot 2.0.
*/
+ @SystemApi
public static final int PROTOCOL_OSEN = 3;
/**
* @hide
* Security protocol type: WAPI.
*/
+ @SystemApi
public static final int PROTOCOL_WAPI = 4;
/**
* @hide
* No security key management scheme.
*/
+ @SystemApi
public static final int KEY_MGMT_NONE = 0;
/**
* @hide
* Security key management scheme: PSK.
*/
+ @SystemApi
public static final int KEY_MGMT_PSK = 1;
/**
* @hide
* Security key management scheme: EAP.
*/
+ @SystemApi
public static final int KEY_MGMT_EAP = 2;
/**
* @hide
* Security key management scheme: FT_PSK.
*/
+ @SystemApi
public static final int KEY_MGMT_FT_PSK = 3;
/**
* @hide
* Security key management scheme: FT_EAP.
*/
+ @SystemApi
public static final int KEY_MGMT_FT_EAP = 4;
/**
* @hide
* Security key management scheme: PSK_SHA256
*/
+ @SystemApi
public static final int KEY_MGMT_PSK_SHA256 = 5;
/**
* @hide
* Security key management scheme: EAP_SHA256.
*/
+ @SystemApi
public static final int KEY_MGMT_EAP_SHA256 = 6;
/**
* @hide
* Security key management scheme: OSEN.
* Used for Hotspot 2.0.
*/
+ @SystemApi
public static final int KEY_MGMT_OSEN = 7;
/**
* @hide
* Security key management scheme: SAE.
*/
+ @SystemApi
public static final int KEY_MGMT_SAE = 8;
/**
* @hide
* Security key management scheme: OWE.
*/
+ @SystemApi
public static final int KEY_MGMT_OWE = 9;
/**
* @hide
* Security key management scheme: SUITE_B_192.
*/
+ @SystemApi
public static final int KEY_MGMT_EAP_SUITE_B_192 = 10;
/**
* @hide
* Security key management scheme: FT_SAE.
*/
+ @SystemApi
public static final int KEY_MGMT_FT_SAE = 11;
/**
* @hide
* Security key management scheme: OWE in transition mode.
*/
+ @SystemApi
public static final int KEY_MGMT_OWE_TRANSITION = 12;
/**
* @hide
@@ -185,35 +203,42 @@
*/
@SystemApi
public static final int KEY_MGMT_WAPI_CERT = 14;
+
/**
* @hide
* No cipher suite.
*/
+ @SystemApi
public static final int CIPHER_NONE = 0;
/**
* @hide
* No group addressed, only used for group data cipher.
*/
+ @SystemApi
public static final int CIPHER_NO_GROUP_ADDRESSED = 1;
/**
* @hide
* Cipher suite: TKIP
*/
+ @SystemApi
public static final int CIPHER_TKIP = 2;
/**
* @hide
* Cipher suite: CCMP
*/
+ @SystemApi
public static final int CIPHER_CCMP = 3;
/**
* @hide
* Cipher suite: GCMP
*/
+ @SystemApi
public static final int CIPHER_GCMP_256 = 4;
/**
* @hide
* Cipher suite: SMS4
*/
+ @SystemApi
public static final int CIPHER_SMS4 = 5;
/**
diff --git a/wifi/java/android/net/wifi/SoftApCapability.java b/wifi/java/android/net/wifi/SoftApCapability.java
index c4474e2..2bbe7d2 100644
--- a/wifi/java/android/net/wifi/SoftApCapability.java
+++ b/wifi/java/android/net/wifi/SoftApCapability.java
@@ -61,11 +61,20 @@
*/
public static final int SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT = 1 << 1;
+
+ /**
+ * Support for WPA3 Simultaneous Authentication of Equals (WPA3-SAE).
+ *
+ * flag when {@link config_wifi_softap_sae_supported)} is true.
+ */
+ public static final int SOFTAP_FEATURE_WPA3_SAE = 1 << 2;
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, prefix = { "SOFTAP_FEATURE_" }, value = {
SOFTAP_FEATURE_ACS_OFFLOAD,
SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT,
+ SOFTAP_FEATURE_WPA3_SAE,
})
public @interface HotspotFeatures {}
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
index 05e245b..65e9b79 100644
--- a/wifi/java/android/net/wifi/SoftApConfiguration.java
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -25,6 +25,7 @@
import android.os.Parcelable;
import android.text.TextUtils;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
@@ -55,6 +56,11 @@
@SystemApi
public final class SoftApConfiguration implements Parcelable {
+ @VisibleForTesting
+ static final int PSK_MIN_LEN = 8;
+ @VisibleForTesting
+ static final int PSK_MAX_LEN = 63;
+
/**
* 2GHz band.
* @hide
@@ -142,9 +148,10 @@
private final @Nullable MacAddress mBssid;
/**
- * Pre-shared key for WPA2-PSK encryption (non-null enables WPA2-PSK).
+ * Pre-shared key for WPA2-PSK or WPA3-SAE-Transition or WPA3-SAE encryption which depends on
+ * the security type.
*/
- private final @Nullable String mWpa2Passphrase;
+ private final @Nullable String mPassphrase;
/**
* This is a network that does not broadcast its SSID, so an
@@ -186,20 +193,30 @@
public static final int SECURITY_TYPE_WPA2_PSK = 1;
/** @hide */
+ @SystemApi
+ public static final int SECURITY_TYPE_WPA3_SAE_TRANSITION = 2;
+
+ /** @hide */
+ @SystemApi
+ public static final int SECURITY_TYPE_WPA3_SAE = 3;
+
+ /** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = { "SECURITY_TYPE" }, value = {
+ @IntDef(prefix = { "SECURITY_TYPE_" }, value = {
SECURITY_TYPE_OPEN,
SECURITY_TYPE_WPA2_PSK,
+ SECURITY_TYPE_WPA3_SAE_TRANSITION,
+ SECURITY_TYPE_WPA3_SAE,
})
public @interface SecurityType {}
/** Private constructor for Builder and Parcelable implementation. */
private SoftApConfiguration(@Nullable String ssid, @Nullable MacAddress bssid,
- @Nullable String wpa2Passphrase, boolean hiddenSsid, @BandType int band, int channel,
+ @Nullable String passphrase, boolean hiddenSsid, @BandType int band, int channel,
@SecurityType int securityType, int maxNumberOfClients) {
mSsid = ssid;
mBssid = bssid;
- mWpa2Passphrase = wpa2Passphrase;
+ mPassphrase = passphrase;
mHiddenSsid = hiddenSsid;
mBand = band;
mChannel = channel;
@@ -218,7 +235,7 @@
SoftApConfiguration other = (SoftApConfiguration) otherObj;
return Objects.equals(mSsid, other.mSsid)
&& Objects.equals(mBssid, other.mBssid)
- && Objects.equals(mWpa2Passphrase, other.mWpa2Passphrase)
+ && Objects.equals(mPassphrase, other.mPassphrase)
&& mHiddenSsid == other.mHiddenSsid
&& mBand == other.mBand
&& mChannel == other.mChannel
@@ -228,7 +245,7 @@
@Override
public int hashCode() {
- return Objects.hash(mSsid, mBssid, mWpa2Passphrase, mHiddenSsid,
+ return Objects.hash(mSsid, mBssid, mPassphrase, mHiddenSsid,
mBand, mChannel, mSecurityType, mMaxNumberOfClients);
}
@@ -237,8 +254,8 @@
StringBuilder sbuf = new StringBuilder();
sbuf.append("ssid=").append(mSsid);
if (mBssid != null) sbuf.append(" \n bssid=").append(mBssid.toString());
- sbuf.append(" \n Wpa2Passphrase =").append(
- TextUtils.isEmpty(mWpa2Passphrase) ? "<empty>" : "<non-empty>");
+ sbuf.append(" \n Passphrase =").append(
+ TextUtils.isEmpty(mPassphrase) ? "<empty>" : "<non-empty>");
sbuf.append(" \n HiddenSsid =").append(mHiddenSsid);
sbuf.append(" \n Band =").append(mBand);
sbuf.append(" \n Channel =").append(mChannel);
@@ -251,7 +268,7 @@
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(mSsid);
dest.writeParcelable(mBssid, flags);
- dest.writeString(mWpa2Passphrase);
+ dest.writeString(mPassphrase);
dest.writeBoolean(mHiddenSsid);
dest.writeInt(mBand);
dest.writeInt(mChannel);
@@ -299,13 +316,26 @@
return mBssid;
}
+ // TODO: Remove it after update the caller
/**
* Returns String set to be passphrase for the WPA2-PSK AP.
- * {@link Builder#setWpa2Passphrase(String)}.
+ * {@link #setWpa2Passphrase(String)}.
*/
@Nullable
public String getWpa2Passphrase() {
- return mWpa2Passphrase;
+ if (mSecurityType == SECURITY_TYPE_WPA2_PSK) {
+ return mPassphrase;
+ }
+ return null;
+ }
+
+ /**
+ * Returns String set to be passphrase for current AP.
+ * {@link #setPassphrase(String, @SecurityType int)}.
+ */
+ @Nullable
+ public String getPassphrase() {
+ return mPassphrase;
}
/**
@@ -360,23 +390,12 @@
public static final class Builder {
private String mSsid;
private MacAddress mBssid;
- private String mWpa2Passphrase;
+ private String mPassphrase;
private boolean mHiddenSsid;
private int mBand;
private int mChannel;
private int mMaxNumberOfClients;
-
- private int setSecurityType() {
- int securityType = SECURITY_TYPE_OPEN;
- if (!TextUtils.isEmpty(mWpa2Passphrase)) { // WPA2-PSK network.
- securityType = SECURITY_TYPE_WPA2_PSK;
- }
- return securityType;
- }
-
- private void clearAllPassphrase() {
- mWpa2Passphrase = null;
- }
+ private int mSecurityType;
/**
* Constructs a Builder with default values (see {@link Builder}).
@@ -384,11 +403,12 @@
public Builder() {
mSsid = null;
mBssid = null;
- mWpa2Passphrase = null;
+ mPassphrase = null;
mHiddenSsid = false;
mBand = BAND_2GHZ;
mChannel = 0;
mMaxNumberOfClients = 0;
+ mSecurityType = SECURITY_TYPE_OPEN;
}
/**
@@ -399,11 +419,12 @@
mSsid = other.mSsid;
mBssid = other.mBssid;
- mWpa2Passphrase = other.mWpa2Passphrase;
+ mPassphrase = other.mPassphrase;
mHiddenSsid = other.mHiddenSsid;
mBand = other.mBand;
mChannel = other.mChannel;
mMaxNumberOfClients = other.mMaxNumberOfClients;
+ mSecurityType = other.mSecurityType;
}
/**
@@ -413,8 +434,8 @@
*/
@NonNull
public SoftApConfiguration build() {
- return new SoftApConfiguration(mSsid, mBssid, mWpa2Passphrase,
- mHiddenSsid, mBand, mChannel, setSecurityType(), mMaxNumberOfClients);
+ return new SoftApConfiguration(mSsid, mBssid, mPassphrase,
+ mHiddenSsid, mBand, mChannel, mSecurityType, mMaxNumberOfClients);
}
/**
@@ -461,6 +482,7 @@
return this;
}
+ // TODO: Remove it after update the caller
/**
* Specifies that this AP should use WPA2-PSK with the given ASCII WPA2 passphrase.
* When set to null, an open network is created.
@@ -473,15 +495,47 @@
*/
@NonNull
public Builder setWpa2Passphrase(@Nullable String passphrase) {
- if (passphrase != null) {
+ return setPassphrase(passphrase, SECURITY_TYPE_WPA2_PSK);
+ }
+
+ /**
+ * Specifies that this AP should use specific security type with the given ASCII passphrase.
+ *
+ * @param securityType one of the security types from {@link @SecurityType}.
+ * @param passphrase The passphrase to use for sepcific {@link @SecurityType} configuration
+ * or null with {@link @SecurityType#SECURITY_TYPE_OPEN}.
+ *
+ * @return Builder for chaining.
+ * @throws IllegalArgumentException when the passphrase length is invalid and
+ * {@code securityType} is not {@link @SecurityType#SECURITY_TYPE_OPEN}
+ * or non-null passphrase and {@code securityType} is
+ * {@link @SecurityType#SECURITY_TYPE_OPEN}.
+ */
+ @NonNull
+ public Builder setPassphrase(@Nullable String passphrase, @SecurityType int securityType) {
+ if (securityType == SECURITY_TYPE_OPEN) {
+ if (passphrase != null) {
+ throw new IllegalArgumentException(
+ "passphrase should be null when security type is open");
+ }
+ } else {
+ Preconditions.checkStringNotEmpty(passphrase);
final CharsetEncoder asciiEncoder = StandardCharsets.US_ASCII.newEncoder();
if (!asciiEncoder.canEncode(passphrase)) {
throw new IllegalArgumentException("passphrase not ASCII encodable");
}
- Preconditions.checkStringNotEmpty(passphrase);
+ if (securityType == SECURITY_TYPE_WPA2_PSK
+ || securityType == SECURITY_TYPE_WPA3_SAE_TRANSITION) {
+ if (passphrase.length() < PSK_MIN_LEN || passphrase.length() > PSK_MAX_LEN) {
+ throw new IllegalArgumentException(
+ "Password size must be at least " + PSK_MIN_LEN
+ + " and no more than " + PSK_MAX_LEN
+ + " for WPA2_PSK and WPA3_SAE_TRANSITION Mode");
+ }
+ }
}
- clearAllPassphrase();
- mWpa2Passphrase = passphrase;
+ mSecurityType = securityType;
+ mPassphrase = passphrase;
return this;
}
diff --git a/wifi/java/android/net/wifi/WifiAnnotations.java b/wifi/java/android/net/wifi/WifiAnnotations.java
index 9223d28..05e5b1d 100644
--- a/wifi/java/android/net/wifi/WifiAnnotations.java
+++ b/wifi/java/android/net/wifi/WifiAnnotations.java
@@ -60,4 +60,45 @@
})
@Retention(RetentionPolicy.SOURCE)
public @interface Bandwidth {}
+
+ @IntDef(prefix = { "PROTOCOL_" }, value = {
+ ScanResult.PROTOCOL_NONE,
+ ScanResult.PROTOCOL_WPA,
+ ScanResult.PROTOCOL_RSN,
+ ScanResult.PROTOCOL_OSEN,
+ ScanResult.PROTOCOL_WAPI
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Protocol {}
+
+ @IntDef(prefix = { "KEY_MGMT_" }, value = {
+ ScanResult.KEY_MGMT_NONE,
+ ScanResult.KEY_MGMT_PSK,
+ ScanResult.KEY_MGMT_EAP,
+ ScanResult.KEY_MGMT_FT_PSK,
+ ScanResult.KEY_MGMT_FT_EAP,
+ ScanResult.KEY_MGMT_PSK_SHA256,
+ ScanResult.KEY_MGMT_EAP_SHA256,
+ ScanResult.KEY_MGMT_OSEN,
+ ScanResult.KEY_MGMT_SAE,
+ ScanResult.KEY_MGMT_OWE,
+ ScanResult.KEY_MGMT_EAP_SUITE_B_192,
+ ScanResult.KEY_MGMT_FT_SAE,
+ ScanResult.KEY_MGMT_OWE_TRANSITION,
+ ScanResult.KEY_MGMT_WAPI_PSK,
+ ScanResult.KEY_MGMT_WAPI_CERT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface KeyMgmt {}
+
+ @IntDef(prefix = { "CIPHER_" }, value = {
+ ScanResult.CIPHER_NONE,
+ ScanResult.CIPHER_NO_GROUP_ADDRESSED,
+ ScanResult.CIPHER_TKIP,
+ ScanResult.CIPHER_CCMP,
+ ScanResult.CIPHER_GCMP_256,
+ ScanResult.CIPHER_SMS4
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Cipher {}
}
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 2a165d3..f4c5b91 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -20,7 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.PackageManager;
import android.net.IpConfiguration;
import android.net.IpConfiguration.ProxySettings;
@@ -253,9 +253,12 @@
/** LEAP/Network EAP (only used with LEAP) */
public static final int LEAP = 2;
+ /** SAE (Used only for WPA3-Personal) */
+ public static final int SAE = 3;
+
public static final String varName = "auth_alg";
- public static final String[] strings = { "OPEN", "SHARED", "LEAP" };
+ public static final String[] strings = { "OPEN", "SHARED", "LEAP", "SAE" };
}
/**
@@ -468,10 +471,13 @@
break;
case SECURITY_TYPE_SAE:
allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SAE);
+ allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
+ allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
requirePMF = true;
break;
case SECURITY_TYPE_EAP_SUITE_B:
allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SUITE_B_192);
+ allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.GCMP_256);
allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256);
allowedGroupManagementCiphers.set(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256);
// Note: allowedSuiteBCiphers bitset will be set by the service once the
@@ -480,6 +486,8 @@
break;
case SECURITY_TYPE_OWE:
allowedKeyManagement.set(WifiConfiguration.KeyMgmt.OWE);
+ allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
+ allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
requirePMF = true;
break;
case SECURITY_TYPE_WAPI_PSK:
@@ -2428,7 +2436,8 @@
} else if (allowedKeyManagement.get(KeyMgmt.WPA_EAP)
|| allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
key = SSID + KeyMgmt.strings[KeyMgmt.WPA_EAP];
- } else if (wepKeys[0] != null) {
+ } else if (wepTxKeyIndex >= 0 && wepTxKeyIndex < wepKeys.length
+ && wepKeys[wepTxKeyIndex] != null) {
key = SSID + "WEP";
} else if (allowedKeyManagement.get(KeyMgmt.OWE)) {
key = SSID + KeyMgmt.strings[KeyMgmt.OWE];
@@ -2590,9 +2599,8 @@
return mPasspointManagementObjectTree;
}
- /** copy constructor {@hide} */
- @UnsupportedAppUsage
- public WifiConfiguration(WifiConfiguration source) {
+ /** Copy constructor */
+ public WifiConfiguration(@NonNull WifiConfiguration source) {
if (source != null) {
networkId = source.networkId;
status = source.status;
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index 7a59a4f..41f7c6e 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -19,7 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -1336,20 +1336,18 @@
}
/**
- * If the current authentication method needs SIM card.
- * @return true if the credential information require SIM card for current authentication
+ * Utility method to determine whether the configuration's authentication method is SIM-based.
+ *
+ * @return true if the credential information requires SIM card for current authentication
* method, otherwise it returns false.
- * @hide
*/
- public boolean requireSimCredential() {
+ public boolean isAuthenticationSimBased() {
if (mEapMethod == Eap.SIM || mEapMethod == Eap.AKA || mEapMethod == Eap.AKA_PRIME) {
return true;
}
if (mEapMethod == Eap.PEAP) {
- if (mPhase2Method == Phase2.SIM || mPhase2Method == Phase2.AKA
- || mPhase2Method == Phase2.AKA_PRIME) {
- return true;
- }
+ return mPhase2Method == Phase2.SIM || mPhase2Method == Phase2.AKA
+ || mPhase2Method == Phase2.AKA_PRIME;
}
return false;
}
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index f728491..62337cb 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -19,7 +19,7 @@
import android.annotation.IntRange;
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.net.NetworkInfo.DetailedState;
import android.net.shared.Inet4AddressUtils;
import android.os.Build;
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 7693f9a..e870481 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -29,8 +29,8 @@
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
import android.annotation.SystemService;
-import android.annotation.UnsupportedAppUsage;
import android.app.ActivityManager;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.pm.ParceledListSlice;
import android.net.ConnectivityManager;
@@ -545,15 +545,22 @@
public static final String EXTRA_WIFI_AP_STATE = "wifi_state";
/**
- * The look up key for an int that indicates why softAP started failed
- * currently support general and no_channel
- * @see #SAP_START_FAILURE_GENERAL
- * @see #SAP_START_FAILURE_NO_CHANNEL
- * @see #SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION
+ * An extra containing the int error code for Soft AP start failure.
+ * Can be obtained from the {@link #WIFI_AP_STATE_CHANGED_ACTION} using
+ * {@link android.content.Intent#getIntExtra}.
+ * This extra will only be attached if {@link #EXTRA_WIFI_AP_STATE} is
+ * attached and is equal to {@link #WIFI_AP_STATE_FAILED}.
+ *
+ * The error code will be one of:
+ * {@link #SAP_START_FAILURE_GENERAL},
+ * {@link #SAP_START_FAILURE_NO_CHANNEL},
+ * {@link #SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}
*
* @hide
*/
- public static final String EXTRA_WIFI_AP_FAILURE_REASON = "wifi_ap_error_code";
+ @SystemApi
+ public static final String EXTRA_WIFI_AP_FAILURE_REASON =
+ "android.net.wifi.extra.WIFI_AP_FAILURE_REASON";
/**
* The previous Wi-Fi state.
*
@@ -4103,6 +4110,23 @@
}
/**
+ * Configure auto-join settings for a Passpoint profile.
+ *
+ * @param fqdn the FQDN (fully qualified domain name) of the passpoint profile.
+ * @param enableAutoJoin true to enable autojoin, false to disable autojoin.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+ public void allowAutojoinPasspoint(@NonNull String fqdn, boolean enableAutoJoin) {
+ try {
+ mService.allowAutojoinPasspoint(fqdn, enableAutoJoin);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Disable an ephemeral network.
*
* @param ssid in the format of WifiConfiguration's SSID.
diff --git a/wifi/java/android/net/wifi/WifiSsid.java b/wifi/java/android/net/wifi/WifiSsid.java
index 90756d8..704ae81 100644
--- a/wifi/java/android/net/wifi/WifiSsid.java
+++ b/wifi/java/android/net/wifi/WifiSsid.java
@@ -18,7 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
index 5befb54..1822e84 100644
--- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
+++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
@@ -17,6 +17,7 @@
package android.net.wifi.hotspot2;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.net.wifi.hotspot2.pps.Credential;
import android.net.wifi.hotspot2.pps.HomeSp;
import android.net.wifi.hotspot2.pps.Policy;
@@ -423,6 +424,41 @@
}
/**
+ * The auto-join configuration specifies whether or not the Passpoint Configuration is
+ * considered for auto-connection. If true then yes, if false then it isn't considered as part
+ * of auto-connection - but can still be manually connected to.
+ */
+ private boolean mIsAutoJoinEnabled = true;
+
+ /**
+ * Configures the auto-association status of this Passpoint configuration. A value of true
+ * indicates that the configuration will be considered for auto-connection, a value of false
+ * indicates that only manual connection will work - the framework will not auto-associate to
+ * this Passpoint network.
+ *
+ * @param autoJoinEnabled true to be considered for framework auto-connection, false otherwise.
+ * @hide
+ */
+ public void setAutoJoinEnabled(boolean autoJoinEnabled) {
+ mIsAutoJoinEnabled = autoJoinEnabled;
+ }
+
+ /**
+ * Indicates whether the Passpoint configuration may be auto-connected to by the framework. A
+ * value of true indicates that auto-connection can happen, a value of false indicates that it
+ * cannot. However, even when auto-connection is not possible manual connection by the user is
+ * possible.
+ *
+ * @return the auto-join configuration: true for auto-connection (or join) enabled, false
+ * otherwise.
+ * @hide
+ */
+ @SystemApi
+ public boolean isAutoJoinEnabled() {
+ return mIsAutoJoinEnabled;
+ }
+
+ /**
* Constructor for creating PasspointConfiguration with default values.
*/
public PasspointConfiguration() {}
@@ -464,6 +500,7 @@
mServiceFriendlyNames = source.mServiceFriendlyNames;
mAaaServerTrustedNames = source.mAaaServerTrustedNames;
mCarrierId = source.mCarrierId;
+ mIsAutoJoinEnabled = source.mIsAutoJoinEnabled;
}
@Override
@@ -493,6 +530,7 @@
(HashMap<String, String>) mServiceFriendlyNames);
dest.writeBundle(bundle);
dest.writeInt(mCarrierId);
+ dest.writeBoolean(mIsAutoJoinEnabled);
}
@Override
@@ -523,6 +561,7 @@
&& mUsageLimitDataLimit == that.mUsageLimitDataLimit
&& mUsageLimitTimeLimitInMinutes == that.mUsageLimitTimeLimitInMinutes
&& mCarrierId == that.mCarrierId
+ && mIsAutoJoinEnabled == that.mIsAutoJoinEnabled
&& (mServiceFriendlyNames == null ? that.mServiceFriendlyNames == null
: mServiceFriendlyNames.equals(that.mServiceFriendlyNames));
}
@@ -533,7 +572,7 @@
mUpdateIdentifier, mCredentialPriority, mSubscriptionCreationTimeInMillis,
mSubscriptionExpirationTimeInMillis, mUsageLimitUsageTimePeriodInMinutes,
mUsageLimitStartTimeInMillis, mUsageLimitDataLimit, mUsageLimitTimeLimitInMinutes,
- mServiceFriendlyNames, mCarrierId);
+ mServiceFriendlyNames, mCarrierId, mIsAutoJoinEnabled);
}
@Override
@@ -587,6 +626,7 @@
builder.append("ServiceFriendlyNames: ").append(mServiceFriendlyNames);
}
builder.append("CarrierId:" + mCarrierId);
+ builder.append("IsAutoJoinEnabled:" + mIsAutoJoinEnabled);
return builder.toString();
}
@@ -692,6 +732,7 @@
"serviceFriendlyNames");
config.setServiceFriendlyNames(friendlyNamesMap);
config.mCarrierId = in.readInt();
+ config.mIsAutoJoinEnabled = in.readBoolean();
return config;
}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
index c9bca4f..8fa9c3d 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
@@ -19,7 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.net.MacAddress;
import android.net.wifi.WpsInfo;
import android.os.Parcel;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
index 98ec208..710175f 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
@@ -18,7 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.java b/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.java
index acf06fb..ededf67 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.java
@@ -16,10 +16,9 @@
package android.net.wifi.p2p;
-import android.annotation.UnsupportedAppUsage;
-import android.os.Parcelable;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
-import android.net.wifi.p2p.WifiP2pDevice;
+import android.os.Parcelable;
import android.text.TextUtils;
import java.util.ArrayList;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
index d8c50f2..21f6704 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
@@ -17,7 +17,7 @@
package android.net.wifi.p2p;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.java b/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.java
index 10fd09a..cdb2806 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.java
@@ -17,7 +17,7 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.LruCache;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index 6120e4e..3459c94 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -24,7 +24,7 @@
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
import android.annotation.SystemService;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.net.NetworkInfo;
import android.net.wifi.WpsInfo;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pProvDiscEvent.java b/wifi/java/android/net/wifi/p2p/WifiP2pProvDiscEvent.java
index 153e03c..d0fe92d 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pProvDiscEvent.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pProvDiscEvent.java
@@ -16,7 +16,7 @@
package android.net.wifi.p2p;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
/**
* A class representing a Wi-Fi p2p provisional discovery request/response
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java b/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java
index 48b0703..a411502 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java
@@ -19,7 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceInfo.java b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceInfo.java
index e32c8e8..0de7ba6 100644
--- a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceInfo.java
+++ b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceInfo.java
@@ -16,7 +16,7 @@
package android.net.wifi.p2p.nsd;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.net.nsd.DnsSdTxtRecord;
import android.os.Build;
import android.text.TextUtils;
diff --git a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.java b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.java
index db0bdb8..37b442b 100644
--- a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.java
+++ b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.java
@@ -16,7 +16,7 @@
package android.net.wifi.p2p.nsd;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.java b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.java
index 87528c4..68cbb88 100644
--- a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.java
+++ b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.java
@@ -16,7 +16,7 @@
package android.net.wifi.p2p.nsd;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.net.wifi.p2p.WifiP2pManager;
import android.os.Build;
import android.os.Parcel;
diff --git a/wifi/java/android/net/wifi/wificond/WifiCondManager.java b/wifi/java/android/net/wifi/wificond/WifiCondManager.java
index 283f2dd..f70bdac 100644
--- a/wifi/java/android/net/wifi/wificond/WifiCondManager.java
+++ b/wifi/java/android/net/wifi/wificond/WifiCondManager.java
@@ -1154,4 +1154,68 @@
mApInterfaceListeners.clear();
mSendMgmtFrameInProgress.set(false);
}
+
+ /**
+ * OEM parsed security type
+ */
+ public static class OemSecurityType {
+ /** The protocol defined in {@link android.net.wifi.WifiAnnotations.Protocol}. */
+ public final @WifiAnnotations.Protocol int protocol;
+ /**
+ * Supported key management types defined
+ * in {@link android.net.wifi.WifiAnnotations.KeyMgmt}.
+ */
+ @NonNull public final List<Integer> keyManagement;
+ /**
+ * Supported pairwise cipher types defined
+ * in {@link android.net.wifi.WifiAnnotations.Cipher}.
+ */
+ @NonNull public final List<Integer> pairwiseCipher;
+ /** The group cipher type defined in {@link android.net.wifi.WifiAnnotations.Cipher}. */
+ public final @WifiAnnotations.Cipher int groupCipher;
+ /**
+ * Default constructor for OemSecurityType
+ *
+ * @param protocol The protocol defined in
+ * {@link android.net.wifi.WifiAnnotations.Protocol}.
+ * @param keyManagement Supported key management types defined
+ * in {@link android.net.wifi.WifiAnnotations.KeyMgmt}.
+ * @param pairwiseCipher Supported pairwise cipher types defined
+ * in {@link android.net.wifi.WifiAnnotations.Cipher}.
+ * @param groupCipher The group cipher type defined
+ * in {@link android.net.wifi.WifiAnnotations.Cipher}.
+ */
+ public OemSecurityType(
+ @WifiAnnotations.Protocol int protocol,
+ @NonNull List<Integer> keyManagement,
+ @NonNull List<Integer> pairwiseCipher,
+ @WifiAnnotations.Cipher int groupCipher) {
+ this.protocol = protocol;
+ this.keyManagement = (keyManagement != null)
+ ? keyManagement : new ArrayList<Integer>();
+ this.pairwiseCipher = (pairwiseCipher != null)
+ ? pairwiseCipher : new ArrayList<Integer>();
+ this.groupCipher = groupCipher;
+ }
+ }
+
+ /**
+ * OEM information element parser for security types not parsed by the framework.
+ *
+ * The OEM method should use the method inputs {@code id}, {@code idExt}, and {@code bytes}
+ * to perform the parsing. The method should place the results in an OemSecurityType objct.
+ *
+ * @param id The information element id.
+ * @param idExt The information element extension id. This is valid only when id is
+ * the extension id, {@code 255}.
+ * @param bytes The raw bytes of information element data, 'Element ID' and 'Length' are
+ * stripped off already.
+ * @return an OemSecurityType object if this IE is parsed successfully, null otherwise.
+ */
+ @Nullable public static OemSecurityType parseOemSecurityTypeElement(
+ int id,
+ int idExt,
+ @NonNull byte[] bytes) {
+ return null;
+ }
}
diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java
index d58083c..d91efbc 100644
--- a/wifi/java/com/android/server/wifi/BaseWifiService.java
+++ b/wifi/java/com/android/server/wifi/BaseWifiService.java
@@ -182,6 +182,11 @@
}
@Override
+ public void allowAutojoinPasspoint(String fqdn, boolean enableAutoJoin) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public boolean startScan(String packageName, String featureId) {
throw new UnsupportedOperationException();
}
diff --git a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
index 1f60103..acd3343 100644
--- a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
@@ -25,8 +25,12 @@
import org.junit.Test;
+import java.util.Random;
+
@SmallTest
public class SoftApConfigurationTest {
+ private static final String TEST_CHAR_SET_AS_STRING = "abcdefghijklmnopqrstuvwxyz0123456789";
+
private SoftApConfiguration parcelUnparcel(SoftApConfiguration configIn) {
Parcel parcel = Parcel.obtain();
parcel.writeParcelable(configIn, 0);
@@ -37,6 +41,25 @@
return configOut;
}
+ /**
+ * Helper method to generate random string.
+ *
+ * Note: this method has limited use as a random string generator.
+ * The characters used in this method do no not cover all valid inputs.
+ * @param length number of characters to generate for the string
+ * @return String generated string of random characters
+ */
+ private String generateRandomString(int length) {
+ Random random = new Random();
+ StringBuilder stringBuilder = new StringBuilder(length);
+ int index = -1;
+ while (stringBuilder.length() < length) {
+ index = random.nextInt(TEST_CHAR_SET_AS_STRING.length());
+ stringBuilder.append(TEST_CHAR_SET_AS_STRING.charAt(index));
+ }
+ return stringBuilder.toString();
+ }
+
@Test
public void testBasicSettings() {
SoftApConfiguration original = new SoftApConfiguration.Builder()
@@ -45,7 +68,7 @@
.build();
assertThat(original.getSsid()).isEqualTo("ssid");
assertThat(original.getBssid()).isEqualTo(MacAddress.fromString("11:22:33:44:55:66"));
- assertThat(original.getWpa2Passphrase()).isNull();
+ assertThat(original.getPassphrase()).isNull();
assertThat(original.getSecurityType()).isEqualTo(SoftApConfiguration.SECURITY_TYPE_OPEN);
assertThat(original.getBand()).isEqualTo(SoftApConfiguration.BAND_2GHZ);
assertThat(original.getChannel()).isEqualTo(0);
@@ -66,9 +89,9 @@
@Test
public void testWpa2() {
SoftApConfiguration original = new SoftApConfiguration.Builder()
- .setWpa2Passphrase("secretsecret")
+ .setPassphrase("secretsecret", SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)
.build();
- assertThat(original.getWpa2Passphrase()).isEqualTo("secretsecret");
+ assertThat(original.getPassphrase()).isEqualTo("secretsecret");
assertThat(original.getSecurityType()).isEqualTo(
SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
assertThat(original.getBand()).isEqualTo(SoftApConfiguration.BAND_2GHZ);
@@ -90,13 +113,12 @@
@Test
public void testWpa2WithAllFieldCustomized() {
SoftApConfiguration original = new SoftApConfiguration.Builder()
- .setWpa2Passphrase("secretsecret")
- .setBand(SoftApConfiguration.BAND_ANY)
+ .setPassphrase("secretsecret", SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)
.setChannel(149, SoftApConfiguration.BAND_5GHZ)
.setHiddenSsid(true)
.setMaxNumberOfClients(10)
.build();
- assertThat(original.getWpa2Passphrase()).isEqualTo("secretsecret");
+ assertThat(original.getPassphrase()).isEqualTo("secretsecret");
assertThat(original.getSecurityType()).isEqualTo(
SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
assertThat(original.getBand()).isEqualTo(SoftApConfiguration.BAND_5GHZ);
@@ -114,4 +136,98 @@
assertThat(copy).isEqualTo(original);
assertThat(copy.hashCode()).isEqualTo(original.hashCode());
}
+
+ @Test
+ public void testWpa3Sae() {
+ SoftApConfiguration original = new SoftApConfiguration.Builder()
+ .setPassphrase("secretsecret", SoftApConfiguration.SECURITY_TYPE_WPA3_SAE)
+ .setChannel(149, SoftApConfiguration.BAND_5GHZ)
+ .setHiddenSsid(true)
+ .build();
+ assertThat(original.getPassphrase()).isEqualTo("secretsecret");
+ assertThat(original.getSecurityType()).isEqualTo(
+ SoftApConfiguration.SECURITY_TYPE_WPA3_SAE);
+ assertThat(original.getBand()).isEqualTo(SoftApConfiguration.BAND_5GHZ);
+ assertThat(original.getChannel()).isEqualTo(149);
+ assertThat(original.isHiddenSsid()).isEqualTo(true);
+
+
+ SoftApConfiguration unparceled = parcelUnparcel(original);
+ assertThat(unparceled).isNotSameAs(original);
+ assertThat(unparceled).isEqualTo(original);
+ assertThat(unparceled.hashCode()).isEqualTo(original.hashCode());
+
+ SoftApConfiguration copy = new SoftApConfiguration.Builder(original).build();
+ assertThat(copy).isNotSameAs(original);
+ assertThat(copy).isEqualTo(original);
+ assertThat(copy.hashCode()).isEqualTo(original.hashCode());
+ }
+
+ @Test
+ public void testWpa3SaeTransition() {
+ SoftApConfiguration original = new SoftApConfiguration.Builder()
+ .setPassphrase("secretsecret",
+ SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION)
+ .setChannel(149, SoftApConfiguration.BAND_5GHZ)
+ .setHiddenSsid(true)
+ .build();
+ assertThat(original.getSecurityType()).isEqualTo(
+ SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION);
+ assertThat(original.getPassphrase()).isEqualTo("secretsecret");
+ assertThat(original.getBand()).isEqualTo(SoftApConfiguration.BAND_5GHZ);
+ assertThat(original.getChannel()).isEqualTo(149);
+ assertThat(original.isHiddenSsid()).isEqualTo(true);
+
+
+ SoftApConfiguration unparceled = parcelUnparcel(original);
+ assertThat(unparceled).isNotSameAs(original);
+ assertThat(unparceled).isEqualTo(original);
+ assertThat(unparceled.hashCode()).isEqualTo(original.hashCode());
+
+ SoftApConfiguration copy = new SoftApConfiguration.Builder(original).build();
+ assertThat(copy).isNotSameAs(original);
+ assertThat(copy).isEqualTo(original);
+ assertThat(copy.hashCode()).isEqualTo(original.hashCode());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidShortPasswordLengthForWpa2() {
+ SoftApConfiguration original = new SoftApConfiguration.Builder()
+ .setPassphrase(generateRandomString(SoftApConfiguration.PSK_MIN_LEN - 1),
+ SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)
+ .setChannel(149, SoftApConfiguration.BAND_5GHZ)
+ .setHiddenSsid(true)
+ .build();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidLongPasswordLengthForWpa2() {
+ SoftApConfiguration original = new SoftApConfiguration.Builder()
+ .setPassphrase(generateRandomString(SoftApConfiguration.PSK_MAX_LEN + 1),
+ SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)
+ .setChannel(149, SoftApConfiguration.BAND_5GHZ)
+ .setHiddenSsid(true)
+ .build();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidShortPasswordLengthForWpa3SaeTransition() {
+ SoftApConfiguration original = new SoftApConfiguration.Builder()
+ .setPassphrase(generateRandomString(SoftApConfiguration.PSK_MIN_LEN - 1),
+ SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION)
+ .setChannel(149, SoftApConfiguration.BAND_5GHZ)
+ .setHiddenSsid(true)
+ .build();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidLongPasswordLengthForWpa3SaeTransition() {
+ SoftApConfiguration original = new SoftApConfiguration.Builder()
+ .setPassphrase(generateRandomString(SoftApConfiguration.PSK_MAX_LEN + 1),
+ SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION)
+ .setChannel(149, SoftApConfiguration.BAND_5GHZ)
+ .setHiddenSsid(true)
+ .build();
+ }
+
}
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index 5d6549e..8689a38 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -16,6 +16,10 @@
package android.net.wifi;
+import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B;
+import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_OWE;
+import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_SAE;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -270,7 +274,23 @@
config.allowedKeyManagement.clear();
assertEquals(mSsid + "WEP", config.getSsidAndSecurityTypeString());
+ // set WEP key and give a valid index.
config.wepKeys[0] = null;
+ config.wepKeys[2] = "TestWep";
+ config.wepTxKeyIndex = 2;
+ config.allowedKeyManagement.clear();
+ assertEquals(mSsid + "WEP", config.getSsidAndSecurityTypeString());
+
+ // set WEP key but does not give a valid index.
+ config.wepKeys[0] = null;
+ config.wepKeys[2] = "TestWep";
+ config.wepTxKeyIndex = 0;
+ config.allowedKeyManagement.clear();
+ config.allowedKeyManagement.set(KeyMgmt.OWE);
+ assertEquals(mSsid + KeyMgmt.strings[KeyMgmt.OWE], config.getSsidAndSecurityTypeString());
+
+ config.wepKeys[0] = null;
+ config.wepTxKeyIndex = 0;
config.allowedKeyManagement.clear();
config.allowedKeyManagement.set(KeyMgmt.OWE);
assertEquals(mSsid + KeyMgmt.strings[KeyMgmt.OWE], config.getSsidAndSecurityTypeString());
@@ -312,4 +332,57 @@
assertNotNull(NetworkSelectionStatus.DISABLE_REASON_INFOS.get(i));
}
}
+
+ /**
+ * Ensure that {@link WifiConfiguration#setSecurityParams(int)} sets up the
+ * {@link WifiConfiguration} object correctly for SAE security type.
+ * @throws Exception
+ */
+ @Test
+ public void testSetSecurityParamsForSae() throws Exception {
+ WifiConfiguration config = new WifiConfiguration();
+
+ config.setSecurityParams(SECURITY_TYPE_SAE);
+
+ assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SAE));
+ assertTrue(config.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.CCMP));
+ assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.CCMP));
+ assertTrue(config.requirePMF);
+ }
+
+ /**
+ * Ensure that {@link WifiConfiguration#setSecurityParams(int)} sets up the
+ * {@link WifiConfiguration} object correctly for OWE security type.
+ * @throws Exception
+ */
+ @Test
+ public void testSetSecurityParamsForOwe() throws Exception {
+ WifiConfiguration config = new WifiConfiguration();
+
+ config.setSecurityParams(SECURITY_TYPE_OWE);
+
+ assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.OWE));
+ assertTrue(config.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.CCMP));
+ assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.CCMP));
+ assertTrue(config.requirePMF);
+ }
+
+ /**
+ * Ensure that {@link WifiConfiguration#setSecurityParams(int)} sets up the
+ * {@link WifiConfiguration} object correctly for Suite-B security type.
+ * @throws Exception
+ */
+ @Test
+ public void testSetSecurityParamsForSuiteB() throws Exception {
+ WifiConfiguration config = new WifiConfiguration();
+
+ config.setSecurityParams(SECURITY_TYPE_EAP_SUITE_B);
+
+ assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SUITE_B_192));
+ assertTrue(config.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.GCMP_256));
+ assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.GCMP_256));
+ assertTrue(config.allowedGroupManagementCiphers
+ .get(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256));
+ assertTrue(config.requirePMF);
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index f9bd31d..5ac50a0 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -1672,10 +1672,22 @@
@Test
public void testAllowAutojoin() throws Exception {
mWifiManager.allowAutojoin(1, true);
- verify(mWifiService).allowAutojoin(eq(1), eq(true));
+ verify(mWifiService).allowAutojoin(1, true);
}
/**
+ * Test behavior of {@link WifiManager#allowAutojoinPasspoint(String, boolean)}
+ * @throws Exception
+ */
+ @Test
+ public void testAllowAutojoinPasspoint() throws Exception {
+ final String fqdn = "FullyQualifiedDomainName";
+ mWifiManager.allowAutojoinPasspoint(fqdn, true);
+ verify(mWifiService).allowAutojoinPasspoint(fqdn, true);
+ }
+
+
+ /**
* Test behavior of {@link WifiManager#disconnect()}
*/
@Test
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
index f501b16..94054fd 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
@@ -171,6 +171,7 @@
assertFalse(config.validate());
assertFalse(config.validateForR2());
+ assertTrue(config.isAutoJoinEnabled());
}
/**