Merge "Add @IntRange to MediaMetadataRetriever#getScaledFrameAtTime"
diff --git a/Android.bp b/Android.bp
index a10f8de..93b37f2 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",
]
}
@@ -432,6 +433,7 @@
// TODO(b/146167933): Use framework-statsd-stubs
"framework-statsd",
"framework-wifi-stubs",
+ "ike-stubs",
],
installable: true,
javac_shard_size: 150,
@@ -481,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",
@@ -642,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",
],
}
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/os/IStatsManagerService.aidl b/apex/statsd/aidl/android/os/IStatsManagerService.aidl
index 05583677..2a3665c 100644
--- a/apex/statsd/aidl/android/os/IStatsManagerService.aidl
+++ b/apex/statsd/aidl/android/os/IStatsManagerService.aidl
@@ -85,4 +85,26 @@
* 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);
}
\ 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 9358439..c08abdd 100644
--- a/apex/statsd/aidl/android/os/IStatsd.aidl
+++ b/apex/statsd/aidl/android/os/IStatsd.aidl
@@ -89,14 +89,14 @@
*
* 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
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 1d03e3b..eff9bda 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
@@ -19,6 +19,7 @@
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;
@@ -245,20 +246,85 @@
}
}
+ @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");
+ }
+
void setStatsCompanionService(StatsCompanionService statsCompanionService) {
mStatsCompanionService = statsCompanionService;
}
- private void enforceDumpAndUsageStatsPermission(String packageName) {
+ /**
+ * 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;
}
+
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,
diff --git a/api/current.txt b/api/current.txt
index e98dccf..65a4217 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6790,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);
@@ -6911,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[]);
@@ -28679,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
@@ -28691,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);
@@ -29477,6 +29483,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);
@@ -30352,6 +30359,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);
diff --git a/api/system-current.txt b/api/system-current.txt
index 42ba962..33e3f5b 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -413,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);
@@ -446,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);
@@ -454,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();
@@ -785,6 +799,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);
@@ -1518,6 +1533,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();
@@ -1535,6 +1558,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";
}
@@ -1660,6 +1684,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";
@@ -2117,6 +2142,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
@@ -4560,6 +4586,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 {
@@ -4768,6 +4824,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;
}
@@ -4831,6 +4888,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";
}
@@ -4901,6 +4964,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;
@@ -5684,6 +5748,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 {
@@ -5692,6 +5757,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();
@@ -5704,6 +5770,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 {
@@ -5715,6 +5783,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);
}
@@ -5935,6 +6004,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";
@@ -6013,6 +6083,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);
}
@@ -6199,6 +6273,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[]);
}
@@ -6913,9 +6991,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;
@@ -7002,11 +7083,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();
@@ -7014,14 +7097,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
@@ -9101,30 +9191,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 {
@@ -9524,6 +9621,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
@@ -10016,6 +10114,7 @@
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);
@@ -10680,6 +10779,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 cb706dc..9967942 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();
@@ -3380,6 +3394,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/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 5ff0f97..b2a5b50 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -1135,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*/,
@@ -1148,12 +1147,9 @@
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();
}
@@ -1477,17 +1473,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
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 0565f3c..56d87f2 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -99,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;
/**
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 42c4d36..4a8e4e2 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -3917,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.
*
@@ -3932,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;
@@ -3958,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;
@@ -3991,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;
}
@@ -4002,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;
}
@@ -4020,6 +4097,13 @@
}
}
mOpNames = opNames;
+
+ if (mOpNames == null) {
+ mFilter &= ~FILTER_BY_OP_NAMES;
+ } else {
+ mFilter |= FILTER_BY_OP_NAMES;
+ }
+
return this;
}
@@ -4044,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);
}
}
}
@@ -4202,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);
@@ -4220,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);
}
}
}
@@ -4251,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 */
@@ -4549,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);
}
}
}
@@ -4574,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);
}
/**
@@ -4733,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) {
@@ -4742,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);
@@ -4753,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<>();
@@ -4777,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);
@@ -4793,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);
@@ -4832,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.
@@ -4880,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);
@@ -4912,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
+
}
/**
@@ -5288,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
@@ -5621,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 {
@@ -5661,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 {
@@ -7503,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/StatsManager.java b/core/java/android/app/StatsManager.java
index 83d1de6..4e50a3f 100644
--- a/core/java/android/app/StatsManager.java
+++ b/core/java/android/app/StatsManager.java
@@ -390,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);
@@ -427,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);
@@ -462,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/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index d3132d8..2aac94c 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -88,7 +88,6 @@
import android.util.ArraySet;
import android.util.Log;
-import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.Preconditions;
@@ -6757,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.
@@ -11215,12 +11242,12 @@
* #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 R.array#cross_profile_apps}</li>
+ * {@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 R.array#cross_profile_apps}
+ * {@link com.android.internal.R.array#cross_profile_apps}
*
* @hide
*/
@@ -11338,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 08c5dff..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);
@@ -452,4 +453,8 @@
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/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.java b/core/java/android/app/timedetector/NetworkTimeSuggestion.java
index 4c55ba1..17e9c5a 100644
--- a/core/java/android/app/timedetector/NetworkTimeSuggestion.java
+++ b/core/java/android/app/timedetector/NetworkTimeSuggestion.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/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 af9ece0..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.
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..a28868e 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";
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/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index b808088..55505ba 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -47,8 +47,7 @@
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/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 431773d..9731f3c 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -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/os/ExternalVibration.java b/core/java/android/os/ExternalVibration.java
index 2991db2..e62244f 100644
--- a/core/java/android/os/ExternalVibration.java
+++ b/core/java/android/os/ExternalVibration.java
@@ -161,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/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/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/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index b94475a..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);
diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java
index 3f679bba..f324113 100644
--- a/core/java/android/util/Log.java
+++ b/core/java/android/util/Log.java
@@ -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/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java
index 2223d14..fa994ba 100644
--- a/core/java/android/util/NtpTrustedTime.java
+++ b/core/java/android/util/NtpTrustedTime.java
@@ -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;
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 95522ba..a0cf534 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -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;
@@ -5900,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/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/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/DisplayId.java b/core/java/com/android/internal/compat/AndroidBuildClassifier.java
similarity index 62%
copy from packages/SystemUI/src/com/android/systemui/dagger/qualifiers/DisplayId.java
copy to core/java/com/android/internal/compat/AndroidBuildClassifier.java
index 155a6d2..0b937fa 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/DisplayId.java
+++ b/core/java/com/android/internal/compat/AndroidBuildClassifier.java
@@ -14,17 +14,21 @@
* limitations under the License.
*/
-package com.android.systemui.dagger.qualifiers;
+package com.android.internal.compat;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import android.os.Build;
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
+/**
+ * Platform private class for determining the type of Android build installed.
+ *
+ */
+public class AndroidBuildClassifier {
-import javax.inject.Qualifier;
+ public boolean isDebuggableBuild() {
+ return Build.IS_DEBUGGABLE;
+ }
-@Qualifier
-@Documented
-@Retention(RUNTIME)
-public @interface DisplayId {
+ 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/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/DisplayId.java b/core/java/com/android/internal/compat/OverrideAllowedState.aidl
similarity index 67%
copy from packages/SystemUI/src/com/android/systemui/dagger/qualifiers/DisplayId.java
copy to core/java/com/android/internal/compat/OverrideAllowedState.aidl
index 155a6d2..10ceac7 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/DisplayId.java
+++ b/core/java/com/android/internal/compat/OverrideAllowedState.aidl
@@ -14,17 +14,6 @@
* limitations under the License.
*/
-package com.android.systemui.dagger.qualifiers;
+package com.android.internal.compat;
-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 {
-}
+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/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index a86d702..179828c 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -35,7 +35,6 @@
import com.android.server.NetworkManagementSocketTagger;
import dalvik.system.RuntimeHooks;
-import dalvik.system.ThreadPrioritySetter;
import dalvik.system.VMRuntime;
import libcore.content.type.MimeMap;
@@ -205,7 +204,6 @@
*/
public static void preForkInit() {
if (DEBUG) Slog.d(TAG, "Entered preForkInit.");
- RuntimeHooks.setThreadPrioritySetter(new RuntimeThreadPrioritySetter());
RuntimeInit.enableDdms();
// TODO(b/142019040#comment13): Decide whether to load the default instance eagerly, i.e.
// MimeMap.setDefault(DefaultMimeMapFactory.create());
@@ -218,35 +216,6 @@
MimeMap.setDefaultSupplier(DefaultMimeMapFactory::create);
}
- private static class RuntimeThreadPrioritySetter implements ThreadPrioritySetter {
- // Should remain consistent with kNiceValues[] in system/libartpalette/palette_android.cc
- private static final int[] NICE_VALUES = {
- Process.THREAD_PRIORITY_LOWEST, // 1 (MIN_PRIORITY)
- Process.THREAD_PRIORITY_BACKGROUND + 6,
- Process.THREAD_PRIORITY_BACKGROUND + 3,
- Process.THREAD_PRIORITY_BACKGROUND,
- Process.THREAD_PRIORITY_DEFAULT, // 5 (NORM_PRIORITY)
- Process.THREAD_PRIORITY_DEFAULT - 2,
- Process.THREAD_PRIORITY_DEFAULT - 4,
- Process.THREAD_PRIORITY_URGENT_DISPLAY + 3,
- Process.THREAD_PRIORITY_URGENT_DISPLAY + 2,
- Process.THREAD_PRIORITY_URGENT_DISPLAY // 10 (MAX_PRIORITY)
- };
-
- @Override
- public void setPriority(int priority) {
- // Check NICE_VALUES[] length first.
- if (NICE_VALUES.length != (1 + Thread.MAX_PRIORITY - Thread.MIN_PRIORITY)) {
- throw new AssertionError("Unexpected NICE_VALUES.length=" + NICE_VALUES.length);
- }
- // Priority should be in the range of MIN_PRIORITY (1) to MAX_PRIORITY (10).
- if (priority < Thread.MIN_PRIORITY || priority > Thread.MAX_PRIORITY) {
- throw new IllegalArgumentException("Priority out of range: " + priority);
- }
- Process.setThreadPriority(NICE_VALUES[priority - Thread.MIN_PRIORITY]);
- }
- }
-
@UnsupportedAppUsage
protected static final void commonInit() {
if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!");
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 6933f16..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);
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 67cfc3a..27c2478 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -121,11 +121,7 @@
}
public static boolean isEnabled(Context ctx) {
- return getInstance(ctx).isEnabled();
- }
-
- public boolean isEnabled() {
- return Build.IS_DEBUGGABLE && mEnabled;
+ return Build.IS_DEBUGGABLE && getInstance(ctx).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..b31ae38 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/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 4f4c8c3..f37a468 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -25,11 +25,11 @@
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;
@@ -55,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;
@@ -1599,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.
*/
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/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/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/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/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/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/values/config.xml b/core/res/res/values/config.xml
index e839709..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>
@@ -4284,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/symbols.xml b/core/res/res/values/symbols.xml
index 129c862..cdbf14b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -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" />
@@ -3605,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/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
index 9b3d0c9..b88c36f 100644
--- a/core/tests/coretests/src/android/app/timedetector/NetworkTimeSuggestionTest.java
+++ b/core/tests/coretests/src/android/app/timedetector/NetworkTimeSuggestionTest.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/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/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
index 25ac400..f497db2 100644
--- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
+++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
@@ -27,9 +27,12 @@
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;
@@ -221,4 +224,101 @@
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/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 bcb313e..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;
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/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/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/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 f5cfde4b..bddfa69 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -177,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;
}
}
@@ -499,7 +499,7 @@
List<MediaRoute2Info> addedRoutes = new ArrayList<>();
synchronized (sRouterLock) {
for (MediaRoute2Info route : routes) {
- mRoutes.put(route.getUniqueId(), route);
+ mRoutes.put(route.getId(), route);
if (route.supportsControlCategories(mControlCategories)) {
addedRoutes.add(route);
}
@@ -515,7 +515,7 @@
List<MediaRoute2Info> removedRoutes = new ArrayList<>();
synchronized (sRouterLock) {
for (MediaRoute2Info route : routes) {
- mRoutes.remove(route.getUniqueId());
+ mRoutes.remove(route.getId());
if (route.supportsControlCategories(mControlCategories)) {
removedRoutes.add(route);
}
@@ -531,7 +531,7 @@
List<MediaRoute2Info> changedRoutes = new ArrayList<>();
synchronized (sRouterLock) {
for (MediaRoute2Info route : routes) {
- mRoutes.put(route.getUniqueId(), route);
+ mRoutes.put(route.getId(), route);
if (route.supportsControlCategories(mControlCategories)) {
changedRoutes.add(route);
}
@@ -935,13 +935,13 @@
}
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;
}
@@ -982,13 +982,13 @@
}
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;
}
@@ -1029,14 +1029,14 @@
}
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;
}
@@ -1084,8 +1084,12 @@
}
}
+ /**
+ * TODO: Change this to package private. (Hidden for debugging purposes)
+ * @hide
+ */
@NonNull
- RouteSessionInfo getRouteSessionInfo() {
+ public RouteSessionInfo getRouteSessionInfo() {
synchronized (mControllerLock) {
return mSessionInfo;
}
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 2d7bc24..4a9298a 100644
--- a/media/java/android/media/RouteSessionInfo.java
+++ b/media/java/android/media/RouteSessionInfo.java
@@ -327,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/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 84%
rename from media/java/android/media/tv/tuner/FilterSettings.java
rename to media/java/android/media/tv/tuner/FilterConfiguration.java
index 4ee61a8..b80a82a 100644
--- a/media/java/android/media/tv/tuner/FilterSettings.java
+++ b/media/java/android/media/tv/tuner/FilterConfiguration.java
@@ -22,20 +22,20 @@
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();
@@ -47,12 +47,12 @@
// 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;
}
@@ -70,7 +70,7 @@
}
/**
- * Builder for TsFilterSettings.
+ * Builder for TsFilterConfiguration.
*/
public static class Builder {
private Settings mSettings;
@@ -93,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);
}
@@ -119,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);
}
@@ -140,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);
}
@@ -159,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/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 63ef4b8..43f9a89 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -21,8 +21,9 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
-import android.media.tv.tuner.FilterSettings.Settings;
+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;
@@ -31,6 +32,9 @@
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;
@@ -127,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.
@@ -168,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);
}
/**
@@ -226,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;
}
@@ -434,6 +425,11 @@
return mFrontend.mId;
}
+ /** @hide */
+ private static DemuxCapabilities getDemuxCapabilities() {
+ return nativeGetDemuxCapabilities();
+ }
+
private List<Integer> getFrontendIds() {
mFrontendIds = nativeGetFrontendIds();
return mFrontendIds;
@@ -456,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();
@@ -487,8 +488,9 @@
*
* @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;
Settings s = settings.getSettings();
if (s != null) {
@@ -501,6 +503,7 @@
* Gets the filter Id.
*
* @return the hardware resource Id for the filter.
+ * @hide
*/
public int getId() {
return nativeGetId();
@@ -517,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);
@@ -526,6 +530,7 @@
* Starts the filter.
*
* @return result status of the operation.
+ * @hide
*/
public int start() {
return nativeStartFilter();
@@ -536,6 +541,7 @@
* Stops the filter.
*
* @return result status of the operation.
+ * @hide
*/
public int stop() {
return nativeStopFilter();
@@ -545,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);
@@ -559,6 +567,7 @@
* Release the Filter instance.
*
* @return result status of the operation.
+ * @hide
*/
public int close() {
return nativeClose();
diff --git a/media/java/android/media/tv/tuner/TunerConstants.java b/media/java/android/media/tv/tuner/TunerConstants.java
index e8e4b22..d24e582 100644
--- a/media/java/android/media/tv/tuner/TunerConstants.java
+++ b/media/java/android/media/tv/tuner/TunerConstants.java
@@ -18,16 +18,22 @@
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
*/
+@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;
@@ -180,6 +186,37 @@
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)
+ 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 {}
@@ -739,119 +776,162 @@
public static final int HIERARCHY_4_INDEPTH =
Constants.FrontendDvbtHierarchy.HIERARCHY_4_INDEPTH;
- @Retention(RetentionPolicy.SOURCE)
+ /** @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;
- @Retention(RetentionPolicy.SOURCE)
+ /** @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;
- @Retention(RetentionPolicy.SOURCE)
+ /** @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;
- @Retention(RetentionPolicy.SOURCE)
+ /** @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;
- @Retention(RetentionPolicy.SOURCE)
+ /** @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;
- @Retention(RetentionPolicy.SOURCE)
+ /** @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;
- @Retention(RetentionPolicy.SOURCE)
+ /** @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,
@@ -859,258 +939,350 @@
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;
- @Retention(RetentionPolicy.SOURCE)
+ /** @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;
- @Retention(RetentionPolicy.SOURCE)
+ /** @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;
- @Retention(RetentionPolicy.SOURCE)
+ /** @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;
- @Retention(RetentionPolicy.SOURCE)
+ /** @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;
- @Retention(RetentionPolicy.SOURCE)
+ /** @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;
- @Retention(RetentionPolicy.SOURCE)
+ /** @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;
- @Retention(RetentionPolicy.SOURCE)
+ /** @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;
- @Retention(RetentionPolicy.SOURCE)
+ /** @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;
- @Retention(RetentionPolicy.SOURCE)
+ /** @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;
- @Retention(RetentionPolicy.SOURCE)
+ /** @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;
- @Retention(RetentionPolicy.SOURCE)
+ /** @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;
- @Retention(RetentionPolicy.SOURCE)
+ /** @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;
diff --git a/media/java/android/media/tv/tuner/filter/FilterEvent.java b/media/java/android/media/tv/tuner/filter/FilterEvent.java
index 0a4fa86..56a77d4 100644
--- a/media/java/android/media/tv/tuner/filter/FilterEvent.java
+++ b/media/java/android/media/tv/tuner/filter/FilterEvent.java
@@ -16,10 +16,13 @@
package android.media.tv.tuner.filter;
+import android.annotation.SystemApi;
+
/**
- * Demux filter event.
+ * 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/MediaEvent.java b/media/java/android/media/tv/tuner/filter/MediaEvent.java
index 5e45350..7703248 100644
--- a/media/java/android/media/tv/tuner/filter/MediaEvent.java
+++ b/media/java/android/media/tv/tuner/filter/MediaEvent.java
@@ -22,7 +22,7 @@
* Media event.
* @hide
*/
-public class MediaEvent {
+public class MediaEvent extends FilterEvent {
private int mStreamId;
private boolean mIsPtsPresent;
private long mPts;
diff --git a/media/java/android/media/tv/tuner/filter/SectionEvent.java b/media/java/android/media/tv/tuner/filter/SectionEvent.java
index 5b5f8c9..e211dda 100644
--- a/media/java/android/media/tv/tuner/filter/SectionEvent.java
+++ b/media/java/android/media/tv/tuner/filter/SectionEvent.java
@@ -16,14 +16,54 @@
package android.media.tv.tuner.filter;
+import android.annotation.SystemApi;
+import android.media.tv.tuner.Tuner.Filter;
+
/**
- * Section event.
+ * Filter event sent from {@link Filter} objects with section type.
+ *
* @hide
*/
-public class SectionEvent {
- // TODO: add constructor and getters
- private int mTableId;
- private int mVersion;
- private int mSectionNum;
- private int mDataLength;
+@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/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/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/DisplayId.java b/media/java/android/media/tv/tuner/frontend/Atsc3PlpSettings.java
similarity index 61%
copy from packages/SystemUI/src/com/android/systemui/dagger/qualifiers/DisplayId.java
copy to media/java/android/media/tv/tuner/frontend/Atsc3PlpSettings.java
index 155a6d2..61c6fec 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/DisplayId.java
+++ b/media/java/android/media/tv/tuner/frontend/Atsc3PlpSettings.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,17 +14,16 @@
* limitations under the License.
*/
-package com.android.systemui.dagger.qualifiers;
+package android.media.tv.tuner.frontend;
-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 {
+/**
+ * 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/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/DisplayId.java b/media/java/android/media/tv/tuner/frontend/DvbsCodeRate.java
similarity index 61%
copy from packages/SystemUI/src/com/android/systemui/dagger/qualifiers/DisplayId.java
copy to media/java/android/media/tv/tuner/frontend/DvbsCodeRate.java
index 155a6d2..bfa4391 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/DisplayId.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbsCodeRate.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.systemui.dagger.qualifiers;
+package android.media.tv.tuner.frontend;
-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 {
+/**
+ * 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..91776e1
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/FrontendCallback.java
@@ -0,0 +1,38 @@
+/*
+ * 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;
+
+import android.media.tv.tuner.ScanMessage;
+
+/**
+ * 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/FrontendInfo.java b/media/java/android/media/tv/tuner/frontend/FrontendInfo.java
similarity index 96%
rename from media/java/android/media/tv/tuner/FrontendInfo.java
rename to media/java/android/media/tv/tuner/frontend/FrontendInfo.java
index 2ab100d..ef6c029 100644
--- a/media/java/android/media/tv/tuner/FrontendInfo.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendInfo.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.FrontendCapabilities;
import android.media.tv.tuner.TunerConstants.FrontendType;
/**
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/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 3562782..f0f3688 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -100,7 +100,7 @@
/////////////// 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);
@@ -880,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();
@@ -1123,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[] = {
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
index 6fe847b..86b9706 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
@@ -701,7 +701,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;
@@ -741,7 +740,7 @@
}
/**
- * 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/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_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
index 27ad4fc..e2e9a33 100644
--- a/packages/CarSystemUI/res/layout/car_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
@@ -114,7 +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"
/>
<Space
@@ -138,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_button.xml b/packages/CarSystemUI/res/layout/car_navigation_button.xml
index bafbb64..837252b 100644
--- a/packages/CarSystemUI/res/layout/car_navigation_button.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_button.xml
@@ -18,13 +18,12 @@
-->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
- <LinearLayout
+ <FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/car_nav_button_icon"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
- android:gravity="center"
android:animateLayoutChanges="true"
android:orientation="vertical">
@@ -32,20 +31,35 @@
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">
- </com.android.keyguard.AlphaOptimizedImageButton>
+ 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">
- </com.android.keyguard.AlphaOptimizedImageButton>
+ android:scaleType="fitCenter"
+ android:clickable="false"
+ />
- </LinearLayout>
+ <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 0b34626..37cd1d4 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"/>
- <include layout="@layout/status_bar_expanded"
+ <ViewStub android:id="@+id/status_bar_expanded"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible"/>
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/CarNavigationButton.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationButton.java
index d08fbbc..b4d4785 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationButton.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationButton.java
@@ -48,13 +48,11 @@
private static final String BUTTON_FILTER_DELIMITER = ";";
private static final String EXTRA_BUTTON_CATEGORIES = "categories";
private static final String EXTRA_BUTTON_PACKAGES = "packages";
- 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;
private Context mContext;
private AlphaOptimizedImageButton mIcon;
private AlphaOptimizedImageButton mMoreIcon;
+ private ImageView mUnseenIcon;
private String mIntent;
private String mLongIntent;
private boolean mBroadcastIntent;
@@ -265,23 +263,22 @@
mIcon = findViewById(R.id.car_nav_button_icon_image);
mIcon.setScaleType(ImageView.ScaleType.CENTER);
- mIcon.setClickable(false);
// 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.setClickable(false);
mMoreIcon.setAlpha(mSelectedAlpha);
mMoreIcon.setVisibility(GONE);
+
+ mUnseenIcon = findViewById(R.id.car_nav_button_unseen_icon);
+
+ mUnseenIcon.setVisibility(mHasUnseen ? VISIBLE : GONE);
}
private void updateImage() {
- if (mHasUnseen) {
- mIcon.setImageResource(mSelected ? UNSEEN_SELECTED_ICON_RESOURCE_ID
- : UNSEEN_ICON_RESOURCE_ID);
- } else {
- mIcon.setImageResource(mSelected ? mSelectedIconResourceId : mIconResourceId);
- }
+ 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 3c3ebe2..77db54c 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -128,11 +128,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;
@@ -465,7 +465,7 @@
super.start();
- mNotificationPanelViewController.setScrollingEnabled(true);
+ mNotificationPanel.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 a1eccce..1ebaef7 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
@@ -86,11 +86,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;
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
index e1ee616..96d567d 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationButtonTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationButtonTest.java
@@ -31,6 +31,7 @@
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;
@@ -216,6 +217,22 @@
}), 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/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 479f255..4869be1 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -17,14 +17,9 @@
*/
-->
-
-<com.android.systemui.statusbar.phone.NotificationPanelView
+<merge
xmlns:android="http://schemas.android.com/apk/res/android"
- 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">
+ xmlns:systemui="http://schemas.android.com/apk/res-auto">
<FrameLayout
android:id="@+id/big_clock_container"
android:layout_width="match_parent"
@@ -102,4 +97,4 @@
android:background="@drawable/qs_navbar_scrim" />
<include layout="@layout/status_bar_expanded_plugin_frame"/>
-</com.android.systemui.statusbar.phone.NotificationPanelView>
+</merge>
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index 9716a00..57834da 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"
/>
- <include layout="@layout/status_bar_expanded"
+ <ViewStub android:id="@+id/status_bar_expanded"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible" />
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/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 bb12b53..73b6584 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,7 +46,6 @@
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;
@@ -73,19 +73,6 @@
@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);
}
@@ -123,6 +110,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 58ddda9..442313d 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.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.phone.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/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index c1934c6..8f2c3ed 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.NotificationPanelViewController;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
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, NotificationPanelViewController panelView,
+ ViewGroup container, NotificationPanelView 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 12749fd..525b5b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
@@ -368,14 +368,13 @@
public static class Builder {
private final DisplayMetrics mDisplayMetrics;
float mMaxLengthSeconds;
- float mSpeedUpFactor;
- float mX2;
- float mY2;
+ float mSpeedUpFactor = 0.0f;
+ float mX2 = -1.0f;
+ float mY2 = 1.0f;
@Inject
public Builder(DisplayMetrics displayMetrics) {
mDisplayMetrics = displayMetrics;
- reset();
}
public Builder setMaxLengthSeconds(float maxLengthSeconds) {
@@ -398,15 +397,6 @@
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/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..976531d 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/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index 6660569..9b31234 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.NotificationPanelViewController;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
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 NotificationPanelViewController mNotificationPanel;
+ private final NotificationPanelView mNotificationPanel;
private final NotificationListContainer mNotificationContainer;
private final float mWindowCornerRadius;
private final StatusBarWindowViewController mStatusBarWindowViewController;
@@ -69,7 +69,7 @@
public ActivityLaunchAnimator(
StatusBarWindowViewController statusBarWindowViewController,
Callback callback,
- NotificationPanelViewController notificationPanel,
+ NotificationPanelView 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/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..6dc647d 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
@@ -109,16 +109,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(NotificationContentInflater.InflationCallback callback) {
+ mInflationCallback = callback;
+ }
+
public void setNotificationClicker(NotificationClicker clicker) {
mNotificationClicker = clicker;
}
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/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 172b72e..30f22ac 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
@@ -397,7 +397,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());
@@ -448,7 +448,7 @@
onViewApplied(newView);
} catch (Exception anotherException) {
runningInflations.remove(inflationId);
- handleInflationError(runningInflations, e, row.getEntry().getSbn(),
+ handleInflationError(runningInflations, e, row.getEntry(),
callback);
}
}
@@ -474,7 +474,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) {
@@ -707,7 +707,7 @@
+ 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,7 +729,7 @@
}
@Override
- public void handleInflationException(StatusBarNotification notification, Exception e) {
+ public void handleInflationException(NotificationEntry entry, Exception e) {
handleError(e);
}
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/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 823dd5a..71342c5 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.NotificationPanelViewController;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -496,7 +496,8 @@
protected boolean mClearAllEnabled;
private Interpolator mHideXInterpolator = Interpolators.FAST_OUT_SLOW_IN;
- private NotificationPanelViewController mNotificationPanelController;
+ private NotificationPanelView mNotificationPanel;
+ private final ShadeController mShadeController = Dependency.get(ShadeController.class);
private final NotificationGutsManager mNotificationGutsManager;
private final NotificationSectionsManager mSectionsManager;
@@ -5518,8 +5519,7 @@
if (viewsToRemove.isEmpty()) {
if (closeShade) {
- Dependency.get(ShadeController.class).animateCollapsePanels(
- CommandQueue.FLAG_EXCLUDE_NONE);
+ mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
}
return;
}
@@ -5577,12 +5577,11 @@
final Runnable onSlideAwayAnimationComplete = () -> {
if (closeShade) {
- Dependency.get(ShadeController.class).addPostCollapseAction(() -> {
+ mShadeController.addPostCollapseAction(() -> {
setDismissAllInProgress(false);
onAnimationComplete.run();
});
- Dependency.get(ShadeController.class).animateCollapsePanels(
- CommandQueue.FLAG_EXCLUDE_NONE);
+ mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
} else {
setDismissAllInProgress(false);
onAnimationComplete.run();
@@ -5658,9 +5657,8 @@
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void setNotificationPanelController(
- NotificationPanelViewController notificationPanelViewController) {
- mNotificationPanelController = notificationPanelViewController;
+ public void setNotificationPanel(NotificationPanelView notificationPanelView) {
+ mNotificationPanel = notificationPanelView;
}
public void updateIconAreaViews() {
@@ -6404,7 +6402,7 @@
if (!mAmbientState.isDozing() || startingChild != null) {
// We have notifications, go to locked shade.
- Dependency.get(ShadeController.class).goToLockedShade(startingChild);
+ mShadeController.goToLockedShade(startingChild);
if (startingChild instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) startingChild;
row.onExpandedByGesture(true /* drag down is always an open */);
@@ -6443,7 +6441,7 @@
@Override
public void setEmptyDragAmount(float amount) {
- mNotificationPanelController.setEmptyDragAmount(amount);
+ mNotificationPanel.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 e03db2c..8b31da4 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.getPanelController().hasCustomClock()) {
+ && mStatusBarComponent.getPanel().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 accd2a4..ce1123e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -91,7 +91,7 @@
private final LockscreenLockIconController mLockscreenLockIconController;
private NotificationIconAreaController mNotificationIconAreaController;
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
- private NotificationPanelViewController mNotificationPanel;
+ private NotificationPanelView mNotificationPanel;
private View mAmbientIndicationContainer;
private StatusBar mStatusBar;
@@ -141,7 +141,7 @@
NotificationIconAreaController notificationIconAreaController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
StatusBarWindowViewController statusBarWindowViewController,
- NotificationPanelViewController notificationPanel, View ambientIndicationContainer) {
+ NotificationPanelView notificationPanel, View ambientIndicationContainer) {
mStatusBar = statusBar;
mNotificationIconAreaController = notificationIconAreaController;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
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 7b20a7b..8e5a912 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 NotificationPanelViewController mNotificationPanelViewController;
+ private final NotificationPanelView mPanelView;
private final Consumer<ExpandableNotificationRow>
mSetTrackingHeadsUp = this::setTrackingHeadsUp;
private final Runnable mUpdatePanelTranslation = this::updatePanelTranslation;
@@ -96,14 +96,13 @@
SysuiStatusBarStateController statusBarStateController,
KeyguardBypassController keyguardBypassController,
KeyguardStateController keyguardStateController,
- NotificationWakeUpCoordinator wakeUpCoordinator, CommandQueue commandQueue,
- NotificationPanelViewController notificationPanelViewController) {
+ NotificationWakeUpCoordinator wakeUpCoordinator, CommandQueue commandQueue) {
this(notificationIconAreaController, headsUpManager, statusBarStateController,
keyguardBypassController, wakeUpCoordinator, keyguardStateController,
commandQueue,
statusbarView.findViewById(R.id.heads_up_status_bar_view),
statusbarView.findViewById(R.id.notification_stack_scroller),
- notificationPanelViewController,
+ statusbarView.findViewById(R.id.notification_panel),
statusbarView.findViewById(R.id.clock),
statusbarView.findViewById(R.id.operator_name_frame),
statusbarView.findViewById(R.id.centered_icon_area));
@@ -120,7 +119,7 @@
CommandQueue commandQueue,
HeadsUpStatusBarView headsUpStatusBarView,
NotificationStackScrollLayout stackScroller,
- NotificationPanelViewController notificationPanelViewController,
+ NotificationPanelView panelView,
View clockView,
View operatorNameView,
View centeredIconView) {
@@ -132,10 +131,10 @@
headsUpStatusBarView.setOnDrawingRectChangedListener(
() -> updateIsolatedIconLocation(true /* requireUpdate */));
mStackScroller = stackScroller;
- mNotificationPanelViewController = notificationPanelViewController;
- notificationPanelViewController.addTrackingHeadsUpListener(mSetTrackingHeadsUp);
- notificationPanelViewController.addVerticalTranslationListener(mUpdatePanelTranslation);
- notificationPanelViewController.setHeadsUpAppearanceController(this);
+ mPanelView = panelView;
+ panelView.addTrackingHeadsUpListener(mSetTrackingHeadsUp);
+ panelView.addVerticalTranslationListener(mUpdatePanelTranslation);
+ panelView.setHeadsUpAppearanceController(this);
mStackScroller.addOnExpandedHeightChangedListener(mSetExpandedHeight);
mStackScroller.addOnLayoutChangeListener(mStackScrollLayoutChangeListener);
mStackScroller.setHeadsUpAppearanceController(this);
@@ -170,9 +169,9 @@
mHeadsUpManager.removeListener(this);
mHeadsUpStatusBarView.setOnDrawingRectChangedListener(null);
mWakeUpCoordinator.removeListener(this);
- mNotificationPanelViewController.removeTrackingHeadsUpListener(mSetTrackingHeadsUp);
- mNotificationPanelViewController.removeVerticalTranslationListener(mUpdatePanelTranslation);
- mNotificationPanelViewController.setHeadsUpAppearanceController(null);
+ mPanelView.removeTrackingHeadsUpListener(mSetTrackingHeadsUp);
+ mPanelView.removeVerticalTranslationListener(mUpdatePanelTranslation);
+ mPanelView.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 c282cb8..ac06d9d 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 NotificationPanelViewController mPanel;
+ private NotificationPanelView mPanel;
private ExpandableNotificationRow mPickedChild;
public HeadsUpTouchHelper(HeadsUpManagerPhone headsUpManager,
Callback callback,
- NotificationPanelViewController notificationPanelView) {
+ NotificationPanelView 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 d3e44ea..d95d2b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -155,6 +155,7 @@
};
private boolean mLeftIsVoiceAssist;
+ private AssistManager mAssistManager;
private Drawable mLeftAssistIcon;
private IntentButton mRightButton = new DefaultRightButton();
@@ -253,6 +254,7 @@
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();
}
@@ -549,7 +551,7 @@
Runnable runnable = new Runnable() {
@Override
public void run() {
- Dependency.get(AssistManager.class).launchVoiceAssistFromKeyguard();
+ mAssistManager.launchVoiceAssistFromKeyguard();
}
};
if (!mKeyguardStateController.canDismissLockScreen()) {
@@ -563,7 +565,7 @@
}
private boolean canLaunchVoiceAssist() {
- return Dependency.get(AssistManager.class).canVoiceAssistBeLaunchedFromKeyguard();
+ return mAssistManager.canVoiceAssistBeLaunchedFromKeyguard();
}
private void launchPhone() {
@@ -645,7 +647,7 @@
}
if (mLeftIsVoiceAssist) {
mLeftPreview = mPreviewInflater.inflatePreviewFromService(
- Dependency.get(AssistManager.class).getVoiceInteractorComponentName());
+ mAssistManager.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 a3f14ba..4e91e4c 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 NotificationPanelViewController#getExpandedFraction()
+ * @see NotificationPanelView#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 a3b1b5f..d4cf272 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().getPanelController());
+ mNavigationBarView.setComponents(mStatusBarLazy.get().getPanel(), mAssistManager);
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 ba9ba6c..5a1b20d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -67,6 +67,7 @@
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;
@@ -147,7 +148,7 @@
private NavigationBarInflaterView mNavigationInflaterView;
private RecentsOnboarding mRecentsOnboarding;
- private NotificationPanelViewController mPanelView;
+ private NotificationPanelView mPanelView;
private FloatingRotationButton mFloatingRotationButton;
private RotationButtonController mRotationButtonController;
@@ -348,7 +349,7 @@
return mBarTransitions.getLightTransitionsController();
}
- public void setComponents(NotificationPanelViewController panel) {
+ public void setComponents(NotificationPanelView panel, AssistManager assistManager) {
mPanelView = panel;
updatePanelSystemUiStateFlags();
}
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 9dc8fbf..c74286d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -16,17 +16,118 @@
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;
-public class NotificationPanelView extends PanelView {
+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 {
private static final boolean DEBUG = false;
@@ -35,26 +136,2542 @@
*/
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 boolean mDozing;
+ 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;
- public NotificationPanelView(Context context, AttributeSet attrs) {
- super(context, attrs);
+ 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);
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) {
@@ -64,10 +2681,328 @@
}
@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);
@@ -76,18 +3011,250 @@
}
}
- float getCurrentPanelAlpha() {
+ public float getCurrentPanelAlpha() {
return mCurrentPanelAlpha;
}
- void setPanelAlphaInternal(float alpha) {
+ 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) {
mCurrentPanelAlpha = (int) alpha;
mAlphaPaint.setARGB(mCurrentPanelAlpha, 255, 255, 255);
invalidate();
}
- public void setDozing(boolean dozing) {
- mDozing = dozing;
+ 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;
}
@Override
@@ -95,4 +3262,382 @@
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);
+ }
+
+ 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);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
deleted file mode 100644
index bbde25b..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ /dev/null
@@ -1,3733 +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.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;
-
- @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);
- }
-
- @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 8d8c8da..063d00b 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;
- PanelViewController mPanel;
+ PanelView mPanel;
private int mState = STATE_CLOSED;
private boolean mTracking;
@@ -83,8 +83,7 @@
super.onFinishInflate();
}
- /** Set the PanelViewController */
- public void setPanel(PanelViewController pv) {
+ public void setPanel(PanelView pv) {
mPanel = pv;
pv.setBar(this);
}
@@ -97,7 +96,7 @@
setImportantForAccessibility(important);
updateVisibility();
- if (mPanel != null) mPanel.getView().setImportantForAccessibility(important);
+ if (mPanel != null) mPanel.setImportantForAccessibility(important);
}
public float getExpansionFraction() {
@@ -109,7 +108,7 @@
}
protected void updateVisibility() {
- mPanel.getView().setVisibility(shouldPanelBeVisible() ? VISIBLE : INVISIBLE);
+ mPanel.setVisibility(shouldPanelBeVisible() ? VISIBLE : INVISIBLE);
}
protected boolean shouldPanelBeVisible() {
@@ -132,7 +131,7 @@
}
if (event.getAction() == MotionEvent.ACTION_DOWN) {
- final PanelViewController panel = mPanel;
+ final PanelView 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)",
@@ -150,7 +149,7 @@
return true;
}
}
- return mPanel == null || mPanel.getView().dispatchTouchEvent(event);
+ return mPanel == null || mPanel.onTouchEvent(event);
}
public abstract void panelScrimMinFractionChanged(float minFraction);
@@ -164,7 +163,7 @@
boolean fullyClosed = true;
boolean fullyOpened = false;
if (SPEW) LOG("panelExpansionChanged: start state=%d", mState);
- PanelViewController pv = mPanel;
+ PanelView pv = mPanel;
mExpanded = expanded;
mPanelFraction = frac;
updateVisibility();
@@ -193,7 +192,7 @@
public void collapsePanel(boolean animate, boolean delayed, float speedUpFactor) {
boolean waiting = false;
- PanelViewController pv = mPanel;
+ PanelView 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 2719a32..cd56d06 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -16,62 +16,1255 @@
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 PanelViewController.TouchHandler mTouchHandler;
+ 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));
+ }
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;
- public PanelView(Context context) {
- super(context);
+ /**
+ * 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, AttributeSet attrs) {
+ 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) {
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);
}
- public PanelView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
+ 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, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
+ 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 setOnTouchListener(PanelViewController.TouchHandler touchHandler) {
- super.setOnTouchListener(touchHandler);
- mTouchHandler = touchHandler;
+ public void setTouchAndAnimationDisabled(boolean disabled) {
+ mTouchDisabled = disabled;
+ if (mTouchDisabled) {
+ cancelHeightAnimator();
+ if (mTracking) {
+ onTrackingStopped(true /* expanded */);
+ }
+ notifyExpandingFinished();
+ }
}
- public void setOnConfigurationChangedListener(OnConfigurationChangedListener listener) {
- mOnConfigurationChangedListener = listener;
+ 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();
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
- return mTouchHandler.onInterceptTouchEvent(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;
}
@Override
- public void dispatchConfigurationChanged(Configuration newConfig) {
- super.dispatchConfigurationChanged(newConfig);
- mOnConfigurationChangedListener.onConfigurationChanged(newConfig);
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ loadDimens();
}
- interface OnConfigurationChangedListener {
- void onConfigurationChanged(Configuration 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;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
deleted file mode 100644
index 3d8e09a..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ /dev/null
@@ -1,1297 +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.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 45f3bf9..312ca26 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.getView().sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ mPanel.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
}
mIsFullyOpenedPanel = true;
maybeShowDivider(!mBar.mPanelExpanded);
@@ -420,8 +420,7 @@
void maybeShowDivider(boolean showDivider) {
int state =
- showDivider && NotificationPanelViewController.isQsSplitEnabled()
- ? View.VISIBLE : View.GONE;
+ showDivider && NotificationPanelView.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 866dc2d..57e7014 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 */);
- getNotificationPanelViewController().expand(false /* animate */);
+ getNotificationPanelView().expand(false /* animate */);
mCommandQueue.recomputeDisableFlags(mDisplayId, false /* animate */);
}
@@ -123,9 +123,8 @@
// TODO(b/62444020): remove when this bug is fixed
Log.v(TAG, "mStatusBarWindow: " + getStatusBarWindowView() + " canPanelBeCollapsed(): "
- + getNotificationPanelViewController().canPanelBeCollapsed());
- if (getStatusBarWindowView() != null
- && getNotificationPanelViewController().canPanelBeCollapsed()) {
+ + getNotificationPanelView().canPanelBeCollapsed());
+ if (getStatusBarWindowView() != null && getNotificationPanelView().canPanelBeCollapsed()) {
// release focus immediately to kick off focus change transition
mStatusBarWindowController.setStatusBarFocusable(false);
@@ -139,7 +138,7 @@
@Override
public boolean closeShadeIfOpen() {
- if (!getNotificationPanelViewController().isFullyCollapsed()) {
+ if (!getNotificationPanelView().isFullyCollapsed()) {
mCommandQueue.animateCollapsePanels(
CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
getStatusBar().visibilityChanged(false);
@@ -150,14 +149,15 @@
@Override
public void postOnShadeExpanded(Runnable executable) {
- getNotificationPanelViewController().addOnGlobalLayoutListener(
+ getNotificationPanelView().getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (getStatusBar().getStatusBarWindow().getHeight()
!= getStatusBar().getStatusBarHeight()) {
- getNotificationPanelViewController().removeOnGlobalLayoutListener(this);
- getNotificationPanelViewController().getView().post(executable);
+ getNotificationPanelView().getViewTreeObserver()
+ .removeOnGlobalLayoutListener(this);
+ getNotificationPanelView().post(executable);
}
}
});
@@ -187,7 +187,7 @@
@Override
public boolean collapsePanel() {
- if (!getNotificationPanelViewController().isFullyCollapsed()) {
+ if (!getNotificationPanelView().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 NotificationPanelViewController getNotificationPanelViewController() {
- return getStatusBar().getPanelController();
+ private NotificationPanelView getNotificationPanelView() {
+ return getStatusBar().getPanel();
}
}
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 277a761..c5d7da4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -131,6 +131,7 @@
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;
@@ -198,6 +199,7 @@
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;
@@ -206,7 +208,6 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
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;
@@ -391,8 +392,7 @@
private final DismissCallbackRegistry mDismissCallbackRegistry;
// expanded notifications
- // the sliding/resizing panel within the notification window
- protected NotificationPanelViewController mNotificationPanelViewController;
+ protected NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window
// settings
private QSPanel mQSPanel;
@@ -461,8 +461,8 @@
mUserSetup = userSetup;
if (!mUserSetup && mStatusBarView != null)
animateCollapseQuickSettings();
- if (mNotificationPanelViewController != null) {
- mNotificationPanelViewController.setUserSetupComplete(mUserSetup);
+ if (mNotificationPanel != null) {
+ mNotificationPanel.setUserSetupComplete(mUserSetup);
}
updateQsExpansionEnabled();
}
@@ -913,8 +913,9 @@
mKeyguardUpdateMonitor.registerCallback(mUpdateCallback);
mDozeServiceHost.initialize(this, mNotificationIconAreaController,
- mStatusBarKeyguardViewManager, mStatusBarWindowViewController,
- mNotificationPanelViewController, mAmbientIndicationContainer);
+ mStatusBarKeyguardViewManager,
+ mStatusBarWindowViewController,
+ mNotificationPanel, mAmbientIndicationContainer);
mConfigurationController.addCallback(this);
@@ -984,6 +985,8 @@
// 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);
@@ -997,9 +1000,8 @@
mWakeUpCoordinator.setIconAreaController(mNotificationIconAreaController);
inflateShelf();
mNotificationIconAreaController.setupShelf(mNotificationShelf);
- mNotificationPanelViewController.setOnReinflationListener(
- mNotificationIconAreaController::initAodIcons);
- mNotificationPanelViewController.addExpansionListener(mWakeUpCoordinator);
+ mNotificationPanel.setOnReinflationListener(mNotificationIconAreaController::initAodIcons);
+ mNotificationPanel.addExpansionListener(mWakeUpCoordinator);
mDarkIconDispatcher.addDarkReceiver(mNotificationIconAreaController);
// Allow plugins to reference DarkIconDispatcher and StatusBarStateController
@@ -1013,7 +1015,7 @@
PhoneStatusBarView oldStatusBarView = mStatusBarView;
mStatusBarView = (PhoneStatusBarView) fragment.getView();
mStatusBarView.setBar(this);
- mStatusBarView.setPanel(mNotificationPanelViewController);
+ mStatusBarView.setPanel(mNotificationPanel);
mStatusBarView.setScrimController(mScrimController);
// CollapsedStatusBarFragment re-inflated PhoneStatusBarView and both of
@@ -1024,7 +1026,7 @@
// it needs to notify PhoneStatusBarView's new instance to update the correct
// status by calling mNotificationPanel.notifyBarPanelExpansionChanged().
if (mHeadsUpManager.hasPinnedHeadsUp()) {
- mNotificationPanelViewController.notifyBarPanelExpansionChanged();
+ mNotificationPanel.notifyBarPanelExpansionChanged();
}
mStatusBarView.setBouncerShowing(mBouncerShowing);
if (oldStatusBarView != null) {
@@ -1038,12 +1040,10 @@
// 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,
- mNotificationPanelViewController);
+ mKeyguardStateController, mWakeUpCoordinator, mCommandQueue);
mHeadsUpAppearanceController.readFrom(oldController);
mLightsOutNotifController.setLightsOutNotifView(
@@ -1059,11 +1059,11 @@
mHeadsUpManager.setUp(mStatusBarWindow, mGroupManager, this, mVisualStabilityManager);
mConfigurationController.addCallback(mHeadsUpManager);
mHeadsUpManager.addListener(this);
- mHeadsUpManager.addListener(mNotificationPanelViewController.getOnHeadsUpChangedListener());
+ mHeadsUpManager.addListener(mNotificationPanel);
mHeadsUpManager.addListener(mGroupManager);
mHeadsUpManager.addListener(mGroupAlertTransferHelper);
mHeadsUpManager.addListener(mVisualStabilityManager);
- mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
+ mNotificationPanel.setHeadsUpManager(mHeadsUpManager);
mGroupManager.setHeadsUpManager(mHeadsUpManager);
mGroupAlertTransferHelper.setHeadsUpManager(mHeadsUpManager);
mNotificationLogger.setHeadsUpManager(mHeadsUpManager);
@@ -1078,8 +1078,7 @@
SystemUIFactory.getInstance().createKeyguardIndicationController(mContext,
mStatusBarWindow.findViewById(R.id.keyguard_indication_area),
mStatusBarWindow.findViewById(R.id.lock_icon));
- mNotificationPanelViewController.setKeyguardIndicationController(
- mKeyguardIndicationController);
+ mNotificationPanel.setKeyguardIndicationController(mKeyguardIndicationController);
mAmbientIndicationContainer = mStatusBarWindow.findViewById(
R.id.ambient_indication_container);
@@ -1114,19 +1113,19 @@
});
mScrimController.attachViews(scrimBehind, scrimInFront, scrimForBubble);
- mNotificationPanelViewController.initDependencies(this, mGroupManager, mNotificationShelf,
- mNotificationIconAreaController, mScrimController);
+ mNotificationPanel.initDependencies(this, mGroupManager, mNotificationShelf,
+ mHeadsUpManager, 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);
- mNotificationPanelViewController.setUserSetupComplete(mUserSetup);
+ mNotificationPanel.setUserSetupComplete(mUserSetup);
if (UserManager.get(mContext).isUserSwitcherEnabled()) {
createUserSwitcher();
}
- mNotificationPanelViewController.setLaunchAffordanceListener(
+ mNotificationPanel.setLaunchAffordanceListener(
mLockscreenLockIconController::onShowingLaunchAffordanceChanged);
// Set up the quick settings tile panel
@@ -1140,7 +1139,6 @@
.withDefault(this::createDefaultQSFragment)
.build());
mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow,
- mNotificationPanelViewController,
(visible) -> {
mBrightnessMirrorVisible = visible;
updateScrimController();
@@ -1234,7 +1232,7 @@
private void setUpPresenter() {
// Set up the initial notification state.
mActivityLaunchAnimator = new ActivityLaunchAnimator(
- mStatusBarWindowViewController, this, mNotificationPanelViewController,
+ mStatusBarWindowViewController, this, mNotificationPanel,
(NotificationListContainer) mStackScroller);
final NotificationRowBinderImpl rowBinder =
@@ -1246,7 +1244,7 @@
mNotificationLogger);
// TODO: inject this.
- mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanelViewController,
+ mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanel,
mHeadsUpManager, mStatusBarWindow, mStackScroller, mDozeScrimController,
mScrimController, mActivityLaunchAnimator, mDynamicPrivacyController,
mNotificationAlertingManager, rowBinder, mKeyguardStateController,
@@ -1267,12 +1265,15 @@
.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(
@@ -1282,7 +1283,7 @@
mNotificationListController.bind();
if (mFeatureFlags.isNewNotifPipelineEnabled()) {
- mNewNotifPipeline.get().initialize(mNotificationListener);
+ mNewNotifPipeline.get().initialize(mNotificationListener, rowBinder);
}
mEntryManager.attach(mNotificationListener);
}
@@ -1377,7 +1378,7 @@
}
// We need the new R.id.keyguard_indication_area before recreating
// mKeyguardIndicationController
- mNotificationPanelViewController.onThemeChanged();
+ mNotificationPanel.onThemeChanged();
onThemeChanged();
}
@@ -1392,7 +1393,7 @@
mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext,
mStatusBarWindow.findViewById(R.id.keyguard_user_switcher),
mStatusBarWindow.findViewById(R.id.keyguard_header),
- mNotificationPanelViewController);
+ mNotificationPanel);
}
private void inflateStatusBarWindow() {
@@ -1401,17 +1402,16 @@
.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(),
- mNotificationPanelViewController, mBiometricUnlockController,
- mDismissCallbackRegistry, mStatusBarWindow.findViewById(R.id.lock_icon_container),
- mStackScroller, mKeyguardBypassController, mFalsingManager);
+ /* statusBar= */ this, getBouncerContainer(), mNotificationPanel,
+ mBiometricUnlockController, mDismissCallbackRegistry,
+ mStatusBarWindow.findViewById(R.id.lock_icon_container), mStackScroller,
+ mKeyguardBypassController, mFalsingManager);
mKeyguardIndicationController
.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
mBiometricUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
@@ -1488,7 +1488,7 @@
&& ((mDisabled2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) == 0)
&& !mDozing
&& !ONLY_CORE_APPS;
- mNotificationPanelViewController.setQsExpansionEnabled(expandEnabled);
+ mNotificationPanel.setQsExpansionEnabled(expandEnabled);
Log.d(TAG, "updateQsExpansionEnabled - QS Expand enabled: " + expandEnabled);
}
@@ -1648,7 +1648,7 @@
public void setQsExpanded(boolean expanded) {
mStatusBarWindowController.setQsExpanded(expanded);
- mNotificationPanelViewController.setStatusAccessibilityImportance(expanded
+ mNotificationPanel.setStatusAccessibilityImportance(expanded
? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
: View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
if (getNavigationBarView() != null) {
@@ -1682,22 +1682,22 @@
if (inPinnedMode) {
mStatusBarWindowController.setHeadsUpShowing(true);
mStatusBarWindowController.setForceStatusBarVisible(true);
- if (mNotificationPanelViewController.isFullyCollapsed()) {
+ if (mNotificationPanel.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.
- mNotificationPanelViewController.getView().requestLayout();
+ mNotificationPanel.requestLayout();
mStatusBarWindowController.setForceWindowCollapsed(true);
- mNotificationPanelViewController.getView().post(() -> {
+ mNotificationPanel.post(() -> {
mStatusBarWindowController.setForceWindowCollapsed(false);
});
}
} else {
boolean bypassKeyguard = mKeyguardBypassController.getBypassEnabled()
&& mState == StatusBarState.KEYGUARD;
- if (!mNotificationPanelViewController.isFullyCollapsed()
- || mNotificationPanelViewController.isTracking() || bypassKeyguard) {
+ if (!mNotificationPanel.isFullyCollapsed() || mNotificationPanel.isTracking()
+ || bypassKeyguard) {
// We are currently tracking or is open and the shade doesn't need to be kept
// open artificially.
mStatusBarWindowController.setHeadsUpShowing(false);
@@ -1708,7 +1708,7 @@
// we need to keep the panel open artificially, let's wait until the animation
// is finished.
mHeadsUpManager.setHeadsUpGoingAway(true);
- mNotificationPanelViewController.runAfterAnimationFinished(() -> {
+ mNotificationPanel.runAfterAnimationFinished(() -> {
if (!mHeadsUpManager.hasPinnedHeadsUp()) {
mStatusBarWindowController.setHeadsUpShowing(false);
mHeadsUpManager.setHeadsUpGoingAway(false);
@@ -1761,7 +1761,7 @@
}
public boolean hideStatusBarIconsWhenExpanded() {
- return mNotificationPanelViewController.hideStatusBarIconsWhenExpanded();
+ return mNotificationPanel.hideStatusBarIconsWhenExpanded();
}
@Override
@@ -1951,21 +1951,19 @@
if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP == key) {
mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_UP);
- mNotificationPanelViewController.collapse(
- false /* delayed */, 1.0f /* speedUpFactor */);
+ mNotificationPanel.collapse(false /* delayed */, 1.0f /* speedUpFactor */);
} else if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN == key) {
mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_DOWN);
- if (mNotificationPanelViewController.isFullyCollapsed()) {
+ if (mNotificationPanel.isFullyCollapsed()) {
if (mVibrateOnOpening) {
mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
}
- mNotificationPanelViewController.expand(true /* animate */);
+ mNotificationPanel.expand(true /* animate */);
((NotificationListContainer) mStackScroller).setWillExpand(true);
mHeadsUpManager.unpinAll(true /* userUnpinned */);
mMetricsLogger.count(NotificationPanelView.COUNTER_PANEL_OPEN, 1);
- } else if (!mNotificationPanelViewController.isInSettings()
- && !mNotificationPanelViewController.isExpanding()) {
- mNotificationPanelViewController.flingSettings(0 /* velocity */,
+ } else if (!mNotificationPanel.isInSettings() && !mNotificationPanel.isExpanding()){
+ mNotificationPanel.flingSettings(0 /* velocity */,
NotificationPanelView.FLING_EXPAND);
mMetricsLogger.count(NotificationPanelView.COUNTER_PANEL_OPEN_QS, 1);
}
@@ -2060,9 +2058,9 @@
}
if (start) {
- mNotificationPanelViewController.startWaitingForOpenPanelGesture();
+ mNotificationPanel.startWaitingForOpenPanelGesture();
} else {
- mNotificationPanelViewController.stopWaitingForOpenPanelGesture(velocity);
+ mNotificationPanel.stopWaitingForOpenPanelGesture(velocity);
}
}
@@ -2073,7 +2071,7 @@
return ;
}
- mNotificationPanelViewController.expandWithoutQs();
+ mNotificationPanel.expandWithoutQs();
if (false) postStartTracing();
}
@@ -2091,7 +2089,7 @@
if (subPanel != null) {
mQSPanel.openDetails(subPanel);
}
- mNotificationPanelViewController.expandWithQs();
+ mNotificationPanel.expandWithQs();
if (false) postStartTracing();
}
@@ -2114,7 +2112,7 @@
mStatusBarView.collapsePanel(/*animate=*/ false, false /* delayed*/,
1.0f /* speedUpFactor */);
- mNotificationPanelViewController.closeQs();
+ mNotificationPanel.closeQs();
mExpandedVisible = false;
visibilityChanged(false);
@@ -2135,8 +2133,7 @@
Log.d(TAG, "Not showing bouncer due to activity showing over lockscreen");
}
mCommandQueue.recomputeDisableFlags(
- mDisplayId,
- mNotificationPanelViewController.hideStatusBarIconsWhenExpanded() /* animate */);
+ mDisplayId, mNotificationPanel.hideStatusBarIconsWhenExpanded() /* animate */);
// Trimming will happen later if Keyguard is showing - doing it here might cause a jank in
// the bouncer appear animation.
@@ -2317,12 +2314,12 @@
batteryLevel, new WirelessChargingAnimation.Callback() {
@Override
public void onAnimationStarting() {
- CrossFadeHelper.fadeOut(mNotificationPanelViewController.getView(), 1);
+ CrossFadeHelper.fadeOut(mNotificationPanel, 1);
}
@Override
public void onAnimationEnded() {
- CrossFadeHelper.fadeIn(mNotificationPanelViewController.getView());
+ CrossFadeHelper.fadeIn(mNotificationPanel);
}
}, mDozing).show();
} else {
@@ -2351,7 +2348,7 @@
// Called by NavigationBarFragment
void setQsScrimEnabled(boolean scrimEnabled) {
- mNotificationPanelViewController.setQsScrimEnabled(scrimEnabled);
+ mNotificationPanel.setQsScrimEnabled(scrimEnabled);
}
void checkBarMode(@TransitionMode int mode, @WindowVisibleState int windowState,
@@ -2443,12 +2440,11 @@
}
pw.println(" Panels: ");
- if (mNotificationPanelViewController != null) {
- pw.println(" mNotificationPanel="
- + mNotificationPanelViewController.getView() + " params="
- + mNotificationPanelViewController.getView().getLayoutParams().debug(""));
+ if (mNotificationPanel != null) {
+ pw.println(" mNotificationPanel=" +
+ mNotificationPanel + " params=" + mNotificationPanel.getLayoutParams().debug(""));
pw.print (" ");
- mNotificationPanelViewController.dump(fd, pw, args);
+ mNotificationPanel.dump(fd, pw, args);
}
pw.println(" mStackScroller: ");
if (mStackScroller instanceof Dumpable) {
@@ -2661,8 +2657,7 @@
// Do it after DismissAction has been processed to conserve the needed ordering.
mHandler.post(mShadeController::runPostCollapseRunnables);
}
- } else if (isInLaunchTransition()
- && mNotificationPanelViewController.isLaunchTransitionFinished()) {
+ } else if (isInLaunchTransition() && mNotificationPanel.isLaunchTransitionFinished()) {
// We are not dismissing the shade, but the launch transition is already finished,
// so nobody will call readyForKeyguardDone anymore. Post it such that
@@ -2819,8 +2814,8 @@
if (mStatusBarView != null) {
mStatusBarView.updateResources();
}
- if (mNotificationPanelViewController != null) {
- mNotificationPanelViewController.updateResources();
+ if (mNotificationPanel != null) {
+ mNotificationPanel.updateResources();
}
if (mBrightnessMirrorController != null) {
mBrightnessMirrorController.updateResources();
@@ -3112,7 +3107,7 @@
public void showKeyguardImpl() {
mIsKeyguard = true;
if (mKeyguardStateController.isLaunchTransitionFadingAway()) {
- mNotificationPanelViewController.cancelAnimation();
+ mNotificationPanel.animate().cancel();
onLaunchTransitionFadingEnded();
}
mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
@@ -3139,8 +3134,8 @@
}
private void onLaunchTransitionFadingEnded() {
- mNotificationPanelViewController.setAlpha(1.0f);
- mNotificationPanelViewController.onAffordanceLaunchEnded();
+ mNotificationPanel.setAlpha(1.0f);
+ mNotificationPanel.onAffordanceLaunchEnded();
releaseGestureWakeLock();
runLaunchTransitionEndRunnable();
mKeyguardStateController.setLaunchTransitionFadingAway(false);
@@ -3148,8 +3143,8 @@
}
public boolean isInLaunchTransition() {
- return mNotificationPanelViewController.isLaunchTransitionRunning()
- || mNotificationPanelViewController.isLaunchTransitionFinished();
+ return mNotificationPanel.isLaunchTransitionRunning()
+ || mNotificationPanel.isLaunchTransitionFinished();
}
/**
@@ -3170,15 +3165,18 @@
}
updateScrimController();
mPresenter.updateMediaMetaData(false, true);
- mNotificationPanelViewController.setAlpha(1);
- mNotificationPanelViewController.fadeOut(
- FADE_KEYGUARD_START_DELAY, FADE_KEYGUARD_DURATION,
- this::onLaunchTransitionFadingEnded);
+ mNotificationPanel.setAlpha(1);
+ mNotificationPanel.animate()
+ .alpha(0)
+ .setStartDelay(FADE_KEYGUARD_START_DELAY)
+ .setDuration(FADE_KEYGUARD_DURATION)
+ .withLayer()
+ .withEndAction(this::onLaunchTransitionFadingEnded);
mCommandQueue.appTransitionStarting(mDisplayId, SystemClock.uptimeMillis(),
LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true);
};
- if (mNotificationPanelViewController.isLaunchTransitionRunning()) {
- mNotificationPanelViewController.setLaunchTransitionEndRunnable(hideRunnable);
+ if (mNotificationPanel.isLaunchTransitionRunning()) {
+ mNotificationPanel.setLaunchTransitionEndRunnable(hideRunnable);
} else {
hideRunnable.run();
}
@@ -3189,18 +3187,22 @@
* fading.
*/
public void fadeKeyguardWhilePulsing() {
- mNotificationPanelViewController.fadeOut(0, FADE_KEYGUARD_DURATION_PULSING,
- ()-> {
- hideKeyguard();
- mStatusBarKeyguardViewManager.onKeyguardFadedAway();
- }).start();
+ mNotificationPanel.animate()
+ .alpha(0f)
+ .setStartDelay(0)
+ .setDuration(FADE_KEYGUARD_DURATION_PULSING)
+ .setInterpolator(Interpolators.ALPHA_OUT)
+ .withEndAction(()-> {
+ hideKeyguard();
+ mStatusBarKeyguardViewManager.onKeyguardFadedAway();
+ }).start();
}
/**
* Plays the animation when an activity that was occluding Keyguard goes away.
*/
public void animateKeyguardUnoccluding() {
- mNotificationPanelViewController.setExpandedFraction(0f);
+ mNotificationPanel.setExpandedFraction(0f);
animateExpandNotificationsPanel();
}
@@ -3216,9 +3218,9 @@
private void onLaunchTransitionTimeout() {
Log.w(TAG, "Launch transition: Timeout!");
- mNotificationPanelViewController.onAffordanceLaunchEnded();
+ mNotificationPanel.onAffordanceLaunchEnded();
releaseGestureWakeLock();
- mNotificationPanelViewController.resetViews(false /* animate */);
+ mNotificationPanel.resetViews(false /* animate */);
}
private void runLaunchTransitionEndRunnable() {
@@ -3251,7 +3253,7 @@
mStatusBarStateController.setLeaveOpenOnKeyguardHide(false);
}
long delay = mKeyguardStateController.calculateGoingToFullShadeDelay();
- mNotificationPanelViewController.animateToFullShade(delay);
+ mNotificationPanel.animateToFullShade(delay);
if (mDraggedDownEntry != null) {
mDraggedDownEntry.setUserLocked(false);
mDraggedDownEntry = null;
@@ -3260,7 +3262,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 (!mNotificationPanelViewController.isCollapsing()) {
+ } else if (!mNotificationPanel.isCollapsing()) {
instantCollapseNotificationPanel();
}
@@ -3271,10 +3273,10 @@
}
mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
releaseGestureWakeLock();
- mNotificationPanelViewController.onAffordanceLaunchEnded();
- mNotificationPanelViewController.cancelAnimation();
- mNotificationPanelViewController.setAlpha(1f);
- mNotificationPanelViewController.resetViewGroupFade();
+ mNotificationPanel.onAffordanceLaunchEnded();
+ mNotificationPanel.animate().cancel();
+ mNotificationPanel.setAlpha(1f);
+ ViewGroupFadeHelper.reset(mNotificationPanel);
updateScrimController();
Trace.endSection();
return staying;
@@ -3349,7 +3351,7 @@
boolean animate = (!mDozing && mDozeServiceHost.shouldAnimateWakeup() && !wakeAndUnlock)
|| (mDozing && mDozeServiceHost.shouldAnimateScreenOff() && sleepingFromKeyguard);
- mNotificationPanelViewController.setDozing(mDozing, animate, mWakeUpTouchLocation);
+ mNotificationPanel.setDozing(mDozing, animate, mWakeUpTouchLocation);
updateQsExpansionEnabled();
Trace.endSection();
}
@@ -3381,27 +3383,27 @@
public void endAffordanceLaunch() {
releaseGestureWakeLock();
- mNotificationPanelViewController.onAffordanceLaunchEnded();
+ mNotificationPanel.onAffordanceLaunchEnded();
}
public boolean onBackPressed() {
boolean isScrimmedBouncer = mScrimController.getState() == ScrimState.BOUNCER_SCRIMMED;
if (mStatusBarKeyguardViewManager.onBackPressed(isScrimmedBouncer /* hideImmediately */)) {
if (!isScrimmedBouncer) {
- mNotificationPanelViewController.expandWithoutQs();
+ mNotificationPanel.expandWithoutQs();
}
return true;
}
- if (mNotificationPanelViewController.isQsExpanded()) {
- if (mNotificationPanelViewController.isQsDetailShowing()) {
- mNotificationPanelViewController.closeQsDetail();
+ if (mNotificationPanel.isQsExpanded()) {
+ if (mNotificationPanel.isQsDetailShowing()) {
+ mNotificationPanel.closeQsDetail();
} else {
- mNotificationPanelViewController.animateCloseQs(false /* animateAway */);
+ mNotificationPanel.animateCloseQs(false /* animateAway */);
}
return true;
}
if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) {
- if (mNotificationPanelViewController.canPanelBeCollapsed()) {
+ if (mNotificationPanel.canPanelBeCollapsed()) {
mShadeController.animateCollapsePanels();
} else {
mBubbleController.performBackPressIfNeeded();
@@ -3431,7 +3433,7 @@
}
void instantCollapseNotificationPanel() {
- mNotificationPanelViewController.instantCollapse();
+ mNotificationPanel.instantCollapse();
mShadeController.runPostCollapseRunnables();
}
@@ -3497,7 +3499,7 @@
// Collapse the notification panel if open
boolean dozingAnimated = mDozeServiceHost.getDozingRequested()
&& mDozeParameters.shouldControlScreenOff();
- mNotificationPanelViewController.resetViews(dozingAnimated);
+ mNotificationPanel.resetViews(dozingAnimated);
updateQsExpansionEnabled();
mKeyguardViewMediator.setDozing(mDozing);
@@ -3571,7 +3573,7 @@
* @return bottom area view
*/
public KeyguardBottomAreaView getKeyguardBottomAreaView() {
- return mNotificationPanelViewController.getKeyguardBottomAreaView();
+ return mNotificationPanel.getKeyguardBottomAreaView();
}
/**
@@ -3610,7 +3612,7 @@
mDraggedDownEntry = entry;
mPendingRemoteInputView = null;
} else {
- mNotificationPanelViewController.animateToFullShade(0 /* delay */);
+ mNotificationPanel.animateToFullShade(0 /* delay */);
mStatusBarStateController.setState(StatusBarState.SHADE_LOCKED);
}
}
@@ -3636,7 +3638,7 @@
* Collapses the notification shade if it is tracking or expanded.
*/
public void collapseShade() {
- if (mNotificationPanelViewController.isTracking()) {
+ if (mNotificationPanel.isTracking()) {
mStatusBarWindowViewController.cancelCurrentTouch();
}
if (mPanelExpanded && mState == StatusBarState.SHADE) {
@@ -3648,7 +3650,7 @@
final WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() {
@Override
public void onFinishedGoingToSleep() {
- mNotificationPanelViewController.onAffordanceLaunchEnded();
+ mNotificationPanel.onAffordanceLaunchEnded();
releaseGestureWakeLock();
mLaunchCameraWhenFinishedWaking = false;
mDeviceInteractive = false;
@@ -3709,8 +3711,7 @@
mBypassHeadsUpNotifier.setFullyAwake(true);
mWakeUpCoordinator.setWakingUp(false);
if (mLaunchCameraWhenFinishedWaking) {
- mNotificationPanelViewController.launchCamera(
- false /* animate */, mLastCameraLaunchSource);
+ mNotificationPanel.launchCamera(false /* animate */, mLastCameraLaunchSource);
mLaunchCameraWhenFinishedWaking = false;
}
updateScrimController();
@@ -3727,7 +3728,7 @@
&& !mDozeParameters.shouldControlScreenOff();
boolean disabled = (!mDeviceInteractive && !mDozeServiceHost.isPulsing())
|| goingToSleepWithoutAnimation;
- mNotificationPanelViewController.setTouchAndAnimationDisabled(disabled);
+ mNotificationPanel.setTouchAndAnimationDisabled(disabled);
mNotificationIconAreaController.setAnimationsEnabled(!disabled);
}
@@ -3735,7 +3736,7 @@
@Override
public void onScreenTurningOn() {
mFalsingManager.onScreenTurningOn();
- mNotificationPanelViewController.onScreenTurningOn();
+ mNotificationPanel.onScreenTurningOn();
}
@Override
@@ -3804,7 +3805,7 @@
mLaunchCameraOnFinishedGoingToSleep = true;
return;
}
- if (!mNotificationPanelViewController.canCameraGestureBeLaunched()) {
+ if (!mNotificationPanel.canCameraGestureBeLaunched()) {
if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Can't launch camera right now");
return;
}
@@ -3834,8 +3835,7 @@
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
mStatusBarKeyguardViewManager.reset(true /* hide */);
}
- mNotificationPanelViewController.launchCamera(
- mDeviceInteractive /* animate */, source);
+ mNotificationPanel.launchCamera(mDeviceInteractive /* animate */, source);
updateScrimController();
} else {
// We need to defer the camera launch until the screen comes on, since otherwise
@@ -3894,7 +3894,7 @@
!mBiometricUnlockController.isBiometricUnlock());
boolean launchingAffordanceWithPreview =
- mNotificationPanelViewController.isLaunchingAffordanceWithPreview();
+ mNotificationPanel.isLaunchingAffordanceWithPreview();
mScrimController.setLaunchingAffordanceWithPreview(launchingAffordanceWithPreview);
if (mBouncerShowing) {
@@ -4243,7 +4243,7 @@
* When {@link KeyguardBouncer} starts to be dismissed, playing its animation.
*/
public void onBouncerPreHideAnimation() {
- mNotificationPanelViewController.onBouncerPreHideAnimation();
+ mNotificationPanel.onBouncerPreHideAnimation();
mLockscreenLockIconController.onBouncerPreHideAnimation();
}
@@ -4286,8 +4286,8 @@
mAssistManagerLazy.get().showDisclosure();
}
- public NotificationPanelViewController getPanelController() {
- return mNotificationPanelViewController;
+ public NotificationPanelView getPanel() {
+ return mNotificationPanel;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarComponent.java
similarity index 74%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarComponent.java
index 21d0bb8..f3c843c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarComponent.java
@@ -14,14 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.phone.dagger;
+package com.android.systemui.statusbar.phone;
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;
@@ -33,8 +29,7 @@
/**
* Dagger subcomponent tied to the lifecycle of StatusBar views.
*/
-@Subcomponent(modules = {StatusBarViewModule.class})
-@StatusBarComponent.StatusBarScope
+@Subcomponent
public interface StatusBarComponent {
/**
* Builder for {@link StatusBarComponent}.
@@ -59,10 +54,4 @@
@StatusBarScope
StatusBarWindowViewController getStatusBarWindowViewController();
- /**
- * Creates a NotificationPanelViewController.
- */
- @StatusBarScope
- NotificationPanelViewController getNotificationPanelViewController();
-
}
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 407d256..f51174b 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 NotificationPanelViewController mNotificationPanelViewController;
+ private NotificationPanelView mNotificationPanelView;
private BiometricUnlockController mBiometricUnlockController;
private ViewGroup mContainer;
@@ -224,7 +224,7 @@
public void registerStatusBar(StatusBar statusBar,
ViewGroup container,
- NotificationPanelViewController notificationPanelViewController,
+ NotificationPanelView notificationPanelView,
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);
- mNotificationPanelViewController = notificationPanelViewController;
- notificationPanelViewController.addExpansionListener(this);
+ mNotificationPanelView = notificationPanelView;
+ notificationPanelView.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 (mNotificationPanelViewController.isUnlockHintRunning()) {
+ if (mNotificationPanelView.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
- && !mNotificationPanelViewController.isQsExpanded();
+ && !mNotificationPanelView.isQsExpanded();
boolean lockVisible = (mBouncer.isShowing() || keyguardWithoutQs)
&& !mBouncer.isAnimatingAway() && !mKeyguardStateController.isKeyguardFadingAway();
@@ -555,7 +555,7 @@
} else if (finishRunnable != null) {
finishRunnable.run();
}
- mNotificationPanelViewController.blockExpansionForCurrentTouch();
+ mNotificationPanelView.blockExpansionForCurrentTouch();
updateLockIcon();
}
@@ -609,8 +609,7 @@
hideBouncer(true /* destroyView */);
if (wakeUnlockPulsing) {
if (needsFading) {
- ViewGroupFadeHelper.fadeOutAllChildrenExcept(
- mNotificationPanelViewController.getView(),
+ ViewGroupFadeHelper.fadeOutAllChildrenExcept(mNotificationPanelView,
mNotificationContainer,
fadeoutDuration,
() -> {
@@ -626,8 +625,7 @@
if (!staying) {
mStatusBarWindowController.setKeyguardFadingAway(true);
if (needsFading) {
- ViewGroupFadeHelper.fadeOutAllChildrenExcept(
- mNotificationPanelViewController.getView(),
+ ViewGroupFadeHelper.fadeOutAllChildrenExcept(mNotificationPanelView,
mNotificationContainer,
fadeoutDuration,
() -> {
@@ -686,7 +684,7 @@
public void onKeyguardFadedAway() {
mContainer.postDelayed(() -> mStatusBarWindowController.setKeyguardFadingAway(false),
100);
- ViewGroupFadeHelper.reset(mNotificationPanelViewController.getView());
+ ViewGroupFadeHelper.reset(mNotificationPanelView);
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 153ca22..12033de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
@@ -69,7 +69,6 @@
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.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
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 0f3b5db..661a7b1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -61,6 +61,7 @@
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;
@@ -101,7 +102,7 @@
private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
private final MetricsLogger mMetricsLogger;
private final Context mContext;
- private final NotificationPanelViewController mNotificationPanel;
+ private final NotificationPanelView mNotificationPanel;
private final NotificationPresenter mPresenter;
private final LockPatternUtils mLockPatternUtils;
private final HeadsUpManagerPhone mHeadsUpManager;
@@ -120,7 +121,7 @@
private boolean mIsCollapsingToShowActivityOverLockscreen;
private StatusBarNotificationActivityStarter(Context context, CommandQueue commandQueue,
- Lazy<AssistManager> assistManagerLazy, NotificationPanelViewController panel,
+ Lazy<AssistManager> assistManagerLazy, NotificationPanelView panel,
NotificationPresenter presenter, NotificationEntryManager entryManager,
HeadsUpManagerPhone headsUpManager, ActivityStarter activityStarter,
ActivityLaunchAnimator activityLaunchAnimator, IStatusBarService statusBarService,
@@ -518,6 +519,7 @@
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;
@@ -525,8 +527,7 @@
private final Executor mUiBgExecutor;
private final ActivityIntentHelper mActivityIntentHelper;
private final BubbleController mBubbleController;
- private NotificationPanelViewController mNotificationPanelViewController;
- private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
+ private final SuperStatusBarViewFactory mSuperStatusBarViewFactory;
private final ShadeController mShadeController;
private NotificationPresenter mNotificationPresenter;
private ActivityLaunchAnimator mActivityLaunchAnimator;
@@ -557,7 +558,8 @@
@UiBackground Executor uiBgExecutor,
ActivityIntentHelper activityIntentHelper,
BubbleController bubbleController,
- ShadeController shadeController) {
+ ShadeController shadeController,
+ SuperStatusBarViewFactory superStatusBarViewFactory) {
mContext = context;
mCommandQueue = commandQueue;
mAssistManagerLazy = assistManagerLazy;
@@ -583,6 +585,7 @@
mActivityIntentHelper = activityIntentHelper;
mBubbleController = bubbleController;
mShadeController = shadeController;
+ mSuperStatusBarViewFactory = superStatusBarViewFactory;
}
/** Sets the status bar to use as {@link StatusBar}. */
@@ -601,19 +604,10 @@
return this;
}
- /** Set the NotificationPanelViewController */
- public Builder setNotificationPanelViewController(
- NotificationPanelViewController notificationPanelViewController) {
- mNotificationPanelViewController = notificationPanelViewController;
- return this;
- }
-
-
-
public StatusBarNotificationActivityStarter build() {
return new StatusBarNotificationActivityStarter(mContext,
mCommandQueue, mAssistManagerLazy,
- mNotificationPanelViewController,
+ mSuperStatusBarViewFactory.getNotificationPanelView(),
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 0fd0dab..ab9d540 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 NotificationPanelViewController mNotificationPanel;
+ private final NotificationPanelView mNotificationPanel;
private final HeadsUpManagerPhone mHeadsUpManager;
private final AboveShelfObserver mAboveShelfObserver;
private final DozeScrimController mDozeScrimController;
@@ -132,7 +132,7 @@
private int mMaxKeyguardNotifications;
public StatusBarNotificationPresenter(Context context,
- NotificationPanelViewController panel,
+ NotificationPanelView panel,
HeadsUpManagerPhone headsUp,
StatusBarWindowView statusBarWindow,
ViewGroup stackScroller,
@@ -172,7 +172,7 @@
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
if (MULTIUSER_DEBUG) {
- mNotificationPanelDebugText = mNotificationPanel.getHeaderDebugInfo();
+ mNotificationPanelDebugText = mNotificationPanel.findViewById(R.id.header_debug_info);
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 1e3c5d6..6b51391 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -75,10 +75,6 @@
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 4935f0e..eb86bcc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java
@@ -26,9 +26,11 @@
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;
@@ -91,7 +93,6 @@
private boolean mSingleTapEnabled;
private boolean mExpandingBelowNotch;
private final DockManager mDockManager;
- private final NotificationPanelViewController mNotificationPanelViewController;
@Inject
public StatusBarWindowViewController(
@@ -112,8 +113,7 @@
CommandQueue commandQueue,
ShadeController shadeController,
DockManager dockManager,
- StatusBarWindowView statusBarWindowView,
- NotificationPanelViewController notificationPanelViewController) {
+ StatusBarWindowView statusBarWindowView) {
mInjectionInflationController = injectionInflationController;
mCoordinator = coordinator;
mPulseExpansionHandler = pulseExpansionHandler;
@@ -132,7 +132,6 @@
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);
@@ -140,6 +139,39 @@
/** 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) -> {
@@ -201,8 +233,8 @@
if (!isCancel && mService.shouldIgnoreTouch()) {
return false;
}
- if (isDown && mNotificationPanelViewController.isFullyCollapsed()) {
- mNotificationPanelViewController.startExpandLatencyTracking();
+ if (isDown && notificationPanelView.isFullyCollapsed()) {
+ notificationPanelView.startExpandLatencyTracking();
}
if (isDown) {
setTouchActive(true);
@@ -255,7 +287,7 @@
return true;
}
boolean intercept = false;
- if (mNotificationPanelViewController.isFullyExpanded()
+ if (notificationPanelView.isFullyExpanded()
&& mDragDownHelper.isDragDownEnabled()
&& !mService.isBouncerShowing()
&& !mStatusBarStateController.isDozing()) {
@@ -271,7 +303,7 @@
MotionEvent cancellation = MotionEvent.obtain(ev);
cancellation.setAction(MotionEvent.ACTION_CANCEL);
mStackScrollLayout.onInterceptTouchEvent(cancellation);
- mNotificationPanelViewController.getView().onInterceptTouchEvent(cancellation);
+ notificationPanelView.onInterceptTouchEvent(cancellation);
cancellation.recycle();
}
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
deleted file mode 100644
index 20bd51d..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ /dev/null
@@ -1,35 +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.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 625d884..88edf8e 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.NotificationPanelViewController;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
import com.android.systemui.statusbar.phone.StatusBarWindowView;
import java.util.Objects;
@@ -38,17 +38,16 @@
private final StatusBarWindowView mStatusBarWindow;
private final Consumer<Boolean> mVisibilityCallback;
- private final NotificationPanelViewController mNotificationPanel;
+ private final NotificationPanelView 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 = notificationPanelViewController;
+ mNotificationPanel = statusBarWindow.findViewById(R.id.notification_panel);
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 d28a6699..4b283fed 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.NotificationPanelViewController;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
/**
* Manages the user switcher on the Keyguard.
@@ -57,8 +57,7 @@
private boolean mAnimating;
public KeyguardUserSwitcher(Context context, ViewStub userSwitcher,
- KeyguardStatusBarView statusBarView,
- NotificationPanelViewController panelViewController) {
+ KeyguardStatusBarView statusBarView, NotificationPanelView panelView) {
boolean keyguardUserSwitcherEnabled =
context.getResources().getBoolean(R.bool.config_keyguardUserSwitcher) || ALWAYS_ON;
UserSwitcherController userSwitcherController = Dependency.get(UserSwitcherController.class);
@@ -68,7 +67,7 @@
reinflateViews();
mStatusBarView = statusBarView;
mStatusBarView.setKeyguardUserSwitcher(this);
- panelViewController.setKeyguardUserSwitcher(this);
+ panelView.setKeyguardUserSwitcher(this);
mAdapter = new Adapter(context, userSwitcherController, this);
mAdapter.registerDataSetObserver(mDataSetObserver);
mUserSwitcherController = userSwitcherController;
diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
index 56aae17..6976649 100644
--- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
+++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
@@ -35,6 +35,7 @@
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;
@@ -128,6 +129,11 @@
NotificationStackScrollLayout createNotificationStackScrollLayout();
/**
+ * Creates the NotificationPanelView.
+ */
+ NotificationPanelView createPanelView();
+
+ /**
* Creates the Shelf.
*/
NotificationShelf creatNotificationShelf();
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..77659df 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -345,7 +345,7 @@
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 a54f733..145a25c 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.NotificationPanelViewController;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
import com.android.systemui.statusbar.phone.StatusBarWindowView;
import com.android.systemui.statusbar.phone.StatusBarWindowViewController;
@@ -64,7 +64,7 @@
mLaunchAnimator = new ActivityLaunchAnimator(
mStatusBarWindowViewController,
mCallback,
- mock(NotificationPanelViewController.class),
+ mock(NotificationPanelView.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..c559265 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;
@@ -143,6 +144,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 +221,8 @@
mEntry.expandedIcon = mock(StatusBarIconView.class);
+ when(mFeatureFlags.isNewNotifPipelineEnabled()).thenReturn(false);
+ when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
mEntryManager = new TestableNotificationEntryManager(
mNotifLog,
mGroupManager,
@@ -230,7 +234,8 @@
mNotifLog,
mock(NotificationSectionsFeatureManager.class),
mock(PeopleNotificationIdentifier.class)),
- mEnvironment
+ mEnvironment,
+ mFeatureFlags
);
mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mHeadsUpManager);
mEntryManager.addNotificationEntryListener(mEntryListener);
@@ -242,7 +247,8 @@
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/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/NotificationContentInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index dc4e498..f916fe5 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
@@ -33,7 +33,6 @@
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;
@@ -179,7 +178,7 @@
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");
@@ -261,7 +260,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 ae87eef..8decae3 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,8 +64,7 @@
mMockNotificiationAreaController = mock(NotificationIconAreaController.class);
mNotificationAreaInner = mock(View.class);
mCenteredNotificationAreaView = mock(View.class);
- when(statusBar.getPanelController()).thenReturn(
- mock(NotificationPanelViewController.class));
+ when(statusBar.getPanel()).thenReturn(mock(NotificationPanelView.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 d31f175..46f6cfe 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 NotificationPanelViewController mNotificationPanel;
+ @Mock private NotificationPanelView 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 7448dbd..0260269 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,8 +53,7 @@
private final NotificationStackScrollLayout mStackScroller =
mock(NotificationStackScrollLayout.class);
- private final NotificationPanelViewController mPanelView =
- mock(NotificationPanelViewController.class);
+ private final NotificationPanelView mPanelView = mock(NotificationPanelView.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 1f37ad8..c165e56 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,13 +16,9 @@
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;
@@ -30,44 +26,37 @@
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.internal.logging.MetricsLogger;
-import com.android.internal.util.LatencyTracker;
-import com.android.keyguard.KeyguardClockSwitch;
+import com.android.keyguard.KeyguardStatusView;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.R;
+import com.android.systemui.SystemUIFactory;
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;
@@ -81,7 +70,6 @@
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.mockito.stubbing.Answer;
import java.util.function.Consumer;
@@ -97,6 +85,8 @@
@Mock
private NotificationStackScrollLayout mNotificationStackScrollLayout;
@Mock
+ private KeyguardStatusView mKeyguardStatusView;
+ @Mock
private KeyguardBottomAreaView mKeyguardBottomArea;
@Mock
private KeyguardBottomAreaView mQsFrame;
@@ -119,89 +109,27 @@
@Mock
private PanelBar mPanelBar;
@Mock
+ private KeyguardAffordanceHelper mAffordanceHelper;
+ @Mock
private KeyguardUpdateMonitor mUpdateMonitor;
@Mock
private FalsingManager mFalsingManager;
@Mock
private KeyguardBypassController mKeyguardBypassController;
- @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;
+ @Mock private DozeParameters mDozeParameters;
+ private NotificationPanelView mNotificationPanelView;
@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(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));
-
+ when(mHeadsUpCallback.getContext()).thenReturn(mContext);
+ mDependency.injectTestDependency(StatusBarStateController.class,
+ mStatusBarStateController);
+ mDependency.injectTestDependency(KeyguardUpdateMonitor.class, mUpdateMonitor);
+ mDependency.injectMockDependency(ConfigurationController.class);
+ mDependency.injectMockDependency(ZenModeController.class);
NotificationWakeUpCoordinator coordinator =
new NotificationWakeUpCoordinator(
mock(HeadsUpManagerPhone.class),
@@ -215,26 +143,18 @@
mock(NotificationRoundnessManager.class),
mStatusBarStateController,
new FalsingManagerFake());
- 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);
+ 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));
}
@Test
public void testSetDozing_notifiesNsslAndStateController() {
- mNotificationPanelViewController.setDozing(true /* dozing */, true /* animate */,
- null /* touch */);
+ mNotificationPanelView.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));
@@ -242,63 +162,103 @@
@Test
public void testSetExpandedHeight() {
- mNotificationPanelViewController.setExpandedHeight(200);
- assertThat((int) mNotificationPanelViewController.getExpandedHeight()).isEqualTo(200);
+ mNotificationPanelView.setExpandedHeight(200);
+ assertThat((int) mNotificationPanelView.getExpandedHeight()).isEqualTo(200);
}
@Test
public void testAffordanceLaunchingListener() {
Consumer<Boolean> listener = spy((showing) -> { });
- mNotificationPanelViewController.setExpandedFraction(1f);
- mNotificationPanelViewController.setLaunchAffordanceListener(listener);
- mNotificationPanelViewController.launchCamera(false /* animate */,
+ mNotificationPanelView.setExpandedFraction(1f);
+ mNotificationPanelView.setLaunchAffordanceListener(listener);
+ mNotificationPanelView.launchCamera(false /* animate */,
StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP);
verify(listener).accept(eq(true));
- mNotificationPanelViewController.onAffordanceLaunchEnded();
+ mNotificationPanelView.onAffordanceLaunchEnded();
verify(listener).accept(eq(false));
}
@Test
public void testOnTouchEvent_expansionCanBeBlocked() {
- onTouchEvent(MotionEvent.obtain(0L /* downTime */,
+ mNotificationPanelView.onTouchEvent(MotionEvent.obtain(0L /* downTime */,
0L /* eventTime */, MotionEvent.ACTION_DOWN, 0f /* x */, 0f /* y */,
0 /* metaState */));
- onTouchEvent(MotionEvent.obtain(0L /* downTime */,
+ mNotificationPanelView.onTouchEvent(MotionEvent.obtain(0L /* downTime */,
0L /* eventTime */, MotionEvent.ACTION_MOVE, 0f /* x */, 200f /* y */,
0 /* metaState */));
- assertThat((int) mNotificationPanelViewController.getExpandedHeight()).isEqualTo(200);
- assertThat(mNotificationPanelViewController.isTrackingBlocked()).isFalse();
+ assertThat((int) mNotificationPanelView.getExpandedHeight()).isEqualTo(200);
+ assertThat(mNotificationPanelView.isTrackingBlocked()).isFalse();
- mNotificationPanelViewController.blockExpansionForCurrentTouch();
- onTouchEvent(MotionEvent.obtain(0L /* downTime */,
+ mNotificationPanelView.blockExpansionForCurrentTouch();
+ mNotificationPanelView.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) mNotificationPanelViewController.getExpandedHeight()).isEqualTo(200);
- assertThat(mNotificationPanelViewController.isTrackingBlocked()).isTrue();
+ assertThat((int) mNotificationPanelView.getExpandedHeight()).isEqualTo(200);
+ assertThat(mNotificationPanelView.isTrackingBlocked()).isTrue();
- onTouchEvent(MotionEvent.obtain(0L /* downTime */,
+ mNotificationPanelView.onTouchEvent(MotionEvent.obtain(0L /* downTime */,
0L /* eventTime */, MotionEvent.ACTION_UP, 0f /* x */, 300f /* y */,
0 /* metaState */));
- assertThat(mNotificationPanelViewController.isTrackingBlocked()).isFalse();
+ assertThat(mNotificationPanelView.isTrackingBlocked()).isFalse();
}
@Test
public void testKeyguardStatusBarVisibility_hiddenForBypass() {
when(mUpdateMonitor.shouldListenForFace()).thenReturn(true);
- mNotificationPanelViewController.mKeyguardUpdateCallback.onBiometricRunningStateChanged(
- true, BiometricSourceType.FACE);
+ mNotificationPanelView.mKeyguardUpdateCallback.onBiometricRunningStateChanged(true,
+ BiometricSourceType.FACE);
verify(mKeyguardStatusBar, never()).setVisibility(View.VISIBLE);
when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true);
- mNotificationPanelViewController.mKeyguardUpdateCallback.onFinishedGoingToSleep(0);
- mNotificationPanelViewController.mKeyguardUpdateCallback.onBiometricRunningStateChanged(
- true, BiometricSourceType.FACE);
+ mNotificationPanelView.mKeyguardUpdateCallback.onFinishedGoingToSleep(0);
+ mNotificationPanelView.mKeyguardUpdateCallback.onBiometricRunningStateChanged(true,
+ BiometricSourceType.FACE);
verify(mKeyguardStatusBar, never()).setVisibility(View.VISIBLE);
}
- private void onTouchEvent(MotionEvent ev) {
- mTouchHandler.onTouch(mView, ev);
+ 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);
+ }
}
}
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 5b5eaad..b27e84a 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 NotificationPanelViewController mNotificationPanelView;
+ private NotificationPanelView mNotificationPanelView;
@Mock
private BiometricUnlockController mBiometrucUnlockController;
@Mock
@@ -281,12 +281,12 @@
@Override
public void registerStatusBar(StatusBar statusBar, ViewGroup container,
- NotificationPanelViewController notificationPanelViewController,
+ NotificationPanelView notificationPanelView,
BiometricUnlockController fingerprintUnlockController,
DismissCallbackRegistry dismissCallbackRegistry,
ViewGroup lockIconContainer, View notificationContainer,
KeyguardBypassController bypassController, FalsingManager falsingManager) {
- super.registerStatusBar(statusBar, container, notificationPanelViewController,
+ super.registerStatusBar(statusBar, container, notificationPanelView,
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 fea4b8b..86b2a44 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,6 +64,7 @@
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;
@@ -123,6 +124,10 @@
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;
@@ -162,6 +167,8 @@
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,
@@ -175,9 +182,9 @@
mKeyguardStateController,
mock(NotificationInterruptionStateProvider.class), mock(MetricsLogger.class),
mock(LockPatternUtils.class), mHandler, mHandler, mUiBgExecutor,
- mActivityIntentHelper, mBubbleController, mShadeController))
+ mActivityIntentHelper, mBubbleController, mShadeController,
+ mSuperStatusBarViewFactory))
.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 5ac7bfb..1296a97 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(NotificationPanelViewController.class), mock(HeadsUpManagerPhone.class),
+ mock(NotificationPanelView.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 7e485f4..1cdba47 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
@@ -124,7 +124,6 @@
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
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;
@@ -173,7 +172,6 @@
@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;
@@ -287,7 +285,6 @@
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);
@@ -419,7 +416,7 @@
mLockIconContainer);
when(mKeyguardViewMediator.registerStatusBar(any(StatusBar.class), any(ViewGroup.class),
- any(NotificationPanelViewController.class), any(BiometricUnlockController.class),
+ any(NotificationPanelView.class), any(BiometricUnlockController.class),
any(ViewGroup.class), any(ViewGroup.class), any(KeyguardBypassController.class)))
.thenReturn(mStatusBarKeyguardViewManager);
@@ -429,7 +426,7 @@
// TODO: we should be able to call mStatusBar.start() and have all the below values
// initialized automatically.
mStatusBar.mStatusBarWindow = mStatusBarWindowView;
- mStatusBar.mNotificationPanelViewController = mNotificationPanelViewController;
+ mStatusBar.mNotificationPanel = mNotificationPanelView;
mStatusBar.mDozeScrimController = mDozeScrimController;
mStatusBar.mNotificationIconAreaController = mNotificationIconAreaController;
mStatusBar.mPresenter = mNotificationPresenter;
@@ -734,20 +731,20 @@
when(mCommandQueue.panelsEnabled()).thenReturn(false);
mStatusBar.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false);
- verify(mNotificationPanelViewController).setQsExpansionEnabled(false);
+ verify(mNotificationPanelView).setQsExpansionEnabled(false);
mStatusBar.animateExpandNotificationsPanel();
- verify(mNotificationPanelViewController, never()).expand(anyBoolean());
+ verify(mNotificationPanelView, never()).expand(anyBoolean());
mStatusBar.animateExpandSettingsPanel(null);
- verify(mNotificationPanelViewController, never()).expand(anyBoolean());
+ verify(mNotificationPanelView, never()).expand(anyBoolean());
when(mCommandQueue.panelsEnabled()).thenReturn(true);
mStatusBar.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
StatusBarManager.DISABLE2_NONE, false);
- verify(mNotificationPanelViewController).setQsExpansionEnabled(true);
+ verify(mNotificationPanelView).setQsExpansionEnabled(true);
mStatusBar.animateExpandNotificationsPanel();
- verify(mNotificationPanelViewController).expandWithoutQs();
+ verify(mNotificationPanelView).expandWithoutQs();
mStatusBar.animateExpandSettingsPanel(null);
- verify(mNotificationPanelViewController).expandWithQs();
+ verify(mNotificationPanelView).expandWithQs();
}
@Test
@@ -837,12 +834,12 @@
when(mDozeServiceHost.getDozingRequested()).thenReturn(true);
mStatusBar.updateIsKeyguard();
// TODO: mNotificationPanelView.expand(false) gets called twice. Should be once.
- verify(mNotificationPanelViewController, times(2)).expand(eq(false));
- clearInvocations(mNotificationPanelViewController);
+ verify(mNotificationPanelView, times(2)).expand(eq(false));
+ clearInvocations(mNotificationPanelView);
mStatusBar.mWakefulnessObserver.onStartedWakingUp();
verify(mDozeServiceHost).stopDozing();
- verify(mNotificationPanelViewController).expand(eq(false));
+ verify(mNotificationPanelView).expand(eq(false));
}
@Test
@@ -851,11 +848,11 @@
when(mStatusBarStateController.isKeyguardRequested()).thenReturn(true);
when(mDozeServiceHost.getDozingRequested()).thenReturn(true);
mStatusBar.updateIsKeyguard();
- clearInvocations(mNotificationPanelViewController);
+ clearInvocations(mNotificationPanelView);
mStatusBar.setBouncerShowing(true);
mStatusBar.mWakefulnessObserver.onStartedWakingUp();
- verify(mNotificationPanelViewController, never()).expand(anyBoolean());
+ verify(mNotificationPanelView, 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 f9848f3..9f899ee 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,7 +16,6 @@
package com.android.systemui.statusbar.phone;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -27,7 +26,6 @@
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;
@@ -42,7 +40,6 @@
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;
@@ -77,16 +74,12 @@
@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 = spy(new StatusBarWindowView(getContext(), null));
- when(mView.findViewById(R.id.notification_stack_scroller))
- .thenReturn(mNotificationStackScrollLayout);
+ mView = new StatusBarWindowView(getContext(), null);
when(mStatusBarStateController.isDozing()).thenReturn(false);
mDependency.injectTestDependency(ShadeController.class, mShadeController);
@@ -111,8 +104,7 @@
new CommandQueue(mContext),
mShadeController,
mDockManager,
- mView,
- mNotificationPanelViewController);
+ mView);
mController.setupExpandedStatusBar();
mController.setService(mStatusBar);
mController.setDragDownHelper(mDragDownHelper);
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/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 4396cdb..bba61d7 100644
--- a/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
+++ b/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
@@ -22,14 +22,13 @@
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;
@@ -39,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;
@@ -612,8 +609,7 @@
// 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);
+ SocketUtils.bindSocketToInterface(mSocket, mInterface.name);
TetheringUtils.setupRaSocket(mSocket, mInterface.index);
} catch (ErrnoException | IOException e) {
Log.e(TAG, "Failed to create RA daemon socket: " + e);
@@ -628,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/SystemUI/src/com/android/systemui/dagger/qualifiers/DisplayId.java b/packages/Tethering/src/android/net/util/TetheringMessageBase.java
similarity index 61%
copy from packages/SystemUI/src/com/android/systemui/dagger/qualifiers/DisplayId.java
copy to packages/Tethering/src/android/net/util/TetheringMessageBase.java
index 155a6d2..1b763ce 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/DisplayId.java
+++ b/packages/Tethering/src/android/net/util/TetheringMessageBase.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.
@@ -13,18 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package android.net.util;
-package com.android.systemui.dagger.qualifiers;
+/**
+ * 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;
-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/Tethering/src/android/net/util/TetheringUtils.java b/packages/Tethering/src/android/net/util/TetheringUtils.java
index 2fb73ce..fa543bd 100644
--- a/packages/Tethering/src/android/net/util/TetheringUtils.java
+++ b/packages/Tethering/src/android/net/util/TetheringUtils.java
@@ -39,4 +39,11 @@
*/
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..66b9ade 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 85164ed..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;
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 0b5759b..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();
}
@@ -1162,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
@@ -1179,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 1d59986..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;
@@ -340,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/services/Android.bp b/services/Android.bp
index ede4c3e..1e11936 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",
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 8d22300..a1f57cb 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -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/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/NetworkTimeUpdateServiceImpl.java b/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java
index cfe56052..d20936c 100644
--- a/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java
@@ -36,11 +36,11 @@
import android.os.Message;
import android.os.PowerManager;
import android.os.SystemClock;
+import android.os.TimestampedValue;
import android.provider.Settings;
import android.util.Log;
import android.util.NtpTrustedTime;
import android.util.TimeUtils;
-import android.util.TimestampedValue;
import com.android.internal.util.DumpUtils;
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 25bad64..3e6ccb5 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -42,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;
@@ -82,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;
@@ -269,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;
@@ -467,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>());
}
}
@@ -551,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);
@@ -1496,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;
}
@@ -1526,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("
@@ -1595,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;
}
@@ -1610,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(
@@ -1779,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;
@@ -1795,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(
@@ -2177,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) {
@@ -2318,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;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4bc0297..c21adb0 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -18673,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/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 e39d6d5..d7ae5b5 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1305,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
diff --git a/services/core/java/com/android/server/integrity/IntegrityFileManager.java b/services/core/java/com/android/server/integrity/IntegrityFileManager.java
index f90fab4..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,7 @@
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;
@@ -152,7 +153,7 @@
throws IOException, RuleParseException {
synchronized (RULES_LOCK) {
// Try to identify indexes from the index file.
- List<List<Integer>> ruleReadingIndexes =
+ List<RuleIndexRange> ruleReadingIndexes =
mRuleIndexingController.identifyRulesToEvaluate(appInstallMetadata);
// Read the rules based on the index information.
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
index b642fa6..c971322 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java
@@ -17,6 +17,7 @@
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;
@@ -24,25 +25,27 @@
import com.android.server.integrity.model.BitInputStream;
-import java.io.FileInputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.util.ArrayList;
+import java.util.LinkedHashMap;
import java.util.List;
-import java.util.TreeMap;
+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 TreeMap<String, Integer> sPackageNameBasedIndexes;
- private static TreeMap<String, Integer> sAppCertificateBasedIndexes;
- private static TreeMap<String, Integer> sUnindexedRuleIndexes;
+ 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(FileInputStream fileInputStream) throws IOException {
- BitInputStream bitInputStream = new BitInputStream(fileInputStream);
+ public RuleIndexingController(InputStream inputStream) throws IOException {
+ BitInputStream bitInputStream = new BitInputStream(inputStream);
sPackageNameBasedIndexes = getNextIndexGroup(bitInputStream);
sAppCertificateBasedIndexes = getNextIndexGroup(bitInputStream);
sUnindexedRuleIndexes = getNextIndexGroup(bitInputStream);
@@ -52,24 +55,57 @@
* Returns a list of integers with the starting and ending bytes of the rules that needs to be
* read and evaluated.
*/
- public List<List<Integer>> identifyRulesToEvaluate(AppInstallMetadata appInstallMetadata) {
- // TODO(b/145493956): Identify and return the indexes that needs to be read.
- return new ArrayList<>();
+ 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 TreeMap<String, Integer> getNextIndexGroup(BitInputStream bitInputStream)
+ private LinkedHashMap<String, Integer> getNextIndexGroup(BitInputStream bitInputStream)
throws IOException {
- TreeMap<String, Integer> keyToIndexMap = new TreeMap<>();
+ LinkedHashMap<String, Integer> keyToIndexMap = new LinkedHashMap<>();
while (bitInputStream.hasNext()) {
String key = getStringValue(bitInputStream);
int value = getIntValue(bitInputStream);
keyToIndexMap.put(key, value);
- if (key == END_INDEXING_KEY) {
+ 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/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 4546ea4..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;
@@ -222,7 +225,10 @@
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")
@@ -483,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);
@@ -550,6 +561,9 @@
mSpManager = injector.getSyntheticPasswordManager(mStorage);
+ mRebootEscrowManager = injector.getRebootEscrowManager(new RebootEscrowCallbacks(),
+ mStorage);
+
LocalServices.addService(LockSettingsInternal.class, new LocalService());
}
@@ -782,7 +796,14 @@
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) {
@@ -790,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() {
@@ -2466,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 {
@@ -3288,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 115155c..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() {
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 22ba1bf..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,
@@ -400,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 {
@@ -556,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);
@@ -569,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);
@@ -661,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) {
@@ -990,7 +983,7 @@
clientRecord, route, controlCategory, requestId);
mSessionCreationRequests.add(request);
- provider.requestCreateSession(clientRecord.mPackageName, route.getId(),
+ provider.requestCreateSession(clientRecord.mPackageName, route.getOriginalId(),
controlCategory, requestId);
}
@@ -1007,7 +1000,8 @@
if (provider == null) {
return;
}
- provider.selectRoute(RouteSessionInfo.getSessionId(uniqueSessionId), route);
+ provider.selectRoute(RouteSessionInfo.getSessionId(uniqueSessionId),
+ route.getOriginalId());
}
private void deselectRouteOnHandler(@NonNull Client2Record clientRecord,
@@ -1023,7 +1017,8 @@
if (provider == null) {
return;
}
- provider.deselectRoute(RouteSessionInfo.getSessionId(uniqueSessionId), route);
+ provider.deselectRoute(RouteSessionInfo.getSessionId(uniqueSessionId),
+ route.getOriginalId());
}
private void transferToRouteOnHandler(@NonNull Client2Record clientRecord,
@@ -1039,7 +1034,8 @@
if (provider == null) {
return;
}
- provider.transferToRoute(RouteSessionInfo.getSessionId(uniqueSessionId), route);
+ provider.transferToRoute(RouteSessionInfo.getSessionId(uniqueSessionId),
+ route.getOriginalId());
}
private boolean checkArgumentsForSessionControl(@NonNull Client2Record clientRecord,
@@ -1246,21 +1242,21 @@
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/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/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/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/DisplayId.java b/services/core/java/com/android/server/people/PeopleServiceInternal.java
similarity index 67%
rename from packages/SystemUI/src/com/android/systemui/dagger/qualifiers/DisplayId.java
rename to services/core/java/com/android/server/people/PeopleServiceInternal.java
index 155a6d2..31d30362 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/DisplayId.java
+++ b/services/core/java/com/android/server/people/PeopleServiceInternal.java
@@ -14,17 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.dagger.qualifiers;
+package com.android.server.people;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import android.service.appprediction.IPredictionService;
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-
-import javax.inject.Qualifier;
-
-@Qualifier
-@Documented
-@Retention(RUNTIME)
-public @interface DisplayId {
-}
+/**
+ * @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/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index c868953..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;
@@ -1684,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/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/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 1455d4a..e5d5b57 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -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/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 5a124a7..e04d10c 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -697,7 +697,7 @@
accessibilityShortcutActivated();
break;
case MSG_BUGREPORT_TV:
- requestFullBugreport();
+ requestFullBugreportOrLaunchHandlerApp();
break;
case MSG_ACCESSIBILITY_TV:
if (mAccessibilityShortcutController.isAccessibilityShortcutAvailable(false)) {
@@ -3061,12 +3061,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);
}
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/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/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
index b4f4eca..f661b5e 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
@@ -22,7 +22,7 @@
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;
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index 02656ea..da848d8 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -24,10 +24,10 @@
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;
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 80f4f0a..a3d6ee5 100644
--- a/services/core/java/com/android/server/utils/quota/UptcMap.java
+++ b/services/core/java/com/android/server/utils/quota/UptcMap.java
@@ -112,37 +112,20 @@
return data.get(tag);
}
- /**
- * Returns the index for which {@link #getUserIdAtIndex(int)} would return the specified userId,
- * or a negative number if the specified userId is not mapped.
- */
- 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);
- }
-
/** 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);
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 65d0c4c..e281712 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -6107,11 +6107,6 @@
// 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.
- mStackSupervisor.processStoppingActivities(null /* launchedActivity */,
- true /* onlyUpdateVisibility */, true /* unused */, null /* unused */);
}
}
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index fe03c06..c959439 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -784,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()) {
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index e8564fc..fd871bd 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -2076,54 +2076,11 @@
boolean processPausingActivities, String reason) {
// Stop any activities that are scheduled to do so but have been waiting for the transition
// animation to finish.
- processStoppingActivities(launchedActivity, false /* onlyUpdateVisibility */,
- processPausingActivities, reason);
-
- 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);
- }
- }
- }
-
- /** Stop or destroy the stopping activities if they are not interactive. */
- void processStoppingActivities(ActivityRecord launchedActivity, boolean onlyUpdateVisibility,
- boolean processPausingActivities, String reason) {
- final int numStoppingActivities = mStoppingActivities.size();
- if (numStoppingActivities == 0) {
- return;
- }
ArrayList<ActivityRecord> readyToStopActivities = null;
-
- final boolean nowVisible = mRootWindowContainer.allResumedActivitiesVisible();
- for (int activityNdx = numStoppingActivities - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord s = mStoppingActivities.get(activityNdx);
- 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 (onlyUpdateVisibility) {
- continue;
- }
-
+ 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=" + nowVisible
+ if (DEBUG_STATES) Slog.v(TAG, "Stopping " + s + ": nowVisible=" + s.nowVisible
+ " animating=" + animating + " finishing=" + s.finishing);
final ActivityStack stack = s.getActivityStack();
@@ -2145,7 +2102,7 @@
}
readyToStopActivities.add(s);
- mStoppingActivities.remove(activityNdx);
+ mStoppingActivities.remove(i);
}
}
@@ -2161,6 +2118,22 @@
}
}
}
+
+ 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) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 0b6a6e0..472baf6 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -772,7 +772,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();
}
@@ -1032,31 +1036,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() {
@@ -4992,6 +4978,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
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 a8ff500..c4b67d7 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -151,10 +151,10 @@
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 = mService.mAnimationHandler;
mUpdateInputForAllWindowsConsumer = new UpdateInputForAllWindowsConsumer();
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 704ab67..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;
}
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/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
index 50cea2e..5633b6b 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
@@ -78,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 */,
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 4e768da..96bc8e9 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -792,8 +792,9 @@
mSeq = seq;
mPowerManagerWrapper = powerManagerWrapper;
mForceSeamlesslyRotate = token.mRoundedCornerOverlay;
- mRequestedInsetsState =
- 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);
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/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 398ce50..2a08f5c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -367,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);
@@ -742,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;
@@ -3245,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();
@@ -3358,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) {
@@ -3455,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);
@@ -3486,6 +3501,7 @@
updateMaximumTimeToLockLocked(userHandle);
updateLockTaskPackagesLocked(policy.mLockTaskPackages, userHandle);
updateLockTaskFeaturesLocked(policy.mLockTaskFeatures, userHandle);
+ updateProtectedPackagesLocked(policy.mProtectedPackages);
if (policy.mStatusBarDisabled) {
setStatusBarDisabledInternal(policy.mStatusBarDisabled, userHandle);
}
@@ -3511,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 {
@@ -8349,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 {
@@ -8585,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);
@@ -9065,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();
}
}
@@ -14558,8 +14602,10 @@
for (int i = 0; i < users.length; i++) {
final ComponentName componentName = getProfileOwner(users[i]);
if (componentName != null) {
- admins.add(getActiveAdminForCallerLocked(
- componentName, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER));
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(componentName, users[i]);
+ if (admin != null) {
+ admins.add(admin);
+ }
}
}
return admins;
@@ -14698,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/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 08221f9..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;
@@ -1917,6 +1918,10 @@
t.traceBegin("StartCrossProfileAppsService");
mSystemServiceManager.startService(CrossProfileAppsService.class);
t.traceEnd();
+
+ t.traceBegin("StartPeopleService");
+ mSystemServiceManager.startService(PeopleService.class);
+ t.traceEnd();
}
if (!isWatch) {
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/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 1232340..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",
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/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 ee94dd6..175c756 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -3636,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();
@@ -5525,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);
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/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/DisplayId.java b/services/tests/servicestests/src/com/android/server/location/CustomCountryDetectorTestClass.java
similarity index 61%
copy from packages/SystemUI/src/com/android/systemui/dagger/qualifiers/DisplayId.java
copy to services/tests/servicestests/src/com/android/server/location/CustomCountryDetectorTestClass.java
index 155a6d2..e159012 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/DisplayId.java
+++ b/services/tests/servicestests/src/com/android/server/location/CustomCountryDetectorTestClass.java
@@ -14,17 +14,23 @@
* limitations under the License.
*/
-package com.android.systemui.dagger.qualifiers;
+package com.android.server.location;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import android.content.Context;
+import android.location.Country;
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
+public class CustomCountryDetectorTestClass extends CountryDetectorBase {
+ public CustomCountryDetectorTestClass(Context ctx) {
+ super(ctx);
+ }
-import javax.inject.Qualifier;
+ @Override
+ public Country detectCountry() {
+ return null;
+ }
-@Qualifier
-@Documented
-@Retention(RUNTIME)
-public @interface DisplayId {
+ @Override
+ public void stop() {
+
+ }
}
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/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 a7275fc7..77376f0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -341,8 +341,8 @@
assertThat(mUserManager.getUserBadgeNoBackgroundResId(userId))
.isEqualTo(userTypeDetails.getBadgeNoBackground());
assertThat(mUserManager.isProfile(userId)).isEqualTo(userTypeDetails.isProfile());
- assertThat(mUserManager.getUserTypeForUser(asHandle(userId)))
- .isEqualTo(userTypeDetails.getName());
+ assertThat(mUserManager.isUserOfType(asHandle(userId), userTypeDetails.getName()))
+ .isTrue();
final int badgeIndex = userInfo.profileBadge;
assertThat(mUserManager.getUserBadgeColor(userId)).isEqualTo(
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/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 71b568c..ae53692 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -37,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;
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 ca6fd08..aaf9799 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
@@ -30,7 +30,7 @@
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;
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/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/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 05d1c76..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.
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/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/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/CellIdentity.java b/telephony/java/android/telephony/CellIdentity.java
index ce32dce..5345469 100644
--- a/telephony/java/android/telephony/CellIdentity.java
+++ b/telephony/java/android/telephony/CellIdentity.java
@@ -191,6 +191,7 @@
*
* @hide
*/
+ @SystemApi
public abstract @NonNull CellIdentity sanitizeLocationInfo();
@Override
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/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index aff1391..be85b30 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -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/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 8139330c..5b12aed 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -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
*/
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 4c8a81b..22eed6e 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1199,12 +1199,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");
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index df8c26d..4a42b6f 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -107,6 +107,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;
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/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index 998b39d..bc60d81 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -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/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/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 3934af1..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 {
@@ -105,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/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/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index 148e8a4..41f7c6e 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -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/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 378c67b..c4ec44e 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -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.
*
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();
+ }
+
}