summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp17
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java10
-rw-r--r--apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java118
-rw-r--r--api/current.txt10
-rw-r--r--api/system-current.txt7
-rw-r--r--api/test-current.txt2
-rw-r--r--cmds/statsd/src/StatsService.cpp7
-rw-r--r--cmds/statsd/src/StatsService.h7
-rw-r--r--core/java/android/app/LoadedApk.java3
-rw-r--r--core/java/android/app/StatsManager.java96
-rw-r--r--core/java/android/content/ContentResolver.java93
-rw-r--r--core/java/android/content/Context.java9
-rw-r--r--core/java/android/content/ContextWrapper.java3
-rw-r--r--core/java/android/content/IContentService.aidl2
-rw-r--r--core/java/android/content/res/AssetManager.java12
-rw-r--r--core/java/android/os/Binder.java17
-rwxr-xr-xcore/java/android/os/Build.java7
-rw-r--r--core/java/android/os/GraphicsEnvironment.java25
-rw-r--r--core/java/android/os/IPullAtomCallback.aidl31
-rw-r--r--core/java/android/os/IPullAtomResultReceiver.aidl32
-rw-r--r--core/java/android/os/IStatsCompanionService.aidl5
-rw-r--r--core/java/android/os/IStatsManager.aidl9
-rw-r--r--core/java/android/os/IStatsPullerCallback.aidl1
-rw-r--r--core/java/android/os/RemoteException.java16
-rw-r--r--core/java/android/provider/MediaStore.java31
-rw-r--r--core/java/android/provider/Settings.java1
-rw-r--r--core/java/android/service/euicc/EuiccService.java7
-rw-r--r--core/java/android/util/StatsEvent.aidl19
-rw-r--r--core/java/android/util/StatsEvent.java39
-rw-r--r--core/java/android/view/SurfaceControl.java26
-rw-r--r--core/java/com/android/internal/os/RuntimeInit.java2
-rw-r--r--core/jni/android_os_GraphicsEnvironment.cpp6
-rw-r--r--core/jni/android_util_AssetManager.cpp13
-rw-r--r--core/jni/android_view_SurfaceControl.cpp9
-rw-r--r--core/proto/android/server/activitymanagerservice.proto3
-rw-r--r--core/proto/android/server/windowmanagerservice.proto8
-rw-r--r--core/proto/android/stats/textclassifier/Android.bp23
-rw-r--r--core/res/res/values/config.xml3
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--core/tests/coretests/src/android/os/BuildTest.java2
-rw-r--r--data/etc/privapp-permissions-platform.xml4
-rw-r--r--data/etc/services.core.protolog.json72
-rw-r--r--data/sounds/AudioPackage11.mk2
-rw-r--r--keystore/java/android/security/KeyChain.java24
-rw-r--r--libs/androidfw/ApkAssets.cpp49
-rw-r--r--libs/androidfw/Idmap.cpp17
-rw-r--r--libs/androidfw/LoadedArsc.cpp41
-rw-r--r--libs/androidfw/include/androidfw/ApkAssets.h21
-rw-r--r--libs/androidfw/include/androidfw/AssetManager2.h9
-rw-r--r--libs/androidfw/include/androidfw/LoadedArsc.h57
-rw-r--r--libs/androidfw/tests/LoadedArsc_test.cpp9
-rw-r--r--libs/services/Android.bp1
-rw-r--r--libs/services/include/android/util/StatsEvent.h43
-rw-r--r--libs/services/src/util/StatsEvent.cpp58
-rw-r--r--location/java/android/location/LocationManager.java43
-rw-r--r--media/java/android/media/ExifInterface.java51
-rw-r--r--media/java/android/media/MediaScannerConnection.java2
-rw-r--r--media/java/android/media/tv/tuner/Tuner.java106
-rw-r--r--media/java/android/media/tv/tuner/TunerConstants.java8
-rw-r--r--media/jni/android_media_tv_Tuner.cpp244
-rw-r--r--media/jni/android_media_tv_Tuner.h20
-rw-r--r--media/lib/tvremote/java/com/android/media/tv/remoteprovider/TvRemoteProvider.java61
-rw-r--r--media/lib/tvremote/tests/Android.bp15
-rw-r--r--media/lib/tvremote/tests/AndroidManifest.xml27
-rw-r--r--media/lib/tvremote/tests/src/com/android/media/tv/remoteprovider/TvRemoteProviderTest.java86
-rw-r--r--mime/java-res/android.mime.types1
-rw-r--r--native/android/Android.bp5
-rw-r--r--native/android/choreographer.cpp225
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java20
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java3
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/TEST_MAPPING12
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java11
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/CarServiceProvider.java8
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarFacetButton.java6
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarFacetButtonController.java11
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/navigationbar/car/hvac/HvacController.java11
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/hvac/HvacControllerTest.java135
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java1
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/Dependency.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIApplication.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/DeviceConfigHelper.java (renamed from packages/SystemUI/src/com/android/systemui/assist/PhenotypeHelper.java)6
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java312
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java500
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt260
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java99
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java217
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt58
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java697
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java254
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt318
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java57
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java38
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeSensorManager.java21
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java8
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java16
-rw-r--r--services/core/Android.bp1
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java9
-rw-r--r--services/core/java/com/android/server/content/ContentService.java9
-rw-r--r--services/core/java/com/android/server/notification/NotificationHistoryDatabase.java119
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java15
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java4
-rw-r--r--services/core/java/com/android/server/policy/LegacyGlobalActions.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityMetricsLogger.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java217
-rw-r--r--services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityStack.java38
-rw-r--r--services/core/java/com/android/server/wm/ActivityStackSupervisor.java11
-rw-r--r--services/core/java/com/android/server/wm/AppTransitionController.java7
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java10
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimation.java4
-rw-r--r--services/core/java/com/android/server/wm/Task.java15
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotController.java23
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotLoader.java18
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotPersister.java12
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java14
-rw-r--r--services/core/java/com/android/server/wm/WallpaperWindowToken.java10
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java1
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java4
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java10
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java45
-rw-r--r--services/core/java/com/android/server/wm/WindowToken.java25
-rw-r--r--services/devicepolicy/Android.bp1
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java5
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java67
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java18
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java48
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java26
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java10
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java4
-rw-r--r--services/usb/java/com/android/server/usb/UsbHostManager.java4
-rw-r--r--services/usb/java/com/android/server/usb/UsbSerialReader.java2
-rw-r--r--services/usb/java/com/android/server/usb/UsbService.java9
-rw-r--r--services/usb/java/com/android/server/usb/UsbUserPermissionManager.java22
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java11
-rw-r--r--telephony/java/android/telephony/euicc/EuiccManager.java4
-rw-r--r--telephony/java/android/telephony/ims/ImsMmTelManager.java12
-rw-r--r--test-mock/Android.bp5
-rw-r--r--test-mock/src/android/test/mock/MockContentResolver.java48
-rw-r--r--tools/aapt2/Android.bp1
-rw-r--r--wifi/java/android/net/wifi/WifiConfiguration.java2
194 files changed, 4015 insertions, 2555 deletions
diff --git a/Android.bp b/Android.bp
index 6e3799b74cd6..5070b5ea2403 100644
--- a/Android.bp
+++ b/Android.bp
@@ -405,6 +405,8 @@ filegroup {
filegroup {
name: "statsd_aidl",
srcs: [
+ "core/java/android/os/IPullAtomCallback.aidl",
+ "core/java/android/os/IPullAtomResultReceiver.aidl",
"core/java/android/os/IStatsCompanionService.aidl",
"core/java/android/os/IStatsManager.aidl",
"core/java/android/os/IStatsPullerCallback.aidl",
@@ -425,6 +427,7 @@ java_library {
name: "framework-minus-apex",
defaults: ["framework-defaults"],
srcs: [":framework-non-updatable-sources"],
+ libs: ["app-compat-annotations"],
installable: true,
javac_shard_size: 150,
required: [
@@ -463,6 +466,15 @@ java_library {
defaults: ["framework-defaults"],
srcs: [":framework-all-sources"],
installable: false,
+ libs: ["app-compat-annotations"],
+}
+
+java_library {
+ name: "framework-annotation-proc",
+ defaults: ["framework-aidl-export-defaults"],
+ srcs: [":framework-all-sources"],
+ libs: ["app-compat-annotations"],
+ installable: false,
plugins: [
"unsupportedappusage-annotation-processor",
"compat-changeid-annotation-processor",
@@ -471,7 +483,7 @@ java_library {
platform_compat_config {
name: "framework-platform-compat-config",
- src: ":framework-all",
+ src: ":framework-annotation-proc",
}
// A library including just UnsupportedAppUsage.java classes.
@@ -502,6 +514,7 @@ java_library {
java_library {
name: "framework-atb-backward-compatibility",
installable: true,
+ libs: ["app-compat-annotations"],
srcs: [
"core/java/android/content/pm/AndroidTestBaseUpdater.java",
],
@@ -1595,7 +1608,7 @@ aidl_mapping {
genrule {
name: "framework-annotation-proc-index",
- srcs: [":framework-all"],
+ srcs: [":framework-annotation-proc"],
cmd: "unzip -qp $(in) unsupportedappusage/unsupportedappusage_index.csv > $(out)",
out: ["unsupportedappusage_index.csv"],
dist: {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 593e49490e94..0a4e020e07cd 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -57,6 +57,7 @@ import android.os.Binder;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -2688,13 +2689,14 @@ public class JobSchedulerService extends com.android.server.SystemService
}
@Override
- protected int handleShellCommand(@NonNull FileDescriptor in, @NonNull FileDescriptor out,
- @NonNull FileDescriptor err, @NonNull String[] args) {
+ protected int handleShellCommand(@NonNull ParcelFileDescriptor in,
+ @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,
+ @NonNull String[] args) {
return (new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
- this, in, out, err, args);
+ this, in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(),
+ args);
}
-
/**
* <b>For internal system user only!</b>
* Returns a list of all currently-executing jobs.
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
index d879273df3bc..5cb9834de130 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
@@ -76,6 +76,7 @@ import android.os.FileUtils;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
+import android.os.IPullAtomCallback;
import android.os.IStatsCompanionService;
import android.os.IStatsManager;
import android.os.IStoraged;
@@ -165,6 +166,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
@@ -272,6 +274,72 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
private final BroadcastReceiver mAppUpdateReceiver;
private final BroadcastReceiver mUserUpdateReceiver;
private final ShutdownEventReceiver mShutdownEventReceiver;
+
+ private static final class PullerKey {
+ private final int mUid;
+ private final int mAtomTag;
+
+ PullerKey(int uid, int atom) {
+ mUid = uid;
+ mAtomTag = atom;
+ }
+
+ public int getUid() {
+ return mUid;
+ }
+
+ public int getAtom() {
+ return mAtomTag;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mUid, mAtomTag);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof PullerKey) {
+ PullerKey other = (PullerKey) obj;
+ return this.mUid == other.getUid() && this.mAtomTag == other.getAtom();
+ }
+ return false;
+ }
+ }
+
+ private static final class PullerValue {
+ private final long mCoolDownNs;
+ private final long mTimeoutNs;
+ private int[] mAdditiveFields;
+ private IPullAtomCallback mCallback;
+
+ PullerValue(long coolDownNs, long timeoutNs, int[] additiveFields,
+ IPullAtomCallback callback) {
+ mCoolDownNs = coolDownNs;
+ mTimeoutNs = timeoutNs;
+ mAdditiveFields = additiveFields;
+ mCallback = callback;
+ }
+
+ public long getCoolDownNs() {
+ return mCoolDownNs;
+ }
+
+ public long getTimeoutNs() {
+ return mTimeoutNs;
+ }
+
+ public int[] getAdditiveFields() {
+ return mAdditiveFields;
+ }
+
+ public IPullAtomCallback getCallback() {
+ return mCallback;
+ }
+ }
+
+ private final HashMap<PullerKey, PullerValue> mPullers = new HashMap<>();
+
private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
private IWifiManager mWifiManager = null;
@@ -323,7 +391,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
@Override
public void onReceive(Context context, Intent intent) {
synchronized (sStatsdLock) {
- sStatsd = fetchStatsdService();
if (sStatsd == null) {
Slog.w(TAG, "Could not access statsd for UserUpdateReceiver");
return;
@@ -1735,7 +1802,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
e.writeString(Build.BRAND);
e.writeString(Build.PRODUCT);
e.writeString(Build.DEVICE);
- e.writeString(Build.VERSION.RELEASE);
+ e.writeString(Build.VERSION.RELEASE_OR_CODENAME);
e.writeString(Build.ID);
e.writeString(Build.VERSION.INCREMENTAL);
e.writeString(Build.TYPE);
@@ -2553,10 +2620,40 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
mContext.enforceCallingPermission(android.Manifest.permission.STATSCOMPANION, null);
}
+ @Override
+ public void registerPullAtomCallback(int atomTag, long coolDownNs, long timeoutNs,
+ int[] additiveFields, IPullAtomCallback pullerCallback) {
+ synchronized (sStatsdLock) {
+ // Always cache the puller in SCS.
+ // If statsd is down, we will register it when it comes back up.
+ int callingUid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+ PullerKey key = new PullerKey(callingUid, atomTag);
+ PullerValue val = new PullerValue(
+ coolDownNs, timeoutNs, additiveFields, pullerCallback);
+ mPullers.put(key, val);
+
+ if (sStatsd == null) {
+ Slog.w(TAG, "Could not access statsd for registering puller for atom " + atomTag);
+ return;
+ }
+ try {
+ sStatsd.registerPullAtomCallback(
+ callingUid, atomTag, coolDownNs, timeoutNs, additiveFields, pullerCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to access statsd to register puller for atom " + atomTag);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
// Lifecycle and related code
/**
- * Fetches the statsd IBinder service
+ * Fetches the statsd IBinder service.
+ * Note: This should only be called from sayHiToStatsd. All other clients should use the cached
+ * sStatsd with a null check.
*/
private static IStatsManager fetchStatsdService() {
return IStatsManager.Stub.asInterface(ServiceManager.getService("stats"));
@@ -2654,6 +2751,8 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
// Pull the latest state of UID->app name, version mapping when
// statsd starts.
informAllUidsLocked(mContext);
+ // Register all pullers. If SCS has just started, this should be empty.
+ registerAllPullersLocked();
} finally {
restoreCallingIdentity(token);
}
@@ -2665,10 +2764,21 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
}
}
+ @GuardedBy("sStatsdLock")
+ private void registerAllPullersLocked() throws RemoteException {
+ // TODO: pass in one call, using a file descriptor (similar to uidmap).
+ for (Map.Entry<PullerKey, PullerValue> entry : mPullers.entrySet()) {
+ PullerKey key = entry.getKey();
+ PullerValue val = entry.getValue();
+ sStatsd.registerPullAtomCallback(key.getUid(), key.getAtom(), val.getCoolDownNs(),
+ val.getTimeoutNs(), val.getAdditiveFields(), val.getCallback());
+ }
+ }
+
private class StatsdDeathRecipient implements IBinder.DeathRecipient {
@Override
public void binderDied() {
- Slog.i(TAG, "Statsd is dead - erase all my knowledge.");
+ Slog.i(TAG, "Statsd is dead - erase all my knowledge, except pullers");
synchronized (sStatsdLock) {
long now = SystemClock.elapsedRealtime();
for (Long timeMillis : mDeathTimeMillis) {
diff --git a/api/current.txt b/api/current.txt
index 424f2e2a9c76..1333e4a6c475 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9650,8 +9650,9 @@ package android.content {
method public static boolean isSyncPending(android.accounts.Account, String);
method @NonNull public android.graphics.Bitmap loadThumbnail(@NonNull android.net.Uri, @NonNull android.util.Size, @Nullable android.os.CancellationSignal) throws java.io.IOException;
method public void notifyChange(@NonNull android.net.Uri, @Nullable android.database.ContentObserver);
- method public void notifyChange(@NonNull android.net.Uri, @Nullable android.database.ContentObserver, boolean);
+ method @Deprecated public void notifyChange(@NonNull android.net.Uri, @Nullable android.database.ContentObserver, boolean);
method public void notifyChange(@NonNull android.net.Uri, @Nullable android.database.ContentObserver, int);
+ method public void notifyChange(@NonNull Iterable<android.net.Uri>, @Nullable android.database.ContentObserver, int);
method @Nullable public final android.content.res.AssetFileDescriptor openAssetFile(@NonNull android.net.Uri, @NonNull String, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException;
method @Nullable public final android.content.res.AssetFileDescriptor openAssetFileDescriptor(@NonNull android.net.Uri, @NonNull String) throws java.io.FileNotFoundException;
method @Nullable public final android.content.res.AssetFileDescriptor openAssetFileDescriptor(@NonNull android.net.Uri, @NonNull String, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException;
@@ -24153,6 +24154,9 @@ package android.media {
field public static final String TAG_MODEL = "Model";
field public static final String TAG_NEW_SUBFILE_TYPE = "NewSubfileType";
field public static final String TAG_OECF = "OECF";
+ field public static final String TAG_OFFSET_TIME = "OffsetTime";
+ field public static final String TAG_OFFSET_TIME_DIGITIZED = "OffsetTimeDigitized";
+ field public static final String TAG_OFFSET_TIME_ORIGINAL = "OffsetTimeOriginal";
field public static final String TAG_ORF_ASPECT_FRAME = "AspectFrame";
field public static final String TAG_ORF_PREVIEW_IMAGE_LENGTH = "PreviewImageLength";
field public static final String TAG_ORF_PREVIEW_IMAGE_START = "PreviewImageStart";
@@ -34389,6 +34393,7 @@ package android.os {
field public static final String INCREMENTAL;
field public static final int PREVIEW_SDK_INT;
field public static final String RELEASE;
+ field @NonNull public static final String RELEASE_OR_CODENAME;
field @Deprecated public static final String SDK;
field public static final int SDK_INT;
field public static final String SECURITY_PATCH;
@@ -35355,6 +35360,8 @@ package android.os {
public class RemoteException extends android.util.AndroidException {
ctor public RemoteException();
ctor public RemoteException(String);
+ method @NonNull public RuntimeException rethrowAsRuntimeException();
+ method @NonNull public RuntimeException rethrowFromSystemServer();
}
public class ResultReceiver implements android.os.Parcelable {
@@ -45224,6 +45231,7 @@ package android.telephony {
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.os.ParcelUuid createSubscriptionGroup(@NonNull java.util.List<java.lang.Integer>);
method @Deprecated public static android.telephony.SubscriptionManager from(android.content.Context);
method public java.util.List<android.telephony.SubscriptionInfo> getAccessibleSubscriptionInfoList();
+ method public static int getActiveDataSubscriptionId();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.SubscriptionInfo getActiveSubscriptionInfo(int);
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getActiveSubscriptionInfoCount();
method public int getActiveSubscriptionInfoCountMax();
diff --git a/api/system-current.txt b/api/system-current.txt
index 4df8e8c1da91..9420a286d67a 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1414,6 +1414,7 @@ package android.content {
method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public android.content.Intent registerReceiverForAllUsers(@Nullable android.content.BroadcastReceiver, @NonNull android.content.IntentFilter, @Nullable String, @Nullable android.os.Handler);
method public abstract void sendBroadcast(android.content.Intent, @Nullable String, @Nullable android.os.Bundle);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public abstract void sendBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle, @Nullable String, @Nullable android.os.Bundle);
+ method public void sendBroadcastMultiplePermissions(@NonNull android.content.Intent, @NonNull String[]);
method public abstract void sendOrderedBroadcast(@NonNull android.content.Intent, @Nullable String, @Nullable android.os.Bundle, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public void startActivityAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.os.UserHandle);
field public static final String APP_PREDICTION_SERVICE = "app_prediction";
@@ -6820,7 +6821,7 @@ package android.service.euicc {
method public android.service.euicc.DownloadSubscriptionResult onDownloadSubscription(int, @NonNull android.telephony.euicc.DownloadableSubscription, boolean, boolean, @Nullable android.os.Bundle);
method @Deprecated public int onDownloadSubscription(int, @NonNull android.telephony.euicc.DownloadableSubscription, boolean, boolean);
method @Deprecated public abstract int onEraseSubscriptions(int);
- method public int onEraseSubscriptionsWithOptions(int, @android.telephony.euicc.EuiccCardManager.ResetOption int);
+ method public int onEraseSubscriptions(int, @android.telephony.euicc.EuiccCardManager.ResetOption int);
method public abstract android.service.euicc.GetDefaultDownloadableSubscriptionListResult onGetDefaultDownloadableSubscriptionList(int, boolean);
method public abstract android.service.euicc.GetDownloadableSubscriptionMetadataResult onGetDownloadableSubscriptionMetadata(int, android.telephony.euicc.DownloadableSubscription, boolean);
method public abstract String onGetEid(int);
@@ -8951,7 +8952,7 @@ package android.telephony.euicc {
public class EuiccManager {
method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void continueOperation(android.content.Intent, android.os.Bundle);
method @Deprecated @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void eraseSubscriptions(@NonNull android.app.PendingIntent);
- method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void eraseSubscriptionsWithOptions(@android.telephony.euicc.EuiccCardManager.ResetOption int, @NonNull android.app.PendingIntent);
+ method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void eraseSubscriptions(@android.telephony.euicc.EuiccCardManager.ResetOption int, @NonNull android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void getDefaultDownloadableSubscriptionList(android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void getDownloadableSubscriptionMetadata(android.telephony.euicc.DownloadableSubscription, android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public int getOtaStatus();
@@ -9227,7 +9228,7 @@ package android.telephony.ims {
public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
method @NonNull public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getFeatureState(@NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull java.util.concurrent.Executor) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getVoWiFiModeSetting();
diff --git a/api/test-current.txt b/api/test-current.txt
index b8806720d8aa..44f736ca918a 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3359,7 +3359,7 @@ package android.telephony.ims {
public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
method @NonNull public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int);
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getFeatureState(@NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull java.util.concurrent.Executor) throws android.telephony.ims.ImsException;
+ method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws android.telephony.ims.ImsException;
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public int getVoWiFiModeSetting();
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index b665a8b99fbd..f072c9c7b121 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -1289,6 +1289,13 @@ Status StatsService::registerPullerCallback(int32_t atomTag,
return Status::ok();
}
+Status StatsService::registerPullAtomCallback(int32_t uid, int32_t atomTag, int64_t coolDownNs,
+ int64_t timeoutNs, const std::vector<int32_t>& additiveFields,
+ const sp<android::os::IPullAtomCallback>& pullerCallback) {
+ VLOG("StatsService::registerPuller called.");
+ return Status::ok();
+}
+
Status StatsService::unregisterPullerCallback(int32_t atomTag, const String16& packageName) {
ENFORCE_DUMP_AND_USAGE_STATS(packageName);
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 949094871936..6d40007826e7 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -180,6 +180,13 @@ public:
const String16& packageName) override;
/**
+ * Binder call to register a callback function for a pulled atom.
+ */
+ virtual Status registerPullAtomCallback(int32_t uid, int32_t atomTag, int64_t coolDownNs,
+ int64_t timeoutNs, const std::vector<int32_t>& additiveFields,
+ const sp<android::os::IPullAtomCallback>& pullerCallback) override;
+
+ /**
* Binder call to unregister any existing callback function for a vendor pulled atom.
*/
virtual Status unregisterPullerCallback(int32_t atomTag, const String16& packageName) override;
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index f0b354650cf4..e858e6a976bc 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -1215,7 +1215,8 @@ public final class LoadedApk {
}
// Rewrite the R 'constants' for all library apks.
- SparseArray<String> packageIdentifiers = getAssets().getAssignedPackageIdentifiers();
+ SparseArray<String> packageIdentifiers = getAssets().getAssignedPackageIdentifiers(
+ false, false);
final int N = packageIdentifiers.size();
for (int i = 0; i < N; i++) {
final int id = packageIdentifiers.keyAt(i);
diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java
index e6682d620b99..92bfee24d402 100644
--- a/core/java/android/app/StatsManager.java
+++ b/core/java/android/app/StatsManager.java
@@ -24,12 +24,22 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
import android.os.IBinder;
+import android.os.IPullAtomCallback;
+import android.os.IPullAtomResultReceiver;
+import android.os.IStatsCompanionService;
import android.os.IStatsManager;
import android.os.IStatsPullerCallback;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.AndroidException;
import android.util.Slog;
+import android.util.StatsEvent;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
/**
* API for statsd clients to send configurations and retrieve data.
@@ -43,8 +53,12 @@ public final class StatsManager {
private final Context mContext;
+ @GuardedBy("this")
private IStatsManager mService;
+ @GuardedBy("this")
+ private IStatsCompanionService mStatsCompanion;
+
/**
* Long extra of uid that added the relevant stats config.
*/
@@ -449,7 +463,9 @@ public final class StatsManager {
* @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service
*
* @hide
+ * @deprecated Please use registerPullAtomCallback
*/
+ @Deprecated
@RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
public void setPullerCallback(int atomTag, IStatsPullerCallback callback)
throws StatsUnavailableException {
@@ -472,6 +488,75 @@ public final class StatsManager {
}
}
+
+ /**
+ * Registers a callback for an atom when that atom is to be pulled. The stats service will
+ * invoke pullData in the callback when the stats service determines that this atom needs to be
+ * pulled.
+ *
+ * @param atomTag The tag of the atom for this puller callback.
+ * @param coolDownNs The minimum time between successive pulls. A cache of the previous
+ * pull will be used if the time between pulls is less than coolDownNs.
+ * @param timeoutNs The maximum time a pull should take. Statsd will wait timeoutNs for
+ * the pull to complete before timing out and marking the pull as
+ * failed.
+ * @param additiveFields Fields that are added when mapping isolated uids to host uids.
+ * @param callback The callback to be invoked when the stats service pulls the atom.
+ * @throws RemoteException if unsuccessful due to failing to connect to system server.
+ *
+ * @hide
+ */
+ public void registerPullAtomCallback(int atomTag, long coolDownNs, long timeoutNs,
+ int[] additiveFields, @NonNull StatsPullAtomCallback callback,
+ @NonNull Executor executor) throws RemoteException, SecurityException {
+ synchronized (this) {
+ IStatsCompanionService service = getIStatsCompanionServiceLocked();
+ PullAtomCallbackInternal rec =
+ new PullAtomCallbackInternal(atomTag, callback, executor);
+ service.registerPullAtomCallback(atomTag, coolDownNs, timeoutNs, additiveFields, rec);
+ }
+ }
+
+ private static class PullAtomCallbackInternal extends IPullAtomCallback.Stub {
+ public final int mAtomId;
+ public final StatsPullAtomCallback mCallback;
+ public final Executor mExecutor;
+
+ PullAtomCallbackInternal(int atomId, StatsPullAtomCallback callback, Executor executor) {
+ mAtomId = atomId;
+ mCallback = callback;
+ mExecutor = executor;
+ }
+
+ @Override
+ public void onPullAtom(int atomTag, IPullAtomResultReceiver resultReceiver) {
+ mExecutor.execute(() -> {
+ List<StatsEvent> data = new ArrayList<>();
+ boolean success = mCallback.onPullAtom(atomTag, data);
+ StatsEvent[] arr = new StatsEvent[data.size()];
+ arr = data.toArray(arr);
+ try {
+ resultReceiver.pullFinished(atomTag, success, arr);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "StatsPullResultReceiver failed for tag " + mAtomId);
+ }
+ });
+ }
+ }
+
+ /**
+ * Callback interface for pulling atoms requested by the stats service.
+ *
+ * @hide
+ */
+ public interface StatsPullAtomCallback {
+ /**
+ * Pull data for the specified atom tag, filling in the provided list of StatsEvent data.
+ * @return if the pull was successful
+ */
+ boolean onPullAtom(int atomTag, List<StatsEvent> data);
+ }
+
private class StatsdDeathRecipient implements IBinder.DeathRecipient {
@Override
public void binderDied() {
@@ -481,6 +566,7 @@ public final class StatsManager {
}
}
+ @GuardedBy("this")
private IStatsManager getIStatsManagerLocked() throws StatsUnavailableException {
if (mService != null) {
return mService;
@@ -497,6 +583,16 @@ public final class StatsManager {
return mService;
}
+ @GuardedBy("this")
+ private IStatsCompanionService getIStatsCompanionServiceLocked() {
+ if (mStatsCompanion != null) {
+ return mStatsCompanion;
+ }
+ mStatsCompanion = IStatsCompanionService.Stub.asInterface(
+ ServiceManager.getService("statscompanion"));
+ return mStatsCompanion;
+ }
+
/**
* Exception thrown when communication with the stats service fails (eg if it is not available).
* This might be thrown early during boot before the stats service has started or if it crashed.
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 2657cc54b91a..61c8db5db124 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -65,6 +65,7 @@ import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
import android.util.Size;
+import android.util.SparseArray;
import com.android.internal.util.MimeIconUtils;
import com.android.internal.util.Preconditions;
@@ -2381,15 +2382,15 @@ public abstract class ContentResolver implements ContentInterface {
* true.
* @param syncToNetwork If true, same as {@link #NOTIFY_SYNC_TO_NETWORK}.
* @see #requestSync(android.accounts.Account, String, android.os.Bundle)
+ * @deprecated callers should consider migrating to
+ * {@link #notifyChange(Uri, ContentObserver, int)}, as it
+ * offers support for many more options than just
+ * {@link #NOTIFY_SYNC_TO_NETWORK}.
*/
+ @Deprecated
public void notifyChange(@NonNull Uri uri, @Nullable ContentObserver observer,
boolean syncToNetwork) {
- Preconditions.checkNotNull(uri, "uri");
- notifyChange(
- ContentProvider.getUriWithoutUserId(uri),
- observer,
- syncToNetwork,
- ContentProvider.getUserIdFromUri(uri, mContext.getUserId()));
+ notifyChange(uri, observer, syncToNetwork ? NOTIFY_SYNC_TO_NETWORK : 0);
}
/**
@@ -2398,10 +2399,10 @@ public abstract class ContentResolver implements ContentInterface {
* To observe events sent through this call, use
* {@link #registerContentObserver(Uri, boolean, ContentObserver)}.
* <p>
- * If syncToNetwork is true, this will attempt to schedule a local sync
- * using the sync adapter that's registered for the authority of the
- * provided uri. No account will be passed to the sync adapter, so all
- * matching accounts will be synchronized.
+ * If {@link #NOTIFY_SYNC_TO_NETWORK} is set, this will attempt to schedule
+ * a local sync using the sync adapter that's registered for the authority
+ * of the provided uri. No account will be passed to the sync adapter, so
+ * all matching accounts will be synchronized.
* <p>
* Starting in {@link android.os.Build.VERSION_CODES#O}, all content
* notifications must be backed by a valid {@link ContentProvider}.
@@ -2427,21 +2428,71 @@ public abstract class ContentResolver implements ContentInterface {
}
/**
+ * Notify registered observers that several rows have been updated.
+ * <p>
+ * To observe events sent through this call, use
+ * {@link #registerContentObserver(Uri, boolean, ContentObserver)}.
+ * <p>
+ * If {@link #NOTIFY_SYNC_TO_NETWORK} is set, this will attempt to schedule
+ * a local sync using the sync adapter that's registered for the authority
+ * of the provided uri. No account will be passed to the sync adapter, so
+ * all matching accounts will be synchronized.
+ * <p>
+ * Starting in {@link android.os.Build.VERSION_CODES#O}, all content
+ * notifications must be backed by a valid {@link ContentProvider}.
+ *
+ * @param uris The uris of the content that was changed.
+ * @param observer The observer that originated the change, may be
+ * <code>null</null>. The observer that originated the change
+ * will only receive the notification if it has requested to
+ * receive self-change notifications by implementing
+ * {@link ContentObserver#deliverSelfNotifications()} to return
+ * true.
+ * @param flags Flags such as {@link #NOTIFY_SYNC_TO_NETWORK} or
+ * {@link #NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS}.
+ */
+ public void notifyChange(@NonNull Iterable<Uri> uris, @Nullable ContentObserver observer,
+ @NotifyFlags int flags) {
+ Preconditions.checkNotNull(uris, "uris");
+
+ // Cluster based on user ID
+ final SparseArray<ArrayList<Uri>> clusteredByUser = new SparseArray<>();
+ for (Uri uri : uris) {
+ final int userId = ContentProvider.getUserIdFromUri(uri, mContext.getUserId());
+ ArrayList<Uri> list = clusteredByUser.get(userId);
+ if (list == null) {
+ list = new ArrayList<>();
+ clusteredByUser.put(userId, list);
+ }
+ list.add(ContentProvider.getUriWithoutUserId(uri));
+ }
+
+ for (int i = 0; i < clusteredByUser.size(); i++) {
+ final int userId = clusteredByUser.keyAt(i);
+ final ArrayList<Uri> list = clusteredByUser.valueAt(i);
+ notifyChange(list.toArray(new Uri[list.size()]), observer, flags, userId);
+ }
+ }
+
+ /**
* Notify registered observers within the designated user(s) that a row was updated.
*
+ * @deprecated callers should consider migrating to
+ * {@link #notifyChange(Uri, ContentObserver, int)}, as it
+ * offers support for many more options than just
+ * {@link #NOTIFY_SYNC_TO_NETWORK}.
* @hide
*/
+ @Deprecated
public void notifyChange(@NonNull Uri uri, ContentObserver observer, boolean syncToNetwork,
@UserIdInt int userHandle) {
- try {
- getContentService().notifyChange(
- uri, observer == null ? null : observer.getContentObserver(),
- observer != null && observer.deliverSelfNotifications(),
- syncToNetwork ? NOTIFY_SYNC_TO_NETWORK : 0,
- userHandle, mTargetSdkVersion, mContext.getPackageName());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ notifyChange(uri, observer, syncToNetwork ? NOTIFY_SYNC_TO_NETWORK : 0, userHandle);
+ }
+
+ /** {@hide} */
+ public void notifyChange(@NonNull Uri uri, ContentObserver observer, @NotifyFlags int flags,
+ @UserIdInt int userHandle) {
+ notifyChange(new Uri[] { uri }, observer, flags, userHandle);
}
/**
@@ -2449,11 +2500,11 @@ public abstract class ContentResolver implements ContentInterface {
*
* @hide
*/
- public void notifyChange(@NonNull Uri uri, ContentObserver observer, @NotifyFlags int flags,
+ public void notifyChange(@NonNull Uri[] uris, ContentObserver observer, @NotifyFlags int flags,
@UserIdInt int userHandle) {
try {
getContentService().notifyChange(
- uri, observer == null ? null : observer.getContentObserver(),
+ uris, observer == null ? null : observer.getContentObserver(),
observer != null && observer.deliverSelfNotifications(), flags,
userHandle, mTargetSdkVersion, mContext.getPackageName());
} catch (RemoteException e) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 41b773e81c46..03b49136bde4 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2072,7 +2072,7 @@ public abstract class Context {
* Intent will receive the broadcast.
* @param receiverPermissions Array of names of permissions that a receiver must hold
* in order to receive your broadcast.
- * If null or empty, no permissions are required.
+ * If empty, no permissions are required.
*
* @see android.content.BroadcastReceiver
* @see #registerReceiver
@@ -2081,8 +2081,11 @@ public abstract class Context {
* @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
* @hide
*/
- public abstract void sendBroadcastMultiplePermissions(Intent intent,
- String[] receiverPermissions);
+ @SystemApi
+ public void sendBroadcastMultiplePermissions(@NonNull Intent intent,
+ @NonNull String[] receiverPermissions) {
+ throw new RuntimeException("Not implemented. Must override in a subclass.");
+ }
/**
* Broadcast the given intent to all interested BroadcastReceivers, allowing
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 5bdea52d63e1..b04f7810ed45 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -464,7 +464,8 @@ public class ContextWrapper extends Context {
/** @hide */
@Override
- public void sendBroadcastMultiplePermissions(Intent intent, String[] receiverPermissions) {
+ public void sendBroadcastMultiplePermissions(@NonNull Intent intent,
+ @NonNull String[] receiverPermissions) {
mBase.sendBroadcastMultiplePermissions(intent, receiverPermissions);
}
diff --git a/core/java/android/content/IContentService.aidl b/core/java/android/content/IContentService.aidl
index a34a9951671c..03c99e1a2344 100644
--- a/core/java/android/content/IContentService.aidl
+++ b/core/java/android/content/IContentService.aidl
@@ -51,7 +51,7 @@ interface IContentService {
* hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL
* USER_CURRENT are properly interpreted.
*/
- void notifyChange(in Uri uri, IContentObserver observer,
+ void notifyChange(in Uri[] uris, IContentObserver observer,
boolean observerWantsSelfNotifications, int flags,
int userHandle, int targetSdkVersion, String callingPackage);
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 23e772075ad6..070e282a0eb2 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -1491,9 +1491,17 @@ public final class AssetManager implements AutoCloseable {
*/
@UnsupportedAppUsage
public SparseArray<String> getAssignedPackageIdentifiers() {
+ return getAssignedPackageIdentifiers(true, true);
+ }
+
+ /**
+ * @hide
+ */
+ public SparseArray<String> getAssignedPackageIdentifiers(boolean includeOverlays,
+ boolean includeLoaders) {
synchronized (this) {
ensureValidLocked();
- return nativeGetAssignedPackageIdentifiers(mObject);
+ return nativeGetAssignedPackageIdentifiers(mObject, includeOverlays, includeLoaders);
}
}
@@ -1557,7 +1565,7 @@ public final class AssetManager implements AutoCloseable {
int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout,
int uiMode, int colorMode, int majorVersion);
private static native @NonNull SparseArray<String> nativeGetAssignedPackageIdentifiers(
- long ptr);
+ long ptr, boolean includeOverlays, boolean includeLoaders);
// File native methods.
private static native @Nullable String[] nativeList(long ptr, @NonNull String path)
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index a856975e2501..ec3919997603 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -932,8 +932,14 @@ public class Binder implements IBinder {
}
int result = -1;
- try {
- result = handleShellCommand(in, out, err, args);
+ try (ParcelFileDescriptor inPfd = ParcelFileDescriptor.dup(in);
+ ParcelFileDescriptor outPfd = ParcelFileDescriptor.dup(out);
+ ParcelFileDescriptor errPfd = ParcelFileDescriptor.dup(err)) {
+ result = handleShellCommand(inPfd, outPfd, errPfd, args);
+ } catch (IOException e) {
+ PrintWriter pw = new FastPrintWriter(new FileOutputStream(err));
+ pw.println("dup() failed: " + e.getMessage());
+ pw.flush();
} finally {
resultReceiver.send(result, null);
}
@@ -954,9 +960,10 @@ public class Binder implements IBinder {
* @hide
*/
// @SystemApi TODO Make it a system API.
- protected int handleShellCommand(@NonNull FileDescriptor in, @NonNull FileDescriptor out,
- @NonNull FileDescriptor err, @NonNull String[] args) {
- FileOutputStream ferr = new FileOutputStream(err);
+ protected int handleShellCommand(@NonNull ParcelFileDescriptor in,
+ @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,
+ @NonNull String[] args) {
+ FileOutputStream ferr = new FileOutputStream(err.getFileDescriptor());
PrintWriter pw = new FastPrintWriter(ferr);
pw.println("No shell command implementation.");
pw.flush();
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 400d98159a7b..733079691a15 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -240,6 +240,13 @@ public class Build {
public static final String RELEASE = getString("ro.build.version.release");
/**
+ * The version string we show to the user; may be {@link #RELEASE} or
+ * {@link #CODENAME} if not a final release build.
+ */
+ @NonNull public static final String RELEASE_OR_CODENAME = getString(
+ "ro.build.version.release_or_codename");
+
+ /**
* The base OS build the product is based on.
*/
public static final String BASE_OS = SystemProperties.get("ro.build.version.base_os", "");
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 947b0a1efebd..034e6a7a06c4 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -177,13 +177,6 @@ public class GraphicsEnvironment {
}
/**
- * Check whether application is debuggable
- */
- private static boolean isDebuggable(Context context) {
- return (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) > 0;
- }
-
- /**
* Check whether application is has set the manifest metadata for layer injection.
*/
private static boolean canInjectLayers(ApplicationInfo ai) {
@@ -246,7 +239,7 @@ public class GraphicsEnvironment {
// 2. ENABLE_GPU_DEBUG_LAYERS is true
// 3. Package name is equal to GPU_DEBUG_APP
- if (isDebuggable(context) || (getCanLoadSystemLibraries() == 1) || canInjectLayers(ai)) {
+ if (isDebuggable() || canInjectLayers(ai)) {
final int enable = coreSettings.getInt(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, 0);
@@ -441,9 +434,7 @@ public class GraphicsEnvironment {
* Check for ANGLE debug package, but only for apps that can load them (dumpable)
*/
private String getAngleDebugPackage(Context context, Bundle coreSettings) {
- final boolean appIsDebuggable = isDebuggable(context);
- final boolean deviceIsDebuggable = getCanLoadSystemLibraries() == 1;
- if (appIsDebuggable || deviceIsDebuggable) {
+ if (isDebuggable()) {
String debugPackage;
if (coreSettings != null) {
@@ -478,12 +469,8 @@ public class GraphicsEnvironment {
* - devices that are running a userdebug build (ro.debuggable) or can inject libraries for
* debugging (PR_SET_DUMPABLE).
*/
- final boolean appIsDebuggable = isDebuggable(context);
- final boolean deviceIsDebuggable = getCanLoadSystemLibraries() == 1;
- if (!(appIsDebuggable || deviceIsDebuggable)) {
- Log.v(TAG, "Skipping loading temporary rules file: "
- + "appIsDebuggable = " + appIsDebuggable + ", "
- + "adbRootEnabled = " + deviceIsDebuggable);
+ if (!isDebuggable()) {
+ Log.v(TAG, "Skipping loading temporary rules file");
return false;
}
@@ -742,7 +729,7 @@ public class GraphicsEnvironment {
final boolean enablePrereleaseDriver =
(ai.metaData != null && ai.metaData.getBoolean(METADATA_DEVELOPER_DRIVER_ENABLE))
- || getCanLoadSystemLibraries() == 1;
+ || isDebuggable();
// Priority for Game Driver settings global on confliction (Higher priority comes first):
// 1. GAME_DRIVER_ALL_APPS
@@ -918,7 +905,7 @@ public class GraphicsEnvironment {
return "";
}
- private static native int getCanLoadSystemLibraries();
+ private static native boolean isDebuggable();
private static native void setLayerPaths(ClassLoader classLoader, String layerPaths);
private static native void setDebugLayers(String layers);
private static native void setDebugLayersGLES(String layers);
diff --git a/core/java/android/os/IPullAtomCallback.aidl b/core/java/android/os/IPullAtomCallback.aidl
new file mode 100644
index 000000000000..88d3c3e46ff5
--- /dev/null
+++ b/core/java/android/os/IPullAtomCallback.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.os.IPullAtomResultReceiver;
+
+/**
+ * Binder interface to pull atoms for the stats service.
+ * {@hide}
+ */
+interface IPullAtomCallback {
+ /**
+ * Initiate a request for a pull for an atom.
+ */
+ void onPullAtom(int atomTag, IPullAtomResultReceiver resultReceiver);
+
+}
diff --git a/core/java/android/os/IPullAtomResultReceiver.aidl b/core/java/android/os/IPullAtomResultReceiver.aidl
new file mode 100644
index 000000000000..bfb35ff0c9d1
--- /dev/null
+++ b/core/java/android/os/IPullAtomResultReceiver.aidl
@@ -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 android.os;
+
+import android.util.StatsEvent;
+
+/**
+ * Binder interface to pull atoms for the stats service.
+ * {@hide}
+ */
+interface IPullAtomResultReceiver {
+
+ /**
+ * Indicate that a pull request for an atom is complete.
+ */
+ oneway void pullFinished(int atomTag, boolean success, in StatsEvent[] output);
+
+}
diff --git a/core/java/android/os/IStatsCompanionService.aidl b/core/java/android/os/IStatsCompanionService.aidl
index 0751b964f85e..22a25374e064 100644
--- a/core/java/android/os/IStatsCompanionService.aidl
+++ b/core/java/android/os/IStatsCompanionService.aidl
@@ -16,6 +16,7 @@
package android.os;
+import android.os.IPullAtomCallback;
import android.os.StatsDimensionsValue;
import android.os.StatsLogEventWrapper;
@@ -85,4 +86,8 @@ interface IStatsCompanionService {
/** Tells StatsCompaionService to grab the uid map snapshot and send it to statsd. */
oneway void triggerUidSnapshot();
+
+ /** Tells StatsCompanionService to tell statsd to register a puller for the given atom id */
+ oneway void registerPullAtomCallback(int atomTag, long coolDownNs, long timeoutNs,
+ in int[] additiveFields, IPullAtomCallback pullerCallback);
}
diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl
index e3f9326048d1..29871b6cf017 100644
--- a/core/java/android/os/IStatsManager.aidl
+++ b/core/java/android/os/IStatsManager.aidl
@@ -17,6 +17,7 @@
package android.os;
import android.os.IStatsPullerCallback;
+import android.os.IPullAtomCallback;
import android.os.ParcelFileDescriptor;
/**
@@ -188,11 +189,19 @@ interface IStatsManager {
* for the specified vendor atom tag.
*
* Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS
+ * @deprecated please use registerPullAtomCallback.
*/
oneway void registerPullerCallback(int atomTag, IStatsPullerCallback pullerCallback,
String packageName);
/**
+ * Registers a puller callback function that, when invoked, pulls the data
+ * for the specified atom tag.
+ */
+ oneway void registerPullAtomCallback(int uid, int atomTag, long coolDownNs, long timeoutNs,
+ in int[] additiveFields, IPullAtomCallback pullerCallback);
+
+ /**
* Unregisters a puller callback function for the given vendor atom.
*
* Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS
diff --git a/core/java/android/os/IStatsPullerCallback.aidl b/core/java/android/os/IStatsPullerCallback.aidl
index 1684aeb0d666..c3e1e55dde06 100644
--- a/core/java/android/os/IStatsPullerCallback.aidl
+++ b/core/java/android/os/IStatsPullerCallback.aidl
@@ -19,6 +19,7 @@ package android.os;
import android.os.StatsLogEventWrapper;
/**
+ * DEPRECATED
* Binder interface to pull atoms for the stats service.
* {@hide}
*/
diff --git a/core/java/android/os/RemoteException.java b/core/java/android/os/RemoteException.java
index 2e673a857a93..10ef27952df4 100644
--- a/core/java/android/os/RemoteException.java
+++ b/core/java/android/os/RemoteException.java
@@ -16,7 +16,7 @@
package android.os;
-import android.annotation.UnsupportedAppUsage;
+import android.annotation.NonNull;
import android.util.AndroidException;
/**
@@ -37,7 +37,15 @@ public class RemoteException extends AndroidException {
super(message, cause, enableSuppression, writableStackTrace);
}
- /** {@hide} */
+ /**
+ * Rethrow this as an unchecked runtime exception.
+ * <p>
+ * Apps making calls into other processes may end up persisting internal
+ * state or making security decisions based on the perceived success or
+ * failure of a call, or any default values returned. For this reason, we
+ * want to strongly throw when there was trouble with the transaction.
+ */
+ @NonNull
public RuntimeException rethrowAsRuntimeException() {
throw new RuntimeException(this);
}
@@ -52,10 +60,8 @@ public class RemoteException extends AndroidException {
* state or making security decisions based on the perceived success or
* failure of a call, or any default values returned. For this reason, we
* want to strongly throw when there was trouble with the transaction.
- *
- * @hide
*/
- @UnsupportedAppUsage
+ @NonNull
public RuntimeException rethrowFromSystemServer() {
if (this instanceof DeadObjectException) {
throw new RuntimeException(new DeadSystemException());
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index aa67d9779da4..01f9c7300fa1 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -104,7 +104,14 @@ public final class MediaStore {
/** The authority for the media provider */
public static final String AUTHORITY = "media";
/** A content:// style uri to the authority for the media provider */
- public static final @NonNull Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
+ public static final @NonNull Uri AUTHORITY_URI =
+ Uri.parse("content://" + AUTHORITY);
+
+ /** @hide */
+ public static final String AUTHORITY_LEGACY = "media_legacy";
+ /** @hide */
+ public static final @NonNull Uri AUTHORITY_LEGACY_URI =
+ Uri.parse("content://" + AUTHORITY_LEGACY);
/**
* Synthetic volume name that provides a view of all content across the
@@ -878,6 +885,16 @@ public final class MediaStore {
}
/**
+ * Rewrite the given {@link Uri} to point at
+ * {@link MediaStore#AUTHORITY_LEGACY}.
+ *
+ * @hide
+ */
+ public static @NonNull Uri rewriteToLegacy(@NonNull Uri uri) {
+ return uri.buildUpon().authority(MediaStore.AUTHORITY_LEGACY).build();
+ }
+
+ /**
* Common media metadata columns.
*/
public interface MediaColumns extends BaseColumns {
@@ -3477,11 +3494,15 @@ public final class MediaStore {
*/
public static @NonNull String getVolumeName(@NonNull Uri uri) {
final List<String> segments = uri.getPathSegments();
- if (uri.getAuthority().equals(AUTHORITY) && segments != null && segments.size() > 0) {
- return segments.get(0);
- } else {
- throw new IllegalArgumentException("Missing volume name: " + uri);
+ switch (uri.getAuthority()) {
+ case AUTHORITY:
+ case AUTHORITY_LEGACY: {
+ if (segments != null && segments.size() > 0) {
+ return segments.get(0);
+ }
+ }
}
+ throw new IllegalArgumentException("Missing volume name: " + uri);
}
/** {@hide} */
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4773d989ec77..ac53f1b27680 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8375,7 +8375,6 @@ public final class Settings {
INSTANT_APP_SETTINGS.add(ANDROID_ID);
- INSTANT_APP_SETTINGS.add(PACKAGE_VERIFIER_USER_CONSENT);
INSTANT_APP_SETTINGS.add(ALLOW_MOCK_LOCATION);
}
diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java
index 2d40ec480379..bc6a9e848e2a 100644
--- a/core/java/android/service/euicc/EuiccService.java
+++ b/core/java/android/service/euicc/EuiccService.java
@@ -546,7 +546,7 @@ public abstract class EuiccService extends Service {
* @see android.telephony.euicc.EuiccManager#eraseSubscriptions
*
* @deprecated From R, callers should specify a flag for specific set of subscriptions to erase
- * and use {@link #onEraseSubscriptionsWithOptions(int, int)} instead
+ * and use {@link #onEraseSubscriptions(int, int)} instead
*/
@Deprecated
public abstract int onEraseSubscriptions(int slotId);
@@ -563,7 +563,7 @@ public abstract class EuiccService extends Service {
* constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}.
* @see android.telephony.euicc.EuiccManager#eraseSubscriptionsWithOptions
*/
- public int onEraseSubscriptionsWithOptions(int slotIndex, @ResetOption int options) {
+ public int onEraseSubscriptions(int slotIndex, @ResetOption int options) {
throw new UnsupportedOperationException(
"This method must be overridden to enable the ResetOption parameter");
}
@@ -809,8 +809,7 @@ public abstract class EuiccService extends Service {
mExecutor.execute(new Runnable() {
@Override
public void run() {
- int result = EuiccService.this.onEraseSubscriptionsWithOptions(
- slotIndex, options);
+ int result = EuiccService.this.onEraseSubscriptions(slotIndex, options);
try {
callback.onComplete(result);
} catch (RemoteException e) {
diff --git a/core/java/android/util/StatsEvent.aidl b/core/java/android/util/StatsEvent.aidl
new file mode 100644
index 000000000000..deac873b0a04
--- /dev/null
+++ b/core/java/android/util/StatsEvent.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+parcelable StatsEvent cpp_header "android/util/StatsEvent.h";
diff --git a/core/java/android/util/StatsEvent.java b/core/java/android/util/StatsEvent.java
index d6ffd38e98e8..10c9d87dfbe8 100644
--- a/core/java/android/util/StatsEvent.java
+++ b/core/java/android/util/StatsEvent.java
@@ -20,6 +20,8 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.os.SystemClock;
import com.android.internal.annotations.GuardedBy;
@@ -42,7 +44,7 @@ import com.android.internal.annotations.VisibleForTesting;
* </pre>
* @hide
**/
-public final class StatsEvent {
+public final class StatsEvent implements Parcelable {
private static final int LOGGER_ENTRY_MAX_PAYLOAD = 4068;
// Max payload size is 4 bytes less as 4 bytes are reserved for statsEventTag.
@@ -631,4 +633,39 @@ public final class StatsEvent {
return 0;
}
}
+
+ /**
+ * Boilerplate for Parcel.
+ *
+ * @hide
+ */
+ public static final @NonNull Parcelable.Creator<StatsEvent> CREATOR =
+ new Parcelable.Creator<StatsEvent>() {
+ public StatsEvent createFromParcel(Parcel in) {
+ // Purposefully leaving this method not implemented.
+ throw new RuntimeException("Not implemented");
+ }
+
+ public StatsEvent[] newArray(int size) {
+ // Purposefully leaving this method not implemented.
+ throw new RuntimeException("Not implemented");
+ }
+ };
+
+ /**
+ * @hide
+ */
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mAtomId);
+ out.writeInt(getNumBytes());
+ out.writeByteArray(getBytes());
+ }
+
+ /**
+ * Boilerplate for Parcel.
+ */
+ public int describeContents() {
+ return 0;
+ }
+
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index b815c641ff25..3251127397b6 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -90,7 +90,8 @@ public final class SurfaceControl implements Parcelable {
Rect sourceCrop, int width, int height, boolean useIdentityTransform, int rotation,
boolean captureSecureLayers);
private static native ScreenshotGraphicBuffer nativeCaptureLayers(IBinder displayToken,
- long layerObject, Rect sourceCrop, float frameScale, long[] excludeLayerObjects);
+ long layerObject, Rect sourceCrop, float frameScale, long[] excludeLayerObjects,
+ int format);
private static native long nativeMirrorSurface(long mirrorOfObject);
private static native long nativeCreateTransaction();
private static native long nativeGetNativeTransactionFinalizer();
@@ -1869,8 +1870,27 @@ public final class SurfaceControl implements Parcelable {
*/
public static ScreenshotGraphicBuffer captureLayers(SurfaceControl layer, Rect sourceCrop,
float frameScale) {
+ return captureLayers(layer, sourceCrop, frameScale, PixelFormat.RGBA_8888);
+ }
+
+ /**
+ * Captures a layer and its children and returns a {@link GraphicBuffer} with the content.
+ *
+ * @param layer The root layer to capture.
+ * @param sourceCrop The portion of the root surface to capture; caller may pass in 'new
+ * Rect()' or null if no cropping is desired.
+ * @param frameScale The desired scale of the returned buffer; the raw
+ * screen will be scaled up/down.
+ * @param format The desired pixel format of the returned buffer.
+ *
+ * @return Returns a GraphicBuffer that contains the layer capture.
+ * @hide
+ */
+ public static ScreenshotGraphicBuffer captureLayers(SurfaceControl layer, Rect sourceCrop,
+ float frameScale, int format) {
final IBinder displayToken = SurfaceControl.getInternalDisplayToken();
- return nativeCaptureLayers(displayToken, layer.mNativeObject, sourceCrop, frameScale, null);
+ return nativeCaptureLayers(displayToken, layer.mNativeObject, sourceCrop, frameScale, null,
+ format);
}
/**
@@ -1885,7 +1905,7 @@ public final class SurfaceControl implements Parcelable {
nativeExcludeObjects[i] = exclude[i].mNativeObject;
}
return nativeCaptureLayers(displayToken, layer.mNativeObject, sourceCrop, frameScale,
- nativeExcludeObjects);
+ nativeExcludeObjects, PixelFormat.RGBA_8888);
}
/**
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index fd3cd42b07a1..da43eddeb0bf 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -277,7 +277,7 @@ public class RuntimeInit {
result.append(System.getProperty("java.vm.version")); // such as 1.1.0
result.append(" (Linux; U; Android ");
- String version = Build.VERSION.RELEASE; // "1.0" or "3.4b5"
+ String version = Build.VERSION.RELEASE_OR_CODENAME; // "1.0" or "3.4b5"
result.append(version.length() > 0 ? version : "1.0");
// add the model for the release build
diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
index 7582cae1a761..4f3f283859b4 100644
--- a/core/jni/android_os_GraphicsEnvironment.cpp
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -23,8 +23,8 @@
namespace {
-int getCanLoadSystemLibraries_native() {
- return android::GraphicsEnv::getInstance().getCanLoadSystemLibraries();
+bool isDebuggable_native() {
+ return android::GraphicsEnv::getInstance().isDebuggable();
}
void setDriverPathAndSphalLibraries_native(JNIEnv* env, jobject clazz, jstring path,
@@ -94,7 +94,7 @@ void hintActivityLaunch_native(JNIEnv* env, jobject clazz) {
}
const JNINativeMethod g_methods[] = {
- { "getCanLoadSystemLibraries", "()I", reinterpret_cast<void*>(getCanLoadSystemLibraries_native) },
+ { "isDebuggable", "()Z", reinterpret_cast<void*>(isDebuggable_native) },
{ "setDriverPathAndSphalLibraries", "(Ljava/lang/String;Ljava/lang/String;)V", reinterpret_cast<void*>(setDriverPathAndSphalLibraries_native) },
{ "setGpuStats", "(Ljava/lang/String;Ljava/lang/String;JJLjava/lang/String;I)V", reinterpret_cast<void*>(setGpuStats_native) },
{ "setInjectLayersPrSetDumpable", "()Z", reinterpret_cast<void*>(setInjectLayersPrSetDumpable_native) },
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 3c0971b9ec1a..5c4dc2335822 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -556,7 +556,9 @@ static void NativeSetConfiguration(JNIEnv* env, jclass /*clazz*/, jlong ptr, jin
assetmanager->SetConfiguration(configuration);
}
-static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
+static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/, jlong ptr,
+ jboolean includeOverlays,
+ jboolean includeLoaders) {
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
jobject sparse_array =
@@ -567,6 +569,10 @@ static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/
return nullptr;
}
+ // Optionally exclude overlays and loaders.
+ uint64_t exclusion_flags = ((includeOverlays) ? 0U : PROPERTY_OVERLAY)
+ | ((includeLoaders) ? 0U : PROPERTY_LOADER);
+
assetmanager->ForEachPackage([&](const std::string& package_name, uint8_t package_id) -> bool {
jstring jpackage_name = env->NewStringUTF(package_name.c_str());
if (jpackage_name == nullptr) {
@@ -577,7 +583,8 @@ static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/
env->CallVoidMethod(sparse_array, gSparseArrayOffsets.put, static_cast<jint>(package_id),
jpackage_name);
return true;
- });
+ }, exclusion_flags);
+
return sparse_array;
}
@@ -1591,7 +1598,7 @@ static const JNINativeMethod gAssetManagerMethods[] = {
{"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;Z)V", (void*)NativeSetApkAssets},
{"nativeSetConfiguration", "(JIILjava/lang/String;IIIIIIIIIIIIIII)V",
(void*)NativeSetConfiguration},
- {"nativeGetAssignedPackageIdentifiers", "(J)Landroid/util/SparseArray;",
+ {"nativeGetAssignedPackageIdentifiers", "(JZZ)Landroid/util/SparseArray;",
(void*)NativeGetAssignedPackageIdentifiers},
// AssetManager file methods.
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index c807e90d5ad4..f8a2744fdcb0 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -274,7 +274,7 @@ static jobject nativeScreenshot(JNIEnv* env, jclass clazz,
static jobject nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject displayTokenObj,
jlong layerObject, jobject sourceCropObj, jfloat frameScale,
- jlongArray excludeObjectArray) {
+ jlongArray excludeObjectArray, jint format) {
auto layer = reinterpret_cast<SurfaceControl *>(layerObject);
if (layer == NULL) {
@@ -311,8 +311,9 @@ static jobject nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject displayTok
dataspace = pickDataspaceFromColorMode(colorMode);
}
status_t res = ScreenshotClient::captureChildLayers(layer->getHandle(), dataspace,
- ui::PixelFormat::RGBA_8888, sourceCrop,
- excludeHandles, frameScale, &buffer);
+ static_cast<ui::PixelFormat>(format),
+ sourceCrop, excludeHandles, frameScale,
+ &buffer);
if (res != NO_ERROR) {
return NULL;
}
@@ -1386,7 +1387,7 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeScreenshot },
{"nativeCaptureLayers",
"(Landroid/os/IBinder;JLandroid/graphics/Rect;"
- "F[J)"
+ "F[JI)"
"Landroid/view/SurfaceControl$ScreenshotGraphicBuffer;",
(void*)nativeCaptureLayers },
{"nativeSetInputWindowInfo", "(JJLandroid/view/InputWindowHandle;)V",
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index ce2717bb2779..8f084abe71a7 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -126,12 +126,11 @@ message ActivityRecordProto {
optional .com.android.server.wm.ConfigurationContainerProto configuration_container = 1 [deprecated=true];
optional .com.android.server.wm.IdentifierProto identifier = 2;
optional string state = 3;
- optional bool visible_requested = 4;
+ optional bool visible = 4;
optional bool front_of_task = 5;
optional int32 proc_id = 6;
optional bool translucent = 7;
optional .com.android.server.wm.AppWindowTokenProto app_window_token = 8;
- optional bool visible = 9;
}
message KeyguardControllerProto {
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 23566a6df847..653d381e2960 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -235,8 +235,8 @@ message AppWindowTokenProto {
optional WindowContainerThumbnailProto thumbnail = 6;
optional bool fills_parent = 7;
optional bool app_stopped = 8;
- optional bool visible_requested = 9;
- optional bool client_visible = 10;
+ optional bool hidden_requested = 9;
+ optional bool client_hidden = 10;
optional bool defer_hiding_client = 11;
optional bool reported_drawn = 12;
optional bool reported_visible = 13;
@@ -248,9 +248,8 @@ message AppWindowTokenProto {
optional IdentifierProto starting_window = 19;
optional bool starting_displayed = 20;
optional bool starting_moved = 21;
- optional bool visible_set_from_transferred_starting_window = 22;
+ optional bool hidden_set_from_transferred_starting_window = 22;
repeated .android.graphics.RectProto frozen_bounds = 23;
- optional bool visible = 24;
}
/* represents WindowToken */
@@ -260,6 +259,7 @@ message WindowTokenProto {
optional WindowContainerProto window_container = 1;
optional int32 hash_code = 2;
repeated WindowStateProto windows = 3;
+ optional bool hidden = 4;
optional bool waiting_to_show = 5;
optional bool paused = 6;
}
diff --git a/core/proto/android/stats/textclassifier/Android.bp b/core/proto/android/stats/textclassifier/Android.bp
new file mode 100644
index 000000000000..bf9022711206
--- /dev/null
+++ b/core/proto/android/stats/textclassifier/Android.bp
@@ -0,0 +1,23 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+java_library_static {
+ name: "textclassifierprotoslite",
+ proto: {
+ type: "lite",
+ },
+ srcs: [
+ "*.proto",
+ ],
+} \ No newline at end of file
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index efa42e5735e1..cb0b5993c420 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2652,6 +2652,9 @@
<!-- The amount to scale fullscreen snapshots for Overview and snapshot starting windows. -->
<item name="config_fullTaskSnapshotScale" format="float" type="dimen">1.0</item>
+ <!-- Feature flag to store TaskSnapshot in 16 bit pixel format to save memory. -->
+ <bool name="config_use16BitTaskSnapshotPixelFormat">false</bool>
+
<!-- Determines whether recent tasks are provided to the user. Default device has recents
property. If this is false, then the following recents config flags are ignored. -->
<bool name="config_hasRecents">true</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 91a8ba4ca3d2..28809daeaad8 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -356,6 +356,7 @@
<java-symbol type="bool" name="config_enableNewAutoSelectNetworkUI"/>
<java-symbol type="bool" name="config_disableUsbPermissionDialogs"/>
<java-symbol type="dimen" name="config_fullTaskSnapshotScale" />
+ <java-symbol type="bool" name="config_use16BitTaskSnapshotPixelFormat" />
<java-symbol type="bool" name="config_lowRamTaskSnapshotsAndRecents" />
<java-symbol type="bool" name="config_hasRecents" />
<java-symbol type="string" name="config_recentsComponentName" />
diff --git a/core/tests/coretests/src/android/os/BuildTest.java b/core/tests/coretests/src/android/os/BuildTest.java
index decc76869a53..2295eb989108 100644
--- a/core/tests/coretests/src/android/os/BuildTest.java
+++ b/core/tests/coretests/src/android/os/BuildTest.java
@@ -60,7 +60,7 @@ public class BuildTest extends TestCase {
assertNotEmpty("BRAND", Build.BRAND);
assertNotEmpty("MODEL", Build.MODEL);
assertNotEmpty("VERSION.INCREMENTAL", Build.VERSION.INCREMENTAL);
- assertNotEmpty("VERSION.RELEASE", Build.VERSION.RELEASE);
+ assertNotEmpty("VERSION.RELEASE", Build.VERSION.RELEASE_OR_CODENAME);
assertNotEmpty("TYPE", Build.TYPE);
Assert.assertNotNull("TAGS", Build.TAGS); // TAGS is allowed to be empty.
assertNotEmpty("FINGERPRINT", Build.FINGERPRINT);
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 77b0dba67be5..d2ce4e0a03b9 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -95,6 +95,9 @@ applications that come with the platform
<privapp-permissions package="com.android.mtp">
<permission name="android.permission.ACCESS_MTP"/>
<permission name="android.permission.MANAGE_USB"/>
+ <permission name="android.permission.MANAGE_USERS"/>
+ <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+ <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
</privapp-permissions>
<privapp-permissions package="com.android.musicfx">
@@ -207,6 +210,7 @@ applications that come with the platform
<permission name="android.permission.USE_RESERVED_DISK"/>
<permission name="android.permission.WRITE_MEDIA_STORAGE"/>
<permission name="android.permission.WATCH_APPOPS"/>
+ <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
<permission name="android.permission.UPDATE_DEVICE_STATS"/>
</privapp-permissions>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 3c89bfd43f17..753f8a05cbe3 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -55,12 +55,6 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
- "-2006946193": {
- "message": "setClientVisible: %s clientVisible=%b Callers=%s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_APP_TRANSITIONS",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"-2002500255": {
"message": "Defer removing snapshot surface in %dms",
"level": "VERBOSE",
@@ -79,18 +73,18 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "-1976550065": {
- "message": "commitVisibility: %s: visible=%b visibleRequested=%b",
- "level": "VERBOSE",
- "group": "WM_DEBUG_APP_TRANSITIONS",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"-1963461591": {
"message": "Removing %s from %s",
"level": "VERBOSE",
"group": "WM_DEBUG_ADD_REMOVE",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-1958209312": {
+ "message": "Clear freezing of %s: hidden=%b freezing=%b",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
"-1953668890": {
"message": "Can't start recents animation, nextAppTransition=%s",
"level": "DEBUG",
@@ -313,6 +307,12 @@
"group": "WM_DEBUG_ADD_REMOVE",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "-1456549051": {
+ "message": "setClientHidden: %s clientHidden=%b Callers=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
"-1455600136": {
"message": "Attempted to add Dream window with unknown token %s. Aborting.",
"level": "WARN",
@@ -547,6 +547,12 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/WindowState.java"
},
+ "-931184679": {
+ "message": "Changing app %s hidden=%b performLayout=%b",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
"-928291778": {
"message": "applyAnimation: anim=%s nextAppTransition=%d transit=%s Callers=%s",
"level": "VERBOSE",
@@ -835,12 +841,6 @@
"group": "WM_DEBUG_APP_TRANSITIONS",
"at": "com\/android\/server\/wm\/AppTransitionController.java"
},
- "-374767836": {
- "message": "setAppVisibility(%s, visible=%b): %s visible=%b mVisibleRequested=%b Callers=%s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_APP_TRANSITIONS",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"-371630969": {
"message": "New wallpaper target=%s, oldWallpaper=%s, openingApps=%s, closingApps=%s",
"level": "VERBOSE",
@@ -1207,6 +1207,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "358613119": {
+ "message": "setAppVisibility(%s, visible=%b): %s hidden=%b hiddenRequested=%b Callers=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
"371641947": {
"message": "Window Manager Crash %s",
"level": "WTF",
@@ -1267,12 +1273,6 @@
"group": "WM_DEBUG_SCREEN_ON",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "466506262": {
- "message": "Clear freezing of %s: visible=%b freezing=%b",
- "level": "VERBOSE",
- "group": "WM_DEBUG_ORIENTATION",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"474000473": {
"message": "No stack above target stack=%s",
"level": "DEBUG",
@@ -1489,12 +1489,6 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
- "841702299": {
- "message": "Changing app %s visible=%b performLayout=%b",
- "level": "VERBOSE",
- "group": "WM_DEBUG_APP_TRANSITIONS",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"845234215": {
"message": "App is requesting an orientation, return %d for display id=%d",
"level": "VERBOSE",
@@ -1507,6 +1501,12 @@
"group": "WM_DEBUG_RECENTS_ANIMATIONS",
"at": "com\/android\/server\/wm\/RecentsAnimation.java"
},
+ "857751535": {
+ "message": "commitVisibility: %s: hidden=%b hiddenRequested=%b",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
"873914452": {
"message": "goodToGo()",
"level": "DEBUG",
@@ -1903,12 +1903,6 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
- "1746778201": {
- "message": "Set freezing of %s: visible=%b freezing=%b visibleRequested=%b. %s",
- "level": "INFO",
- "group": "WM_DEBUG_ORIENTATION",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"1747941491": {
"message": "SURFACE controller=%s alpha=%f matrix=[%f*%f,%f*%f][%f*%f,%f*%f]: %s",
"level": "INFO",
@@ -1999,6 +1993,12 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
+ "1966564525": {
+ "message": "Set freezing of %s: hidden=%b freezing=%b hiddenRequested=%b. %s",
+ "level": "INFO",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
"1984470582": {
"message": "Creating TaskScreenshotAnimatable: task: %s width: %d height: %d",
"level": "DEBUG",
diff --git a/data/sounds/AudioPackage11.mk b/data/sounds/AudioPackage11.mk
index 99dfd0a6d455..2392b333c587 100644
--- a/data/sounds/AudioPackage11.mk
+++ b/data/sounds/AudioPackage11.mk
@@ -32,7 +32,7 @@ PRODUCT_COPY_FILES += \
$(LOCAL_PATH)/effects/ogg/Lock_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
$(LOCAL_PATH)/effects/ogg/Unlock_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
$(LOCAL_PATH)/effects/ogg/Trusted_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
- $(LOCAL_PATH)/effects/ogg/ChargingStarted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/ChargingStarted.ogg \.
+ $(LOCAL_PATH)/effects/ogg/ChargingStarted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/ChargingStarted.ogg \
$(LOCAL_PATH)/effects/ogg/InCallNotification.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/InCallNotification.ogg \
$(LOCAL_PATH)/effects/material/ogg/WirelessChargingStarted_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/WirelessChargingStarted.ogg \
$(LOCAL_PATH)/notifications/ogg/Adara.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Adara.ogg \
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index 538319c3f1d7..a7e17d13c9e1 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -55,8 +55,8 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicReference;
import javax.security.auth.x500.X500Principal;
@@ -811,27 +811,22 @@ public final class KeyChain {
throw new NullPointerException("context == null");
}
ensureNotOnMainThread(context);
- final BlockingQueue<IKeyChainService> q = new LinkedBlockingQueue<IKeyChainService>(1);
+ final CountDownLatch countDownLatch = new CountDownLatch(1);
+ final AtomicReference<IKeyChainService> keyChainService = new AtomicReference<>();
ServiceConnection keyChainServiceConnection = new ServiceConnection() {
volatile boolean mConnectedAtLeastOnce = false;
@Override public void onServiceConnected(ComponentName name, IBinder service) {
if (!mConnectedAtLeastOnce) {
mConnectedAtLeastOnce = true;
- try {
- q.put(IKeyChainService.Stub.asInterface(Binder.allowBlocking(service)));
- } catch (InterruptedException e) {
- // will never happen, since the queue starts with one available slot
- }
+ keyChainService.set(
+ IKeyChainService.Stub.asInterface(Binder.allowBlocking(service)));
+ countDownLatch.countDown();
}
}
@Override public void onBindingDied(ComponentName name) {
if (!mConnectedAtLeastOnce) {
mConnectedAtLeastOnce = true;
- try {
- q.put(null);
- } catch (InterruptedException e) {
- // will never happen, since the queue starts with one available slot
- }
+ countDownLatch.countDown();
}
}
@Override public void onServiceDisconnected(ComponentName name) {}
@@ -843,7 +838,8 @@ public final class KeyChain {
intent, keyChainServiceConnection, Context.BIND_AUTO_CREATE, user)) {
throw new AssertionError("could not bind to KeyChainService");
}
- IKeyChainService service = q.take();
+ countDownLatch.await();
+ IKeyChainService service = keyChainService.get();
if (service != null) {
return new KeyChainConnection(context, keyChainServiceConnection, service);
} else {
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index 16dbbf61351a..18934fd55bad 100644
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -43,20 +43,22 @@ static const std::string kResourcesArsc("resources.arsc");
ApkAssets::ApkAssets(ZipArchiveHandle unmanaged_handle,
const std::string& path,
time_t last_mod_time,
- bool for_loader)
+ package_property_t property_flags)
: zip_handle_(unmanaged_handle, ::CloseArchive), path_(path), last_mod_time_(last_mod_time),
- for_loader_(for_loader) {
+ property_flags_(property_flags) {
}
std::unique_ptr<const ApkAssets> ApkAssets::Load(const std::string& path, bool system,
bool for_loader) {
- return LoadImpl({} /*fd*/, path, nullptr, nullptr, system, false /*load_as_shared_library*/,
- for_loader);
+ package_property_t flags = (system ? PROPERTY_SYSTEM : 0U) |
+ (for_loader ? PROPERTY_LOADER : 0U);
+ return LoadImpl({} /*fd*/, path, nullptr, nullptr, flags);
}
std::unique_ptr<const ApkAssets> ApkAssets::LoadAsSharedLibrary(const std::string& path,
bool system) {
- return LoadImpl({} /*fd*/, path, nullptr, nullptr, system, true /*load_as_shared_library*/);
+ package_property_t flags = PROPERTY_DYNAMIC | (system ? PROPERTY_SYSTEM : 0U);
+ return LoadImpl({} /*fd*/, path, nullptr, nullptr, flags);
}
std::unique_ptr<const ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path,
@@ -74,27 +76,33 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap
LOG(ERROR) << "failed to load IDMAP " << idmap_path;
return {};
}
- return LoadImpl({} /*fd*/, loaded_idmap->OverlayApkPath(), std::move(idmap_asset),
- std::move(loaded_idmap), system, true /*load_as_shared_library*/);
+
+ return LoadImpl({} /*fd*/, loaded_idmap->OverlayApkPath(),
+ std::move(idmap_asset),
+ std::move(loaded_idmap),
+ PROPERTY_OVERLAY | (system ? PROPERTY_SYSTEM : 0U));
}
std::unique_ptr<const ApkAssets> ApkAssets::LoadFromFd(unique_fd fd,
const std::string& friendly_name,
bool system, bool force_shared_lib,
bool for_loader) {
+ package_property_t flags = (system ? PROPERTY_SYSTEM : 0U) |
+ (force_shared_lib ? PROPERTY_DYNAMIC : 0U) |
+ (for_loader ? PROPERTY_LOADER : 0U);
return LoadImpl(std::move(fd), friendly_name, nullptr /*idmap_asset*/, nullptr /*loaded_idmap*/,
- system, force_shared_lib, for_loader);
+ flags);
}
std::unique_ptr<const ApkAssets> ApkAssets::LoadArsc(const std::string& path,
bool for_loader) {
- return LoadArscImpl({} /*fd*/, path, for_loader);
+ return LoadArscImpl({} /*fd*/, path, for_loader ? PROPERTY_LOADER : 0U);
}
std::unique_ptr<const ApkAssets> ApkAssets::LoadArsc(unique_fd fd,
const std::string& friendly_name,
bool for_loader) {
- return LoadArscImpl(std::move(fd), friendly_name, for_loader);
+ return LoadArscImpl(std::move(fd), friendly_name, for_loader ? PROPERTY_LOADER : 0U);
}
std::unique_ptr<Asset> ApkAssets::CreateAssetFromFile(const std::string& path) {
@@ -120,8 +128,7 @@ std::unique_ptr<Asset> ApkAssets::CreateAssetFromFile(const std::string& path) {
std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
unique_fd fd, const std::string& path, std::unique_ptr<Asset> idmap_asset,
- std::unique_ptr<const LoadedIdmap> loaded_idmap, bool system, bool load_as_shared_library,
- bool for_loader) {
+ std::unique_ptr<const LoadedIdmap> loaded_idmap, package_property_t property_flags) {
::ZipArchiveHandle unmanaged_handle;
int32_t result;
if (fd >= 0) {
@@ -141,7 +148,7 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
// Wrap the handle in a unique_ptr so it gets automatically closed.
std::unique_ptr<ApkAssets>
- loaded_apk(new ApkAssets(unmanaged_handle, path, last_mod_time, for_loader));
+ loaded_apk(new ApkAssets(unmanaged_handle, path, last_mod_time, property_flags));
// Find the resource table.
::ZipEntry entry;
@@ -170,9 +177,8 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
const StringPiece data(
reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)),
loaded_apk->resources_asset_->getLength());
- loaded_apk->loaded_arsc_ =
- LoadedArsc::Load(data, loaded_apk->loaded_idmap_.get(), system, load_as_shared_library,
- for_loader);
+ loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, loaded_apk->loaded_idmap_.get(),
+ property_flags);
if (loaded_apk->loaded_arsc_ == nullptr) {
LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'.";
return {};
@@ -184,7 +190,7 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
std::unique_ptr<const ApkAssets> ApkAssets::LoadArscImpl(unique_fd fd,
const std::string& path,
- bool for_loader) {
+ package_property_t property_flags) {
std::unique_ptr<Asset> resources_asset;
if (fd >= 0) {
@@ -201,13 +207,14 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadArscImpl(unique_fd fd,
time_t last_mod_time = getFileModDate(path.c_str());
- std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(nullptr, path, last_mod_time, for_loader));
+ std::unique_ptr<ApkAssets> loaded_apk(
+ new ApkAssets(nullptr, path, last_mod_time, property_flags));
loaded_apk->resources_asset_ = std::move(resources_asset);
const StringPiece data(
reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)),
loaded_apk->resources_asset_->getLength());
- loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, nullptr, false, false, for_loader);
+ loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, nullptr, property_flags);
if (loaded_apk->loaded_arsc_ == nullptr) {
LOG(ERROR) << "Failed to load '" << kResourcesArsc << path;
return {};
@@ -320,8 +327,8 @@ bool ApkAssets::ForEachFile(const std::string& root_path,
}
bool ApkAssets::IsUpToDate() const {
- // Loaders are invalidated by the app, not the system, so assume up to date
- if (for_loader_) {
+ if (IsLoader()) {
+ // Loaders are invalidated by the app, not the system, so assume up to date.
return true;
}
diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp
index 2b69c923597f..773353d32d51 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -44,8 +44,8 @@ static bool compare_overlay_entries(const Idmap_overlay_entry& e1, const uint32_
}
OverlayStringPool::OverlayStringPool(const LoadedIdmap* loaded_idmap)
- : data_header_(loaded_idmap->data_header_),
- idmap_string_pool_(loaded_idmap->string_pool_.get()) { };
+ : data_header_(loaded_idmap->data_header_),
+ idmap_string_pool_(loaded_idmap->string_pool_.get()) { };
OverlayStringPool::~OverlayStringPool() {
uninit();
@@ -188,11 +188,12 @@ LoadedIdmap::LoadedIdmap(const Idmap_header* header,
const Idmap_data_header* data_header,
const Idmap_target_entry* target_entries,
const Idmap_overlay_entry* overlay_entries,
- ResStringPool* string_pool) : header_(header),
- data_header_(data_header),
- target_entries_(target_entries),
- overlay_entries_(overlay_entries),
- string_pool_(string_pool) {
+ ResStringPool* string_pool)
+ : header_(header),
+ data_header_(data_header),
+ target_entries_(target_entries),
+ overlay_entries_(overlay_entries),
+ string_pool_(string_pool) {
size_t length = strnlen(reinterpret_cast<const char*>(header_->overlay_path),
arraysize(header_->overlay_path));
@@ -264,7 +265,7 @@ std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_da
}
}
- // Can't use make_unique because LoadedImpl constructor is private.
+ // Can't use make_unique because LoadedIdmap constructor is private.
std::unique_ptr<LoadedIdmap> loaded_idmap = std::unique_ptr<LoadedIdmap>(
new LoadedIdmap(header, data_header, target_entries, overlay_entries,
idmap_string_pool.release()));
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index c8962416d082..e35c0249fbdf 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -397,9 +397,7 @@ const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const {
}
std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
- bool system,
- bool load_as_shared_library,
- bool for_loader) {
+ package_property_t property_flags) {
ATRACE_NAME("LoadedPackage::Load");
std::unique_ptr<LoadedPackage> loaded_package(new LoadedPackage());
@@ -413,17 +411,24 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
return {};
}
- loaded_package->system_ = system;
+ if ((property_flags & PROPERTY_SYSTEM) != 0) {
+ loaded_package->property_flags_ |= PROPERTY_SYSTEM;
+ }
- loaded_package->package_id_ = dtohl(header->id);
- if (loaded_package->package_id_ == 0 ||
- (loaded_package->package_id_ == kAppPackageId && load_as_shared_library)) {
- // Package ID of 0 means this is a shared library.
- loaded_package->dynamic_ = true;
+ if ((property_flags & PROPERTY_LOADER) != 0) {
+ loaded_package->property_flags_ |= PROPERTY_LOADER;
}
- if (for_loader) {
- loaded_package->custom_loader_ = true;
+ if ((property_flags & PROPERTY_OVERLAY) != 0) {
+ // Overlay resources must have an exclusive resource id space for referencing internal
+ // resources.
+ loaded_package->property_flags_ |= PROPERTY_OVERLAY | PROPERTY_DYNAMIC;
+ }
+
+ loaded_package->package_id_ = dtohl(header->id);
+ if (loaded_package->package_id_ == 0 ||
+ (loaded_package->package_id_ == kAppPackageId && (property_flags & PROPERTY_DYNAMIC) != 0)) {
+ loaded_package->property_flags_ |= PROPERTY_DYNAMIC;
}
if (header->header.headerSize >= sizeof(ResTable_package)) {
@@ -677,7 +682,7 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
}
bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
- bool load_as_shared_library, bool for_loader) {
+ package_property_t property_flags) {
const ResTable_header* header = chunk.header<ResTable_header>();
if (header == nullptr) {
LOG(ERROR) << "RES_TABLE_TYPE too small.";
@@ -720,7 +725,7 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
packages_seen++;
std::unique_ptr<const LoadedPackage> loaded_package =
- LoadedPackage::Load(child_chunk, system_, load_as_shared_library, for_loader);
+ LoadedPackage::Load(child_chunk, property_flags);
if (!loaded_package) {
return false;
}
@@ -744,24 +749,18 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
std::unique_ptr<const LoadedArsc> LoadedArsc::Load(const StringPiece& data,
const LoadedIdmap* loaded_idmap,
- bool system,
- bool load_as_shared_library,
- bool for_loader) {
+ package_property_t property_flags) {
ATRACE_NAME("LoadedArsc::Load");
// Not using make_unique because the constructor is private.
std::unique_ptr<LoadedArsc> loaded_arsc(new LoadedArsc());
- loaded_arsc->system_ = system;
ChunkIterator iter(data.data(), data.size());
while (iter.HasNext()) {
const Chunk chunk = iter.Next();
switch (chunk.type()) {
case RES_TABLE_TYPE:
- if (!loaded_arsc->LoadTable(chunk,
- loaded_idmap,
- load_as_shared_library,
- for_loader)) {
+ if (!loaded_arsc->LoadTable(chunk, loaded_idmap, property_flags)) {
return {};
}
break;
diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h
index 20472872263e..af802b0e50b9 100644
--- a/libs/androidfw/include/androidfw/ApkAssets.h
+++ b/libs/androidfw/include/androidfw/ApkAssets.h
@@ -76,10 +76,10 @@ class ApkAssets {
// Takes ownership of the file descriptor.
static std::unique_ptr<const ApkAssets> LoadArsc(base::unique_fd fd,
const std::string& friendly_name,
- bool resource_loader = false);
+ bool for_loader = false);
// Creates a totally empty ApkAssets with no resources table and no file entries.
- static std::unique_ptr<const ApkAssets> LoadEmpty(bool resource_loader = false);
+ static std::unique_ptr<const ApkAssets> LoadEmpty(bool for_loader = false);
std::unique_ptr<Asset> Open(const std::string& path,
Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM) const;
@@ -100,12 +100,12 @@ class ApkAssets {
return loaded_idmap_.get();
}
- inline bool IsOverlay() const {
- return idmap_asset_.get() != nullptr;
+ inline bool IsLoader() const {
+ return (property_flags_ & PROPERTY_LOADER) != 0;
}
- inline bool IsLoader() const {
- return for_loader_;
+ inline bool IsOverlay() const {
+ return (property_flags_ & PROPERTY_OVERLAY) != 0;
}
bool IsUpToDate() const;
@@ -119,24 +119,23 @@ class ApkAssets {
static std::unique_ptr<const ApkAssets> LoadImpl(base::unique_fd fd, const std::string& path,
std::unique_ptr<Asset> idmap_asset,
std::unique_ptr<const LoadedIdmap> loaded_idmap,
- bool system, bool load_as_shared_library,
- bool resource_loader = false);
+ package_property_t property_flags);
static std::unique_ptr<const ApkAssets> LoadArscImpl(base::unique_fd fd,
const std::string& path,
- bool resource_loader = false);
+ package_property_t property_flags);
ApkAssets(ZipArchiveHandle unmanaged_handle,
const std::string& path,
time_t last_mod_time,
- bool for_loader = false);
+ package_property_t property_flags);
using ZipArchivePtr = std::unique_ptr<ZipArchive, void (*)(ZipArchiveHandle)>;
ZipArchivePtr zip_handle_;
const std::string path_;
time_t last_mod_time_;
- bool for_loader_;
+ package_property_t property_flags_ = 0U;
std::unique_ptr<Asset> resources_asset_;
std::unique_ptr<Asset> idmap_asset_;
std::unique_ptr<const LoadedArsc> loaded_arsc_;
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index 20e40234b418..00cbbcad56e6 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -263,10 +263,13 @@ class AssetManager2 {
// Creates a new Theme from this AssetManager.
std::unique_ptr<Theme> NewTheme();
- void ForEachPackage(const std::function<bool(const std::string&, uint8_t)> func) const {
+ void ForEachPackage(const std::function<bool(const std::string&, uint8_t)> func,
+ package_property_t excluded_property_flags = 0U) const {
for (const PackageGroup& package_group : package_groups_) {
- if (!func(package_group.packages_.front().loaded_package_->GetPackageName(),
- package_group.dynamic_ref_table->mAssignedPackageId)) {
+ const auto loaded_package = package_group.packages_.front().loaded_package_;
+ if ((loaded_package->GetPropertyFlags() & excluded_property_flags) == 0U
+ && !func(loaded_package->GetPackageName(),
+ package_group.dynamic_ref_table->mAssignedPackageId)) {
return;
}
}
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index ba1beaa7827c..6cbda07b6950 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -69,6 +69,14 @@ struct TypeSpec {
}
};
+using package_property_t = uint32_t;
+enum : package_property_t {
+ PROPERTY_DYNAMIC = 1,
+ PROPERTY_LOADER = 2,
+ PROPERTY_OVERLAY = 4,
+ PROPERTY_SYSTEM = 8,
+};
+
// TypeSpecPtr points to a block of memory that holds a TypeSpec struct, followed by an array of
// ResTable_type pointers.
// TypeSpecPtr is a managed pointer that knows how to delete itself.
@@ -131,9 +139,8 @@ class LoadedPackage {
return iterator(this, resource_ids_.size() + 1, 0);
}
- static std::unique_ptr<const LoadedPackage> Load(const Chunk& chunk, bool system,
- bool load_as_shared_library,
- bool load_as_custom_loader);
+ static std::unique_ptr<const LoadedPackage> Load(const Chunk& chunk,
+ package_property_t property_flags);
~LoadedPackage();
@@ -170,17 +177,26 @@ class LoadedPackage {
// Returns true if this package is dynamic (shared library) and needs to have an ID assigned.
inline bool IsDynamic() const {
- return dynamic_;
+ return (property_flags_ & PROPERTY_DYNAMIC) != 0;
+ }
+
+ // Returns true if this package is a Runtime Resource Overlay.
+ inline bool IsOverlay() const {
+ return (property_flags_ & PROPERTY_OVERLAY) != 0;
}
// Returns true if this package originates from a system provided resource.
inline bool IsSystem() const {
- return system_;
+ return (property_flags_ & PROPERTY_SYSTEM) != 0;
}
- // Returns true if this package is a custom loader and should behave like an overlay
+ // Returns true if this package is a custom loader and should behave like an overlay.
inline bool IsCustomLoader() const {
- return custom_loader_;
+ return (property_flags_ & PROPERTY_LOADER) != 0;
+ }
+
+ inline package_property_t GetPropertyFlags() const {
+ return property_flags_;
}
// Returns the map of package name to package ID used in this LoadedPackage. At runtime, a
@@ -248,12 +264,10 @@ class LoadedPackage {
ResStringPool type_string_pool_;
ResStringPool key_string_pool_;
std::string package_name_;
+ bool defines_overlayable_ = false;
int package_id_ = -1;
int type_id_offset_ = 0;
- bool dynamic_ = false;
- bool system_ = false;
- bool custom_loader_ = false;
- bool defines_overlayable_ = false;
+ package_property_t property_flags_ = 0U;
ByteBucketArray<TypeSpecPtr> type_specs_;
ByteBucketArray<uint32_t> resource_ids_;
@@ -274,9 +288,7 @@ class LoadedArsc {
// ID.
static std::unique_ptr<const LoadedArsc> Load(const StringPiece& data,
const LoadedIdmap* loaded_idmap = nullptr,
- bool system = false,
- bool load_as_shared_library = false,
- bool for_loader = false);
+ package_property_t property_flags = 0U);
// Create an empty LoadedArsc. This is used when an APK has no resources.arsc.
static std::unique_ptr<const LoadedArsc> CreateEmpty();
@@ -296,28 +308,15 @@ class LoadedArsc {
return packages_;
}
- // Returns true if this is a system provided resource.
- inline bool IsSystem() const {
- return system_;
- }
-
private:
DISALLOW_COPY_AND_ASSIGN(LoadedArsc);
LoadedArsc() = default;
- bool LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool load_as_shared_library,
- bool for_loader);
-
- static std::unique_ptr<const LoadedArsc> LoadData(std::unique_ptr<LoadedArsc>& loaded_arsc,
- const char* data,
- size_t length,
- const LoadedIdmap* loaded_idmap = nullptr,
- bool load_as_shared_library = false,
- bool for_loader = false);
+ bool LoadTable(
+ const Chunk& chunk, const LoadedIdmap* loaded_idmap, package_property_t property_flags);
std::unique_ptr<ResStringPool> global_string_pool_ = util::make_unique<ResStringPool>();
std::vector<std::unique_ptr<const LoadedPackage>> packages_;
- bool system_ = false;
};
} // namespace android
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index 82dd33523c75..8615069e98dd 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -144,8 +144,7 @@ TEST(LoadedArscTest, LoadAppAsSharedLibrary) {
"resources.arsc", &contents));
std::unique_ptr<const LoadedArsc> loaded_arsc =
- LoadedArsc::Load(StringPiece(contents), nullptr /* loaded_idmap */, false /*system*/,
- true /*load_as_shared_library*/);
+ LoadedArsc::Load(StringPiece(contents), nullptr /* loaded_idmap */, PROPERTY_DYNAMIC);
ASSERT_THAT(loaded_arsc, NotNull());
const auto& packages = loaded_arsc->GetPackages();
@@ -227,9 +226,7 @@ TEST(LoadedArscTest, LoadOverlayable) {
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlayable/overlayable.apk",
"resources.arsc", &contents));
- std::unique_ptr<const LoadedArsc> loaded_arsc =
- LoadedArsc::Load(StringPiece(contents), nullptr /* loaded_idmap */, false /*system*/,
- false /*load_as_shared_library*/);
+ std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
ASSERT_THAT(loaded_arsc, NotNull());
const LoadedPackage* package = loaded_arsc->GetPackageById(
@@ -346,7 +343,7 @@ TEST(LoadedArscTest, LoadCustomLoader) {
asset->getLength());
std::unique_ptr<const LoadedArsc> loaded_arsc =
- LoadedArsc::Load(data, nullptr, false, false, true);
+ LoadedArsc::Load(data, nullptr, PROPERTY_LOADER);
ASSERT_THAT(loaded_arsc, NotNull());
const LoadedPackage* package =
diff --git a/libs/services/Android.bp b/libs/services/Android.bp
index 1b9939d9a598..b0fad57dfd29 100644
--- a/libs/services/Android.bp
+++ b/libs/services/Android.bp
@@ -22,6 +22,7 @@ cc_library_shared {
"src/os/DropBoxManager.cpp",
"src/os/StatsDimensionsValue.cpp",
"src/os/StatsLogEventWrapper.cpp",
+ "src/util/StatsEvent.cpp",
],
shared_libs: [
diff --git a/libs/services/include/android/util/StatsEvent.h b/libs/services/include/android/util/StatsEvent.h
new file mode 100644
index 000000000000..48631174f7dd
--- /dev/null
+++ b/libs/services/include/android/util/StatsEvent.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef STATS_EVENT_H
+#define STATS_EVENT_H
+
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <binder/Status.h>
+#include <vector>
+
+namespace android {
+namespace util {
+class StatsEvent : public android::Parcelable {
+ public:
+ StatsEvent();
+
+ StatsEvent(StatsEvent&& in) = default;
+
+ android::status_t writeToParcel(android::Parcel* out) const;
+
+ android::status_t readFromParcel(const android::Parcel* in);
+
+ private:
+ int mAtomTag;
+ std::vector<uint8_t> mBuffer;
+};
+} // Namespace util
+} // Namespace android
+
+#endif // STATS_ EVENT_H \ No newline at end of file
diff --git a/libs/services/src/util/StatsEvent.cpp b/libs/services/src/util/StatsEvent.cpp
new file mode 100644
index 000000000000..8b8579167b00
--- /dev/null
+++ b/libs/services/src/util/StatsEvent.cpp
@@ -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.
+ */
+#include <android/util/StatsEvent.h>
+
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <binder/Status.h>
+#include <vector>
+
+using android::Parcel;
+using android::Parcelable;
+using android::status_t;
+using std::vector;
+
+namespace android {
+namespace util {
+
+StatsEvent::StatsEvent(){};
+
+status_t StatsEvent::writeToParcel(Parcel* out) const {
+ // Implement me if desired. We don't currently use this.
+ ALOGE("Cannot do c++ StatsEvent.writeToParcel(); it is not implemented.");
+ (void)out; // To prevent compile error of unused parameter 'out'
+ return UNKNOWN_ERROR;
+};
+
+status_t StatsEvent::readFromParcel(const Parcel* in) {
+ status_t res = OK;
+ if (in == NULL) {
+ ALOGE("statsd received parcel argument was NULL.");
+ return BAD_VALUE;
+ }
+ if ((res = in->readInt32(&mAtomTag)) != OK) {
+ ALOGE("statsd could not read atom tag from parcel");
+ return res;
+ }
+ if ((res = in->readByteVector(&mBuffer)) != OK) {
+ ALOGE("statsd could not read buffer from parcel");
+ return res;
+ }
+ return NO_ERROR;
+};
+
+} // Namespace util
+} // Namespace android
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 39a7e25cb68b..5030e6625d5d 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -2642,10 +2642,47 @@ public class LocationManager {
@Override
public void onRemoved() {
- unregister();
- synchronized (mListeners) {
- mListeners.remove(mListener, this);
+ // TODO: onRemoved is necessary to GC hanging listeners, but introduces some interesting
+ // broken edge cases. luckily these edge cases are quite unlikely. consider the
+ // following interleaving for instance:
+ // 1) client adds single shot location request (A)
+ // 2) client gets removal callback, and schedules it for execution
+ // 3) client replaces single shot request with a different location request (B)
+ // 4) prior removal callback is executed, removing location request (B) incorrectly
+ // what's needed is a way to identify which listener a callback belongs to. currently
+ // we reuse the same transport object for the same listeners (so that we don't leak
+ // transport objects on the server side). there seem to be two solutions:
+ // 1) when reregistering a request, first unregister the current transport, then
+ // register with a new transport object (never reuse transport objects) - the
+ // downside is that this breaks the server's knowledge that the request is the
+ // same object, and thus breaks optimizations such as reusing the same transport
+ // state.
+ // 2) pass some other type of marker in addition to the transport (for example an
+ // incrementing integer representing the transport "version"), and pass this
+ // marker back into callbacks so that each callback knows which transport
+ // "version" it belongs to and can not execute itself if the version does not
+ // match.
+ // (1) seems like the preferred solution as it's simpler to implement and the above
+ // mentioned server optimizations are not terribly important (they can be bypassed by
+ // clients that use a new listener every time anyways).
+
+ Executor currentExecutor = mExecutor;
+ if (currentExecutor == null) {
+ // we've already been unregistered, no work to do anyways
+ return;
}
+
+ // must be executed on the same executor so callback execution cannot be reordered
+ currentExecutor.execute(() -> {
+ if (currentExecutor != mExecutor) {
+ return;
+ }
+
+ unregister();
+ synchronized (mListeners) {
+ mListeners.remove(mListener, this);
+ }
+ });
}
private void locationCallbackFinished() {
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index d6a4ea7cb39f..da8e402ffb9f 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -237,11 +237,56 @@ public class ExifInterface {
public static final String TAG_NEW_SUBFILE_TYPE = "NewSubfileType";
/** Type is String. */
public static final String TAG_OECF = "OECF";
- /** Type is String. {@hide} */
+ /**
+ * <p>A tag used to record the offset from UTC (the time difference from Universal Time
+ * Coordinated including daylight saving time) of the time of DateTime tag. The format when
+ * recording the offset is "±HH:MM". The part of "±" shall be recorded as "+" or "-". When
+ * the offsets are unknown, all the character spaces except colons (":") should be filled
+ * with blank characters, or else the Interoperability field should be filled with blank
+ * characters. The character string length is 7 Bytes including NULL for termination. When
+ * the field is left blank, it is treated as unknown.</p>
+ *
+ * <ul>
+ * <li>Tag = 36880</li>
+ * <li>Type = String</li>
+ * <li>Length = 7</li>
+ * <li>Default = None</li>
+ * </ul>
+ */
public static final String TAG_OFFSET_TIME = "OffsetTime";
- /** Type is String. {@hide} */
+ /**
+ * <p>A tag used to record the offset from UTC (the time difference from Universal Time
+ * Coordinated including daylight saving time) of the time of DateTimeOriginal tag. The format
+ * when recording the offset is "±HH:MM". The part of "±" shall be recorded as "+" or "-". When
+ * the offsets are unknown, all the character spaces except colons (":") should be filled
+ * with blank characters, or else the Interoperability field should be filled with blank
+ * characters. The character string length is 7 Bytes including NULL for termination. When
+ * the field is left blank, it is treated as unknown.</p>
+ *
+ * <ul>
+ * <li>Tag = 36881</li>
+ * <li>Type = String</li>
+ * <li>Length = 7</li>
+ * <li>Default = None</li>
+ * </ul>
+ */
public static final String TAG_OFFSET_TIME_ORIGINAL = "OffsetTimeOriginal";
- /** Type is String. {@hide} */
+ /**
+ * <p>A tag used to record the offset from UTC (the time difference from Universal Time
+ * Coordinated including daylight saving time) of the time of DateTimeDigitized tag. The format
+ * when recording the offset is "±HH:MM". The part of "±" shall be recorded as "+" or "-". When
+ * the offsets are unknown, all the character spaces except colons (":") should be filled
+ * with blank characters, or else the Interoperability field should be filled with blank
+ * characters. The character string length is 7 Bytes including NULL for termination. When
+ * the field is left blank, it is treated as unknown.</p>
+ *
+ * <ul>
+ * <li>Tag = 36882</li>
+ * <li>Type = String</li>
+ * <li>Length = 7</li>
+ * <li>Default = None</li>
+ * </ul>
+ */
public static final String TAG_OFFSET_TIME_DIGITIZED = "OffsetTimeDigitized";
/** Type is int. */
public static final String TAG_PIXEL_X_DIMENSION = "PixelXDimension";
diff --git a/media/java/android/media/MediaScannerConnection.java b/media/java/android/media/MediaScannerConnection.java
index 7eec8d9f6cc3..8d857243bc05 100644
--- a/media/java/android/media/MediaScannerConnection.java
+++ b/media/java/android/media/MediaScannerConnection.java
@@ -197,7 +197,7 @@ public class MediaScannerConnection implements ServiceConnection {
private static Uri scanFileQuietly(ContentProviderClient client, File file) {
Uri uri = null;
try {
- uri = MediaStore.scanFile(client, file);
+ uri = MediaStore.scanFile(client, file.getCanonicalFile());
Log.d(TAG, "Scanned " + file + " to " + uri);
} catch (Exception e) {
Log.w(TAG, "Failed to scan " + file + ": " + e);
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 7d68d0279f1f..3d89909b87aa 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -17,6 +17,7 @@
package android.media.tv.tuner;
import android.annotation.Nullable;
+import android.media.tv.tuner.TunerConstants.DemuxPidType;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -35,6 +36,7 @@ public final class Tuner implements AutoCloseable {
private static final int MSG_ON_FRONTEND_EVENT = 1;
private static final int MSG_ON_FILTER_EVENT = 2;
private static final int MSG_ON_FILTER_STATUS = 3;
+ private static final int MSG_ON_LNB_EVENT = 4;
static {
System.loadLibrary("media_tv_tuner");
@@ -45,6 +47,9 @@ public final class Tuner implements AutoCloseable {
private Frontend mFrontend;
private EventHandler mHandler;
+ private List<Integer> mLnbIds;
+ private Lnb mLnb;
+
public Tuner() {
nativeSetup();
}
@@ -76,6 +81,11 @@ public final class Tuner implements AutoCloseable {
private native Filter nativeOpenFilter(int type, int subType, int bufferSize);
+ private native List<Integer> nativeGetLnbIds();
+ private native Lnb nativeOpenLnbById(int id);
+
+ private native Descrambler nativeOpenDescrambler();
+
/**
* Frontend Callback.
@@ -89,6 +99,16 @@ public final class Tuner implements AutoCloseable {
}
/**
+ * LNB Callback.
+ */
+ public interface LnbCallback {
+ /**
+ * Invoked when there is a LNB event.
+ */
+ void onEvent(int lnbEventType);
+ }
+
+ /**
* Frontend Callback.
*/
public interface FilterCallback {
@@ -129,6 +149,11 @@ public final class Tuner implements AutoCloseable {
}
break;
}
+ case MSG_ON_LNB_EVENT: {
+ if (mLnb != null && mLnb.mCallback != null) {
+ mLnb.mCallback.onEvent(msg.arg1);
+ }
+ }
default:
// fall through
}
@@ -193,6 +218,11 @@ public final class Tuner implements AutoCloseable {
private long mNativeContext;
private FilterCallback mCallback;
int mId;
+
+ private native boolean nativeStartFilter();
+ private native boolean nativeStopFilter();
+ private native boolean nativeFlushFilter();
+
private Filter(int id) {
mId = id;
}
@@ -203,6 +233,18 @@ public final class Tuner implements AutoCloseable {
mHandler.obtainMessage(MSG_ON_FILTER_STATUS, status, 0, this));
}
}
+
+ public boolean start() {
+ return nativeStartFilter();
+ }
+
+ public boolean stop() {
+ return nativeStopFilter();
+ }
+
+ public boolean flush() {
+ return nativeFlushFilter();
+ }
}
private Filter openFilter(int type, int subType, int bufferSize, FilterCallback cb) {
@@ -215,4 +257,68 @@ public final class Tuner implements AutoCloseable {
}
return filter;
}
+
+ protected class Lnb {
+ private int mId;
+ private LnbCallback mCallback;
+
+ private Lnb(int id) {
+ mId = id;
+ }
+
+ public void setCallback(@Nullable LnbCallback callback) {
+ mCallback = callback;
+ if (mCallback == null) {
+ return;
+ }
+ if (mHandler == null) {
+ mHandler = createEventHandler();
+ }
+ }
+ }
+
+ private List<Integer> getLnbIds() {
+ mLnbIds = nativeGetLnbIds();
+ return mLnbIds;
+ }
+
+ private Lnb openLnbById(int id) {
+ if (mLnbIds == null) {
+ mLnbIds = getLnbIds();
+ }
+ if (!mLnbIds.contains(id)) {
+ return null;
+ }
+ mLnb = nativeOpenLnbById(id);
+ return mLnb;
+ }
+
+ private void onLnbEvent(int eventType) {
+ if (mHandler != null) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_ON_LNB_EVENT, eventType, 0));
+ }
+ }
+
+ protected class Descrambler {
+ private long mNativeContext;
+
+ private native boolean nativeAddPid(int pidType, int pid, Filter filter);
+ private native boolean nativeRemovePid(int pidType, int pid, Filter filter);
+
+ private Descrambler() {}
+
+ private boolean addPid(@DemuxPidType int pidType, int pid, Filter filter) {
+ return nativeAddPid(pidType, pid, filter);
+ }
+
+ private boolean removePid(@DemuxPidType int pidType, int pid, Filter filter) {
+ return nativeRemovePid(pidType, pid, filter);
+ }
+
+ }
+
+ private Descrambler openDescrambler() {
+ Descrambler descrambler = nativeOpenDescrambler();
+ return descrambler;
+ }
}
diff --git a/media/java/android/media/tv/tuner/TunerConstants.java b/media/java/android/media/tv/tuner/TunerConstants.java
index 411882ed13bd..01f9367dd4f3 100644
--- a/media/java/android/media/tv/tuner/TunerConstants.java
+++ b/media/java/android/media/tv/tuner/TunerConstants.java
@@ -67,6 +67,14 @@ final class TunerConstants {
public static final int DATA_FORMAT_ES = Constants.DataFormat.ES;
public static final int DATA_FORMAT_SHV_TLV = Constants.DataFormat.SHV_TLV;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({DEMUX_T_PID, DEMUX_MMPT_PID})
+ public @interface DemuxPidType {}
+
+ public static final int DEMUX_T_PID = 1;
+ public static final int DEMUX_MMPT_PID = 2;
+
private TunerConstants() {
}
}
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 26405720c31f..efdd333f97f2 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -28,22 +28,45 @@
using ::android::hardware::Void;
using ::android::hardware::hidl_vec;
using ::android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
+using ::android::hardware::tv::tuner::V1_0::DemuxMmtpPid;
+using ::android::hardware::tv::tuner::V1_0::DemuxTpid;
using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterType;
using ::android::hardware::tv::tuner::V1_0::ITuner;
using ::android::hardware::tv::tuner::V1_0::Result;
struct fields_t {
- jfieldID context;
+ jfieldID tunerContext;
jfieldID filterContext;
+ jfieldID descramblerContext;
jmethodID frontendInitID;
jmethodID filterInitID;
jmethodID onFrontendEventID;
jmethodID onFilterStatusID;
+ jmethodID lnbInitID;
+ jmethodID onLnbEventID;
+ jmethodID descramblerInitID;
};
static fields_t gFields;
namespace android {
+/////////////// LnbCallback ///////////////////////
+LnbCallback::LnbCallback(jweak tunerObj, LnbId id) : mObject(tunerObj), mId(id) {}
+
+Return<void> LnbCallback::onEvent(LnbEventType lnbEventType) {
+ ALOGD("LnbCallback::onEvent, type=%d", lnbEventType);
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ env->CallVoidMethod(
+ mObject,
+ gFields.onLnbEventID,
+ (jint)lnbEventType);
+ return Void();
+}
+Return<void> LnbCallback::onDiseqcMessage(const hidl_vec<uint8_t>& /*diseqcMessage*/) {
+ ALOGD("LnbCallback::onDiseqcMessage");
+ return Void();
+}
+
/////////////// FilterCallback ///////////////////////
//TODO: implement filter callback
Return<void> FilterCallback::onFilterEvent(const DemuxFilterEvent& /*filterEvent*/) {
@@ -175,6 +198,52 @@ jobject JTuner::openFrontendById(int id) {
(jint) jId);
}
+jobject JTuner::getLnbIds() {
+ ALOGD("JTuner::getLnbIds()");
+ mTuner->getLnbIds([&](Result, const hidl_vec<FrontendId>& lnbIds) {
+ mLnbIds = lnbIds;
+ });
+ if (mLnbIds.size() == 0) {
+ ALOGW("Lnb isn't available");
+ return NULL;
+ }
+
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ jclass arrayListClazz = env->FindClass("java/util/ArrayList");
+ jmethodID arrayListAdd = env->GetMethodID(arrayListClazz, "add", "(Ljava/lang/Object;)Z");
+ jobject obj = env->NewObject(arrayListClazz, env->GetMethodID(arrayListClazz, "<init>", "()V"));
+
+ jclass integerClazz = env->FindClass("java/lang/Integer");
+ jmethodID intInit = env->GetMethodID(integerClazz, "<init>", "(I)V");
+
+ for (int i=0; i < mLnbIds.size(); i++) {
+ jobject idObj = env->NewObject(integerClazz, intInit, mLnbIds[i]);
+ env->CallBooleanMethod(obj, arrayListAdd, idObj);
+ }
+ return obj;
+}
+
+jobject JTuner::openLnbById(int id) {
+ sp<ILnb> lnbSp;
+ mTuner->openLnbById(id, [&](Result, const sp<ILnb>& lnb) {
+ lnbSp = lnb;
+ });
+ if (lnbSp == nullptr) {
+ ALOGE("Failed to open lnb");
+ return NULL;
+ }
+ mLnb = lnbSp;
+ sp<LnbCallback> lnbCb = new LnbCallback(mObject, id);
+ mLnb->setCallback(lnbCb);
+
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ return env->NewObject(
+ env->FindClass("android/media/tv/tuner/Tuner$Lnb"),
+ gFields.lnbInitID,
+ mObject,
+ id);
+}
+
bool JTuner::openDemux() {
if (mTuner == nullptr) {
return false;
@@ -193,6 +262,33 @@ bool JTuner::openDemux() {
return true;
}
+jobject JTuner::openDescrambler() {
+ ALOGD("JTuner::openDescrambler");
+ if (mTuner == nullptr) {
+ return NULL;
+ }
+ sp<IDescrambler> descramblerSp;
+ mTuner->openDescrambler([&](Result, const sp<IDescrambler>& descrambler) {
+ descramblerSp = descrambler;
+ });
+
+ if (descramblerSp == NULL) {
+ return NULL;
+ }
+
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ jobject descramblerObj =
+ env->NewObject(
+ env->FindClass("android/media/tv/tuner/Tuner$Descrambler"),
+ gFields.descramblerInitID,
+ mObject);
+
+ descramblerSp->incStrong(descramblerObj);
+ env->SetLongField(descramblerObj, gFields.descramblerContext, (jlong)descramblerSp.get());
+
+ return descramblerObj;
+}
+
jobject JTuner::openFilter(DemuxFilterType type, int bufferSize) {
if (mDemux == NULL) {
if (!openDemux()) {
@@ -238,7 +334,7 @@ jobject JTuner::openFilter(DemuxFilterType type, int bufferSize) {
using namespace android;
static sp<JTuner> setTuner(JNIEnv *env, jobject thiz, const sp<JTuner> &tuner) {
- sp<JTuner> old = (JTuner *)env->GetLongField(thiz, gFields.context);
+ sp<JTuner> old = (JTuner *)env->GetLongField(thiz, gFields.tunerContext);
if (tuner != NULL) {
tuner->incStrong(thiz);
@@ -246,13 +342,27 @@ static sp<JTuner> setTuner(JNIEnv *env, jobject thiz, const sp<JTuner> &tuner) {
if (old != NULL) {
old->decStrong(thiz);
}
- env->SetLongField(thiz, gFields.context, (jlong)tuner.get());
+ env->SetLongField(thiz, gFields.tunerContext, (jlong)tuner.get());
return old;
}
static sp<JTuner> getTuner(JNIEnv *env, jobject thiz) {
- return (JTuner *)env->GetLongField(thiz, gFields.context);
+ return (JTuner *)env->GetLongField(thiz, gFields.tunerContext);
+}
+
+static sp<IDescrambler> getDescrambler(JNIEnv *env, jobject descrambler) {
+ return (IDescrambler *)env->GetLongField(descrambler, gFields.descramblerContext);
+}
+
+static DemuxPid getDemuxPid(int pidType, int pid) {
+ DemuxPid demuxPid;
+ if ((int)pidType == 1) {
+ demuxPid.tPid(static_cast<DemuxTpid>(pid));
+ } else if ((int)pidType == 2) {
+ demuxPid.mmtpPid(static_cast<DemuxMmtpPid>(pid));
+ }
+ return demuxPid;
}
static sp<IFilter> getFilter(JNIEnv *env, jobject filter) {
@@ -263,21 +373,32 @@ static void android_media_tv_Tuner_native_init(JNIEnv *env) {
jclass clazz = env->FindClass("android/media/tv/tuner/Tuner");
CHECK(clazz != NULL);
- gFields.context = env->GetFieldID(clazz, "mNativeContext", "J");
- CHECK(gFields.context != NULL);
+ gFields.tunerContext = env->GetFieldID(clazz, "mNativeContext", "J");
+ CHECK(gFields.tunerContext != NULL);
gFields.onFrontendEventID = env->GetMethodID(clazz, "onFrontendEvent", "(I)V");
+ gFields.onLnbEventID = env->GetMethodID(clazz, "onLnbEvent", "(I)V");
+
jclass frontendClazz = env->FindClass("android/media/tv/tuner/Tuner$Frontend");
gFields.frontendInitID =
env->GetMethodID(frontendClazz, "<init>", "(Landroid/media/tv/tuner/Tuner;I)V");
+ jclass lnbClazz = env->FindClass("android/media/tv/tuner/Tuner$Lnb");
+ gFields.lnbInitID =
+ env->GetMethodID(lnbClazz, "<init>", "(Landroid/media/tv/tuner/Tuner;I)V");
+
jclass filterClazz = env->FindClass("android/media/tv/tuner/Tuner$Filter");
gFields.filterContext = env->GetFieldID(filterClazz, "mNativeContext", "J");
gFields.filterInitID =
env->GetMethodID(filterClazz, "<init>", "(Landroid/media/tv/tuner/Tuner;I)V");
gFields.onFilterStatusID =
env->GetMethodID(filterClazz, "onFilterStatus", "(I)V");
+
+ jclass descramblerClazz = env->FindClass("android/media/tv/tuner/Tuner$Descrambler");
+ gFields.descramblerContext = env->GetFieldID(descramblerClazz, "mNativeContext", "J");
+ gFields.descramblerInitID =
+ env->GetMethodID(descramblerClazz, "<init>", "(Landroid/media/tv/tuner/Tuner;)V");
}
static void android_media_tv_Tuner_native_setup(JNIEnv *env, jobject thiz) {
@@ -295,6 +416,16 @@ static jobject android_media_tv_Tuner_open_frontend_by_id(JNIEnv *env, jobject t
return tuner->openFrontendById(id);
}
+static jobject android_media_tv_Tuner_get_lnb_ids(JNIEnv *env, jobject thiz) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ return tuner->getLnbIds();
+}
+
+static jobject android_media_tv_Tuner_open_lnb_by_id(JNIEnv *env, jobject thiz, jint id) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ return tuner->openLnbById(id);
+}
+
static jobject android_media_tv_Tuner_open_filter(
JNIEnv *env, jobject thiz, jint type, jint subType, jint bufferSize) {
sp<JTuner> tuner = getTuner(env, thiz);
@@ -308,7 +439,61 @@ static jobject android_media_tv_Tuner_open_filter(
return tuner->openFilter(filterType, bufferSize);
}
-static const JNINativeMethod gMethods[] = {
+static bool android_media_tv_Tuner_start_filter(JNIEnv *env, jobject filter) {
+ sp<IFilter> filterSp = getFilter(env, filter);
+ if (filterSp == NULL) {
+ ALOGD("Failed to start filter: filter not found");
+ return false;
+ }
+ return filterSp->start() == Result::SUCCESS;
+}
+
+static bool android_media_tv_Tuner_stop_filter(JNIEnv *env, jobject filter) {
+ sp<IFilter> filterSp = getFilter(env, filter);
+ if (filterSp == NULL) {
+ ALOGD("Failed to stop filter: filter not found");
+ return false;
+ }
+ return filterSp->stop() == Result::SUCCESS;
+}
+
+static bool android_media_tv_Tuner_flush_filter(JNIEnv *env, jobject filter) {
+ sp<IFilter> filterSp = getFilter(env, filter);
+ if (filterSp == NULL) {
+ ALOGD("Failed to flush filter: filter not found");
+ return false;
+ }
+ return filterSp->flush() == Result::SUCCESS;
+}
+
+static jobject android_media_tv_Tuner_open_descrambler(JNIEnv *env, jobject thiz) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ return tuner->openDescrambler();
+}
+
+static bool android_media_tv_Tuner_add_pid(
+ JNIEnv *env, jobject descrambler, jint pidType, jint pid, jobject filter) {
+ sp<IDescrambler> descramblerSp = getDescrambler(env, descrambler);
+ if (descramblerSp == NULL) {
+ return false;
+ }
+ sp<IFilter> filterSp = getFilter(env, filter);
+ Result result = descramblerSp->addPid(getDemuxPid((int)pidType, (int)pid), filterSp);
+ return result == Result::SUCCESS;
+}
+
+static bool android_media_tv_Tuner_remove_pid(
+ JNIEnv *env, jobject descrambler, jint pidType, jint pid, jobject filter) {
+ sp<IDescrambler> descramblerSp = getDescrambler(env, descrambler);
+ if (descramblerSp == NULL) {
+ return false;
+ }
+ sp<IFilter> filterSp = getFilter(env, filter);
+ Result result = descramblerSp->removePid(getDemuxPid((int)pidType, (int)pid), filterSp);
+ return result == Result::SUCCESS;
+}
+
+static const JNINativeMethod gTunerMethods[] = {
{ "nativeInit", "()V", (void *)android_media_tv_Tuner_native_init },
{ "nativeSetup", "()V", (void *)android_media_tv_Tuner_native_setup },
{ "nativeGetFrontendIds", "()Ljava/util/List;",
@@ -317,11 +502,48 @@ static const JNINativeMethod gMethods[] = {
(void *)android_media_tv_Tuner_open_frontend_by_id },
{ "nativeOpenFilter", "(III)Landroid/media/tv/tuner/Tuner$Filter;",
(void *)android_media_tv_Tuner_open_filter },
+ { "nativeGetLnbIds", "()Ljava/util/List;",
+ (void *)android_media_tv_Tuner_get_lnb_ids },
+ { "nativeOpenLnbById", "(I)Landroid/media/tv/tuner/Tuner$Lnb;",
+ (void *)android_media_tv_Tuner_open_lnb_by_id },
+ { "nativeOpenDescrambler", "()Landroid/media/tv/tuner/Tuner$Descrambler;",
+ (void *)android_media_tv_Tuner_open_descrambler },
+};
+
+static const JNINativeMethod gFilterMethods[] = {
+ { "nativeStartFilter", "()Z", (void *)android_media_tv_Tuner_start_filter },
+ { "nativeStopFilter", "()Z", (void *)android_media_tv_Tuner_stop_filter },
+ { "nativeFlushFilter", "()Z", (void *)android_media_tv_Tuner_flush_filter },
};
-static int register_android_media_tv_Tuner(JNIEnv *env) {
- return AndroidRuntime::registerNativeMethods(
- env, "android/media/tv/tuner/Tuner", gMethods, NELEM(gMethods));
+static const JNINativeMethod gDescramblerMethods[] = {
+ { "nativeAddPid", "(IILandroid/media/tv/tuner/Tuner$Filter;)Z",
+ (void *)android_media_tv_Tuner_add_pid },
+ { "nativeRemovePid", "(IILandroid/media/tv/tuner/Tuner$Filter;)Z",
+ (void *)android_media_tv_Tuner_remove_pid },
+};
+
+static bool register_android_media_tv_Tuner(JNIEnv *env) {
+ if (AndroidRuntime::registerNativeMethods(
+ env, "android/media/tv/tuner/Tuner", gTunerMethods, NELEM(gTunerMethods)) != JNI_OK) {
+ ALOGE("Failed to register tuner native methods");
+ return false;
+ }
+ if (AndroidRuntime::registerNativeMethods(
+ env, "android/media/tv/tuner/Tuner$Filter",
+ gFilterMethods,
+ NELEM(gFilterMethods)) != JNI_OK) {
+ ALOGE("Failed to register filter native methods");
+ return false;
+ }
+ if (AndroidRuntime::registerNativeMethods(
+ env, "android/media/tv/tuner/Tuner$Descrambler",
+ gDescramblerMethods,
+ NELEM(gDescramblerMethods)) != JNI_OK) {
+ ALOGE("Failed to register descrambler native methods");
+ return false;
+ }
+ return true;
}
jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
@@ -335,7 +557,7 @@ jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
}
assert(env != NULL);
- if (register_android_media_tv_Tuner(env) != JNI_OK) {
+ if (!register_android_media_tv_Tuner(env)) {
ALOGE("ERROR: Tuner native registration failed\n");
return result;
}
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index ab48761f36be..d3aec64c075e 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -28,19 +28,33 @@ using ::android::hardware::hidl_vec;
using ::android::hardware::tv::tuner::V1_0::DemuxFilterEvent;
using ::android::hardware::tv::tuner::V1_0::DemuxFilterStatus;
using ::android::hardware::tv::tuner::V1_0::DemuxFilterType;
+using ::android::hardware::tv::tuner::V1_0::DemuxPid;
using ::android::hardware::tv::tuner::V1_0::FrontendEventType;
using ::android::hardware::tv::tuner::V1_0::FrontendId;
using ::android::hardware::tv::tuner::V1_0::FrontendScanMessage;
using ::android::hardware::tv::tuner::V1_0::FrontendScanMessageType;
using ::android::hardware::tv::tuner::V1_0::IDemux;
+using ::android::hardware::tv::tuner::V1_0::IDescrambler;
using ::android::hardware::tv::tuner::V1_0::IFilter;
using ::android::hardware::tv::tuner::V1_0::IFilterCallback;
using ::android::hardware::tv::tuner::V1_0::IFrontend;
using ::android::hardware::tv::tuner::V1_0::IFrontendCallback;
+using ::android::hardware::tv::tuner::V1_0::ILnb;
+using ::android::hardware::tv::tuner::V1_0::ILnbCallback;
using ::android::hardware::tv::tuner::V1_0::ITuner;
+using ::android::hardware::tv::tuner::V1_0::LnbEventType;
+using ::android::hardware::tv::tuner::V1_0::LnbId;
namespace android {
+struct LnbCallback : public ILnbCallback {
+ LnbCallback(jweak tunerObj, LnbId id);
+ virtual Return<void> onEvent(LnbEventType lnbEventType);
+ virtual Return<void> onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage);
+ jweak mObject;
+ LnbId mId;
+};
+
struct FilterCallback : public IFilterCallback {
virtual Return<void> onFilterEvent(const DemuxFilterEvent& filterEvent);
virtual Return<void> onFilterStatus(const DemuxFilterStatus status);
@@ -67,7 +81,11 @@ struct JTuner : public RefBase {
sp<ITuner> getTunerService();
jobject getFrontendIds();
jobject openFrontendById(int id);
+ jobject getLnbIds();
+ jobject openLnbById(int id);
jobject openFilter(DemuxFilterType type, int bufferSize);
+ jobject openDescrambler();
+
protected:
bool openDemux();
virtual ~JTuner();
@@ -78,6 +96,8 @@ private:
static sp<ITuner> mTuner;
hidl_vec<FrontendId> mFeIds;
sp<IFrontend> mFe;
+ hidl_vec<LnbId> mLnbIds;
+ sp<ILnb> mLnb;
sp<IDemux> mDemux;
int mDemuxId;
};
diff --git a/media/lib/tvremote/java/com/android/media/tv/remoteprovider/TvRemoteProvider.java b/media/lib/tvremote/java/com/android/media/tv/remoteprovider/TvRemoteProvider.java
index 35322ad8ee6b..0bf0f97d2c5e 100644
--- a/media/lib/tvremote/java/com/android/media/tv/remoteprovider/TvRemoteProvider.java
+++ b/media/lib/tvremote/java/com/android/media/tv/remoteprovider/TvRemoteProvider.java
@@ -19,18 +19,18 @@ package com.android.media.tv.remoteprovider;
import android.content.Context;
import android.media.tv.ITvRemoteProvider;
import android.media.tv.ITvRemoteServiceInput;
-import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
+import java.util.LinkedList;
+
/**
* Base class for emote providers implemented in unbundled service.
* <p/>
* This object is not thread safe. It is only intended to be accessed on the
* {@link Context#getMainLooper main looper thread} of an application.
+ * The callback {@link #onInputBridgeConnected()} may be called from a different thread.
* </p><p>
* IMPORTANT: This class is effectively a system API for unbundled emote service, and
* must remain API stable. See README.txt in the root of this package for more information.
@@ -50,11 +50,9 @@ public abstract class TvRemoteProvider {
private static final String TAG = "TvRemoteProvider";
private static final boolean DEBUG_KEYS = false;
- private static final int MSG_SET_SERVICE_INPUT = 1;
- private static final int MSG_SEND_INPUTBRIDGE_CONNECTED = 2;
private final Context mContext;
private final ProviderStub mStub;
- private final ProviderHandler mHandler;
+ private final LinkedList<Runnable> mOpenBridgeRunnables;
private ITvRemoteServiceInput mRemoteServiceInput;
/**
@@ -67,7 +65,7 @@ public abstract class TvRemoteProvider {
public TvRemoteProvider(Context context) {
mContext = context.getApplicationContext();
mStub = new ProviderStub();
- mHandler = new ProviderHandler(mContext.getMainLooper());
+ mOpenBridgeRunnables = new LinkedList<Runnable>();
}
/**
@@ -77,7 +75,6 @@ public abstract class TvRemoteProvider {
return mContext;
}
-
/**
* Gets the Binder associated with the provider.
* <p>
@@ -105,7 +102,11 @@ public abstract class TvRemoteProvider {
* @param tvServiceInput sink defined in framework service
*/
private void setRemoteServiceInputSink(ITvRemoteServiceInput tvServiceInput) {
- mRemoteServiceInput = tvServiceInput;
+ synchronized (mOpenBridgeRunnables) {
+ mRemoteServiceInput = tvServiceInput;
+ }
+ mOpenBridgeRunnables.forEach(Runnable::run);
+ mOpenBridgeRunnables.clear();
}
/**
@@ -125,8 +126,25 @@ public abstract class TvRemoteProvider {
*/
public void openRemoteInputBridge(IBinder token, String name, int width, int height,
int maxPointers) throws RuntimeException {
+ synchronized (mOpenBridgeRunnables) {
+ if (mRemoteServiceInput == null) {
+ Log.d(TAG, "Delaying openRemoteInputBridge() for " + name);
+
+ mOpenBridgeRunnables.add(() -> {
+ try {
+ mRemoteServiceInput.openInputBridge(
+ token, name, width, height, maxPointers);
+ Log.d(TAG, "Delayed openRemoteInputBridge() for " + name + ": success");
+ } catch (RemoteException re) {
+ Log.e(TAG, "Delayed openRemoteInputBridge() for " + name + ": failure", re);
+ }
+ });
+ return;
+ }
+ }
try {
mRemoteServiceInput.openInputBridge(token, name, width, height, maxPointers);
+ Log.d(TAG, "openRemoteInputBridge() for " + name + ": success");
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -271,33 +289,12 @@ public abstract class TvRemoteProvider {
private final class ProviderStub extends ITvRemoteProvider.Stub {
@Override
public void setRemoteServiceInputSink(ITvRemoteServiceInput tvServiceInput) {
- mHandler.obtainMessage(MSG_SET_SERVICE_INPUT, tvServiceInput).sendToTarget();
+ TvRemoteProvider.this.setRemoteServiceInputSink(tvServiceInput);
}
@Override
public void onInputBridgeConnected(IBinder token) {
- mHandler.obtainMessage(MSG_SEND_INPUTBRIDGE_CONNECTED, 0, 0,
- (IBinder) token).sendToTarget();
- }
- }
-
- private final class ProviderHandler extends Handler {
- public ProviderHandler(Looper looper) {
- super(looper, null, true);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_SET_SERVICE_INPUT: {
- setRemoteServiceInputSink((ITvRemoteServiceInput) msg.obj);
- break;
- }
- case MSG_SEND_INPUTBRIDGE_CONNECTED: {
- onInputBridgeConnected((IBinder) msg.obj);
- break;
- }
- }
+ TvRemoteProvider.this.onInputBridgeConnected(token);
}
}
}
diff --git a/media/lib/tvremote/tests/Android.bp b/media/lib/tvremote/tests/Android.bp
new file mode 100644
index 000000000000..f00eed070798
--- /dev/null
+++ b/media/lib/tvremote/tests/Android.bp
@@ -0,0 +1,15 @@
+android_test {
+ name: "TvRemoteTests",
+ srcs: ["src/**/*.java"],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ "com.android.media.tv.remoteprovider",
+ ],
+ static_libs: [
+ "mockito-target-minus-junit4",
+ ],
+ platform_apis: true,
+ certificate: "platform",
+ privileged: true,
+}
diff --git a/media/lib/tvremote/tests/AndroidManifest.xml b/media/lib/tvremote/tests/AndroidManifest.xml
new file mode 100644
index 000000000000..4f843f701d20
--- /dev/null
+++ b/media/lib/tvremote/tests/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.media.tv.remoteprovider">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="com.android.media.tv.remoteprovider"
+ android:label="Tests for TV Remote"/>
+</manifest>
diff --git a/media/lib/tvremote/tests/src/com/android/media/tv/remoteprovider/TvRemoteProviderTest.java b/media/lib/tvremote/tests/src/com/android/media/tv/remoteprovider/TvRemoteProviderTest.java
new file mode 100644
index 000000000000..c9ce56138217
--- /dev/null
+++ b/media/lib/tvremote/tests/src/com/android/media/tv/remoteprovider/TvRemoteProviderTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.media.tv.remoteprovider;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.content.Context;
+import android.media.tv.ITvRemoteProvider;
+import android.media.tv.ITvRemoteServiceInput;
+import android.os.Binder;
+import android.os.IBinder;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.util.ArrayList;
+
+public class TvRemoteProviderTest extends AndroidTestCase {
+ private static final String TAG = TvRemoteProviderTest.class.getSimpleName();
+
+ @SmallTest
+ public void testOpenRemoteInputBridge() throws Exception {
+ Binder tokenA = new Binder();
+ Binder tokenB = new Binder();
+ Binder tokenC = new Binder();
+
+ class LocalTvRemoteProvider extends TvRemoteProvider {
+ private final ArrayList<IBinder> mTokens = new ArrayList<IBinder>();
+
+ LocalTvRemoteProvider(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onInputBridgeConnected(IBinder token) {
+ mTokens.add(token);
+ }
+
+ public boolean verifyTokens() {
+ return mTokens.size() == 3
+ && mTokens.contains(tokenA)
+ && mTokens.contains(tokenB)
+ && mTokens.contains(tokenC);
+ }
+ }
+
+ LocalTvRemoteProvider tvProvider = new LocalTvRemoteProvider(getContext());
+ ITvRemoteProvider binder = (ITvRemoteProvider) tvProvider.getBinder();
+
+ ITvRemoteServiceInput tvServiceInput = mock(ITvRemoteServiceInput.class);
+ doAnswer((i) -> {
+ binder.onInputBridgeConnected(i.getArgument(0));
+ return null;
+ }).when(tvServiceInput).openInputBridge(any(), any(), anyInt(), anyInt(), anyInt());
+
+ tvProvider.openRemoteInputBridge(tokenA, "A", 1, 1, 1);
+ tvProvider.openRemoteInputBridge(tokenB, "B", 1, 1, 1);
+ binder.setRemoteServiceInputSink(tvServiceInput);
+ tvProvider.openRemoteInputBridge(tokenC, "C", 1, 1, 1);
+
+ verify(tvServiceInput).openInputBridge(tokenA, "A", 1, 1, 1);
+ verify(tvServiceInput).openInputBridge(tokenB, "B", 1, 1, 1);
+ verify(tvServiceInput).openInputBridge(tokenC, "C", 1, 1, 1);
+ verifyNoMoreInteractions(tvServiceInput);
+
+ assertTrue(tvProvider.verifyTokens());
+ }
+}
diff --git a/mime/java-res/android.mime.types b/mime/java-res/android.mime.types
index ce022a8b0f19..cb04d921bd67 100644
--- a/mime/java-res/android.mime.types
+++ b/mime/java-res/android.mime.types
@@ -73,6 +73,7 @@
?audio/3gpp 3gpp 3ga
?audio/aac-adts aac
?audio/ac3 ac3 a52
+?audio/amr amr
?audio/imelody imy
?audio/midi rtttl xmf
?audio/mobile-xmf mxmf
diff --git a/native/android/Android.bp b/native/android/Android.bp
index 91297b0de02e..9d93c9b7b605 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -36,7 +36,6 @@ cc_library_shared {
srcs: [
"asset_manager.cpp",
- "choreographer.cpp",
"configuration.cpp",
"hardware_buffer_jni.cpp",
"input.cpp",
@@ -80,7 +79,7 @@ cc_library_shared {
"libarect",
],
- whole_static_libs: ["libnativewindow"],
+ whole_static_libs: ["libnativedisplay", "libnativewindow"],
export_static_lib_headers: ["libarect"],
@@ -142,4 +141,4 @@ filegroup {
"aidl/com/android/internal/compat/IPlatformCompatNative.aidl",
],
path: "aidl",
-} \ No newline at end of file
+}
diff --git a/native/android/choreographer.cpp b/native/android/choreographer.cpp
deleted file mode 100644
index 63e073405fe0..000000000000
--- a/native/android/choreographer.cpp
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "Choreographer"
-//#define LOG_NDEBUG 0
-
-#include <cinttypes>
-#include <queue>
-#include <thread>
-
-#include <android/choreographer.h>
-#include <androidfw/DisplayEventDispatcher.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/SurfaceComposerClient.h>
-#include <utils/Looper.h>
-#include <utils/Mutex.h>
-#include <utils/Timers.h>
-
-namespace android {
-
-static inline const char* toString(bool value) {
- return value ? "true" : "false";
-}
-
-struct FrameCallback {
- AChoreographer_frameCallback callback;
- AChoreographer_frameCallback64 callback64;
- void* data;
- nsecs_t dueTime;
-
- inline bool operator<(const FrameCallback& rhs) const {
- // Note that this is intentionally flipped because we want callbacks due sooner to be at
- // the head of the queue
- return dueTime > rhs.dueTime;
- }
-};
-
-
-class Choreographer : public DisplayEventDispatcher, public MessageHandler {
-public:
- void postFrameCallbackDelayed(AChoreographer_frameCallback cb,
- AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay);
-
- enum {
- MSG_SCHEDULE_CALLBACKS = 0,
- MSG_SCHEDULE_VSYNC = 1
- };
- virtual void handleMessage(const Message& message) override;
-
- static Choreographer* getForThread();
-
-protected:
- virtual ~Choreographer() = default;
-
-private:
- explicit Choreographer(const sp<Looper>& looper);
- Choreographer(const Choreographer&) = delete;
-
- void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) override;
- void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
- void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
- int32_t configId) override;
-
- void scheduleCallbacks();
-
- // Protected by mLock
- std::priority_queue<FrameCallback> mCallbacks;
-
- mutable Mutex mLock;
-
- const sp<Looper> mLooper;
- const std::thread::id mThreadId;
-};
-
-
-static thread_local Choreographer* gChoreographer;
-Choreographer* Choreographer::getForThread() {
- if (gChoreographer == nullptr) {
- sp<Looper> looper = Looper::getForThread();
- if (!looper.get()) {
- ALOGW("No looper prepared for thread");
- return nullptr;
- }
- gChoreographer = new Choreographer(looper);
- status_t result = gChoreographer->initialize();
- if (result != OK) {
- ALOGW("Failed to initialize");
- return nullptr;
- }
- }
- return gChoreographer;
-}
-
-Choreographer::Choreographer(const sp<Looper>& looper) :
- DisplayEventDispatcher(looper), mLooper(looper), mThreadId(std::this_thread::get_id()) {
-}
-
-void Choreographer::postFrameCallbackDelayed(
- AChoreographer_frameCallback cb, AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay) {
- nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- FrameCallback callback{cb, cb64, data, now + delay};
- {
- AutoMutex _l{mLock};
- mCallbacks.push(callback);
- }
- if (callback.dueTime <= now) {
- if (std::this_thread::get_id() != mThreadId) {
- Message m{MSG_SCHEDULE_VSYNC};
- mLooper->sendMessage(this, m);
- } else {
- scheduleVsync();
- }
- } else {
- Message m{MSG_SCHEDULE_CALLBACKS};
- mLooper->sendMessageDelayed(delay, this, m);
- }
-}
-
-void Choreographer::scheduleCallbacks() {
- AutoMutex _{mLock};
- nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- if (mCallbacks.top().dueTime <= now) {
- ALOGV("choreographer %p ~ scheduling vsync", this);
- scheduleVsync();
- return;
- }
-}
-
-// TODO(b/74619554): The PhysicalDisplayId is ignored because SF only emits VSYNC events for the
-// internal display and DisplayEventReceiver::requestNextVsync only allows requesting VSYNC for
-// the internal display implicitly.
-void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t) {
- std::vector<FrameCallback> callbacks{};
- {
- AutoMutex _l{mLock};
- nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- while (!mCallbacks.empty() && mCallbacks.top().dueTime < now) {
- callbacks.push_back(mCallbacks.top());
- mCallbacks.pop();
- }
- }
- for (const auto& cb : callbacks) {
- if (cb.callback64 != nullptr) {
- cb.callback64(timestamp, cb.data);
- } else if (cb.callback != nullptr) {
- cb.callback(timestamp, cb.data);
- }
- }
-}
-
-void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) {
- ALOGV("choreographer %p ~ received hotplug event (displayId=%"
- ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", connected=%s), ignoring.",
- this, displayId, toString(connected));
-}
-
-void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId displayId,
- int32_t configId) {
- ALOGV("choreographer %p ~ received config changed event (displayId=%"
- ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", configId=%s), ignoring.",
- this, displayId, toString(configId));
-}
-
-void Choreographer::handleMessage(const Message& message) {
- switch (message.what) {
- case MSG_SCHEDULE_CALLBACKS:
- scheduleCallbacks();
- break;
- case MSG_SCHEDULE_VSYNC:
- scheduleVsync();
- break;
- }
-}
-
-}
-
-/* Glue for the NDK interface */
-
-using android::Choreographer;
-
-static inline Choreographer* AChoreographer_to_Choreographer(AChoreographer* choreographer) {
- return reinterpret_cast<Choreographer*>(choreographer);
-}
-
-static inline AChoreographer* Choreographer_to_AChoreographer(Choreographer* choreographer) {
- return reinterpret_cast<AChoreographer*>(choreographer);
-}
-
-AChoreographer* AChoreographer_getInstance() {
- return Choreographer_to_AChoreographer(Choreographer::getForThread());
-}
-
-void AChoreographer_postFrameCallback(AChoreographer* choreographer,
- AChoreographer_frameCallback callback, void* data) {
- AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
- callback, nullptr, data, 0);
-}
-void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer,
- AChoreographer_frameCallback callback, void* data, long delayMillis) {
- AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
- callback, nullptr, data, ms2ns(delayMillis));
-}
-void AChoreographer_postFrameCallback64(AChoreographer* choreographer,
- AChoreographer_frameCallback64 callback, void* data) {
- AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
- nullptr, callback, data, 0);
-}
-void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer,
- AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis) {
- AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
- nullptr, callback, data, ms2ns(delayMillis));
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
index c35303e1e6c9..2bd5fe228f41 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
@@ -19,36 +19,16 @@ package com.android.systemui;
import android.content.Context;
import com.android.systemui.dagger.SystemUIRootComponent;
-import com.android.systemui.navigationbar.car.CarFacetButtonController;
-
-import javax.inject.Singleton;
-
-import dagger.Component;
/**
* Class factory to provide car specific SystemUI components.
*/
public class CarSystemUIFactory extends SystemUIFactory {
- private CarDependencyComponent mCarDependencyComponent;
-
@Override
protected SystemUIRootComponent buildSystemUIRootComponent(Context context) {
- mCarDependencyComponent = DaggerCarSystemUIFactory_CarDependencyComponent.builder()
- .contextHolder(new ContextHolder(context))
- .build();
return DaggerCarSystemUIRootComponent.builder()
.contextHolder(new ContextHolder(context))
.build();
}
-
- public CarDependencyComponent getCarDependencyComponent() {
- return mCarDependencyComponent;
- }
-
- @Singleton
- @Component(modules = ContextHolder.class)
- public interface CarDependencyComponent {
- CarFacetButtonController getCarFacetButtonController();
- }
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
index 91d00262dc18..818fdeaef8db 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -38,7 +38,6 @@ import com.android.systemui.statusbar.car.CarStatusBar;
import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -101,7 +100,7 @@ abstract class CarSystemUIModule {
abstract DockManager bindDockManager(DockManagerImpl dockManager);
@Binds
- abstract NotificationData.KeyguardEnvironment bindKeyguardEnvironment(
+ abstract NotificationEntryManager.KeyguardEnvironment bindKeyguardEnvironment(
KeyguardEnvironmentImpl keyguardEnvironment);
@Binds
diff --git a/packages/CarSystemUI/src/com/android/systemui/TEST_MAPPING b/packages/CarSystemUI/src/com/android/systemui/TEST_MAPPING
new file mode 100644
index 000000000000..f90947cbeb15
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "auto-postsubmit": [
+ {
+ "name": "AndroidAutoUiTests",
+ "options" : [
+ {
+ "include-filter": "android.test.functional.auto.apps.HomeHelperTest"
+ }
+ ]
+ }
+ ]
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
index 53a88a9a54e9..ed945e7d4e72 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
@@ -19,8 +19,9 @@ import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
import com.android.systemui.statusbar.notification.logging.NotifLog;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -35,8 +36,12 @@ import javax.inject.Singleton;
public class CarNotificationEntryManager extends NotificationEntryManager {
@Inject
- public CarNotificationEntryManager(NotificationData notificationData, NotifLog notifLog) {
- super(notificationData, notifLog);
+ public CarNotificationEntryManager(
+ NotifLog notifLog,
+ NotificationGroupManager groupManager,
+ NotificationRankingManager rankingManager,
+ KeyguardEnvironment keyguardEnvironment) {
+ super(notifLog, groupManager, rankingManager, keyguardEnvironment);
}
@Override
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarServiceProvider.java b/packages/CarSystemUI/src/com/android/systemui/car/CarServiceProvider.java
index f8bfeec6df2d..80ee37127965 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/CarServiceProvider.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarServiceProvider.java
@@ -19,6 +19,8 @@ package com.android.systemui.car;
import android.car.Car;
import android.content.Context;
+import androidx.annotation.VisibleForTesting;
+
import java.util.ArrayList;
import java.util.List;
@@ -50,6 +52,12 @@ public class CarServiceProvider {
});
}
+ @VisibleForTesting
+ public CarServiceProvider(Context context, Car car) {
+ mContext = context;
+ mCar = car;
+ }
+
/**
* Let's other components hook into the connection to the car service. If we're already
* connected to the car service, the callback is immediately triggered.
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarFacetButton.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarFacetButton.java
index c46e6e7433a3..0b8999263c73 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarFacetButton.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarFacetButton.java
@@ -29,9 +29,7 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import com.android.keyguard.AlphaOptimizedImageButton;
-import com.android.systemui.CarSystemUIFactory;
import com.android.systemui.R;
-import com.android.systemui.SystemUIFactory;
/**
* CarFacetButton is a ui component designed to be used as a shortcut for an app of a defined
@@ -82,10 +80,6 @@ public class CarFacetButton extends LinearLayout {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarFacetButton);
setupIntents(typedArray);
setupIcons(typedArray);
- CarSystemUIFactory factory = SystemUIFactory.getInstance();
- CarFacetButtonController carFacetButtonController = factory.getCarDependencyComponent()
- .getCarFacetButtonController();
- carFacetButtonController.addFacetButton(this);
}
/**
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarFacetButtonController.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarFacetButtonController.java
index 30f63f052b9f..f66e8280197e 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarFacetButtonController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarFacetButtonController.java
@@ -43,6 +43,8 @@ import javax.inject.Singleton;
@Singleton
public class CarFacetButtonController {
+ private final Set<CarFacetButton> mRegisteredViews = new HashSet<>();
+
protected ButtonMap mButtonsByCategory = new ButtonMap();
protected ButtonMap mButtonsByPackage = new ButtonMap();
protected ButtonMap mButtonsByComponentName = new ButtonMap();
@@ -60,7 +62,11 @@ public class CarFacetButtonController {
* to get a reference to this controller via {@link com.android.systemui.Dependency}
* and self add.
*/
- public void addFacetButton(CarFacetButton facetButton) {
+ private void addFacetButton(CarFacetButton facetButton) {
+ if (mRegisteredViews.contains(facetButton)) {
+ return;
+ }
+
String[] categories = facetButton.getCategories();
for (int i = 0; i < categories.length; i++) {
mButtonsByCategory.add(categories[i], facetButton);
@@ -74,6 +80,8 @@ public class CarFacetButtonController {
for (int i = 0; i < componentNames.length; i++) {
mButtonsByComponentName.add(componentNames[i], facetButton);
}
+
+ mRegisteredViews.add(facetButton);
}
/** Removes all buttons from the button maps. */
@@ -82,6 +90,7 @@ public class CarFacetButtonController {
mButtonsByPackage.clear();
mButtonsByComponentName.clear();
mSelectedFacetButtons.clear();
+ mRegisteredViews.clear();
}
/**
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/hvac/HvacController.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/hvac/HvacController.java
index af2cb0ab5950..fd9c488278ba 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/hvac/HvacController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/hvac/HvacController.java
@@ -32,10 +32,12 @@ import com.android.systemui.car.CarServiceProvider;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -49,6 +51,7 @@ public class HvacController {
public static final String TAG = "HvacController";
private final CarServiceProvider mCarServiceProvider;
+ private final Set<TemperatureView> mRegisteredViews = new HashSet<>();
private CarHvacManager mHvacManager;
private HashMap<HvacKey, List<TemperatureView>> mTempComponents = new HashMap<>();
@@ -112,7 +115,10 @@ public class HvacController {
/**
* Add component to list and initialize it if the connection is up.
*/
- public void addHvacTextView(TemperatureView temperatureView) {
+ private void addHvacTextView(TemperatureView temperatureView) {
+ if (mRegisteredViews.contains(temperatureView)) {
+ return;
+ }
HvacKey hvacKey = new HvacKey(temperatureView.getPropertyId(), temperatureView.getAreaId());
if (!mTempComponents.containsKey(hvacKey)) {
@@ -120,6 +126,8 @@ public class HvacController {
}
mTempComponents.get(hvacKey).add(temperatureView);
initComponent(temperatureView);
+
+ mRegisteredViews.add(temperatureView);
}
private void initComponents() {
@@ -165,6 +173,7 @@ public class HvacController {
*/
public void removeAllComponents() {
mTempComponents.clear();
+ mRegisteredViews.clear();
}
/**
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/hvac/HvacControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/hvac/HvacControllerTest.java
new file mode 100644
index 000000000000..a71d1db3ee70
--- /dev/null
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/hvac/HvacControllerTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.navigationbar.car.hvac;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.car.Car;
+import android.car.hardware.hvac.CarHvacManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.car.CarServiceProvider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class HvacControllerTest extends SysuiTestCase {
+
+ private static final int PROPERTY_ID = 1;
+ private static final int AREA_ID = 1;
+ private static final float VALUE = 72.0f;
+
+ private HvacController mHvacController;
+ private CarServiceProvider mCarServiceProvider;
+
+ @Mock
+ private Car mCar;
+ @Mock
+ private CarHvacManager mCarHvacManager;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mCar.isConnected()).thenReturn(true);
+ when(mCar.getCarManager(Car.HVAC_SERVICE)).thenReturn(mCarHvacManager);
+
+ mCarServiceProvider = new CarServiceProvider(mContext, mCar);
+ mHvacController = new HvacController(mCarServiceProvider);
+ mHvacController.connectToCarService();
+ }
+
+ @Test
+ public void connectToCarService_registersCallback() {
+ verify(mCarHvacManager).registerCallback(any());
+ }
+
+ @Test
+ public void addTemperatureViewToController_usingTemperatureView_registersView() {
+ TemperatureTextView v = setupMockTemperatureTextView(PROPERTY_ID, AREA_ID, VALUE);
+ mHvacController.addTemperatureViewToController(v);
+
+ verify(v).setTemp(VALUE);
+ }
+
+ @Test
+ public void addTemperatureViewToController_usingSameTemperatureView_registersFirstView() {
+ TemperatureTextView v = setupMockTemperatureTextView(PROPERTY_ID, AREA_ID, VALUE);
+ mHvacController.addTemperatureViewToController(v);
+ verify(v).setTemp(VALUE);
+ resetTemperatureView(v, PROPERTY_ID, AREA_ID);
+
+ mHvacController.addTemperatureViewToController(v);
+ verify(v, never()).setTemp(VALUE);
+ }
+
+ @Test
+ public void addTemperatureViewToController_usingDifferentTemperatureView_registersBothViews() {
+ TemperatureTextView v1 = setupMockTemperatureTextView(PROPERTY_ID, AREA_ID, VALUE);
+ mHvacController.addTemperatureViewToController(v1);
+ verify(v1).setTemp(VALUE);
+
+ TemperatureTextView v2 = setupMockTemperatureTextView(
+ PROPERTY_ID + 1,
+ AREA_ID + 1,
+ VALUE + 1);
+ mHvacController.addTemperatureViewToController(v2);
+ verify(v2).setTemp(VALUE + 1);
+ }
+
+ @Test
+ public void removeAllComponents_ableToRegisterSameView() {
+ TemperatureTextView v = setupMockTemperatureTextView(PROPERTY_ID, AREA_ID, VALUE);
+ mHvacController.addTemperatureViewToController(v);
+ verify(v).setTemp(VALUE);
+
+ mHvacController.removeAllComponents();
+ resetTemperatureView(v, PROPERTY_ID, AREA_ID);
+
+ mHvacController.addTemperatureViewToController(v);
+ verify(v).setTemp(VALUE);
+ }
+
+ private TemperatureTextView setupMockTemperatureTextView(int propertyId, int areaId,
+ float value) {
+ TemperatureTextView v = mock(TemperatureTextView.class);
+ resetTemperatureView(v, propertyId, areaId);
+ when(mCarHvacManager.isPropertyAvailable(propertyId, areaId)).thenReturn(true);
+ when(mCarHvacManager.getFloatProperty(propertyId, areaId)).thenReturn(value);
+ return v;
+ }
+
+ private void resetTemperatureView(TemperatureTextView view, int propertyId, int areaId) {
+ reset(view);
+ when(view.getPropertyId()).thenReturn(propertyId);
+ when(view.getAreaId()).thenReturn(areaId);
+ }
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 776593590a67..f5d1ccfe378b 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -154,7 +154,6 @@ public class SettingsProvider extends ContentProvider {
private static final String TABLE_SYSTEM = "system";
private static final String TABLE_SECURE = "secure";
private static final String TABLE_GLOBAL = "global";
- private static final String TABLE_CONFIG = "config";
// Old tables no longer exist.
private static final String TABLE_FAVORITES = "favorites";
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index b9fe9334d14c..91b22d178b9f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -46,6 +46,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SystemUIFactory;
@@ -617,6 +618,15 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
StatsLog.write(StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED,
StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__SUCCESS);
mLockPatternUtils.reportSuccessfulPasswordAttempt(userId);
+ // Force a garbage collection in an attempt to erase any lockscreen password left in
+ // memory. Do it asynchronously with a 5-sec delay to avoid making the keyguard
+ // dismiss animation janky.
+ ThreadUtils.postOnBackgroundThread(() -> {
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException ignored) { }
+ Runtime.getRuntime().gc();
+ });
} else {
StatsLog.write(StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED,
StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__FAILURE);
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index dca5c8a5a36f..1a0690c1a273 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -75,10 +75,10 @@ import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.NotificationAlertingManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
import com.android.systemui.statusbar.notification.NotificationFilter;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ChannelEditorDialogController;
import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
index 385de4acfea8..15a5c2773f0b 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
@@ -139,8 +139,7 @@ public class ForegroundServiceController {
// Update appOp if there's an associated pending or visible notification:
final String foregroundKey = getStandardLayoutKey(userId, packageName);
if (foregroundKey != null) {
- final NotificationEntry entry = mEntryManager.getPendingOrCurrentNotif(
- foregroundKey);
+ final NotificationEntry entry = mEntryManager.getPendingOrActiveNotif(foregroundKey);
if (entry != null
&& uid == entry.getSbn().getUid()
&& packageName.equals(entry.getSbn().getPackageName())) {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index aab4041aba5f..4b28e4af7d8b 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -141,7 +141,7 @@ public class SystemUIApplication extends Application implements SysUiServiceProv
public void startServicesIfNeeded() {
String[] names = getResources().getStringArray(R.array.config_systemUIServiceComponents);
- startServicesIfNeeded(names);
+ startServicesIfNeeded(/* metricsPrefix= */ "StartServices", names);
}
/**
@@ -153,10 +153,10 @@ public class SystemUIApplication extends Application implements SysUiServiceProv
void startSecondaryUserServicesIfNeeded() {
String[] names =
getResources().getStringArray(R.array.config_systemUIServiceComponentsPerUser);
- startServicesIfNeeded(names);
+ startServicesIfNeeded(/* metricsPrefix= */ "StartSecondaryServices", names);
}
- private void startServicesIfNeeded(String[] services) {
+ private void startServicesIfNeeded(String metricsPrefix, String[] services) {
if (mServicesStarted) {
return;
}
@@ -177,12 +177,12 @@ public class SystemUIApplication extends Application implements SysUiServiceProv
Process.myUserHandle().getIdentifier() + ".");
TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",
Trace.TRACE_TAG_APP);
- log.traceBegin("StartServices");
+ log.traceBegin(metricsPrefix);
final int N = services.length;
for (int i = 0; i < N; i++) {
String clsName = services[i];
if (DEBUG) Log.d(TAG, "loading: " + clsName);
- log.traceBegin("StartServices" + clsName);
+ log.traceBegin(metricsPrefix + clsName);
long ti = System.currentTimeMillis();
try {
SystemUI obj = mComponentHelper.resolveSystemUI(clsName);
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java
index 4516996345b9..170c25a82101 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java
@@ -71,7 +71,7 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac
private final Runnable mHideHandles = this::hideHandles;
private final Runnable mShowAndGo = this::showAndGoInternal;
private final Provider<AssistHandleViewController> mAssistHandleViewController;
- private final PhenotypeHelper mPhenotypeHelper;
+ private final DeviceConfigHelper mDeviceConfigHelper;
private final Map<AssistHandleBehavior, BehaviorController> mBehaviorMap;
private boolean mHandlesShowing = false;
@@ -90,7 +90,7 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac
AssistUtils assistUtils,
@Named(ASSIST_HANDLE_THREAD_NAME) Handler handler,
Provider<AssistHandleViewController> assistHandleViewController,
- PhenotypeHelper phenotypeHelper,
+ DeviceConfigHelper deviceConfigHelper,
Map<AssistHandleBehavior, BehaviorController> behaviorMap,
NavigationModeController navigationModeController,
DumpController dumpController) {
@@ -98,14 +98,14 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac
mAssistUtils = assistUtils;
mHandler = handler;
mAssistHandleViewController = assistHandleViewController;
- mPhenotypeHelper = phenotypeHelper;
+ mDeviceConfigHelper = deviceConfigHelper;
mBehaviorMap = behaviorMap;
mInGesturalMode = QuickStepContract.isGesturalMode(
navigationModeController.addListener(this::handleNavigationModeChange));
setBehavior(getBehaviorMode());
- mPhenotypeHelper.addOnPropertiesChangedListener(
+ mDeviceConfigHelper.addOnPropertiesChangedListener(
mHandler::post,
(properties) -> {
if (properties.getKeyset().contains(
@@ -205,19 +205,19 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac
}
private long getShownFrequencyThreshold() {
- return mPhenotypeHelper.getLong(
+ return mDeviceConfigHelper.getLong(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOWN_FREQUENCY_THRESHOLD_MS,
DEFAULT_SHOWN_FREQUENCY_THRESHOLD_MS);
}
private long getShowAndGoDuration() {
- return mPhenotypeHelper.getLong(
+ return mDeviceConfigHelper.getLong(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DURATION_MS,
DEFAULT_SHOW_AND_GO_DURATION_MS);
}
private String getBehaviorMode() {
- return mPhenotypeHelper.getString(
+ return mDeviceConfigHelper.getString(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_BEHAVIOR_MODE,
DEFAULT_BEHAVIOR.toString());
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
index 46ae84a95fad..9793d727f038 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
@@ -155,7 +155,7 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
private final Clock mClock;
private final Handler mHandler;
- private final PhenotypeHelper mPhenotypeHelper;
+ private final DeviceConfigHelper mDeviceConfigHelper;
private final Lazy<StatusBarStateController> mStatusBarStateController;
private final Lazy<ActivityManagerWrapper> mActivityManagerWrapper;
private final Lazy<OverviewProxyService> mOverviewProxyService;
@@ -189,7 +189,7 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
AssistHandleReminderExpBehavior(
@Named(UPTIME_NAME) Clock clock,
@Named(ASSIST_HANDLE_THREAD_NAME) Handler handler,
- PhenotypeHelper phenotypeHelper,
+ DeviceConfigHelper deviceConfigHelper,
Lazy<StatusBarStateController> statusBarStateController,
Lazy<ActivityManagerWrapper> activityManagerWrapper,
Lazy<OverviewProxyService> overviewProxyService,
@@ -199,7 +199,7 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
Lazy<BroadcastDispatcher> broadcastDispatcher) {
mClock = clock;
mHandler = handler;
- mPhenotypeHelper = phenotypeHelper;
+ mDeviceConfigHelper = deviceConfigHelper;
mStatusBarStateController = statusBarStateController;
mActivityManagerWrapper = activityManagerWrapper;
mOverviewProxyService = overviewProxyService;
@@ -465,55 +465,55 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
}
private long getLearningTimeMs() {
- return mPhenotypeHelper.getLong(
+ return mDeviceConfigHelper.getLong(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_LEARN_TIME_MS,
DEFAULT_LEARNING_TIME_MS);
}
private int getLearningCount() {
- return mPhenotypeHelper.getInt(
+ return mDeviceConfigHelper.getInt(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_LEARN_COUNT,
DEFAULT_LEARNING_COUNT);
}
private long getShowAndGoDelayedShortDelayMs() {
- return mPhenotypeHelper.getLong(
+ return mDeviceConfigHelper.getLong(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DELAYED_SHORT_DELAY_MS,
DEFAULT_SHOW_AND_GO_DELAYED_SHORT_DELAY_MS);
}
private long getShowAndGoDelayedLongDelayMs() {
- return mPhenotypeHelper.getLong(
+ return mDeviceConfigHelper.getLong(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DELAYED_LONG_DELAY_MS,
DEFAULT_SHOW_AND_GO_DELAYED_LONG_DELAY_MS);
}
private long getShowAndGoDelayResetTimeoutMs() {
- return mPhenotypeHelper.getLong(
+ return mDeviceConfigHelper.getLong(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DELAY_RESET_TIMEOUT_MS,
DEFAULT_SHOW_AND_GO_DELAY_RESET_TIMEOUT_MS);
}
private boolean getSuppressOnLockscreen() {
- return mPhenotypeHelper.getBoolean(
+ return mDeviceConfigHelper.getBoolean(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_SUPPRESS_ON_LOCKSCREEN,
DEFAULT_SUPPRESS_ON_LOCKSCREEN);
}
private boolean getSuppressOnLauncher() {
- return mPhenotypeHelper.getBoolean(
+ return mDeviceConfigHelper.getBoolean(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_SUPPRESS_ON_LAUNCHER,
DEFAULT_SUPPRESS_ON_LAUNCHER);
}
private boolean getSuppressOnApps() {
- return mPhenotypeHelper.getBoolean(
+ return mDeviceConfigHelper.getBoolean(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_SUPPRESS_ON_APPS,
DEFAULT_SUPPRESS_ON_APPS);
}
private boolean getShowWhenTaught() {
- return mPhenotypeHelper.getBoolean(
+ return mDeviceConfigHelper.getBoolean(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_WHEN_TAUGHT,
DEFAULT_SHOW_WHEN_TAUGHT);
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/PhenotypeHelper.java b/packages/SystemUI/src/com/android/systemui/assist/DeviceConfigHelper.java
index 05a01dd48641..86b7c748ea77 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/PhenotypeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/DeviceConfigHelper.java
@@ -28,15 +28,15 @@ import javax.inject.Inject;
import javax.inject.Singleton;
/**
- * Wrapper class for retrieving phenotype flag values.
+ * Wrapper class for retrieving System UI device configuration values.
*
* Can be mocked in tests for ease of testing the effects of particular values.
*/
@Singleton
-public class PhenotypeHelper {
+public class DeviceConfigHelper {
@Inject
- public PhenotypeHelper() {}
+ public DeviceConfigHelper() {}
public long getLong(String name, long defaultValue) {
return whitelistIpcs(() ->
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 9f7bdd43fb11..db1185fb96f9 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -47,6 +47,7 @@ import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.RemoteException;
@@ -78,10 +79,10 @@ import com.android.systemui.statusbar.NotificationRemoveInterceptor;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -352,14 +353,13 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
* @param userId the id of the user
*/
private void restoreBubbles(@UserIdInt int userId) {
- NotificationData notificationData =
- mNotificationEntryManager.getNotificationData();
ArraySet<String> savedBubbleKeys = mSavedBubbleKeysPerUser.get(userId);
if (savedBubbleKeys == null) {
// There were no bubbles saved for this used.
return;
}
- for (NotificationEntry e : notificationData.getNotificationsForCurrentUser()) {
+ for (NotificationEntry e :
+ mNotificationEntryManager.getActiveNotificationsForCurrentUser()) {
if (savedBubbleKeys.contains(e.getKey())
&& mNotificationInterruptionStateProvider.shouldBubbleUp(e)
&& canLaunchInActivityView(mContext, e)) {
@@ -458,7 +458,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
public boolean isBubbleNotificationSuppressedFromShade(String key) {
boolean isBubbleAndSuppressed = mBubbleData.hasBubbleWithKey(key)
&& !mBubbleData.getBubbleWithKey(key).showInShadeWhenBubble();
- NotificationEntry entry = mNotificationEntryManager.getNotificationData().get(key);
+ NotificationEntry entry = mNotificationEntryManager.getActiveNotificationUnfiltered(key);
String groupKey = entry != null ? entry.getSbn().getGroupKey() : null;
boolean isSuppressedSummary = mBubbleData.isSummarySuppressed(groupKey);
boolean isSummary = key.equals(mBubbleData.getSummaryKey(groupKey));
@@ -571,7 +571,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
new NotificationRemoveInterceptor() {
@Override
public boolean onNotificationRemoveRequested(String key, int reason) {
- NotificationEntry entry = mNotificationEntryManager.getNotificationData().get(key);
+ NotificationEntry entry =
+ mNotificationEntryManager.getActiveNotificationUnfiltered(key);
String groupKey = entry != null ? entry.getSbn().getGroupKey() : null;
ArrayList<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(groupKey);
@@ -768,7 +769,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
String notifKey = mBubbleData.getSummaryKey(groupKey);
mBubbleData.removeSuppressedSummary(groupKey);
NotificationEntry entry =
- mNotificationEntryManager.getNotificationData().get(notifKey);
+ mNotificationEntryManager.getActiveNotificationUnfiltered(notifKey);
mNotificationEntryManager.performRemoveNotification(
entry.getSbn(), UNDEFINED_DISMISS_REASON);
}
@@ -1007,8 +1008,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
Log.w(TAG, "Unable to create bubble -- no intent: " + entry.getKey());
return false;
}
+ PackageManager packageManager = StatusBar.getPackageManagerForUser(
+ context, entry.getSbn().getUser().getIdentifier());
ActivityInfo info =
- intent.getIntent().resolveActivityInfo(context.getPackageManager(), 0);
+ intent.getIntent().resolveActivityInfo(packageManager, 0);
if (info == null) {
Log.w(TAG, "Unable to send as bubble, "
+ entry.getKey() + " couldn't find activity info for intent: "
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index f4d48b2a71a7..2ca993bd200a 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -184,8 +184,9 @@ public class BubbleData {
if (DEBUG_BUBBLE_DATA) {
Log.d(TAG, "notificationEntryUpdated: " + entry);
}
+
Bubble bubble = getBubbleWithKey(entry.getKey());
- suppressFlyout = !entry.getRanking().visuallyInterruptive() || suppressFlyout;
+ suppressFlyout |= !shouldShowFlyout(entry);
if (bubble == null) {
// Create a new bubble
@@ -298,6 +299,15 @@ public class BubbleData {
return bubbleChildren;
}
+ private boolean shouldShowFlyout(NotificationEntry notif) {
+ if (notif.getRanking().visuallyInterruptive()) {
+ return true;
+ }
+ final boolean suppressedFromShade = hasBubbleWithKey(notif.getKey())
+ && !getBubbleWithKey(notif.getKey()).showInShadeWhenBubble();
+ return suppressedFromShade;
+ }
+
private void doAdd(Bubble bubble) {
if (DEBUG_BUBBLE_DATA) {
Log.d(TAG, "doAdd: " + bubble);
@@ -510,7 +520,7 @@ public class BubbleData {
* required to keep grouping intact.
*
* @param minPosition the first insert point to consider
- * @param newBubble the bubble to insert
+ * @param newBubble the bubble to insert
* @return the position where the bubble was inserted
*/
private int insertBubble(int minPosition, Bubble newBubble) {
@@ -683,15 +693,19 @@ public class BubbleData {
* Description of current bubble data state.
*/
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.print("selected: "); pw.println(mSelectedBubble != null
+ pw.print("selected: ");
+ pw.println(mSelectedBubble != null
? mSelectedBubble.getKey()
: "null");
- pw.print("expanded: "); pw.println(mExpanded);
- pw.print("count: "); pw.println(mBubbles.size());
+ pw.print("expanded: ");
+ pw.println(mExpanded);
+ pw.print("count: ");
+ pw.println(mBubbles.size());
for (Bubble bubble : mBubbles) {
bubble.dump(fd, pw, args);
}
- pw.print("summaryKeys: "); pw.println(mSuppressedGroupKeys.size());
+ pw.print("summaryKeys: ");
+ pw.println(mSuppressedGroupKeys.size());
for (String key : mSuppressedGroupKeys.keySet()) {
pw.println(" suppressing: " + key);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java
index 6744d74004f0..7007f9defee6 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java
@@ -35,6 +35,7 @@ import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
import com.android.systemui.statusbar.phone.DozeServiceHost;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.ManagedProfileController;
import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -50,6 +51,7 @@ import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.ExtensionControllerImpl;
import com.android.systemui.statusbar.policy.FlashlightController;
import com.android.systemui.statusbar.policy.FlashlightControllerImpl;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.HotspotControllerImpl;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -257,4 +259,7 @@ public abstract class DependencyBinder {
@Binds
public abstract VolumeComponent provideVolumeComponent(
VolumeDialogComponent volumeDialogComponent);
+ /** */
+ @Binds
+ public abstract HeadsUpManager bindHeadsUpManager(HeadsUpManagerPhone headsUpManagerPhone);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index 48c72d311b1c..f1d02bb61ef9 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -33,7 +33,7 @@ import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -74,7 +74,7 @@ abstract class SystemUIDefaultModule {
abstract DockManager bindDockManager(DockManagerImpl dockManager);
@Binds
- abstract NotificationData.KeyguardEnvironment bindKeyguardEnvironment(
+ abstract NotificationEntryManager.KeyguardEnvironment bindKeyguardEnvironment(
KeyguardEnvironmentImpl keyguardEnvironment);
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index 0134aa3a15df..5de6d1c42b4f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -169,7 +169,7 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
if (DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)) {
v.setText(mContext.getString(
com.android.internal.R.string.bugreport_status,
- Build.VERSION.RELEASE,
+ Build.VERSION.RELEASE_OR_CODENAME,
Build.ID));
v.setVisibility(View.VISIBLE);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 3ff6d0d2278d..7dc3236915e8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -59,13 +59,16 @@ import android.graphics.Picture;
import android.graphics.PixelFormat;
import android.graphics.PointF;
import android.graphics.Rect;
+import android.media.ExifInterface;
import android.media.MediaActionSound;
import android.net.Uri;
import android.os.AsyncTask;
+import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
@@ -107,9 +110,15 @@ import java.io.IOException;
import java.io.OutputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.Date;
import java.util.List;
+import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
@@ -323,11 +332,41 @@ public class GlobalScreenshot {
final Uri uri = MediaStore.createPending(context, params);
final MediaStore.PendingSession session = MediaStore.openPending(context, uri);
try {
+ // First, write the actual data for our screenshot
try (OutputStream out = session.openOutputStream()) {
if (!image.compress(Bitmap.CompressFormat.PNG, 100, out)) {
throw new IOException("Failed to compress");
}
}
+
+ // Next, write metadata to help index the screenshot
+ try (ParcelFileDescriptor pfd = session.open()) {
+ final ExifInterface exif = new ExifInterface(pfd.getFileDescriptor());
+
+ exif.setAttribute(ExifInterface.TAG_SOFTWARE,
+ "Android " + Build.DISPLAY);
+
+ exif.setAttribute(ExifInterface.TAG_IMAGE_WIDTH,
+ Integer.toString(image.getWidth()));
+ exif.setAttribute(ExifInterface.TAG_IMAGE_LENGTH,
+ Integer.toString(image.getHeight()));
+
+ final ZonedDateTime time = ZonedDateTime.ofInstant(
+ Instant.ofEpochMilli(mImageTime), ZoneId.systemDefault());
+ exif.setAttribute(ExifInterface.TAG_DATETIME_ORIGINAL,
+ DateTimeFormatter.ofPattern("yyyy:MM:dd HH:mm:ss").format(time));
+ exif.setAttribute(ExifInterface.TAG_SUBSEC_TIME_ORIGINAL,
+ DateTimeFormatter.ofPattern("SSS").format(time));
+
+ if (Objects.equals(time.getOffset(), ZoneOffset.UTC)) {
+ exif.setAttribute(ExifInterface.TAG_OFFSET_TIME_ORIGINAL, "+00:00");
+ } else {
+ exif.setAttribute(ExifInterface.TAG_OFFSET_TIME_ORIGINAL,
+ DateTimeFormatter.ofPattern("XXX").format(time));
+ }
+
+ exif.saveAttributes();
+ }
session.publish();
} catch (Exception e) {
session.abandon();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index c4de2d3572bd..98a267599f7e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -110,8 +110,7 @@ public class NotificationListener extends NotificationListenerWithPlugins {
}
String key = sbn.getKey();
- boolean isUpdate =
- mEntryManager.getNotificationData().get(key) != null;
+ boolean isUpdate = mEntryManager.getActiveNotificationUnfiltered(key) != null;
// In case we don't allow child notifications, we ignore children of
// notifications that have a summary, since` we're not going to show them
// anyway. This is true also when the summary is canceled,
@@ -126,8 +125,7 @@ public class NotificationListener extends NotificationListenerWithPlugins {
if (isUpdate) {
mEntryManager.removeNotification(key, rankingMap, UNDEFINED_DISMISS_REASON);
} else {
- mEntryManager.getNotificationData()
- .updateRanking(rankingMap, "onNotificationPosted");
+ mEntryManager.updateRanking(rankingMap, "onNotificationPosted");
}
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 571d3d7a11be..021e7e15c7f6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -15,6 +15,7 @@
*/
package com.android.systemui.statusbar;
+import static android.app.Notification.VISIBILITY_SECRET;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
import static com.android.systemui.DejankUtils.whitelistIpcs;
@@ -126,7 +127,7 @@ public class NotificationLockscreenUserManagerImpl implements
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().getNotificationData().filterAndSort("user switched");
+ getEntryManager().reapplyFilterAndSort("user switched");
mPresenter.onUserSwitched(mCurrentUserId);
for (UserChangedListener listener : mListeners) {
@@ -148,17 +149,17 @@ public class NotificationLockscreenUserManagerImpl implements
}
}
if (notificationKey != null) {
- final int count =
- getEntryManager().getNotificationData().getActiveNotifications().size();
- final int rank = getEntryManager().getNotificationData().getRank(notificationKey);
+ NotificationEntry entry =
+ getEntryManager().getActiveNotificationUnfiltered(notificationKey);
+ final int count = getEntryManager().getActiveNotificationsCount();
+ final int rank = entry != null ? entry.getRanking().getRank() : 0;
NotificationVisibility.NotificationLocation location =
- NotificationLogger.getNotificationLocation(
- getEntryManager().getNotificationData().get(notificationKey));
+ NotificationLogger.getNotificationLocation(entry);
final NotificationVisibility nv = NotificationVisibility.obtain(notificationKey,
rank, count, true, location);
try {
mBarService.onNotificationClick(notificationKey, nv);
- } catch (RemoteException e) {
+ } catch (RemoteException exception) {
/* ignore */
}
}
@@ -311,9 +312,9 @@ public class NotificationLockscreenUserManagerImpl implements
Log.wtf(TAG, "mEntryManager was null!", new Throwable());
return true;
}
- return isLockscreenPublicMode(mCurrentUserId)
- && getEntryManager().getNotificationData().getVisibilityOverride(key) ==
- Notification.VISIBILITY_SECRET;
+ NotificationEntry visibleEntry = getEntryManager().getActiveNotificationUnfiltered(key);
+ return isLockscreenPublicMode(mCurrentUserId) && visibleEntry != null
+ && visibleEntry.getRanking().getVisibilityOverride() == VISIBILITY_SECRET;
}
public boolean shouldShowOnKeyguard(NotificationEntry entry) {
@@ -326,8 +327,7 @@ public class NotificationLockscreenUserManagerImpl implements
&& hideSilentNotificationsOnLockscreen()) {
exceedsPriorityThreshold = entry.getBucket() != BUCKET_SILENT;
} else {
- exceedsPriorityThreshold =
- !getEntryManager().getNotificationData().isAmbient(entry.getKey());
+ exceedsPriorityThreshold = !entry.getRanking().isAmbient();
}
return mShowLockscreenNotifications && exceedsPriorityThreshold;
}
@@ -467,8 +467,9 @@ public class NotificationLockscreenUserManagerImpl implements
Log.wtf(TAG, "mEntryManager was null!", new Throwable());
return true;
}
- return getEntryManager().getNotificationData().getVisibilityOverride(key) ==
- Notification.VISIBILITY_PRIVATE;
+ NotificationEntry entry = getEntryManager().getActiveNotificationUnfiltered(key);
+ return entry != null
+ && entry.getRanking().getVisibilityOverride() == Notification.VISIBILITY_PRIVATE;
}
private void updateCurrentProfilesCache() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index d668665f062c..a98f826c0284 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -102,8 +102,7 @@ public class NotificationMediaManager implements Dumpable {
}
- // Late binding
- private NotificationEntryManager mEntryManager;
+ private final NotificationEntryManager mEntryManager;
// Late binding, also @Nullable due to being in com.android.systemui.statusbar.phone package
@Nullable
@@ -258,8 +257,9 @@ public class NotificationMediaManager implements Dumpable {
if (mMediaNotificationKey == null) {
return null;
}
- synchronized (mEntryManager.getNotificationData()) {
- NotificationEntry entry = mEntryManager.getNotificationData().get(mMediaNotificationKey);
+ synchronized (mEntryManager) {
+ NotificationEntry entry = mEntryManager
+ .getActiveNotificationUnfiltered(mMediaNotificationKey);
if (entry == null || entry.expandedIcon == null) {
return null;
}
@@ -281,8 +281,9 @@ public class NotificationMediaManager implements Dumpable {
public void findAndUpdateMediaNotifications() {
boolean metaDataChanged = false;
- synchronized (mEntryManager.getNotificationData()) {
- Set<NotificationEntry> allNotifications = mEntryManager.getAllNotifs();
+ synchronized (mEntryManager) {
+ Set<NotificationEntry> allNotifications =
+ mEntryManager.getPendingAndActiveNotifications();
// Promote the media notification with a controller in 'playing' state, if any.
NotificationEntry mediaNotification = null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 35f06f9d95c8..e10d27b241cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -184,8 +184,9 @@ public class NotificationRemoteInputManager implements Dumpable {
ViewGroup actionGroup = (ViewGroup) parent;
buttonIndex = actionGroup.indexOfChild(view);
}
- final int count = mEntryManager.getNotificationData().getActiveNotifications().size();
- final int rank = mEntryManager.getNotificationData().getRank(key);
+ final int count = mEntryManager.getActiveNotificationsCount();
+ final int rank = mEntryManager
+ .getActiveNotificationUnfiltered(key).getRanking().getRank();
// Notification may be updated before this function is executed, and thus play safe
// here and verify that the action object is still the one that where the click happens.
@@ -202,7 +203,7 @@ public class NotificationRemoteInputManager implements Dumpable {
}
NotificationVisibility.NotificationLocation location =
NotificationLogger.getNotificationLocation(
- mEntryManager.getNotificationData().get(key));
+ mEntryManager.getActiveNotificationUnfiltered(key));
final NotificationVisibility nv =
NotificationVisibility.obtain(key, rank, count, true, location);
try {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 20a3e35791c4..ef733a967840 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -140,8 +140,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
Assert.isMainThread();
beginUpdate();
- ArrayList<NotificationEntry> activeNotifications = mEntryManager.getNotificationData()
- .getActiveNotifications();
+ List<NotificationEntry> activeNotifications = mEntryManager.getVisibleNotifications();
ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size());
final int N = activeNotifications.size();
for (int i = 0; i < N; i++) {
@@ -339,7 +338,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
}
for (ExpandableNotificationRow remove : toRemove) {
parent.removeChildNotification(remove);
- if (mEntryManager.getNotificationData().get(
+ if (mEntryManager.getActiveNotificationUnfiltered(
remove.getStatusBarNotification().getKey()) == null) {
// We only want to add an animation if the view is completely removed
// otherwise it's just a transfer
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
index 7bdb21d0eac5..40f8e394f054 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
@@ -73,8 +73,8 @@ public class SmartReplyController {
public void smartActionClicked(
NotificationEntry entry, int actionIndex, Notification.Action action,
boolean generatedByAssistant) {
- final int count = mEntryManager.getNotificationData().getActiveNotifications().size();
- final int rank = mEntryManager.getNotificationData().getRank(entry.getKey());
+ final int count = mEntryManager.getActiveNotificationsCount();
+ final int rank = entry.getRanking().getRank();
NotificationVisibility.NotificationLocation location =
NotificationLogger.getNotificationLocation(entry);
final NotificationVisibility nv = NotificationVisibility.obtain(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt
index 314dc04e574f..015c32348bb0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt
@@ -37,14 +37,14 @@ import javax.inject.Singleton
*/
@Singleton
class BypassHeadsUpNotifier @Inject constructor(
- private val context: Context,
- private val bypassController: KeyguardBypassController,
- private val statusBarStateController: StatusBarStateController,
- private val headsUpManager: HeadsUpManagerPhone,
- private val notificationLockscreenUserManager: NotificationLockscreenUserManager,
- private val mediaManager: NotificationMediaManager,
- tunerService: TunerService) : StatusBarStateController.StateListener,
- NotificationMediaManager.MediaListener {
+ private val context: Context,
+ private val bypassController: KeyguardBypassController,
+ private val statusBarStateController: StatusBarStateController,
+ private val headsUpManager: HeadsUpManagerPhone,
+ private val notificationLockscreenUserManager: NotificationLockscreenUserManager,
+ private val mediaManager: NotificationMediaManager,
+ tunerService: TunerService
+) : StatusBarStateController.StateListener, NotificationMediaManager.MediaListener {
private lateinit var entryManager: NotificationEntryManager
private var currentMediaEntry: NotificationEntry? = null
@@ -77,7 +77,8 @@ class BypassHeadsUpNotifier @Inject constructor(
override fun onMetadataOrStateChanged(metadata: MediaMetadata?, state: Int) {
val previous = currentMediaEntry
- var newEntry = entryManager.notificationData.get(mediaManager.mediaNotificationKey)
+ var newEntry = entryManager
+ .getActiveNotificationUnfiltered(mediaManager.mediaNotificationKey)
if (!NotificationMediaManager.isPlayingState(state)) {
newEntry = null
}
@@ -101,7 +102,7 @@ class BypassHeadsUpNotifier @Inject constructor(
*/
private fun canAutoHeadsUp(entry: NotificationEntry): Boolean {
if (!isAutoHeadsUpAllowed()) {
- return false;
+ return false
}
if (entry.isSensitive) {
// filter sensitive notifications
@@ -111,7 +112,7 @@ class BypassHeadsUpNotifier @Inject constructor(
// filter notifications invisible on Keyguard
return false
}
- if (!entryManager.notificationData.activeNotifications.contains(entry)) {
+ if (entryManager.getActiveNotificationUnfiltered(entry.key) != null) {
// filter notifications not the active list currently
return false
}
@@ -125,7 +126,7 @@ class BypassHeadsUpNotifier @Inject constructor(
/**
* @return {@code true} if autoHeadsUp is possible right now.
*/
- private fun isAutoHeadsUpAllowed() : Boolean {
+ private fun isAutoHeadsUpAllowed(): Boolean {
if (!enabled) {
return false
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
index df78fa3fd4a4..06949208c2bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
@@ -99,7 +99,8 @@ public interface NotificationEntryListener {
/**
* Called whenever notification ranking changes, in response to
* {@link NotificationListenerService#onNotificationRankingUpdate}. This is called after
- * NotificationData has processed the update and notifications have been re-sorted and filtered.
+ * NotificationEntryManager has processed the update and notifications have been re-sorted
+ * and filtered.
*
* @param rankingMap provides access to ranking information on currently active notifications
*/
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 13d90ffdfca2..7a58097f3ec1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -18,10 +18,12 @@ package com.android.systemui.statusbar.notification;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_ERROR;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.util.Log;
@@ -36,9 +38,8 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationRemoveInterceptor;
import com.android.systemui.statusbar.NotificationUiAdjustment;
import com.android.systemui.statusbar.NotificationUpdateHandler;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
-import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
import com.android.systemui.statusbar.notification.collection.NotificationRowBinder;
import com.android.systemui.statusbar.notification.logging.NotifEvent;
import com.android.systemui.statusbar.notification.logging.NotifLog;
@@ -46,12 +47,15 @@ import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.util.Assert;
import com.android.systemui.util.leak.LeakDetector;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -62,9 +66,29 @@ import javax.inject.Inject;
import javax.inject.Singleton;
/**
- * NotificationEntryManager is responsible for the adding, removing, and updating of notifications.
- * It also handles tasks such as their inflation and their interaction with other
- * Notification.*Manager objects.
+ * NotificationEntryManager is responsible for the adding, removing, and updating of
+ * {@link NotificationEntry}s. It also handles tasks such as their inflation and their interaction
+ * with other Notification.*Manager objects.
+ *
+ * We track notification entries through this lifecycle:
+ * 1. Pending
+ * 2. Active
+ * 3. Sorted / filtered (visible)
+ *
+ * Every entry spends some amount of time in the pending state, while it is being inflated. Once
+ * inflated, an entry moves into the active state, where it _could_ potentially be shown to the
+ * user. After an entry makes its way into the active state, we sort and filter the entire set to
+ * repopulate the visible set.
+ *
+ * There are a few different things that other classes may be interested in, and most of them
+ * involve the current set of notifications. Here's a brief overview of things you may want to know:
+ * @see #getVisibleNotifications() for the visible set
+ * @see #getActiveNotificationUnfiltered(String) to check if a key exists
+ * @see #getPendingNotificationsIterator() for an iterator over the pending notifications
+ * @see #getPendingOrActiveNotif(String) to find a notification exists for that key in any list
+ * @see #getPendingAndActiveNotifications() to get the entire set of Notifications that we're
+ * aware of
+ * @see #getActiveNotificationsForCurrentUser() to see every notification that the current user owns
*/
@Singleton
public class NotificationEntryManager implements
@@ -78,12 +102,23 @@ public class NotificationEntryManager implements
/**
* Used when a notification is removed and it doesn't have a reason that maps to one of the
* reasons defined in NotificationListenerService
- * (e.g. {@link NotificationListenerService.REASON_CANCEL})
+ * (e.g. {@link NotificationListenerService#REASON_CANCEL})
*/
public static final int UNDEFINED_DISMISS_REASON = 0;
+ /** Pending notifications are ones awaiting inflation */
@VisibleForTesting
protected final HashMap<String, NotificationEntry> mPendingNotifications = new HashMap<>();
+ /**
+ * Active notifications have been inflated / prepared and could become visible, but may get
+ * filtered out if for instance they are not for the current user
+ */
+ private final ArrayMap<String, NotificationEntry> mActiveNotifications = new ArrayMap<>();
+ @VisibleForTesting
+ /** This is the list of "active notifications for this user in this context" */
+ protected final ArrayList<NotificationEntry> mSortedAndFiltered = new ArrayList<>();
+ private final List<NotificationEntry> mReadOnlyNotifications =
+ Collections.unmodifiableList(mSortedAndFiltered);
private final Map<NotificationEntry, NotificationLifetimeExtender> mRetainedNotifications =
new ArrayMap<>();
@@ -92,10 +127,12 @@ public class NotificationEntryManager implements
private NotificationRemoteInputManager mRemoteInputManager;
private NotificationRowBinder mNotificationRowBinder;
+ private final KeyguardEnvironment mKeyguardEnvironment;
+ private final NotificationGroupManager mGroupManager;
+ private final NotificationRankingManager mRankingManager;
+
private NotificationPresenter mPresenter;
- private NotificationListenerService.RankingMap mLatestRankingMap;
- @VisibleForTesting
- protected NotificationData mNotificationData;
+ private RankingMap mLatestRankingMap;
private NotifLog mNotifLog;
@VisibleForTesting
@@ -129,10 +166,14 @@ public class NotificationEntryManager implements
@Inject
public NotificationEntryManager(
- NotificationData notificationData,
- NotifLog notifLog) {
- mNotificationData = notificationData;
+ NotifLog notifLog,
+ NotificationGroupManager groupManager,
+ NotificationRankingManager rankingManager,
+ KeyguardEnvironment keyguardEnvironment) {
mNotifLog = notifLog;
+ mGroupManager = groupManager;
+ mRankingManager = rankingManager;
+ mKeyguardEnvironment = keyguardEnvironment;
}
/** Adds a {@link NotificationEntryListener}. */
@@ -171,7 +212,6 @@ public class NotificationEntryManager implements
NotificationListContainer listContainer,
HeadsUpManager headsUpManager) {
mPresenter = presenter;
- mNotificationData.setHeadsUpManager(headsUpManager);
}
/** Adds multiple {@link NotificationLifetimeExtender}s. */
@@ -188,10 +228,6 @@ public class NotificationEntryManager implements
UNDEFINED_DISMISS_REASON));
}
- public NotificationData getNotificationData() {
- return mNotificationData;
- }
-
@Override
public void onReorderingAllowed() {
updateNotifications("reordering is now allowed");
@@ -212,10 +248,17 @@ public class NotificationEntryManager implements
}
private NotificationVisibility obtainVisibility(String key) {
- final int rank = mNotificationData.getRank(key);
- final int count = mNotificationData.getActiveNotifications().size();
+ NotificationEntry e = mActiveNotifications.get(key);
+ final int rank;
+ if (e != null) {
+ rank = e.getRanking().getRank();
+ } else {
+ rank = 0;
+ }
+
+ final int count = mActiveNotifications.size();
NotificationVisibility.NotificationLocation location =
- NotificationLogger.getNotificationLocation(getNotificationData().get(key));
+ NotificationLogger.getNotificationLocation(getActiveNotificationUnfiltered(key));
return NotificationVisibility.obtain(key, rank, count, true, location);
}
@@ -227,7 +270,7 @@ public class NotificationEntryManager implements
mNotifLog.log(NotifEvent.INFLATION_ABORTED, entry.getSbn(), null,
"PendingNotification aborted. " + reason);
}
- NotificationEntry addedEntry = mNotificationData.get(key);
+ NotificationEntry addedEntry = getActiveNotificationUnfiltered(key);
if (addedEntry != null) {
addedEntry.abortTask();
mNotifLog.log(NotifEvent.INFLATION_ABORTED, addedEntry.getSbn(),
@@ -258,13 +301,13 @@ public class NotificationEntryManager implements
// If there was an async task started after the removal, we don't want to add it back to
// the list, otherwise we might get leaks.
if (!entry.isRowRemoved()) {
- boolean isNew = mNotificationData.get(entry.getKey()) == null;
+ boolean isNew = getActiveNotificationUnfiltered(entry.getKey()) == null;
if (isNew) {
for (NotificationEntryListener listener : mNotificationEntryListeners) {
mNotifLog.log(NotifEvent.INFLATED, entry);
listener.onEntryInflated(entry, inflatedFlags);
}
- mNotificationData.add(entry);
+ addActiveNotification(entry);
updateNotifications("onAsyncInflationFinished");
for (NotificationEntryListener listener : mNotificationEntryListeners) {
listener.onNotificationAdded(entry);
@@ -278,8 +321,34 @@ public class NotificationEntryManager implements
}
}
+ /**
+ * Equivalent to the old NotificationData#add
+ * @param entry - an entry which is prepared for display
+ */
+ private void addActiveNotification(NotificationEntry entry) {
+ Assert.isMainThread();
+
+ mActiveNotifications.put(entry.getKey(), entry);
+ mGroupManager.onEntryAdded(entry);
+ updateRankingAndSort(mRankingManager.getRankingMap(), "addEntryInternalInternal");
+ }
+
+ /**
+ * Available so that tests can directly manipulate the list of active notifications easily
+ *
+ * @param entry the entry to add directly to the visible notification map
+ */
+ @VisibleForTesting
+ public void addActiveNotificationForTest(NotificationEntry entry) {
+ mActiveNotifications.put(entry.getKey(), entry);
+ mGroupManager.onEntryAdded(entry);
+
+ reapplyFilterAndSort("addVisibleNotification");
+ }
+
+
@Override
- public void removeNotification(String key, NotificationListenerService.RankingMap ranking,
+ public void removeNotification(String key, RankingMap ranking,
int reason) {
removeNotificationInternal(key, ranking, obtainVisibility(key), false /* forceRemove */,
false /* removedByUser */, reason);
@@ -287,7 +356,7 @@ public class NotificationEntryManager implements
private void removeNotificationInternal(
String key,
- @Nullable NotificationListenerService.RankingMap ranking,
+ @Nullable RankingMap ranking,
@Nullable NotificationVisibility visibility,
boolean forceRemove,
boolean removedByUser,
@@ -300,7 +369,7 @@ public class NotificationEntryManager implements
return;
}
- final NotificationEntry entry = mNotificationData.get(key);
+ final NotificationEntry entry = getActiveNotificationUnfiltered(key);
boolean lifetimeExtended = false;
// Notification was canceled before it got inflated
@@ -355,8 +424,7 @@ public class NotificationEntryManager implements
// Let's remove the children if this was a summary
handleGroupSummaryRemoved(key);
-
- mNotificationData.remove(key, ranking);
+ removeVisibleNotification(key);
updateNotifications("removeNotificationInternal");
Dependency.get(LeakDetector.class).trackGarbage(entry);
removedByUser |= entryDismissed;
@@ -381,7 +449,7 @@ public class NotificationEntryManager implements
*
*/
private void handleGroupSummaryRemoved(String key) {
- NotificationEntry entry = mNotificationData.get(key);
+ NotificationEntry entry = getActiveNotificationUnfiltered(key);
if (entry != null && entry.rowExists() && entry.isSummaryWithChildren()) {
if (entry.getSbn().getOverrideGroupKey() != null && !entry.isRowDismissed()) {
// We don't want to remove children for autobundled notifications as they are not
@@ -413,13 +481,14 @@ public class NotificationEntryManager implements
}
private void addNotificationInternal(StatusBarNotification notification,
- NotificationListenerService.RankingMap rankingMap) throws InflationException {
+ RankingMap rankingMap) throws InflationException {
String key = notification.getKey();
if (DEBUG) {
Log.d(TAG, "addNotification key=" + key);
}
- mNotificationData.updateRanking(rankingMap, "addNotificationInternal");
+ updateRankingAndSort(rankingMap, "addNotificationInternal");
+
Ranking ranking = new Ranking();
rankingMap.getRanking(key, ranking);
@@ -439,8 +508,7 @@ public class NotificationEntryManager implements
}
@Override
- public void addNotification(StatusBarNotification notification,
- NotificationListenerService.RankingMap ranking) {
+ public void addNotification(StatusBarNotification notification, RankingMap ranking) {
try {
addNotificationInternal(notification, ranking);
} catch (InflationException e) {
@@ -449,12 +517,12 @@ public class NotificationEntryManager implements
}
private void updateNotificationInternal(StatusBarNotification notification,
- NotificationListenerService.RankingMap ranking) throws InflationException {
+ RankingMap ranking) throws InflationException {
if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
final String key = notification.getKey();
abortExistingInflation(key, "updateNotification");
- NotificationEntry entry = mNotificationData.get(key);
+ NotificationEntry entry = getActiveNotificationUnfiltered(key);
if (entry == null) {
return;
}
@@ -463,7 +531,11 @@ public class NotificationEntryManager implements
// to keep its lifetime extended.
cancelLifetimeExtension(entry);
- mNotificationData.update(entry, ranking, notification, "updateNotificationInternal");
+ updateRankingAndSort(ranking, "updateNotificationInternal");
+ StatusBarNotification oldSbn = entry.getSbn();
+ entry.setSbn(notification);
+ mGroupManager.onEntryUpdated(entry, oldSbn);
+
mNotifLog.log(NotifEvent.NOTIF_UPDATED, entry.getSbn(), entry.getRanking());
for (NotificationEntryListener listener : mNotificationEntryListeners) {
listener.onPreEntryUpdated(entry);
@@ -486,8 +558,7 @@ public class NotificationEntryManager implements
}
@Override
- public void updateNotification(StatusBarNotification notification,
- NotificationListenerService.RankingMap ranking) {
+ public void updateNotification(StatusBarNotification notification, RankingMap ranking) {
try {
updateNotificationInternal(notification, ranking);
} catch (InflationException e) {
@@ -500,16 +571,16 @@ public class NotificationEntryManager implements
* @param reason why the notifications are updating
*/
public void updateNotifications(String reason) {
- mNotificationData.filterAndSort(reason);
+ reapplyFilterAndSort(reason);
if (mPresenter != null) {
mPresenter.updateNotificationViews();
}
}
@Override
- public void updateNotificationRanking(NotificationListenerService.RankingMap rankingMap) {
+ public void updateNotificationRanking(RankingMap rankingMap) {
List<NotificationEntry> entries = new ArrayList<>();
- entries.addAll(mNotificationData.getActiveNotifications());
+ entries.addAll(getVisibleNotifications());
entries.addAll(mPendingNotifications.values());
// Has a copy of the current UI adjustments.
@@ -523,7 +594,7 @@ public class NotificationEntryManager implements
}
// Populate notification entries from the new rankings.
- mNotificationData.updateRanking(rankingMap, "updateNotificationRanking");
+ updateRankingAndSort(rankingMap, "updateNotificationRanking");
updateRankingOfPendingNotifications(rankingMap);
// By comparing the old and new UI adjustments, reinflate the view accordingly.
@@ -542,8 +613,7 @@ public class NotificationEntryManager implements
}
}
- private void updateRankingOfPendingNotifications(
- @Nullable NotificationListenerService.RankingMap rankingMap) {
+ private void updateRankingOfPendingNotifications(@Nullable RankingMap rankingMap) {
if (rankingMap == null) {
return;
}
@@ -565,23 +635,35 @@ public class NotificationEntryManager implements
}
/**
- * @return all notification we're currently aware of (both pending and visible notifications)
+ * @return all notifications we're currently aware of (both pending and active notifications)
*/
- public Set<NotificationEntry> getAllNotifs() {
+ public Set<NotificationEntry> getPendingAndActiveNotifications() {
Set<NotificationEntry> allNotifs = new HashSet<>(mPendingNotifications.values());
- allNotifs.addAll(mNotificationData.getActiveNotifications());
+ allNotifs.addAll(mSortedAndFiltered);
return allNotifs;
}
/**
+ * Use this method to retrieve a notification entry that has been prepared for presentation.
+ * Note that the notification may be filtered out and never shown to the user.
+ *
+ * @see #getVisibleNotifications() for the currently sorted and filtered list
+ *
+ * @return a {@link NotificationEntry} if it has been prepared, else null
+ */
+ public NotificationEntry getActiveNotificationUnfiltered(String key) {
+ return mActiveNotifications.get(key);
+ }
+
+ /**
* Gets the pending or visible notification entry with the given key. Returns null if
* notification doesn't exist.
*/
- public NotificationEntry getPendingOrCurrentNotif(String key) {
+ public NotificationEntry getPendingOrActiveNotif(String key) {
if (mPendingNotifications.containsKey(key)) {
return mPendingNotifications.get(key);
} else {
- return mNotificationData.get(key);
+ return mActiveNotifications.get(key);
}
}
@@ -608,4 +690,136 @@ public class NotificationEntryManager implements
}
return mNotificationRowBinder;
}
+
+ /*
+ * -----
+ * Annexed from NotificationData below:
+ * Some of these methods may be redundant but require some reworking to remove. For now
+ * we'll try to keep the behavior the same and can simplify these interfaces in another pass
+ */
+
+ /** Internalization of NotificationData#remove */
+ private void removeVisibleNotification(String key) {
+ // no need to synchronize if we're on the main thread dawg
+ Assert.isMainThread();
+
+ NotificationEntry removed = mActiveNotifications.remove(key);
+
+ if (removed == null) return;
+ mGroupManager.onEntryRemoved(removed);
+ }
+
+ /** @return list of active notifications filtered for the current user */
+ public List<NotificationEntry> getActiveNotificationsForCurrentUser() {
+ Assert.isMainThread();
+ ArrayList<NotificationEntry> filtered = new ArrayList<>();
+
+ final int len = mActiveNotifications.size();
+ for (int i = 0; i < len; i++) {
+ NotificationEntry entry = mActiveNotifications.valueAt(i);
+ final StatusBarNotification sbn = entry.getSbn();
+ if (!mKeyguardEnvironment.isNotificationForCurrentProfiles(sbn)) {
+ continue;
+ }
+ filtered.add(entry);
+ }
+
+ return filtered;
+ }
+
+ //TODO: Get rid of this in favor of NotificationUpdateHandler#updateNotificationRanking
+ /**
+ * @param rankingMap the {@link RankingMap} to apply to the current notification list
+ * @param reason the reason for calling this method, for {@link NotifLog}
+ */
+ public void updateRanking(RankingMap rankingMap, String reason) {
+ updateRankingAndSort(rankingMap, reason);
+ }
+
+ /** Resorts / filters the current notification set with the current RankingMap */
+ public void reapplyFilterAndSort(String reason) {
+ updateRankingAndSort(mRankingManager.getRankingMap(), reason);
+ }
+
+ /** Calls to NotificationRankingManager and updates mSortedAndFiltered */
+ private void updateRankingAndSort(@NonNull RankingMap rankingMap, String reason) {
+ mSortedAndFiltered.clear();
+ mSortedAndFiltered.addAll(mRankingManager.updateRanking(
+ rankingMap, mActiveNotifications.values(), reason));
+ }
+
+ /** dump the current active notification list. Called from StatusBar */
+ public void dump(PrintWriter pw, String indent) {
+ pw.println("NotificationEntryManager");
+ int filteredLen = mSortedAndFiltered.size();
+ pw.print(indent);
+ pw.println("active notifications: " + filteredLen);
+ int active;
+ for (active = 0; active < filteredLen; active++) {
+ NotificationEntry e = mSortedAndFiltered.get(active);
+ dumpEntry(pw, indent, active, e);
+ }
+ synchronized (mActiveNotifications) {
+ int totalLen = mActiveNotifications.size();
+ pw.print(indent);
+ pw.println("inactive notifications: " + (totalLen - active));
+ int inactiveCount = 0;
+ for (int i = 0; i < totalLen; i++) {
+ NotificationEntry entry = mActiveNotifications.valueAt(i);
+ if (!mSortedAndFiltered.contains(entry)) {
+ dumpEntry(pw, indent, inactiveCount, entry);
+ inactiveCount++;
+ }
+ }
+ }
+ }
+
+ private void dumpEntry(PrintWriter pw, String indent, int i, NotificationEntry e) {
+ pw.print(indent);
+ pw.println(" [" + i + "] key=" + e.getKey() + " icon=" + e.icon);
+ StatusBarNotification n = e.getSbn();
+ pw.print(indent);
+ pw.println(" pkg=" + n.getPackageName() + " id=" + n.getId() + " importance="
+ + e.getRanking().getImportance());
+ pw.print(indent);
+ pw.println(" notification=" + n.getNotification());
+ }
+
+ /**
+ * This is the answer to the question "what notifications should the user be seeing right now?"
+ * These are sorted and filtered, and directly inform the notification shade what to show
+ *
+ * @return A read-only list of the currently active notifications
+ */
+ public List<NotificationEntry> getVisibleNotifications() {
+ return mReadOnlyNotifications;
+ }
+
+ /** @return A count of the active notifications */
+ public int getActiveNotificationsCount() {
+ return mReadOnlyNotifications.size();
+ }
+
+ /**
+ * @return {@code true} if there is at least one notification that should be visible right now
+ */
+ public boolean hasActiveNotifications() {
+ return mReadOnlyNotifications.size() != 0;
+ }
+
+ /*
+ * End annexation
+ * -----
+ */
+
+
+ /**
+ * Provides access to keyguard state and user settings dependent data.
+ */
+ public interface KeyguardEnvironment {
+ /** true if the device is provisioned (should always be true in practice) */
+ boolean isDeviceProvisioned();
+ /** true if the notification is for the current profiles */
+ boolean isNotificationForCurrentProfiles(StatusBarNotification sbn);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
index b1164093acdd..e5f44bd3b9f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
@@ -28,7 +28,6 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dependency;
import com.android.systemui.ForegroundServiceController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.ShadeController;
@@ -44,7 +43,7 @@ public class NotificationFilter {
private final NotificationGroupManager mGroupManager = Dependency.get(
NotificationGroupManager.class);
- private NotificationData.KeyguardEnvironment mEnvironment;
+ private NotificationEntryManager.KeyguardEnvironment mEnvironment;
private ShadeController mShadeController;
private ForegroundServiceController mFsc;
private NotificationLockscreenUserManager mUserManager;
@@ -52,9 +51,9 @@ public class NotificationFilter {
@Inject
public NotificationFilter() {}
- private NotificationData.KeyguardEnvironment getEnvironment() {
+ private NotificationEntryManager.KeyguardEnvironment getEnvironment() {
if (mEnvironment == null) {
- mEnvironment = Dependency.get(NotificationData.KeyguardEnvironment.class);
+ mEnvironment = Dependency.get(NotificationEntryManager.KeyguardEnvironment.class);
}
return mEnvironment;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
deleted file mode 100644
index a0229d16d6eb..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
+++ /dev/null
@@ -1,500 +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.notification.collection;
-
-import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING;
-import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE;
-import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT;
-
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.Person;
-import android.service.notification.NotificationListenerService.Ranking;
-import android.service.notification.NotificationListenerService.RankingMap;
-import android.service.notification.SnoozeCriterion;
-import android.service.notification.StatusBarNotification;
-import android.util.ArrayMap;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Dependency;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.notification.NotificationFilter;
-import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
-import com.android.systemui.statusbar.notification.logging.NotifEvent;
-import com.android.systemui.statusbar.notification.logging.NotifLog;
-import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Objects;
-
-import javax.inject.Inject;
-
-/**
- * The list of currently displaying notifications.
- */
-public class NotificationData {
- private static final String TAG = "NotificationData";
-
- private final NotificationFilter mNotificationFilter = Dependency.get(NotificationFilter.class);
-
- /**
- * These dependencies are late init-ed
- */
- private KeyguardEnvironment mEnvironment;
- private NotificationMediaManager mMediaManager;
-
- private HeadsUpManager mHeadsUpManager;
-
- private final ArrayMap<String, NotificationEntry> mEntries = new ArrayMap<>();
- private final ArrayList<NotificationEntry> mSortedAndFiltered = new ArrayList<>();
-
- private final NotificationGroupManager mGroupManager =
- Dependency.get(NotificationGroupManager.class);
-
- private RankingMap mRankingMap;
- private final Ranking mTmpRanking = new Ranking();
- private final boolean mUsePeopleFiltering;
- private final NotifLog mNotifLog;
- private final PeopleNotificationIdentifier mPeopleNotificationIdentifier;
-
- @Inject
- public NotificationData(
- NotificationSectionsFeatureManager sectionsFeatureManager,
- NotifLog notifLog,
- PeopleNotificationIdentifier peopleNotificationIdentifier) {
- mUsePeopleFiltering = sectionsFeatureManager.isFilteringEnabled();
- mNotifLog = notifLog;
- mPeopleNotificationIdentifier = peopleNotificationIdentifier;
- }
-
- public void setHeadsUpManager(HeadsUpManager headsUpManager) {
- mHeadsUpManager = headsUpManager;
- }
-
- @VisibleForTesting
- protected final Comparator<NotificationEntry> mRankingComparator =
- new Comparator<NotificationEntry>() {
- @Override
- public int compare(NotificationEntry a, NotificationEntry b) {
- final StatusBarNotification na = a.getSbn();
- final StatusBarNotification nb = b.getSbn();
- int aRank = getRank(a.getKey());
- int bRank = getRank(b.getKey());
-
- boolean aPeople = isPeopleNotification(a);
- boolean bPeople = isPeopleNotification(b);
-
- boolean aMedia = isImportantMedia(a);
- boolean bMedia = isImportantMedia(b);
-
- boolean aSystemMax = isSystemMax(a);
- boolean bSystemMax = isSystemMax(b);
-
- boolean aHeadsUp = a.isRowHeadsUp();
- boolean bHeadsUp = b.isRowHeadsUp();
-
- if (mUsePeopleFiltering && aPeople != bPeople) {
- return aPeople ? -1 : 1;
- } else if (aHeadsUp != bHeadsUp) {
- return aHeadsUp ? -1 : 1;
- } else if (aHeadsUp) {
- // Provide consistent ranking with headsUpManager
- return mHeadsUpManager.compare(a, b);
- } else if (aMedia != bMedia) {
- // Upsort current media notification.
- return aMedia ? -1 : 1;
- } else if (aSystemMax != bSystemMax) {
- // Upsort PRIORITY_MAX system notifications
- return aSystemMax ? -1 : 1;
- } else if (a.isHighPriority() != b.isHighPriority()) {
- return -1 * Boolean.compare(a.isHighPriority(), b.isHighPriority());
- } else if (aRank != bRank) {
- return aRank - bRank;
- } else {
- return Long.compare(nb.getNotification().when, na.getNotification().when);
- }
- }
- };
-
- private KeyguardEnvironment getEnvironment() {
- if (mEnvironment == null) {
- mEnvironment = Dependency.get(KeyguardEnvironment.class);
- }
- return mEnvironment;
- }
-
- private NotificationMediaManager getMediaManager() {
- if (mMediaManager == null) {
- mMediaManager = Dependency.get(NotificationMediaManager.class);
- }
- return mMediaManager;
- }
-
- /**
- * Returns the sorted list of active notifications (depending on {@link KeyguardEnvironment}
- *
- * <p>
- * This call doesn't update the list of active notifications. Call {@link #filterAndSort()}
- * when the environment changes.
- * <p>
- * Don't hold on to or modify the returned list.
- */
- public ArrayList<NotificationEntry> getActiveNotifications() {
- return mSortedAndFiltered;
- }
-
- public ArrayList<NotificationEntry> getNotificationsForCurrentUser() {
- synchronized (mEntries) {
- final int len = mEntries.size();
- ArrayList<NotificationEntry> filteredForUser = new ArrayList<>(len);
-
- for (int i = 0; i < len; i++) {
- NotificationEntry entry = mEntries.valueAt(i);
- final StatusBarNotification sbn = entry.getSbn();
- if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) {
- continue;
- }
- filteredForUser.add(entry);
- }
- return filteredForUser;
- }
- }
-
- public NotificationEntry get(String key) {
- return mEntries.get(key);
- }
-
- public void add(NotificationEntry entry) {
- synchronized (mEntries) {
- mEntries.put(entry.getSbn().getKey(), entry);
- }
- mGroupManager.onEntryAdded(entry);
-
- updateRankingAndSort(mRankingMap, "addEntry=" + entry.getSbn());
- }
-
- public NotificationEntry remove(String key, RankingMap ranking) {
- NotificationEntry removed;
- synchronized (mEntries) {
- removed = mEntries.remove(key);
- }
- if (removed == null) return null;
- mGroupManager.onEntryRemoved(removed);
- updateRankingAndSort(ranking, "removeEntry=" + removed.getSbn());
- return removed;
- }
-
- /** Updates the given notification entry with the provided ranking. */
- public void update(
- NotificationEntry entry,
- RankingMap ranking,
- StatusBarNotification notification,
- String reason) {
- updateRanking(ranking, reason);
- final StatusBarNotification oldNotification = entry.getSbn();
- entry.setSbn(notification);
- mGroupManager.onEntryUpdated(entry, oldNotification);
- }
-
- /**
- * Update ranking and trigger a re-sort
- */
- public void updateRanking(RankingMap ranking, String reason) {
- updateRankingAndSort(ranking, reason);
- }
-
- /**
- * Returns true if this notification should be displayed in the high-priority notifications
- * section
- */
- public boolean isHighPriority(StatusBarNotification statusBarNotification) {
- if (mRankingMap != null) {
- getRanking(statusBarNotification.getKey(), mTmpRanking);
- if (mTmpRanking.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT
- || hasHighPriorityCharacteristics(
- mTmpRanking.getChannel(), statusBarNotification)) {
- return true;
- }
- if (mGroupManager.isSummaryOfGroup(statusBarNotification)) {
- final ArrayList<NotificationEntry> logicalChildren =
- mGroupManager.getLogicalChildren(statusBarNotification);
- for (NotificationEntry child : logicalChildren) {
- if (isHighPriority(child.getSbn())) {
- return true;
- }
- }
- }
- }
- return false;
- }
-
- private boolean hasHighPriorityCharacteristics(NotificationChannel channel,
- StatusBarNotification statusBarNotification) {
-
- if (isImportantOngoing(statusBarNotification.getNotification())
- || statusBarNotification.getNotification().hasMediaSession()
- || hasPerson(statusBarNotification.getNotification())
- || hasStyle(statusBarNotification.getNotification(),
- Notification.MessagingStyle.class)) {
- // Users who have long pressed and demoted to silent should not see the notification
- // in the top section
- if (channel != null && channel.hasUserSetImportance()) {
- return false;
- }
- return true;
- }
-
- return false;
- }
-
- private boolean isImportantOngoing(Notification notification) {
- return notification.isForegroundService()
- && mTmpRanking.getImportance() >= NotificationManager.IMPORTANCE_LOW;
- }
-
- private boolean hasStyle(Notification notification, Class targetStyle) {
- Class<? extends Notification.Style> style = notification.getNotificationStyle();
- return targetStyle.equals(style);
- }
-
- private boolean hasPerson(Notification notification) {
- // TODO: cache favorite and recent contacts to check contact affinity
- ArrayList<Person> people = notification.extras != null
- ? notification.extras.getParcelableArrayList(Notification.EXTRA_PEOPLE_LIST)
- : new ArrayList<>();
- return people != null && !people.isEmpty();
- }
-
- public boolean isAmbient(String key) {
- if (mRankingMap != null) {
- getRanking(key, mTmpRanking);
- return mTmpRanking.isAmbient();
- }
- return false;
- }
-
- public int getVisibilityOverride(String key) {
- if (mRankingMap != null) {
- getRanking(key, mTmpRanking);
- return mTmpRanking.getVisibilityOverride();
- }
- return Ranking.VISIBILITY_NO_OVERRIDE;
- }
-
- public List<SnoozeCriterion> getSnoozeCriteria(String key) {
- if (mRankingMap != null) {
- getRanking(key, mTmpRanking);
- return mTmpRanking.getSnoozeCriteria();
- }
- return null;
- }
-
- public NotificationChannel getChannel(String key) {
- if (mRankingMap != null) {
- getRanking(key, mTmpRanking);
- return mTmpRanking.getChannel();
- }
- return null;
- }
-
- public int getRank(String key) {
- if (mRankingMap != null) {
- getRanking(key, mTmpRanking);
- return mTmpRanking.getRank();
- }
- return 0;
- }
-
- private boolean isImportantMedia(NotificationEntry e) {
- int importance = e.getRanking().getImportance();
- boolean media = e.getKey().equals(getMediaManager().getMediaNotificationKey())
- && importance > NotificationManager.IMPORTANCE_MIN;
-
- return media;
- }
-
- private boolean isSystemMax(NotificationEntry e) {
- int importance = e.getRanking().getImportance();
- boolean sys = importance >= NotificationManager.IMPORTANCE_HIGH
- && isSystemNotification(e.getSbn());
-
- return sys;
- }
-
- public boolean shouldHide(String key) {
- if (mRankingMap != null) {
- getRanking(key, mTmpRanking);
- return mTmpRanking.isSuspended();
- }
- return false;
- }
-
- private void updateRankingAndSort(RankingMap rankingMap, String reason) {
- if (rankingMap != null) {
- mRankingMap = rankingMap;
- synchronized (mEntries) {
- final int len = mEntries.size();
- for (int i = 0; i < len; i++) {
- NotificationEntry entry = mEntries.valueAt(i);
- Ranking newRanking = new Ranking();
- if (!getRanking(entry.getKey(), newRanking)) {
- continue;
- }
- entry.setRanking(newRanking);
-
- final StatusBarNotification oldSbn = entry.getSbn().cloneLight();
- final String overrideGroupKey = newRanking.getOverrideGroupKey();
- if (!Objects.equals(oldSbn.getOverrideGroupKey(), overrideGroupKey)) {
- entry.getSbn().setOverrideGroupKey(overrideGroupKey);
- mGroupManager.onEntryUpdated(entry, oldSbn);
- }
- entry.setIsHighPriority(isHighPriority(entry.getSbn()));
- }
- }
- }
- filterAndSort(reason);
- }
-
- /**
- * Get the ranking from the current ranking map.
- *
- * @param key the key to look up
- * @param outRanking the ranking to populate
- *
- * @return {@code true} if the ranking was properly obtained.
- */
- @VisibleForTesting
- protected boolean getRanking(String key, Ranking outRanking) {
- return mRankingMap.getRanking(key, outRanking);
- }
-
- // TODO: This should not be public. Instead the Environment should notify this class when
- // anything changed, and this class should call back the UI so it updates itself.
- /**
- * Filters and sorts the list of notification entries
- */
- public void filterAndSort(String reason) {
- mNotifLog.log(NotifEvent.FILTER_AND_SORT, reason);
- mSortedAndFiltered.clear();
-
- synchronized (mEntries) {
- final int len = mEntries.size();
- for (int i = 0; i < len; i++) {
- NotificationEntry entry = mEntries.valueAt(i);
-
- if (mNotificationFilter.shouldFilterOut(entry)) {
- continue;
- }
-
- mSortedAndFiltered.add(entry);
- }
- }
-
- Collections.sort(mSortedAndFiltered, mRankingComparator);
-
- int bucket = BUCKET_PEOPLE;
- for (NotificationEntry e : mSortedAndFiltered) {
- assignBucketForEntry(e);
- if (e.getBucket() < bucket) {
- android.util.Log.wtf(TAG, "Detected non-contiguous bucket!");
- }
- bucket = e.getBucket();
- }
- }
-
- private void assignBucketForEntry(NotificationEntry e) {
- boolean isHeadsUp = e.isRowHeadsUp();
- boolean isMedia = isImportantMedia(e);
- boolean isSystemMax = isSystemMax(e);
-
- setBucket(e, isHeadsUp, isMedia, isSystemMax);
- }
-
- private void setBucket(
- NotificationEntry e,
- boolean isHeadsUp,
- boolean isMedia,
- boolean isSystemMax) {
- if (mUsePeopleFiltering && isPeopleNotification(e)) {
- e.setBucket(BUCKET_PEOPLE);
- } else if (isHeadsUp || isMedia || isSystemMax || e.isHighPriority()) {
- e.setBucket(BUCKET_ALERTING);
- } else {
- e.setBucket(BUCKET_SILENT);
- }
- }
-
- private boolean isPeopleNotification(NotificationEntry e) {
- return mPeopleNotificationIdentifier.isPeopleNotification(e.getSbn());
- }
-
- public void dump(PrintWriter pw, String indent) {
- int filteredLen = mSortedAndFiltered.size();
- pw.print(indent);
- pw.println("active notifications: " + filteredLen);
- int active;
- for (active = 0; active < filteredLen; active++) {
- NotificationEntry e = mSortedAndFiltered.get(active);
- dumpEntry(pw, indent, active, e);
- }
- synchronized (mEntries) {
- int totalLen = mEntries.size();
- pw.print(indent);
- pw.println("inactive notifications: " + (totalLen - active));
- int inactiveCount = 0;
- for (int i = 0; i < totalLen; i++) {
- NotificationEntry entry = mEntries.valueAt(i);
- if (!mSortedAndFiltered.contains(entry)) {
- dumpEntry(pw, indent, inactiveCount, entry);
- inactiveCount++;
- }
- }
- }
- }
-
- private void dumpEntry(PrintWriter pw, String indent, int i, NotificationEntry e) {
- getRanking(e.getKey(), mTmpRanking);
- pw.print(indent);
- pw.println(" [" + i + "] key=" + e.getKey() + " icon=" + e.icon);
- StatusBarNotification n = e.getSbn();
- pw.print(indent);
- pw.println(" pkg=" + n.getPackageName() + " id=" + n.getId() + " importance="
- + mTmpRanking.getImportance());
- pw.print(indent);
- pw.println(" notification=" + n.getNotification());
- }
-
- private static boolean isSystemNotification(StatusBarNotification sbn) {
- String sbnPackage = sbn.getPackageName();
- return "android".equals(sbnPackage) || "com.android.systemui".equals(sbnPackage);
- }
-
- /**
- * Provides access to keyguard state and user settings dependent data.
- */
- public interface KeyguardEnvironment {
- boolean isDeviceProvisioned();
- boolean isNotificationForCurrentProfiles(StatusBarNotification sbn);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
new file mode 100644
index 000000000000..8bce528bab8c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection
+
+import android.app.Notification
+import android.app.NotificationManager.IMPORTANCE_DEFAULT
+import android.app.NotificationManager.IMPORTANCE_HIGH
+import android.app.NotificationManager.IMPORTANCE_LOW
+import android.app.NotificationManager.IMPORTANCE_MIN
+import android.app.Person
+import android.service.notification.NotificationListenerService.Ranking
+import android.service.notification.NotificationListenerService.RankingMap
+import android.service.notification.StatusBarNotification
+import com.android.internal.annotations.VisibleForTesting
+
+import com.android.systemui.statusbar.NotificationMediaManager
+import com.android.systemui.statusbar.notification.NotificationFilter
+import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
+import com.android.systemui.statusbar.notification.logging.NotifEvent
+import com.android.systemui.statusbar.notification.logging.NotifLog
+import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING
+import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE
+import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT
+import com.android.systemui.statusbar.phone.NotificationGroupManager
+import com.android.systemui.statusbar.policy.HeadsUpManager
+
+import java.util.Objects
+import java.util.ArrayList
+
+import javax.inject.Inject
+
+import kotlin.Comparator
+
+import dagger.Lazy
+
+private const val TAG = "NotifRankingManager"
+
+/**
+ * NotificationRankingManager is responsible for holding on to the most recent [RankingMap], and
+ * updating SystemUI's set of [NotificationEntry]s with their own ranking. It also sorts and filters
+ * a set of entries (but retains none of them). We also set buckets on the entries here since
+ * bucketing is tied closely to sorting.
+ *
+ * For the curious: this class is one iteration closer to null of what used to be called
+ * NotificationData.java.
+ */
+open class NotificationRankingManager @Inject constructor(
+ private val mediaManagerLazy: Lazy<NotificationMediaManager>,
+ private val groupManager: NotificationGroupManager,
+ private val headsUpManager: HeadsUpManager,
+ private val notifFilter: NotificationFilter,
+ private val notifLog: NotifLog,
+ sectionsFeatureManager: NotificationSectionsFeatureManager
+) {
+
+ var rankingMap: RankingMap? = null
+ protected set
+ private val mediaManager by lazy {
+ mediaManagerLazy.get()
+ }
+ private val usePeopleFiltering: Boolean = sectionsFeatureManager.isFilteringEnabled()
+ private val rankingComparator: Comparator<NotificationEntry> = Comparator { a, b ->
+ val na = a.sbn
+ val nb = b.sbn
+ val aRank = a.ranking.rank
+ val bRank = b.ranking.rank
+
+ val aMedia = isImportantMedia(a)
+ val bMedia = isImportantMedia(b)
+
+ val aSystemMax = a.isSystemMax()
+ val bSystemMax = b.isSystemMax()
+
+ val aHeadsUp = a.isRowHeadsUp
+ val bHeadsUp = b.isRowHeadsUp
+
+ if (usePeopleFiltering && a.isPeopleNotification() != b.isPeopleNotification()) {
+ if (a.isPeopleNotification()) -1 else 1
+ } else if (aHeadsUp != bHeadsUp) {
+ if (aHeadsUp) -1 else 1
+ } else if (aHeadsUp) {
+ // Provide consistent ranking with headsUpManager
+ headsUpManager.compare(a, b)
+ } else if (aMedia != bMedia) {
+ // Upsort current media notification.
+ if (aMedia) -1 else 1
+ } else if (aSystemMax != bSystemMax) {
+ // Upsort PRIORITY_MAX system notifications
+ if (aSystemMax) -1 else 1
+ } else if (a.isHighPriority != b.isHighPriority) {
+ -1 * java.lang.Boolean.compare(a.isHighPriority, b.isHighPriority)
+ } else if (aRank != bRank) {
+ aRank - bRank
+ } else {
+ nb.notification.`when`.compareTo(na.notification.`when`)
+ }
+ }
+
+ private fun isImportantMedia(entry: NotificationEntry): Boolean {
+ val importance = entry.ranking.importance
+ return entry.key == mediaManager.mediaNotificationKey && importance > IMPORTANCE_MIN
+ }
+
+ @VisibleForTesting
+ protected fun isHighPriority(entry: NotificationEntry): Boolean {
+ if (entry.importance >= IMPORTANCE_DEFAULT ||
+ hasHighPriorityCharacteristics(entry)) {
+ return true
+ }
+
+ if (groupManager.isSummaryOfGroup(entry.sbn)) {
+ val logicalChildren = groupManager.getLogicalChildren(entry.sbn)
+ for (child in logicalChildren) {
+ if (isHighPriority(child)) {
+ return true
+ }
+ }
+ }
+
+ return false
+ }
+
+ private fun hasHighPriorityCharacteristics(entry: NotificationEntry): Boolean {
+ val c = entry.channel
+ val n = entry.sbn.notification
+
+ if (((n.isForegroundService && entry.ranking.importance >= IMPORTANCE_LOW) ||
+ n.hasMediaSession() ||
+ n.hasPerson() ||
+ n.hasStyle(Notification.MessagingStyle::class.java))) {
+ // Users who have long pressed and demoted to silent should not see the notification
+ // in the top section
+ if (c != null && c.hasUserSetImportance()) {
+ return false
+ }
+
+ return true
+ }
+
+ return false
+ }
+
+ fun updateRanking(
+ newRankingMap: RankingMap?,
+ entries: Collection<NotificationEntry>,
+ reason: String
+ ): List<NotificationEntry> {
+ val eSeq = entries.asSequence()
+
+ // TODO: may not be ideal to guard on null here, but this code is implementing exactly what
+ // NotificationData used to do
+ if (newRankingMap != null) {
+ rankingMap = newRankingMap
+ updateRankingForEntries(eSeq)
+ }
+
+ val filtered: Sequence<NotificationEntry>
+ synchronized(this) {
+ filtered = filterAndSortLocked(eSeq, reason)
+ }
+
+ return filtered.toList()
+ }
+
+ /** Uses the [rankingComparator] to sort notifications which aren't filtered */
+ private fun filterAndSortLocked(
+ entries: Sequence<NotificationEntry>,
+ reason: String
+ ): Sequence<NotificationEntry> {
+ notifLog.log(NotifEvent.FILTER_AND_SORT, reason)
+
+ return entries.filter { !notifFilter.shouldFilterOut(it) }
+ .sortedWith(rankingComparator)
+ .map {
+ assignBucketForEntry(it)
+ it
+ }
+ }
+
+ private fun assignBucketForEntry(entry: NotificationEntry) {
+ val isHeadsUp = entry.isRowHeadsUp
+ val isMedia = isImportantMedia(entry)
+ val isSystemMax = entry.isSystemMax()
+ setBucket(entry, isHeadsUp, isMedia, isSystemMax)
+ }
+
+ private fun setBucket(
+ entry: NotificationEntry,
+ isHeadsUp: Boolean,
+ isMedia: Boolean,
+ isSystemMax: Boolean
+ ) {
+ if (usePeopleFiltering && entry.hasAssociatedPeople()) {
+ entry.bucket = BUCKET_PEOPLE
+ } else if (isHeadsUp || isMedia || isSystemMax || entry.isHighPriority) {
+ entry.bucket = BUCKET_ALERTING
+ } else {
+ entry.bucket = BUCKET_SILENT
+ }
+ }
+
+ private fun updateRankingForEntries(entries: Sequence<NotificationEntry>) {
+ rankingMap?.let { rankingMap ->
+ synchronized(entries) {
+ entries.forEach { entry ->
+ val newRanking = Ranking()
+ if (!rankingMap.getRanking(entry.key, newRanking)) {
+ return@forEach
+ }
+ entry.ranking = newRanking
+
+ val oldSbn = entry.sbn.cloneLight()
+ val newOverrideGroupKey = newRanking.overrideGroupKey
+ if (!Objects.equals(oldSbn.overrideGroupKey, newOverrideGroupKey)) {
+ entry.sbn.overrideGroupKey = newOverrideGroupKey
+ // TODO: notify group manager here?
+ groupManager.onEntryUpdated(entry, oldSbn)
+ }
+ entry.setIsHighPriority(isHighPriority(entry))
+ }
+ }
+ }
+ }
+}
+
+// Convenience functions
+private fun NotificationEntry.isSystemMax(): Boolean {
+ return importance >= IMPORTANCE_HIGH && sbn.isSystemNotification()
+}
+
+private fun StatusBarNotification.isSystemNotification(): Boolean {
+ return "android" == packageName || "com.android.systemui" == packageName
+}
+
+private fun Notification.hasPerson(): Boolean {
+ val people: ArrayList<Person> =
+ (extras?.getParcelableArrayList(Notification.EXTRA_PEOPLE_LIST)) ?: ArrayList()
+ return people.isNotEmpty()
+}
+
+private fun Notification.hasStyle(targetStyleClass: Class<*>): Boolean {
+ return targetStyleClass == notificationStyle
+}
+
+private fun NotificationEntry.isPeopleNotification(): Boolean =
+ sbn.notification.hasStyle(Notification.MessagingStyle::class.java)
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 52fd07937546..1c0a9d4f14af 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
@@ -32,7 +32,6 @@ import android.view.ViewGroup;
import com.android.internal.util.NotificationMessagingUtil;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.UiOffloadThread;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
@@ -61,7 +60,6 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
Dependency.get(NotificationGroupManager.class);
private final NotificationGutsManager mGutsManager =
Dependency.get(NotificationGutsManager.class);
- private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
Dependency.get(NotificationInterruptionStateProvider.class);
@@ -81,16 +79,20 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
private BindRowCallback mBindRowCallback;
private NotificationClicker mNotificationClicker;
- private final NotificationLogger mNotificationLogger = Dependency.get(NotificationLogger.class);
+ private final NotificationLogger mNotificationLogger;
- public NotificationRowBinderImpl(Context context, boolean allowLongPress,
+ public NotificationRowBinderImpl(
+ Context context,
+ boolean allowLongPress,
KeyguardBypassController keyguardBypassController,
- StatusBarStateController statusBarStateController) {
+ StatusBarStateController statusBarStateController,
+ NotificationLogger logger) {
mContext = context;
mMessagingUtil = new NotificationMessagingUtil(context);
mAllowLongPress = allowLongPress;
mKeyguardBypassController = keyguardBypassController;
mStatusBarStateController = statusBarStateController;
+ mNotificationLogger = logger;
}
private NotificationRemoteInputManager getRemoteInputManager() {
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 90c5502bd119..77ccf19f65ee 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
@@ -43,9 +43,9 @@ import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.policy.HeadsUpManager;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.List;
import java.util.Map;
import javax.inject.Inject;
@@ -113,7 +113,7 @@ public class NotificationLogger implements StateListener {
public void run() {
mLastVisibilityReportUptimeMs = SystemClock.uptimeMillis();
- // 1. Loop over mNotificationData entries:
+ // 1. Loop over active entries:
// A. Keep list of visible notifications.
// B. Keep list of previously hidden, now visible notifications.
// 2. Compute no-longer visible notifications by removing currently
@@ -121,8 +121,7 @@ public class NotificationLogger implements StateListener {
// notifications.
// 3. Report newly visible and no-longer visible notifications.
// 4. Keep currently visible notifications for next report.
- ArrayList<NotificationEntry> activeNotifications = mEntryManager
- .getNotificationData().getActiveNotifications();
+ List<NotificationEntry> activeNotifications = mEntryManager.getVisibleNotifications();
int N = activeNotifications.size();
for (int i = 0; i < N; i++) {
NotificationEntry entry = activeNotifications.get(i);
@@ -403,7 +402,7 @@ public class NotificationLogger implements StateListener {
*/
public void onExpansionChanged(String key, boolean isUserAction, boolean isExpanded) {
NotificationVisibility.NotificationLocation location =
- getNotificationLocation(mEntryManager.getNotificationData().get(key));
+ getNotificationLocation(mEntryManager.getActiveNotificationUnfiltered(key));
mExpansionStateLogger.onExpansionChanged(key, isUserAction, isExpanded, location);
}
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 0d8e30ef6988..462fa59c785c 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
@@ -696,8 +696,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void updateFooter() {
boolean showDismissView = mClearAllEnabled && hasActiveClearableNotifications(ROWS_ALL);
- boolean showFooterView = (showDismissView ||
- mEntryManager.getNotificationData().getActiveNotifications().size() != 0)
+ boolean showFooterView = (showDismissView || mEntryManager.hasActiveNotifications())
&& mStatusBarState != StatusBarState.KEYGUARD
&& !mRemoteInputManager.getController().isRemoteInputActive();
@@ -5787,11 +5786,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public boolean hasActiveNotifications() {
- return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty();
- }
-
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void updateSpeedBumpIndex() {
int speedBumpIndex = 0;
int currentIndex = 0;
@@ -6400,7 +6394,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
@Override
public boolean onDraggedDown(View startingChild, int dragLengthY) {
if (mStatusBarState == StatusBarState.KEYGUARD
- && hasActiveNotifications()) {
+ && mEntryManager.hasActiveNotifications()) {
mLockscreenGestureLogger.write(
MetricsEvent.ACTION_LS_SHADE,
(int) (dragLengthY / mDisplayMetrics.density),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java
index 2c931ae1c8ef..e763496da859 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java
@@ -22,7 +22,7 @@ import android.util.Log;
import com.android.systemui.Dependency;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
+import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import javax.inject.Inject;
@@ -42,12 +42,12 @@ public class KeyguardEnvironmentImpl implements KeyguardEnvironment {
public KeyguardEnvironmentImpl() {
}
- @Override // NotificationData.KeyguardEnvironment
+ @Override // NotificationEntryManager.KeyguardEnvironment
public boolean isDeviceProvisioned() {
return mDeviceProvisionedController.isDeviceProvisioned();
}
- @Override // NotificationData.KeyguardEnvironment
+ @Override // NotificationEntryManager.KeyguardEnvironment
public boolean isNotificationForCurrentProfiles(StatusBarNotification n) {
final int notificationUserId = n.getUserId();
if (DEBUG && MULTIUSER_DEBUG) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
index 93887a6617f9..5703f0653398 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
@@ -97,7 +97,7 @@ public class LightsOutNotifController {
}
private boolean hasActiveNotifications() {
- return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty();
+ return mEntryManager.hasActiveNotifications();
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 1e10b6f025f2..3554b54db99b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -15,7 +15,6 @@ import androidx.collection.ArrayMap;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.ContrastColorUtil;
import com.android.settingslib.Utils;
-import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.plugins.DarkIconDispatcher;
@@ -26,7 +25,6 @@ import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -48,7 +46,6 @@ public class NotificationIconAreaController implements DarkReceiver,
private static final long AOD_ICONS_APPEAR_DURATION = 200;
private final ContrastColorUtil mContrastColorUtil;
- private final NotificationEntryManager mEntryManager;
private final Runnable mUpdateStatusBarIcons = this::updateStatusBarIcons;
private final StatusBarStateController mStatusBarStateController;
private final NotificationMediaManager mMediaManager;
@@ -91,7 +88,6 @@ public class NotificationIconAreaController implements DarkReceiver,
mStatusBar = statusBar;
mContrastColorUtil = ContrastColorUtil.getInstance(context);
mContext = context;
- mEntryManager = Dependency.get(NotificationEntryManager.class);
mStatusBarStateController = statusBarStateController;
mStatusBarStateController.addCallback(this);
mMediaManager = notificationMediaManager;
@@ -247,7 +243,7 @@ public class NotificationIconAreaController implements DarkReceiver,
if (hideCenteredIcon && isCenteredNotificationIcon && !entry.isRowHeadsUp()) {
return false;
}
- if (mEntryManager.getNotificationData().isAmbient(entry.getKey()) && !showAmbient) {
+ if (entry.getRanking().isAmbient() && !showAmbient) {
return false;
}
if (hideCurrentMedia && entry.getKey().equals(mMediaManager.getMediaNotificationKey())) {
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 6839fb432c0a..6176cff82f6f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -3562,8 +3562,7 @@ public class NotificationPanelView extends PanelView implements
private void updateShowEmptyShadeView() {
boolean showEmptyShadeView =
- mBarState != StatusBarState.KEYGUARD &&
- mEntryManager.getNotificationData().getActiveNotifications().size() == 0;
+ mBarState != StatusBarState.KEYGUARD && mEntryManager.hasActiveNotifications();
showEmptyShadeView(showEmptyShadeView);
}
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 0e1985de4371..adea8c6aa014 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1228,7 +1228,8 @@ public class StatusBar extends SystemUI implements DemoMode,
mContext,
mAllowNotificationLongPress,
mKeyguardBypassController,
- mStatusBarStateController);
+ mStatusBarStateController,
+ mNotificationLogger);
mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanel,
mHeadsUpManager, mStatusBarWindow, mStackScroller, mDozeScrimController,
@@ -2494,8 +2495,8 @@ public class StatusBar extends SystemUI implements DemoMode,
}
if (DUMPTRUCK) {
- synchronized (mEntryManager.getNotificationData()) {
- mEntryManager.getNotificationData().dump(pw, " ");
+ synchronized (mEntryManager) {
+ mEntryManager.dump(pw, " ");
}
if (false) {
@@ -2753,11 +2754,7 @@ public class StatusBar extends SystemUI implements DemoMode,
};
public void resetUserExpandedStates() {
- ArrayList<NotificationEntry> activeNotifications = mEntryManager.getNotificationData()
- .getActiveNotifications();
- final int notificationCount = activeNotifications.size();
- for (int i = 0; i < notificationCount; i++) {
- NotificationEntry entry = activeNotifications.get(i);
+ for (NotificationEntry entry : mEntryManager.getVisibleNotifications()) {
entry.resetUserExpansion();
}
}
@@ -2857,8 +2854,7 @@ public class StatusBar extends SystemUI implements DemoMode,
try {
// consider the transition from peek to expanded to be a panel open,
// but not one that clears notification effects.
- int notificationLoad = mEntryManager.getNotificationData()
- .getActiveNotifications().size();
+ int notificationLoad = mEntryManager.getActiveNotificationsCount();
mBarService.onPanelRevealed(false, notificationLoad);
} catch (RemoteException ex) {
// Won't fail unless the world has ended.
@@ -2876,8 +2872,7 @@ public class StatusBar extends SystemUI implements DemoMode,
!mPresenter.isPresenterFullyCollapsed() &&
(mState == StatusBarState.SHADE
|| mState == StatusBarState.SHADE_LOCKED);
- int notificationLoad = mEntryManager.getNotificationData().getActiveNotifications()
- .size();
+ int notificationLoad = mEntryManager.getActiveNotificationsCount();
if (pinnedHeadsUp && mPresenter.isPresenterFullyCollapsed()) {
notificationLoad = 1;
}
@@ -3862,10 +3857,6 @@ public class StatusBar extends SystemUI implements DemoMode,
mScreenPinningRequest.showPrompt(taskId, allowCancel);
}
- public boolean hasActiveNotifications() {
- return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty();
- }
-
@Override
public void appTransitionCancelled(int displayId) {
if (displayId == mDisplayId) {
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 64a45e16d749..1cf43cc310ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -326,12 +326,14 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
collapseOnMainThread();
}
- final int count =
- mEntryManager.getNotificationData().getActiveNotifications().size();
- final int rank = mEntryManager.getNotificationData().getRank(notificationKey);
+ //TODO(b/144306683): prove that this `activeEntry` is the same as `entry` above and simplify
+ // this call stack
+ NotificationEntry activeEntry =
+ mEntryManager.getActiveNotificationUnfiltered(notificationKey);
+ final int count = mEntryManager.getActiveNotificationsCount();
+ final int rank = activeEntry != null ? activeEntry.getRanking().getRank() : 0;
NotificationVisibility.NotificationLocation location =
- NotificationLogger.getNotificationLocation(
- mEntryManager.getNotificationData().get(notificationKey));
+ NotificationLogger.getNotificationLocation(activeEntry);
final NotificationVisibility nv = NotificationVisibility.obtain(notificationKey,
rank, count, true, location);
try {
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 38ff86227733..30e26e57e435 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -75,7 +75,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationListContain
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import java.util.ArrayList;
+import java.util.List;
public class StatusBarNotificationPresenter implements NotificationPresenter,
ConfigurationController.ConfigurationListener,
@@ -252,8 +252,8 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
}
private void updateNotificationOnUiModeChanged() {
- ArrayList<NotificationEntry> userNotifications
- = mEntryManager.getNotificationData().getNotificationsForCurrentUser();
+ List<NotificationEntry> userNotifications =
+ mEntryManager.getActiveNotificationsForCurrentUser();
for (int i = 0; i < userNotifications.size(); i++) {
NotificationEntry entry = userNotifications.get(i);
ExpandableNotificationRow row = entry.getRow();
@@ -264,8 +264,8 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
}
private void updateNotificationsOnDensityOrFontScaleChanged() {
- ArrayList<NotificationEntry> userNotifications =
- mEntryManager.getNotificationData().getNotificationsForCurrentUser();
+ List<NotificationEntry> userNotifications =
+ mEntryManager.getActiveNotificationsForCurrentUser();
for (int i = 0; i < userNotifications.size(); i++) {
NotificationEntry entry = userNotifications.get(i);
entry.onDensityOrFontScaleChanged();
@@ -326,7 +326,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
}
public boolean hasActiveNotifications() {
- return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty();
+ return mEntryManager.hasActiveNotifications();
}
public boolean canHeadsUp(NotificationEntry entry, StatusBarNotification sbn) {
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java
index a60ca6201419..49ada1a5e41e 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java
@@ -158,7 +158,7 @@ public class DemoModeFragment extends PreferenceFragment implements OnPreference
String demoTime = "1010"; // 10:10, a classic choice of horologists
try {
- String[] versionParts = android.os.Build.VERSION.RELEASE.split("\\.");
+ String[] versionParts = android.os.Build.VERSION.RELEASE_OR_CODENAME.split("\\.");
int majorVersion = Integer.valueOf(versionParts[0]);
demoTime = String.format("%02d00", majorVersion % 24);
} catch (IllegalArgumentException ex) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
index e63b6d6670fd..02a3766fb3b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
@@ -102,7 +102,7 @@ public class ForegroundServiceControllerTest extends SysuiTestCase {
public void testAppOps_appOpAddedToForegroundNotif() {
// GIVEN a notification associated with a foreground service
NotificationEntry entry = addFgEntry();
- when(mEntryManager.getPendingOrCurrentNotif(entry.getKey())).thenReturn(entry);
+ when(mEntryManager.getPendingOrActiveNotif(entry.getKey())).thenReturn(entry);
// WHEN we are notified of a new app op for this notification
mFsc.onAppOpChanged(
@@ -123,7 +123,7 @@ public class ForegroundServiceControllerTest extends SysuiTestCase {
// GIVEN a foreground service associated notification that already has the correct app op
NotificationEntry entry = addFgEntry();
entry.mActiveAppOps.add(AppOpsManager.OP_CAMERA);
- when(mEntryManager.getPendingOrCurrentNotif(entry.getKey())).thenReturn(entry);
+ when(mEntryManager.getPendingOrActiveNotif(entry.getKey())).thenReturn(entry);
// WHEN we are notified of the same app op for this notification
mFsc.onAppOpChanged(
@@ -143,7 +143,7 @@ public class ForegroundServiceControllerTest extends SysuiTestCase {
public void testAppOps_appOpNotAddedToUnrelatedNotif() {
// GIVEN no notification entries correspond to the newly updated appOp
NotificationEntry entry = addFgEntry();
- when(mEntryManager.getPendingOrCurrentNotif(entry.getKey())).thenReturn(null);
+ when(mEntryManager.getPendingOrActiveNotif(entry.getKey())).thenReturn(null);
// WHEN a new app op is detected
mFsc.onAppOpChanged(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java
index fbb8e0c171cd..f832b7bf769f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java
@@ -65,7 +65,7 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
@Mock private AssistUtils mMockAssistUtils;
@Mock private Handler mMockHandler;
- @Mock private PhenotypeHelper mMockPhenotypeHelper;
+ @Mock private DeviceConfigHelper mMockDeviceConfigHelper;
@Mock private AssistHandleOffBehavior mMockOffBehavior;
@Mock private AssistHandleLikeHomeBehavior mMockLikeHomeBehavior;
@Mock private AssistHandleReminderExpBehavior mMockReminderExpBehavior;
@@ -97,7 +97,7 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
mMockAssistUtils,
mMockHandler,
() -> mMockAssistHandleViewController,
- mMockPhenotypeHelper,
+ mMockDeviceConfigHelper,
behaviorMap,
mMockNavigationModeController,
mMockDumpController);
@@ -216,7 +216,7 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
public void showAndGo_doesNothingIfRecentlyHidden() {
// Arrange
when(mMockAssistUtils.getAssistComponentForUser(anyInt())).thenReturn(COMPONENT_NAME);
- when(mMockPhenotypeHelper.getLong(
+ when(mMockDeviceConfigHelper.getLong(
eq(SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOWN_FREQUENCY_THRESHOLD_MS),
anyLong())).thenReturn(10000L);
mAssistHandleBehaviorController.showAndGo();
@@ -297,7 +297,7 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
public void showAndGoDelayed_doesNothingIfRecentlyHidden() {
// Arrange
when(mMockAssistUtils.getAssistComponentForUser(anyInt())).thenReturn(COMPONENT_NAME);
- when(mMockPhenotypeHelper.getLong(
+ when(mMockDeviceConfigHelper.getLong(
eq(SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOWN_FREQUENCY_THRESHOLD_MS),
anyLong())).thenReturn(10000L);
mAssistHandleBehaviorController.showAndGo();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index a7c02045667d..8c9f75950eb4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -72,7 +72,6 @@ import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationFilter;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.phone.DozeParameters;
@@ -141,8 +140,6 @@ public class BubbleControllerTest extends SysuiTestCase {
private ExpandableNotificationRow mNonBubbleNotifRow;
@Mock
- private NotificationData mNotificationData;
- @Mock
private BubbleController.BubbleStateChangeListener mBubbleStateChangeListener;
@Mock
private BubbleController.BubbleExpandListener mBubbleExpandListener;
@@ -183,10 +180,9 @@ public class BubbleControllerTest extends SysuiTestCase {
mNonBubbleNotifRow = mNotificationTestHelper.createRow();
// Return non-null notification data from the NEM
- when(mNotificationEntryManager.getNotificationData()).thenReturn(mNotificationData);
- when(mNotificationData.get(mRow.getEntry().getKey())).thenReturn(mRow.getEntry());
- when(mNotificationData.getChannel(mRow.getEntry().getKey())).thenReturn(
- mRow.getEntry().getChannel());
+ when(mNotificationEntryManager
+ .getActiveNotificationUnfiltered(mRow.getEntry().getKey())).thenReturn(
+ mRow.getEntry());
mZenModeConfig.suppressedVisualEffects = 0;
when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
index 32361cd0c1d5..a9be30ba82a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.bubbles;
+import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
@@ -72,6 +74,8 @@ public class BubbleDataTest extends SysuiTestCase {
private NotificationEntry mEntryB2;
private NotificationEntry mEntryB3;
private NotificationEntry mEntryC1;
+ private NotificationEntry mEntryInterruptive;
+ private NotificationEntry mEntryDismissed;
private Bubble mBubbleA1;
private Bubble mBubbleA2;
@@ -110,6 +114,13 @@ public class BubbleDataTest extends SysuiTestCase {
mEntryB3 = createBubbleEntry(1, "b3", "package.b");
mEntryC1 = createBubbleEntry(1, "c1", "package.c");
+ mEntryInterruptive = createBubbleEntry(1, "interruptive", "package.d");
+ modifyRanking(mEntryInterruptive)
+ .setVisuallyInterruptive(true)
+ .build();
+
+ mEntryDismissed = createBubbleEntry(1, "dismissed", "package.d");
+
mBubbleA1 = new Bubble(mContext, mEntryA1);
mBubbleA2 = new Bubble(mContext, mEntryA2);
mBubbleA3 = new Bubble(mContext, mEntryA3);
@@ -160,6 +171,77 @@ public class BubbleDataTest extends SysuiTestCase {
assertBubbleRemoved(mBubbleA1, BubbleController.DISMISS_USER_GESTURE);
}
+ @Test
+ public void ifSuppress_hideFlyout() {
+ // Setup
+ mBubbleData.setListener(mListener);
+
+ // Test
+ mBubbleData.notificationEntryUpdated(mEntryC1, /* suppressFlyout */ true, /* showInShade */
+ true);
+
+ // Verify
+ verifyUpdateReceived();
+ BubbleData.Update update = mUpdateCaptor.getValue();
+ assertThat(update.addedBubble.showFlyoutForBubble()).isFalse();
+ }
+
+ @Test
+ public void ifInterruptiveAndNotSuppressed_thenShowFlyout() {
+ // Setup
+ mBubbleData.setListener(mListener);
+
+ // Test
+ mBubbleData.notificationEntryUpdated(mEntryInterruptive, /* suppressFlyout */
+ false, /* showInShade */
+ true);
+
+ // Verify
+ verifyUpdateReceived();
+ BubbleData.Update update = mUpdateCaptor.getValue();
+ assertThat(update.addedBubble.showFlyoutForBubble()).isTrue();
+ }
+
+ @Test
+ public void sameUpdate_InShade_thenHideFlyout() {
+ // Setup
+ mBubbleData.setListener(mListener);
+
+ // Test
+ mBubbleData.notificationEntryUpdated(mEntryC1, /* suppressFlyout */ false, /* showInShade */
+ true);
+ verifyUpdateReceived();
+
+ mBubbleData.notificationEntryUpdated(mEntryC1, /* suppressFlyout */ false, /* showInShade */
+ true);
+ verifyUpdateReceived();
+
+ // Verify
+ BubbleData.Update update = mUpdateCaptor.getValue();
+ assertThat(update.updatedBubble.showFlyoutForBubble()).isFalse();
+ }
+
+ @Test
+ public void sameUpdate_NotInShade_showFlyout() {
+ // Setup
+ mBubbleData.setListener(mListener);
+ setMetadataFlags(mEntryDismissed,
+ Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, /* enableFlag */ true);
+
+ // Test
+ mBubbleData.notificationEntryUpdated(mEntryDismissed, /* suppressFlyout */ false,
+ /* showInShade */ false);
+ verifyUpdateReceived();
+
+ mBubbleData.notificationEntryUpdated(mEntryDismissed, /* suppressFlyout */
+ false, /* showInShade */ false);
+ verifyUpdateReceived();
+
+ // Verify
+ BubbleData.Update update = mUpdateCaptor.getValue();
+ assertThat(update.updatedBubble.showFlyoutForBubble()).isTrue();
+ }
+
// COLLAPSED / ADD
/**
@@ -854,6 +936,23 @@ public class BubbleDataTest extends SysuiTestCase {
}
/**
+ * Sets the bubble metadata flags for this entry. These flags are normally set by
+ * NotificationManagerService when the notification is sent, however, these tests do not
+ * go through that path so we set them explicitly when testing.
+ */
+ private void setMetadataFlags(NotificationEntry entry, int flag, boolean enableFlag) {
+ Notification.BubbleMetadata bubbleMetadata =
+ entry.getSbn().getNotification().getBubbleMetadata();
+ int flags = bubbleMetadata.getFlags();
+ if (enableFlag) {
+ flags |= flag;
+ } else {
+ flags &= ~flag;
+ }
+ bubbleMetadata.setFlags(flags);
+ }
+
+ /**
* No ExpandableNotificationRow is required to test BubbleData. This setup is all that is
* required for BubbleData functionality and verification. NotificationTestHelper is used only
* as a convenience to create a Notification w/BubbleMetadata.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
index 86869bd65900..2f53a01b1d69 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
@@ -37,7 +37,6 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
import org.junit.Before;
import org.junit.Test;
@@ -54,7 +53,6 @@ public class NotificationListenerTest extends SysuiTestCase {
@Mock private NotificationPresenter mPresenter;
@Mock private NotificationListenerService.RankingMap mRanking;
- @Mock private NotificationData mNotificationData;
// Dependency mocks:
@Mock private NotificationEntryManager mEntryManager;
@@ -74,8 +72,6 @@ public class NotificationListenerTest extends SysuiTestCase {
new Handler(TestableLooper.get(this).getLooper()));
mContext.addMockSystemService(NotificationManager.class, mNotificationManager);
- when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
-
mListener = new NotificationListener(mContext);
mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
new Notification(), UserHandle.CURRENT, null, 0);
@@ -90,7 +86,7 @@ public class NotificationListenerTest extends SysuiTestCase {
@Test
public void testNotificationUpdateCallsUpdateNotification() {
- when(mNotificationData.get(mSbn.getKey()))
+ when(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()))
.thenReturn(
new NotificationEntryBuilder()
.setSbn(mSbn)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 548f7a86adf3..d54e24ba2602 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.content.Intent.ACTION_USER_SWITCHED;
import static android.provider.Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL;
@@ -24,7 +25,6 @@ import static com.android.systemui.statusbar.notification.stack.NotificationSect
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -49,7 +49,6 @@ import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -72,7 +71,6 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
// Dependency mocks:
@Mock private NotificationEntryManager mEntryManager;
- @Mock private NotificationData mNotificationData;
@Mock private DeviceProvisionedController mDeviceProvisionedController;
@Mock private StatusBarKeyguardViewManager mKeyguardViewManager;
@Mock private BroadcastDispatcher mBroadcastDispatcher;
@@ -93,7 +91,6 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
when(mUserManager.getProfiles(mCurrentUserId)).thenReturn(Lists.newArrayList(
new UserInfo(mCurrentUserId, "", 0), new UserInfo(mCurrentUserId + 1, "", 0)));
- when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
Handler.createAsync(Looper.myLooper()));
@@ -170,9 +167,10 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
NOTIFICATION_NEW_INTERRUPTION_MODEL, 1);
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1);
- when(mNotificationData.isHighPriority(any())).thenReturn(false);
- NotificationEntry entry = new NotificationEntryBuilder().build();
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setImportance(IMPORTANCE_LOW)
+ .build();
entry.setBucket(BUCKET_SILENT);
assertTrue(mLockscreenUserManager.shouldShowOnKeyguard(entry));
@@ -186,10 +184,12 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
NOTIFICATION_NEW_INTERRUPTION_MODEL, 1);
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0);
- when(mNotificationData.isHighPriority(any())).thenReturn(false);
- NotificationEntry entry = new NotificationEntryBuilder().build();
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setImportance(IMPORTANCE_LOW)
+ .build();
entry.setBucket(BUCKET_SILENT);
+ entry.setIsHighPriority(true);
assertFalse(mLockscreenUserManager.shouldShowOnKeyguard(entry));
}
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 90bd0e9936be..88546b9b31a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -26,7 +26,6 @@ import static org.mockito.Mockito.mock;
import android.annotation.Nullable;
import android.app.ActivityManager;
-import android.app.Instrumentation;
import android.app.Notification;
import android.app.Notification.BubbleMetadata;
import android.app.NotificationChannel;
@@ -40,8 +39,6 @@ import android.text.TextUtils;
import android.view.LayoutInflater;
import android.widget.RemoteViews;
-import androidx.test.InstrumentationRegistry;
-
import com.android.systemui.TestableDependency;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.bubbles.BubblesTestActivity;
@@ -72,7 +69,6 @@ public class NotificationTestHelper {
private static final String GROUP_KEY = "gruKey";
private final Context mContext;
- private final Instrumentation mInstrumentation;
private int mId;
private final NotificationGroupManager mGroupManager;
private ExpandableNotificationRow mRow;
@@ -83,7 +79,7 @@ public class NotificationTestHelper {
dependency.injectMockDependency(NotificationMediaManager.class);
dependency.injectMockDependency(BubbleController.class);
dependency.injectMockDependency(StatusBarWindowController.class);
- mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ dependency.injectMockDependency(SmartReplyController.class);
StatusBarStateController stateController = mock(StatusBarStateController.class);
mGroupManager = new NotificationGroupManager(stateController);
mHeadsUpManager = new HeadsUpManagerPhone(mContext, stateController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 18649bfe68d3..99c94ac7dec2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -46,7 +46,6 @@ import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -73,7 +72,6 @@ import java.util.List;
@TestableLooper.RunWithLooper
public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
@Mock private NotificationPresenter mPresenter;
- @Mock private NotificationData mNotificationData;
@Spy private FakeListContainer mListContainer = new FakeListContainer();
// Dependency mocks:
@@ -105,8 +103,6 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
mHelper = new NotificationTestHelper(mContext, mDependency);
- when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
-
mViewHierarchyManager = new NotificationViewHierarchyManager(mContext,
mHandler, mLockscreenUserManager, mGroupManager, mVisualStabilityManager,
mock(StatusBarStateControllerImpl.class), mEntryManager,
@@ -139,7 +135,7 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
mListContainer.addContainerView(entry0.getRow());
mListContainer.addContainerView(entry1.getRow());
mListContainer.addContainerView(entry2.getRow());
- when(mNotificationData.getActiveNotifications()).thenReturn(
+ when(mEntryManager.getVisibleNotifications()).thenReturn(
Lists.newArrayList(entry0, entry1, entry2));
// Set up group manager to report that they should be bundled now.
@@ -168,7 +164,7 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
// Set up the prior state to look like one top level notification.
mListContainer.addContainerView(entry0.getRow());
- when(mNotificationData.getActiveNotifications()).thenReturn(
+ when(mEntryManager.getVisibleNotifications()).thenReturn(
Lists.newArrayList(entry0, entry1, entry2));
// Set up group manager to report that they should not be bundled now.
@@ -197,7 +193,7 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
// Set up the prior state to look like a top level notification.
mListContainer.addContainerView(entry0.getRow());
- when(mNotificationData.getActiveNotifications()).thenReturn(
+ when(mEntryManager.getVisibleNotifications()).thenReturn(
Lists.newArrayList(entry0, entry1));
// Set up group manager to report a suppressed summary now.
@@ -219,7 +215,7 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
public void testUpdateNotificationViews_appOps() throws Exception {
NotificationEntry entry0 = createEntry();
entry0.setRow(spy(entry0.getRow()));
- when(mNotificationData.getActiveNotifications()).thenReturn(
+ when(mEntryManager.getVisibleNotifications()).thenReturn(
Lists.newArrayList(entry0));
mListContainer.addContainerView(entry0.getRow());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
index 820f4652e685..d003b994f6dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
@@ -175,6 +175,11 @@ public class RankingBuilder {
return this;
}
+ public RankingBuilder setVisuallyInterruptive(boolean interruptive) {
+ mIsVisuallyInterruptive = interruptive;
+ return this;
+ }
+
public RankingBuilder setImportance(@Importance int importance) {
mImportance = importance;
return this;
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 86ef6e8593fc..a98945f49b99 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
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON;
@@ -28,7 +29,6 @@ import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
@@ -40,14 +40,15 @@ import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.Notification;
-import android.app.NotificationManager;
+import android.app.NotificationChannel;
import android.app.PendingIntent;
import android.content.Intent;
import android.graphics.drawable.Icon;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
-import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -73,18 +74,18 @@ import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationRemoveInterceptor;
+import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.StatusBarIconView;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
-import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
+import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
import com.android.systemui.statusbar.notification.collection.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.logging.NotifLog;
-import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.RowInflaterTask;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -93,6 +94,7 @@ import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.util.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -104,13 +106,14 @@ import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
+@TestableLooper.RunWithLooper()
public class NotificationEntryManagerTest extends SysuiTestCase {
private static final String TEST_PACKAGE_NAME = "test";
private static final int TEST_UID = 0;
@@ -123,7 +126,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
@Mock private NotificationRemoveInterceptor mRemoveInterceptor;
@Mock private NotificationRowBinderImpl.BindRowCallback mBindCallback;
@Mock private HeadsUpManager mHeadsUpManager;
- @Mock private NotificationListenerService.RankingMap mRankingMap;
+ @Mock private RankingMap mRankingMap;
@Mock private RemoteInputController mRemoteInputController;
// Dependency mocks:
@@ -139,6 +142,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
@Mock private SmartReplyController mSmartReplyController;
@Mock private RowInflaterTask mAsyncInflationTask;
@Mock private NotificationRowBinder mMockedRowBinder;
+ @Mock private NotifLog mNotifLog;
private int mId;
private NotificationEntry mEntry;
@@ -146,43 +150,9 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
private TestableNotificationEntryManager mEntryManager;
private CountDownLatch mCountDownLatch;
- private class TestableNotificationEntryManager extends NotificationEntryManager {
- private final CountDownLatch mCountDownLatch;
-
- TestableNotificationEntryManager() {
- super(
- new NotificationData(
- mock(NotificationSectionsFeatureManager.class),
- mock(NotifLog.class),
- mock(PeopleNotificationIdentifier.class)),
- mock(NotifLog.class));
- mCountDownLatch = new CountDownLatch(1);
- }
-
- public void setNotificationData(NotificationData data) {
- mNotificationData = data;
- }
-
- @Override
- public void onAsyncInflationFinished(NotificationEntry entry,
- @InflationFlag int inflatedFlags) {
- super.onAsyncInflationFinished(entry, inflatedFlags);
-
- mCountDownLatch.countDown();
- }
-
- public CountDownLatch getCountDownLatch() {
- return mCountDownLatch;
- }
-
- public ArrayList<NotificationLifetimeExtender> getLifetimeExtenders() {
- return mNotificationLifetimeExtenders;
- }
- }
-
private void setUserSentiment(String key, int sentiment) {
doAnswer(invocationOnMock -> {
- NotificationListenerService.Ranking ranking = (NotificationListenerService.Ranking)
+ Ranking ranking = (Ranking)
invocationOnMock.getArguments()[1];
ranking.populate(
key,
@@ -190,16 +160,16 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
false,
0,
0,
- NotificationManager.IMPORTANCE_DEFAULT,
+ IMPORTANCE_DEFAULT,
null, null,
null, null, null, true, sentiment, false, -1, false, null, null, false, false);
return true;
- }).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class));
+ }).when(mRankingMap).getRanking(eq(key), any(Ranking.class));
}
private void setSmartActions(String key, ArrayList<Notification.Action> smartActions) {
doAnswer(invocationOnMock -> {
- NotificationListenerService.Ranking ranking = (NotificationListenerService.Ranking)
+ Ranking ranking = (Ranking)
invocationOnMock.getArguments()[1];
ranking.populate(
key,
@@ -207,13 +177,13 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
false,
0,
0,
- NotificationManager.IMPORTANCE_DEFAULT,
+ IMPORTANCE_DEFAULT,
null, null,
null, null, null, true,
- NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL, false, -1,
+ Ranking.USER_SENTIMENT_NEUTRAL, false, -1,
false, smartActions, null, false, false);
return true;
- }).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class));
+ }).when(mRankingMap).getRanking(eq(key), any(Ranking.class));
}
@Before
@@ -249,7 +219,18 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
mEntry.expandedIcon = mock(StatusBarIconView.class);
- mEntryManager = new TestableNotificationEntryManager();
+ mEntryManager = new TestableNotificationEntryManager(
+ mNotifLog,
+ mGroupManager,
+ new NotificationRankingManager(
+ () -> mock(NotificationMediaManager.class),
+ mGroupManager,
+ mHeadsUpManager,
+ mock(NotificationFilter.class),
+ mNotifLog,
+ mock(NotificationSectionsFeatureManager.class)),
+ mEnvironment
+ );
Dependency.get(InitController.class).executePostInitTasks();
mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mHeadsUpManager);
mEntryManager.addNotificationEntryListener(mEntryListener);
@@ -258,19 +239,19 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
NotificationRowBinderImpl notificationRowBinder =
new NotificationRowBinderImpl(mContext, true, /* allowLongPress */
mock(KeyguardBypassController.class),
- mock(StatusBarStateController.class));
+ mock(StatusBarStateController.class),
+ mock(NotificationLogger.class));
notificationRowBinder.setUpWithPresenter(
mPresenter, mListContainer, mHeadsUpManager, mEntryManager, mBindCallback);
notificationRowBinder.setNotificationClicker(mock(NotificationClicker.class));
mEntryManager.setRowBinder(notificationRowBinder);
setUserSentiment(
- mEntry.getKey(), NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
+ mEntry.getKey(), Ranking.USER_SENTIMENT_NEUTRAL);
}
@Test
public void testAddNotification() throws Exception {
- com.android.systemui.util.Assert.isNotMainThread();
TestableLooper.get(this).processAllMessages();
doAnswer(invocation -> {
@@ -299,21 +280,20 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
verify(mEntryListener).onNotificationAdded(entry);
verify(mPresenter).updateNotificationViews();
- assertEquals(mEntryManager.getNotificationData().get(mSbn.getKey()), entry);
+ assertEquals(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()), entry);
assertNotNull(entry.getRow());
assertEquals(mEntry.getUserSentiment(),
- NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
+ Ranking.USER_SENTIMENT_NEUTRAL);
}
@Test
public void testUpdateNotification() throws Exception {
- com.android.systemui.util.Assert.isNotMainThread();
TestableLooper.get(this).processAllMessages();
- mEntryManager.getNotificationData().add(mEntry);
+ mEntryManager.addActiveNotificationForTest(mEntry);
setUserSentiment(
- mEntry.getKey(), NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
+ mEntry.getKey(), Ranking.USER_SENTIMENT_NEGATIVE);
mEntryManager.updateNotification(mSbn, mRankingMap);
TestableLooper.get(this).processMessages(1);
@@ -327,19 +307,15 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
verify(mEntryListener).onPostEntryUpdated(mEntry);
assertNotNull(mEntry.getRow());
- assertEquals(mEntry.getUserSentiment(),
- NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
+ assertEquals(Ranking.USER_SENTIMENT_NEGATIVE,
+ mEntry.getUserSentiment());
}
@Test
public void testUpdateNotification_prePostEntryOrder() throws Exception {
- com.android.systemui.util.Assert.isNotMainThread();
TestableLooper.get(this).processAllMessages();
- NotificationData notifData = mock(NotificationData.class);
- when(notifData.get(mEntry.getKey())).thenReturn(mEntry);
-
- mEntryManager.setNotificationData(notifData);
+ mEntryManager.addActiveNotificationForTest(mEntry);
mEntryManager.updateNotification(mSbn, mRankingMap);
TestableLooper.get(this).processMessages(1);
@@ -349,9 +325,8 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
verify(mEntryListener, never()).onInflationError(any(), any());
// Ensure that update callbacks happen in correct order
- InOrder order = inOrder(mEntryListener, notifData, mPresenter, mEntryListener);
+ InOrder order = inOrder(mEntryListener, mPresenter, mEntryListener);
order.verify(mEntryListener).onPreEntryUpdated(mEntry);
- order.verify(notifData).filterAndSort(anyString());
order.verify(mPresenter).updateNotificationViews();
order.verify(mEntryListener).onPostEntryUpdated(mEntry);
@@ -359,11 +334,12 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
}
@Test
- public void testRemoveNotification() throws Exception {
- com.android.systemui.util.Assert.isNotMainThread();
+ public void testRemoveNotification() {
+ // Row inflation happens off thread, so pretend that this test looper is main
+ Assert.sMainLooper = TestableLooper.get(this).getLooper();
mEntry.setRow(mRow);
- mEntryManager.getNotificationData().add(mEntry);
+ mEntryManager.addActiveNotificationForTest(mEntry);
mEntryManager.removeNotification(mSbn.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
@@ -374,12 +350,11 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
eq(mEntry), any(), eq(false) /* removedByUser */);
verify(mRow).setRemoved();
- assertNull(mEntryManager.getNotificationData().get(mSbn.getKey()));
+ assertNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()));
}
@Test
public void testRemoveNotification_onEntryRemoveNotFiredIfEntryDoesntExist() {
- com.android.systemui.util.Assert.isNotMainThread();
mEntryManager.removeNotification("not_a_real_key", mRankingMap, UNDEFINED_DISMISS_REASON);
@@ -388,8 +363,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
}
@Test
- public void testRemoveNotification_whilePending() throws InterruptedException {
- com.android.systemui.util.Assert.isNotMainThread();
+ public void testRemoveNotification_whilePending() {
mEntryManager.setRowBinder(mMockedRowBinder);
@@ -408,7 +382,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
mEntry.setRow(mRow);
mEntry.setInflationTask(mAsyncInflationTask);
- mEntryManager.getNotificationData().add(mEntry);
+ mEntryManager.addActiveNotificationForTest(mEntry);
setSmartActions(mEntry.getKey(), new ArrayList<>(Arrays.asList(createAction())));
mEntryManager.updateNotificationRanking(mRankingMap);
@@ -424,7 +398,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
mEntry.setRow(mRow);
- mEntryManager.getNotificationData().add(mEntry);
+ mEntryManager.addActiveNotificationForTest(mEntry);
setSmartActions(mEntry.getKey(), null);
mEntryManager.updateNotificationRanking(mRankingMap);
@@ -438,7 +412,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
mEntry.setRow(null);
- mEntryManager.getNotificationData().add(mEntry);
+ mEntryManager.addActiveNotificationForTest(mEntry);
setSmartActions(mEntry.getKey(), new ArrayList<>(Arrays.asList(createAction())));
mEntryManager.updateNotificationRanking(mRankingMap);
@@ -466,7 +440,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
public void testLifetimeExtenders_ifNotificationIsRetainedItIsntRemoved() {
// GIVEN an entry manager with a notification
mEntryManager.setRowBinder(mMockedRowBinder);
- mEntryManager.getNotificationData().add(mEntry);
+ mEntryManager.addActiveNotificationForTest(mEntry);
// GIVEN a lifetime extender that always tries to extend lifetime
NotificationLifetimeExtender extender = mock(NotificationLifetimeExtender.class);
@@ -479,15 +453,18 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
// THEN the extender is asked to manage the lifetime
verify(extender).setShouldManageLifetime(mEntry, true);
// THEN the notification is retained
- assertNotNull(mEntryManager.getNotificationData().get(mSbn.getKey()));
+ assertNotNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()));
verify(mEntryListener, never()).onEntryRemoved(eq(mEntry), any(), eq(false));
}
@Test
public void testLifetimeExtenders_whenRetentionEndsNotificationIsRemoved() {
+ // Row inflation happens off thread, so pretend that this test looper is main
+ Assert.sMainLooper = TestableLooper.get(this).getLooper();
+
// GIVEN an entry manager with a notification whose life has been extended
mEntryManager.setRowBinder(mMockedRowBinder);
- mEntryManager.getNotificationData().add(mEntry);
+ mEntryManager.addActiveNotificationForTest(mEntry);
final FakeNotificationLifetimeExtender extender = new FakeNotificationLifetimeExtender();
mEntryManager.addNotificationLifetimeExtender(extender);
mEntryManager.removeNotification(mEntry.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
@@ -498,7 +475,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
extender.getCallback().onSafeToRemove(mEntry.getKey());
// THEN the notification is removed
- assertNull(mEntryManager.getNotificationData().get(mSbn.getKey()));
+ assertNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()));
verify(mEntryListener).onEntryRemoved(eq(mEntry), any(), eq(false));
}
@@ -506,7 +483,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
public void testLifetimeExtenders_whenNotificationUpdatedRetainersAreCanceled() {
// GIVEN an entry manager with a notification whose life has been extended
mEntryManager.setRowBinder(mMockedRowBinder);
- mEntryManager.getNotificationData().add(mEntry);
+ mEntryManager.addActiveNotificationForTest(mEntry);
NotificationLifetimeExtender extender = mock(NotificationLifetimeExtender.class);
when(extender.shouldExtendLifetime(mEntry)).thenReturn(true);
mEntryManager.addNotificationLifetimeExtender(extender);
@@ -523,7 +500,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
public void testLifetimeExtenders_whenNewExtenderTakesPrecedenceOldExtenderIsCanceled() {
// GIVEN an entry manager with a notification
mEntryManager.setRowBinder(mMockedRowBinder);
- mEntryManager.getNotificationData().add(mEntry);
+ mEntryManager.addActiveNotificationForTest(mEntry);
// GIVEN two lifetime extenders, the first which never extends and the second which
// always extends
@@ -554,15 +531,15 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
*/
@Test
public void testPerformRemoveNotification_removedEntry() {
- mEntryManager.getNotificationData().remove(mSbn.getKey(), null /* ranking */);
+ mEntryManager.removeNotification(mSbn.getKey(), null, 0);
mEntryManager.performRemoveNotification(mSbn, REASON_CANCEL);
}
@Test
- public void testRemoveInterceptor_interceptsDontGetRemoved() {
+ public void testRemoveInterceptor_interceptsDontGetRemoved() throws InterruptedException {
// GIVEN an entry manager with a notification
mEntryManager.setRowBinder(mMockedRowBinder);
- mEntryManager.getNotificationData().add(mEntry);
+ mEntryManager.addActiveNotificationForTest(mEntry);
// GIVEN interceptor that intercepts that entry
when(mRemoveInterceptor.onNotificationRemoveRequested(eq(mEntry.getKey()), anyInt()))
@@ -572,16 +549,19 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
mEntryManager.removeNotification(mEntry.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
// THEN the interceptor intercepts & the entry is not removed & no listeners are called
- assertNotNull(mEntryManager.getNotificationData().get(mEntry.getKey()));
+ assertNotNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()));
verify(mEntryListener, never()).onEntryRemoved(eq(mEntry),
any(NotificationVisibility.class), anyBoolean());
}
@Test
public void testRemoveInterceptor_notInterceptedGetsRemoved() {
+ // Row inflation happens off thread, so pretend that this test looper is main
+ Assert.sMainLooper = TestableLooper.get(this).getLooper();
+
// GIVEN an entry manager with a notification
mEntryManager.setRowBinder(mMockedRowBinder);
- mEntryManager.getNotificationData().add(mEntry);
+ mEntryManager.addActiveNotificationForTest(mEntry);
// GIVEN interceptor that doesn't intercept
when(mRemoveInterceptor.onNotificationRemoveRequested(eq(mEntry.getKey()), anyInt()))
@@ -591,7 +571,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
mEntryManager.removeNotification(mEntry.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
// THEN the interceptor intercepts & the entry is not removed & no listeners are called
- assertNull(mEntryManager.getNotificationData().get(mEntry.getKey()));
+ assertNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()));
verify(mEntryListener, atLeastOnce()).onEntryRemoved(eq(mEntry),
any(NotificationVisibility.class), anyBoolean());
}
@@ -612,6 +592,63 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
.build();
}
+ /* Tests annexed from NotificationDataTest go here */
+
+ @Test
+ public void testChannelIsSetWhenAdded() {
+ NotificationChannel nc = new NotificationChannel(
+ "testId",
+ "testName",
+ IMPORTANCE_DEFAULT);
+
+ Ranking r = new RankingBuilder()
+ .setKey(mEntry.getKey())
+ .setChannel(nc)
+ .build();
+
+ RankingMap rm = new RankingMap(new Ranking[] { r });
+
+ // GIVEN: a notification is added, and the ranking updated
+ mEntryManager.addActiveNotificationForTest(mEntry);
+ mEntryManager.updateRanking(rm, "testReason");
+
+ // THEN the notification entry better have a channel on it
+ assertEquals(
+ "Channel must be set when adding a notification",
+ nc.getName(),
+ mEntry.getChannel().getName());
+ }
+
+ @Test
+ public void testGetNotificationsForCurrentUser_shouldFilterNonCurrentUserNotifications() {
+ Assert.sMainLooper = TestableLooper.get(this).getLooper();
+ Notification.Builder n = new Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .setContentTitle("Title")
+ .setContentText("Text");
+
+ NotificationEntry e2 = new NotificationEntryBuilder()
+ .setPkg(TEST_PACKAGE_NAME)
+ .setOpPkg(TEST_PACKAGE_NAME)
+ .setUid(TEST_UID)
+ .setId(mId++)
+ .setNotification(n.build())
+ .setUser(new UserHandle(ActivityManager.getCurrentUser()))
+ .build();
+
+ mEntryManager.addActiveNotificationForTest(mEntry);
+ mEntryManager.addActiveNotificationForTest(e2);
+
+ when(mEnvironment.isNotificationForCurrentProfiles(mEntry.getSbn())).thenReturn(false);
+ when(mEnvironment.isNotificationForCurrentProfiles(e2.getSbn())).thenReturn(true);
+
+ List<NotificationEntry> result = mEntryManager.getActiveNotificationsForCurrentUser();
+ assertEquals(result.size(), 1);
+ junit.framework.Assert.assertEquals(result.get(0), e2);
+ }
+
+ /* End annex */
+
private Notification.Action createAction() {
return new Notification.Action.Builder(
Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
index d85f2752a247..68730d12dee3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
@@ -44,7 +44,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationEntryBuilder;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationTestHelper;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -71,7 +71,7 @@ public class NotificationFilterTest extends SysuiTestCase {
@Mock
ForegroundServiceController mFsc;
@Mock
- NotificationData.KeyguardEnvironment mEnvironment;
+ KeyguardEnvironment mEnvironment;
private final IPackageManager mMockPackageManager = mock(IPackageManager.class);
private NotificationFilter mNotificationFilter;
@@ -96,7 +96,7 @@ public class NotificationFilterTest extends SysuiTestCase {
new NotificationGroupManager(mock(StatusBarStateController.class)));
mDependency.injectMockDependency(ShadeController.class);
mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
- mDependency.injectTestDependency(NotificationData.KeyguardEnvironment.class, mEnvironment);
+ mDependency.injectTestDependency(KeyguardEnvironment.class, mEnvironment);
when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
mRow = new NotificationTestHelper(getContext(), mDependency).createRow();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java
index cc56949d67f7..133d52b4f946 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java
@@ -17,9 +17,7 @@
package com.android.systemui.statusbar.notification;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.Notification;
@@ -34,14 +32,10 @@ import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.NotificationEntryBuilder;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.logging.NotifLog;
-import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
-import com.android.systemui.util.DeviceConfigProxyFake;
import org.junit.Before;
import org.junit.Test;
@@ -67,13 +61,6 @@ public class NotificationListControllerTest extends SysuiTestCase {
private NotificationEntryListener mEntryListener;
private DeviceProvisionedListener mProvisionedListener;
- // TODO: Remove this once EntryManager no longer needs to be mocked
- private NotificationData mNotificationData =
- new NotificationData(
- new NotificationSectionsFeatureManager(new DeviceConfigProxyFake(), mContext),
- mock(NotifLog.class),
- mock(PeopleNotificationIdentifier.class));
-
private int mNextNotifId = 0;
@Before
@@ -81,8 +68,6 @@ public class NotificationListControllerTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
- when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
-
mController = new NotificationListController(
mEntryManager,
mListContainer,
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
new file mode 100644
index 000000000000..34beefe9843b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt
@@ -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.systemui.statusbar.notification
+
+import com.android.systemui.statusbar.NotificationPresenter
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationRankingManager
+import com.android.systemui.statusbar.notification.logging.NotifLog
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
+import com.android.systemui.statusbar.phone.NotificationGroupManager
+
+import java.util.concurrent.CountDownLatch
+
+/**
+ * Enable some test capabilities for NEM without making everything public on the base class
+ */
+class TestableNotificationEntryManager(
+ log: NotifLog,
+ gm: NotificationGroupManager,
+ rm: NotificationRankingManager,
+ ke: KeyguardEnvironment
+) : NotificationEntryManager(log, gm, rm, ke) {
+
+ public var countDownLatch: CountDownLatch = CountDownLatch(1)
+
+ override fun onAsyncInflationFinished(entry: NotificationEntry?, inflatedFlags: Int) {
+ super.onAsyncInflationFinished(entry, inflatedFlags)
+ countDownLatch.countDown()
+ }
+
+ fun setUpForTest(
+ presenter: NotificationPresenter?,
+ listContainer: NotificationListContainer?,
+ headsUpManager: HeadsUpManagerPhone?
+ ) {
+ super.setUpWithPresenter(presenter, listContainer, headsUpManager)
+ }
+
+ fun setActiveNotificationList(activeList: List<NotificationEntry>) {
+ mSortedAndFiltered.clear()
+ mSortedAndFiltered.addAll(activeList)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
deleted file mode 100644
index 1a469d881d2c..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
+++ /dev/null
@@ -1,697 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.notification.collection;
-
-import static android.app.Notification.CATEGORY_ALARM;
-import static android.app.Notification.CATEGORY_CALL;
-import static android.app.Notification.CATEGORY_EVENT;
-import static android.app.Notification.CATEGORY_MESSAGE;
-import static android.app.Notification.CATEGORY_REMINDER;
-import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
-import static android.app.NotificationManager.IMPORTANCE_LOW;
-import static android.app.NotificationManager.IMPORTANCE_MIN;
-
-import static com.android.systemui.statusbar.NotificationEntryHelper.modifySbn;
-import static com.android.systemui.statusbar.notification.collection.NotificationDataTest.TestableNotificationData.OVERRIDE_CHANNEL;
-import static com.android.systemui.statusbar.notification.collection.NotificationDataTest.TestableNotificationData.OVERRIDE_IMPORTANCE;
-import static com.android.systemui.statusbar.notification.collection.NotificationDataTest.TestableNotificationData.OVERRIDE_RANK;
-import static com.android.systemui.statusbar.notification.collection.NotificationDataTest.TestableNotificationData.OVERRIDE_VIS_EFFECTS;
-import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING;
-import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT;
-
-import static junit.framework.Assert.assertEquals;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.Manifest;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.Person;
-import android.content.Intent;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
-import android.graphics.drawable.Icon;
-import android.media.session.MediaSession;
-import android.os.Bundle;
-import android.service.notification.NotificationListenerService;
-import android.service.notification.NotificationListenerService.Ranking;
-import android.service.notification.SnoozeCriterion;
-import android.service.notification.StatusBarNotification;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.Dependency;
-import com.android.systemui.ForegroundServiceController;
-import com.android.systemui.InitController;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.NotificationEntryBuilder;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationTestHelper;
-import com.android.systemui.statusbar.RankingBuilder;
-import com.android.systemui.statusbar.SbnBuilder;
-import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
-import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
-import com.android.systemui.statusbar.notification.logging.NotifLog;
-import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.ShadeController;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
-public class NotificationDataTest extends SysuiTestCase {
-
- private static final int UID_NORMAL = 123;
- private static final int UID_ALLOW_DURING_SETUP = 456;
- private static final NotificationChannel NOTIFICATION_CHANNEL =
- new NotificationChannel("id", "name", NotificationChannel.USER_LOCKED_IMPORTANCE);
-
- private NotificationEntry mEntry;
-
- @Mock
- ForegroundServiceController mFsc;
- @Mock
- NotificationData.KeyguardEnvironment mEnvironment;
-
- private final IPackageManager mMockPackageManager = mock(IPackageManager.class);
- private TestableNotificationData mNotificationData;
- private ExpandableNotificationRow mRow;
-
- @Before
- public void setUp() throws Exception {
- com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
- MockitoAnnotations.initMocks(this);
-
- mEntry = new NotificationEntryBuilder()
- .setUid(UID_NORMAL)
- .build();
-
- when(mMockPackageManager.checkUidPermission(
- eq(Manifest.permission.NOTIFICATION_DURING_SETUP),
- eq(UID_NORMAL)))
- .thenReturn(PackageManager.PERMISSION_DENIED);
- when(mMockPackageManager.checkUidPermission(
- eq(Manifest.permission.NOTIFICATION_DURING_SETUP),
- eq(UID_ALLOW_DURING_SETUP)))
- .thenReturn(PackageManager.PERMISSION_GRANTED);
-
- mDependency.injectTestDependency(ForegroundServiceController.class, mFsc);
- mDependency.injectTestDependency(NotificationGroupManager.class,
- new NotificationGroupManager(mock(StatusBarStateController.class)));
- mDependency.injectMockDependency(ShadeController.class);
- mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
- mDependency.injectTestDependency(KeyguardEnvironment.class, mEnvironment);
- when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
- when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
- mNotificationData = new TestableNotificationData(
- mock(NotificationSectionsFeatureManager.class));
- mNotificationData.updateRanking(mock(NotificationListenerService.RankingMap.class), "");
- mRow = new NotificationTestHelper(getContext(), mDependency).createRow();
- Dependency.get(InitController.class).executePostInitTasks();
- }
-
- @Test
- public void testChannelSetWhenAdded() {
- Bundle override = new Bundle();
- override.putParcelable(OVERRIDE_CHANNEL, NOTIFICATION_CHANNEL);
- mNotificationData.rankingOverrides.put(mRow.getEntry().getKey(), override);
- mNotificationData.add(mRow.getEntry());
- assertEquals(NOTIFICATION_CHANNEL, mRow.getEntry().getChannel());
- }
-
- @Test
- public void testGetNotificationsForCurrentUser_shouldFilterNonCurrentUserNotifications()
- throws Exception {
- mNotificationData.add(mRow.getEntry());
- ExpandableNotificationRow row2 = new NotificationTestHelper(getContext(), mDependency)
- .createRow();
- mNotificationData.add(row2.getEntry());
-
- when(mEnvironment.isNotificationForCurrentProfiles(
- mRow.getEntry().getSbn())).thenReturn(false);
- when(mEnvironment.isNotificationForCurrentProfiles(
- row2.getEntry().getSbn())).thenReturn(true);
- ArrayList<NotificationEntry> result =
- mNotificationData.getNotificationsForCurrentUser();
-
- assertEquals(result.size(), 1);
- junit.framework.Assert.assertEquals(result.get(0), row2.getEntry());
- }
-
- @Test
- public void testIsExemptFromDndVisualSuppression_foreground() {
- initStatusBarNotification(false);
-
- mEntry.getSbn().getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
- mEntry.setRow(mRow);
- mNotificationData.add(mEntry);
- Bundle override = new Bundle();
- override.putInt(OVERRIDE_VIS_EFFECTS, 255);
- mNotificationData.rankingOverrides.put(mEntry.getKey(), override);
-
- assertTrue(mEntry.isExemptFromDndVisualSuppression());
- assertFalse(mEntry.shouldSuppressAmbient());
- }
-
- @Test
- public void testIsExemptFromDndVisualSuppression_media() {
- initStatusBarNotification(false);
- Notification n = mEntry.getSbn().getNotification();
- Notification.Builder nb = Notification.Builder.recoverBuilder(mContext, n);
- nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class)));
- n = nb.build();
- modifySbn(mEntry)
- .setNotification(n)
- .build();
- mEntry.setRow(mRow);
- mNotificationData.add(mEntry);
- Bundle override = new Bundle();
- override.putInt(OVERRIDE_VIS_EFFECTS, 255);
- mNotificationData.rankingOverrides.put(mEntry.getKey(), override);
-
- assertTrue(mEntry.isExemptFromDndVisualSuppression());
- assertFalse(mEntry.shouldSuppressAmbient());
- }
-
- @Test
- public void testIsExemptFromDndVisualSuppression_system() {
- initStatusBarNotification(false);
- mEntry.setRow(mRow);
- mEntry.mIsSystemNotification = true;
- mNotificationData.add(mEntry);
- Bundle override = new Bundle();
- override.putInt(OVERRIDE_VIS_EFFECTS, 255);
- mNotificationData.rankingOverrides.put(mEntry.getKey(), override);
-
- assertTrue(mEntry.isExemptFromDndVisualSuppression());
- assertFalse(mEntry.shouldSuppressAmbient());
- }
-
- @Test
- public void testIsNotExemptFromDndVisualSuppression_hiddenCategories() {
- initStatusBarNotification(false);
- NotificationEntry entry = new NotificationEntryBuilder()
- .setUid(UID_NORMAL)
- .build();
- entry.setRow(mRow);
- entry.mIsSystemNotification = true;
- Bundle override = new Bundle();
- override.putInt(OVERRIDE_VIS_EFFECTS, NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT);
- mNotificationData.rankingOverrides.put(entry.getKey(), override);
- mNotificationData.add(entry);
-
- modifySbn(entry)
- .setNotification(
- new Notification.Builder(mContext, "").setCategory(CATEGORY_CALL).build())
- .build();
- assertFalse(entry.isExemptFromDndVisualSuppression());
- assertTrue(entry.shouldSuppressAmbient());
-
- modifySbn(entry)
- .setNotification(
- new Notification.Builder(mContext, "")
- .setCategory(CATEGORY_REMINDER)
- .build())
- .build();
- assertFalse(entry.isExemptFromDndVisualSuppression());
-
- modifySbn(entry)
- .setNotification(
- new Notification.Builder(mContext, "").setCategory(CATEGORY_ALARM).build())
- .build();
- assertFalse(entry.isExemptFromDndVisualSuppression());
-
- modifySbn(entry)
- .setNotification(
- new Notification.Builder(mContext, "").setCategory(CATEGORY_EVENT).build())
- .build();
- assertFalse(entry.isExemptFromDndVisualSuppression());
-
- modifySbn(entry)
- .setNotification(
- new Notification.Builder(mContext, "")
- .setCategory(CATEGORY_MESSAGE)
- .build())
- .build();
- assertFalse(entry.isExemptFromDndVisualSuppression());
- }
-
- @Test
- public void testCreateNotificationDataEntry_RankingUpdate() {
- StatusBarNotification sbn = new SbnBuilder().build();
- sbn.getNotification().actions =
- new Notification.Action[] { createContextualAction("appGeneratedAction") };
-
- ArrayList<Notification.Action> systemGeneratedSmartActions =
- createActions("systemGeneratedAction");
-
- SnoozeCriterion snoozeCriterion = new SnoozeCriterion("id", "explanation", "confirmation");
- ArrayList<SnoozeCriterion> snoozeCriterions = new ArrayList<>();
- snoozeCriterions.add(snoozeCriterion);
-
- Ranking ranking = new RankingBuilder()
- .setKey(sbn.getKey())
- .setSmartActions(systemGeneratedSmartActions)
- .setChannel(NOTIFICATION_CHANNEL)
- .setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE)
- .setSnoozeCriteria(snoozeCriterions)
- .build();
-
- NotificationEntry entry =
- new NotificationEntry(sbn, ranking);
-
- assertEquals(systemGeneratedSmartActions, entry.getSmartActions());
- assertEquals(NOTIFICATION_CHANNEL, entry.getChannel());
- assertEquals(Ranking.USER_SENTIMENT_NEGATIVE, entry.getUserSentiment());
- assertEquals(snoozeCriterions, entry.getSnoozeCriteria());
- }
-
- @Test
- public void notificationDataEntry_testIsLastMessageFromReply() {
- Person.Builder person = new Person.Builder()
- .setName("name")
- .setKey("abc")
- .setUri("uri")
- .setBot(true);
-
- // EXTRA_MESSAGING_PERSON is the same Person as the sender in last message in EXTRA_MESSAGES
- Bundle bundle = new Bundle();
- bundle.putParcelable(Notification.EXTRA_MESSAGING_PERSON, person.build());
- Bundle[] messagesBundle = new Bundle[]{ new Notification.MessagingStyle.Message(
- "text", 0, person.build()).toBundle() };
- bundle.putParcelableArray(Notification.EXTRA_MESSAGES, messagesBundle);
-
- Notification notification = new Notification.Builder(mContext, "test")
- .addExtras(bundle)
- .build();
-
- NotificationEntry entry = new NotificationEntryBuilder()
- .setPkg("pkg")
- .setOpPkg("pkg")
- .setTag("tag")
- .setNotification(notification)
- .setUser(mContext.getUser())
- .setOverrideGroupKey("")
- .build();
- entry.setHasSentReply();
-
- assertTrue(entry.isLastMessageFromReply());
- }
-
- @Test
- public void personHighPriority() {
- Person person = new Person.Builder()
- .setName("name")
- .setKey("abc")
- .setUri("uri")
- .setBot(true)
- .build();
-
- Notification notification = new Notification.Builder(mContext, "test")
- .addPerson(person)
- .build();
-
- StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
- notification, mContext.getUser(), "", 0);
-
- assertTrue(mNotificationData.isHighPriority(sbn));
- }
-
- @Test
- public void messagingStyleHighPriority() {
-
- Notification notification = new Notification.Builder(mContext, "test")
- .setStyle(new Notification.MessagingStyle(""))
- .build();
-
- StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
- notification, mContext.getUser(), "", 0);
-
- assertTrue(mNotificationData.isHighPriority(sbn));
- }
-
- @Test
- public void minForegroundNotHighPriority() {
- Notification notification = mock(Notification.class);
- when(notification.isForegroundService()).thenReturn(true);
-
- StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
- notification, mContext.getUser(), "", 0);
-
- Bundle override = new Bundle();
- override.putInt(OVERRIDE_IMPORTANCE, IMPORTANCE_MIN);
- mNotificationData.rankingOverrides.put(sbn.getKey(), override);
-
- assertFalse(mNotificationData.isHighPriority(sbn));
- }
-
- @Test
- public void lowForegroundHighPriority() {
- Notification notification = mock(Notification.class);
- when(notification.isForegroundService()).thenReturn(true);
-
- StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
- notification, mContext.getUser(), "", 0);
-
- Bundle override = new Bundle();
- override.putInt(OVERRIDE_IMPORTANCE, IMPORTANCE_LOW);
- mNotificationData.rankingOverrides.put(sbn.getKey(), override);
-
- assertTrue(mNotificationData.isHighPriority(sbn));
- }
-
- @Test
- public void userChangeTrumpsHighPriorityCharacteristics() {
- Person person = new Person.Builder()
- .setName("name")
- .setKey("abc")
- .setUri("uri")
- .setBot(true)
- .build();
-
- Notification notification = new Notification.Builder(mContext, "test")
- .addPerson(person)
- .setStyle(new Notification.MessagingStyle(""))
- .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
- .build();
-
- StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
- notification, mContext.getUser(), "", 0);
-
- NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
- channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
-
- Bundle override = new Bundle();
- override.putParcelable(OVERRIDE_CHANNEL, channel);
- mNotificationData.rankingOverrides.put(sbn.getKey(), override);
-
- assertFalse(mNotificationData.isHighPriority(sbn));
- }
-
- @Test
- public void testSort_highPriorityTrumpsNMSRank() {
- // NMS rank says A and then B. But A is not high priority and B is, so B should sort in
- // front
- Notification aN = new Notification.Builder(mContext, "test")
- .setStyle(new Notification.MessagingStyle(""))
- .build();
- NotificationEntry a = new NotificationEntryBuilder()
- .setPkg("pkg")
- .setOpPkg("pkg")
- .setTag("tag")
- .setNotification(aN)
- .setUser(mContext.getUser())
- .setOverrideGroupKey("")
- .build();
- a.setRow(mock(ExpandableNotificationRow.class));
- a.setIsHighPriority(false);
-
- Bundle override = new Bundle();
- override.putInt(OVERRIDE_IMPORTANCE, IMPORTANCE_LOW);
- override.putInt(OVERRIDE_RANK, 1);
- mNotificationData.rankingOverrides.put(a.getKey(), override);
-
- Notification bN = new Notification.Builder(mContext, "test")
- .setStyle(new Notification.MessagingStyle(""))
- .build();
- NotificationEntry b = new NotificationEntryBuilder()
- .setPkg("pkg2")
- .setOpPkg("pkg2")
- .setTag("tag")
- .setNotification(bN)
- .setUser(mContext.getUser())
- .setOverrideGroupKey("")
- .build();
- b.setIsHighPriority(true);
- b.setRow(mock(ExpandableNotificationRow.class));
-
- Bundle bOverride = new Bundle();
- bOverride.putInt(OVERRIDE_IMPORTANCE, IMPORTANCE_LOW);
- bOverride.putInt(OVERRIDE_RANK, 2);
- mNotificationData.rankingOverrides.put(b.getKey(), bOverride);
-
- assertEquals(1, mNotificationData.mRankingComparator.compare(a, b));
- }
-
- @Test
- public void testSort_samePriorityUsesNMSRank() {
- // NMS rank says A and then B, and they are the same priority so use that rank
- Notification aN = new Notification.Builder(mContext, "test")
- .setStyle(new Notification.MessagingStyle(""))
- .build();
- NotificationEntry a = new NotificationEntryBuilder()
- .setPkg("pkg")
- .setOpPkg("pkg")
- .setTag("tag")
- .setNotification(aN)
- .setUser(mContext.getUser())
- .setOverrideGroupKey("")
- .build();
- a.setRow(mock(ExpandableNotificationRow.class));
- a.setIsHighPriority(false);
-
- Bundle override = new Bundle();
- override.putInt(OVERRIDE_IMPORTANCE, IMPORTANCE_LOW);
- override.putInt(OVERRIDE_RANK, 1);
- mNotificationData.rankingOverrides.put(a.getKey(), override);
-
- Notification bN = new Notification.Builder(mContext, "test")
- .setStyle(new Notification.MessagingStyle(""))
- .build();
- NotificationEntry b = new NotificationEntryBuilder()
- .setPkg("pkg2")
- .setOpPkg("pkg2")
- .setTag("tag")
- .setNotification(bN)
- .setUser(mContext.getUser())
- .setOverrideGroupKey("")
- .build();
- b.setRow(mock(ExpandableNotificationRow.class));
- b.setIsHighPriority(false);
-
- Bundle bOverride = new Bundle();
- bOverride.putInt(OVERRIDE_IMPORTANCE, IMPORTANCE_LOW);
- bOverride.putInt(OVERRIDE_RANK, 2);
- mNotificationData.rankingOverrides.put(b.getKey(), bOverride);
-
- assertEquals(-1, mNotificationData.mRankingComparator.compare(a, b));
- }
-
- @Test
- public void testSort_properlySetsAlertingBucket() {
- Notification notification = new Notification.Builder(mContext, "test")
- .build();
- NotificationEntry entry = new NotificationEntryBuilder()
- .setPkg("pkg")
- .setOpPkg("pkg")
- .setTag("tag")
- .setNotification(notification)
- .setUser(mContext.getUser())
- .setOverrideGroupKey("")
- .build();
-
- Bundle override = new Bundle();
- override.putInt(OVERRIDE_IMPORTANCE, IMPORTANCE_DEFAULT);
- mNotificationData.rankingOverrides.put(entry.getKey(), override);
-
- entry.setRow(mRow);
- mNotificationData.add(entry);
-
- assertEquals(entry.getBucket(), BUCKET_ALERTING);
- }
-
- @Test
- public void testSort_properlySetsSilentBucket() {
- Notification notification = new Notification.Builder(mContext, "test")
- .build();
-
- NotificationEntry entry = new NotificationEntryBuilder()
- .setPkg("pkg")
- .setOpPkg("pkg")
- .setTag("tag")
- .setNotification(notification)
- .setUser(mContext.getUser())
- .setOverrideGroupKey("")
- .build();
-
- Bundle override = new Bundle();
- override.putInt(OVERRIDE_IMPORTANCE, IMPORTANCE_LOW);
- mNotificationData.rankingOverrides.put(entry.getKey(), override);
-
- entry.setRow(mRow);
- mNotificationData.add(entry);
-
- assertEquals(entry.getBucket(), BUCKET_SILENT);
- }
-
- private void initStatusBarNotification(boolean allowDuringSetup) {
- Bundle bundle = new Bundle();
- bundle.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, allowDuringSetup);
- Notification notification = new Notification.Builder(mContext, "test")
- .addExtras(bundle)
- .build();
- modifySbn(mEntry)
- .setNotification(notification)
- .build();
- }
-
- public static class TestableNotificationData extends NotificationData {
- public TestableNotificationData(NotificationSectionsFeatureManager sectionsFeatureManager) {
- super(
- sectionsFeatureManager,
- mock(NotifLog.class),
- mock(PeopleNotificationIdentifier.class));
- }
-
- public static final String OVERRIDE_RANK = "r";
- public static final String OVERRIDE_DND = "dnd";
- public static final String OVERRIDE_VIS_OVERRIDE = "vo";
- public static final String OVERRIDE_VIS_EFFECTS = "ve";
- public static final String OVERRIDE_IMPORTANCE = "i";
- public static final String OVERRIDE_IMP_EXP = "ie";
- public static final String OVERRIDE_GROUP = "g";
- public static final String OVERRIDE_CHANNEL = "c";
- public static final String OVERRIDE_PEOPLE = "p";
- public static final String OVERRIDE_SNOOZE_CRITERIA = "sc";
- public static final String OVERRIDE_BADGE = "b";
- public static final String OVERRIDE_USER_SENTIMENT = "us";
- public static final String OVERRIDE_HIDDEN = "h";
- public static final String OVERRIDE_LAST_ALERTED = "la";
- public static final String OVERRIDE_NOISY = "n";
- public static final String OVERRIDE_SMART_ACTIONS = "sa";
- public static final String OVERRIDE_SMART_REPLIES = "sr";
- public static final String OVERRIDE_BUBBLE = "cb";
- public static final String OVERRIDE_VISUALLY_INTERRUPTIVE = "vi";
-
- public Map<String, Bundle> rankingOverrides = new HashMap<>();
-
- @Override
- protected boolean getRanking(String key, Ranking outRanking) {
- super.getRanking(key, outRanking);
-
- ArrayList<String> currentAdditionalPeople = new ArrayList<>();
- if (outRanking.getAdditionalPeople() != null) {
- currentAdditionalPeople.addAll(outRanking.getAdditionalPeople());
- }
-
- ArrayList<SnoozeCriterion> currentSnooze = new ArrayList<>();
- if (outRanking.getSnoozeCriteria() != null) {
- currentSnooze.addAll(outRanking.getSnoozeCriteria());
- }
-
- ArrayList<Notification.Action> currentActions = new ArrayList<>();
- if (outRanking.getSmartActions() != null) {
- currentActions.addAll(outRanking.getSmartActions());
- }
-
- ArrayList<CharSequence> currentReplies = new ArrayList<>();
- if (outRanking.getSmartReplies() != null) {
- currentReplies.addAll(outRanking.getSmartReplies());
- }
-
- if (rankingOverrides.get(key) != null) {
- Bundle overrides = rankingOverrides.get(key);
- outRanking.populate(key,
- overrides.getInt(OVERRIDE_RANK, outRanking.getRank()),
- overrides.getBoolean(OVERRIDE_DND, outRanking.matchesInterruptionFilter()),
- overrides.getInt(OVERRIDE_VIS_OVERRIDE, outRanking.getVisibilityOverride()),
- overrides.getInt(OVERRIDE_VIS_EFFECTS,
- outRanking.getSuppressedVisualEffects()),
- overrides.getInt(OVERRIDE_IMPORTANCE, outRanking.getImportance()),
- overrides.getCharSequence(OVERRIDE_IMP_EXP,
- outRanking.getImportanceExplanation()),
- overrides.getString(OVERRIDE_GROUP, outRanking.getOverrideGroupKey()),
- overrides.containsKey(OVERRIDE_CHANNEL)
- ? (NotificationChannel) overrides.getParcelable(OVERRIDE_CHANNEL)
- : outRanking.getChannel(),
- overrides.containsKey(OVERRIDE_PEOPLE)
- ? overrides.getStringArrayList(OVERRIDE_PEOPLE)
- : currentAdditionalPeople,
- overrides.containsKey(OVERRIDE_SNOOZE_CRITERIA)
- ? overrides.getParcelableArrayList(OVERRIDE_SNOOZE_CRITERIA)
- : currentSnooze,
- overrides.getBoolean(OVERRIDE_BADGE, outRanking.canShowBadge()),
- overrides.getInt(OVERRIDE_USER_SENTIMENT, outRanking.getUserSentiment()),
- overrides.getBoolean(OVERRIDE_HIDDEN, outRanking.isSuspended()),
- overrides.getLong(OVERRIDE_LAST_ALERTED,
- outRanking.getLastAudiblyAlertedMillis()),
- overrides.getBoolean(OVERRIDE_NOISY, outRanking.isNoisy()),
- overrides.containsKey(OVERRIDE_SMART_ACTIONS)
- ? overrides.getParcelableArrayList(OVERRIDE_SMART_ACTIONS)
- : currentActions,
- overrides.containsKey(OVERRIDE_SMART_REPLIES)
- ? overrides.getCharSequenceArrayList(OVERRIDE_SMART_REPLIES)
- : currentReplies,
- overrides.getBoolean(OVERRIDE_BUBBLE, outRanking.canBubble()),
- overrides.getBoolean(OVERRIDE_VISUALLY_INTERRUPTIVE,
- outRanking.visuallyInterruptive()));
- } else {
- outRanking.populate(
- new RankingBuilder()
- .setKey(key)
- .build());
- }
- return true;
- }
- }
-
- private Notification.Action createContextualAction(String title) {
- return new Notification.Action.Builder(
- Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon),
- title,
- PendingIntent.getBroadcast(getContext(), 0, new Intent("Action"), 0))
- .setContextual(true)
- .build();
- }
-
- private Notification.Action createAction(String title) {
- return new Notification.Action.Builder(
- Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon),
- title,
- PendingIntent.getBroadcast(getContext(), 0, new Intent("Action"), 0)).build();
- }
-
- private ArrayList<Notification.Action> createActions(String... titles) {
- ArrayList<Notification.Action> actions = new ArrayList<>();
- for (String title : titles) {
- actions.add(createAction(title));
- }
- return actions;
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
new file mode 100644
index 000000000000..536aeb4d0163
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection;
+
+import static android.app.Notification.CATEGORY_ALARM;
+import static android.app.Notification.CATEGORY_CALL;
+import static android.app.Notification.CATEGORY_EVENT;
+import static android.app.Notification.CATEGORY_MESSAGE;
+import static android.app.Notification.CATEGORY_REMINDER;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
+
+import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
+import static com.android.systemui.statusbar.NotificationEntryHelper.modifySbn;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.PendingIntent;
+import android.app.Person;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
+import android.media.session.MediaSession;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.SnoozeCriterion;
+import android.service.notification.StatusBarNotification;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationEntryBuilder;
+import com.android.systemui.statusbar.RankingBuilder;
+import com.android.systemui.statusbar.SbnBuilder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class NotificationEntryTest extends SysuiTestCase {
+ private static final String TEST_PACKAGE_NAME = "test";
+ private static final int TEST_UID = 0;
+ private static final int UID_NORMAL = 123;
+ private static final NotificationChannel NOTIFICATION_CHANNEL =
+ new NotificationChannel("id", "name", NotificationChannel.USER_LOCKED_IMPORTANCE);
+
+ private int mId;
+
+ private NotificationEntry mEntry;
+
+ @Before
+ public void setup() {
+ Notification.Builder n = new Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .setContentTitle("Title")
+ .setContentText("Text");
+
+ mEntry = new NotificationEntryBuilder()
+ .setPkg(TEST_PACKAGE_NAME)
+ .setOpPkg(TEST_PACKAGE_NAME)
+ .setUid(TEST_UID)
+ .setId(mId++)
+ .setNotification(n.build())
+ .setUser(new UserHandle(ActivityManager.getCurrentUser()))
+ .build();
+ }
+
+ @Test
+ public void testIsExemptFromDndVisualSuppression_foreground() {
+ mEntry.getSbn().getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
+
+ assertTrue(mEntry.isExemptFromDndVisualSuppression());
+ assertFalse(mEntry.shouldSuppressAmbient());
+ }
+
+ @Test
+ public void testIsExemptFromDndVisualSuppression_media() {
+ Notification.Builder n = new Notification.Builder(mContext, "")
+ .setStyle(new Notification.MediaStyle()
+ .setMediaSession(mock(MediaSession.Token.class)))
+ .setSmallIcon(R.drawable.ic_person)
+ .setContentTitle("Title")
+ .setContentText("Text");
+ NotificationEntry e1 = new NotificationEntryBuilder()
+ .setNotification(n.build())
+ .build();
+
+ assertTrue(e1.isExemptFromDndVisualSuppression());
+ assertFalse(e1.shouldSuppressAmbient());
+ }
+
+ @Test
+ public void testIsExemptFromDndVisualSuppression_system() {
+ mEntry.mIsSystemNotification = true;
+
+ assertTrue(mEntry.isExemptFromDndVisualSuppression());
+ assertFalse(mEntry.shouldSuppressAmbient());
+ }
+
+ @Test
+ public void testIsNotExemptFromDndVisualSuppression_hiddenCategories() {
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setUid(UID_NORMAL)
+ .build();
+ entry.mIsSystemNotification = true;
+ modifyRanking(entry).setSuppressedVisualEffects(SUPPRESSED_EFFECT_AMBIENT).build();
+
+ modifySbn(entry)
+ .setNotification(
+ new Notification.Builder(mContext, "").setCategory(CATEGORY_CALL).build())
+ .build();
+ assertFalse(entry.isExemptFromDndVisualSuppression());
+ assertTrue(entry.shouldSuppressAmbient());
+
+ modifySbn(entry)
+ .setNotification(
+ new Notification.Builder(mContext, "")
+ .setCategory(CATEGORY_REMINDER)
+ .build())
+ .build();
+ assertFalse(entry.isExemptFromDndVisualSuppression());
+
+ modifySbn(entry)
+ .setNotification(
+ new Notification.Builder(mContext, "").setCategory(CATEGORY_ALARM).build())
+ .build();
+ assertFalse(entry.isExemptFromDndVisualSuppression());
+
+ modifySbn(entry)
+ .setNotification(
+ new Notification.Builder(mContext, "").setCategory(CATEGORY_EVENT).build())
+ .build();
+ assertFalse(entry.isExemptFromDndVisualSuppression());
+
+ modifySbn(entry)
+ .setNotification(
+ new Notification.Builder(mContext, "")
+ .setCategory(CATEGORY_MESSAGE)
+ .build())
+ .build();
+ assertFalse(entry.isExemptFromDndVisualSuppression());
+ }
+
+ @Test
+ public void testCreateNotificationDataEntry_RankingUpdate() {
+ StatusBarNotification sbn = new SbnBuilder().build();
+ sbn.getNotification().actions =
+ new Notification.Action[]{createContextualAction("appGeneratedAction")};
+
+ ArrayList<Notification.Action> systemGeneratedSmartActions =
+ createActions("systemGeneratedAction");
+
+ SnoozeCriterion snoozeCriterion = new SnoozeCriterion("id", "explanation", "confirmation");
+ ArrayList<SnoozeCriterion> snoozeCriterions = new ArrayList<>();
+ snoozeCriterions.add(snoozeCriterion);
+
+ Ranking ranking = new RankingBuilder()
+ .setKey(sbn.getKey())
+ .setSmartActions(systemGeneratedSmartActions)
+ .setChannel(NOTIFICATION_CHANNEL)
+ .setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE)
+ .setSnoozeCriteria(snoozeCriterions)
+ .build();
+
+ NotificationEntry entry =
+ new NotificationEntry(sbn, ranking);
+
+ assertEquals(systemGeneratedSmartActions, entry.getSmartActions());
+ assertEquals(NOTIFICATION_CHANNEL, entry.getChannel());
+ assertEquals(Ranking.USER_SENTIMENT_NEGATIVE, entry.getUserSentiment());
+ assertEquals(snoozeCriterions, entry.getSnoozeCriteria());
+ }
+
+ @Test
+ public void notificationDataEntry_testIsLastMessageFromReply() {
+ Person.Builder person = new Person.Builder()
+ .setName("name")
+ .setKey("abc")
+ .setUri("uri")
+ .setBot(true);
+
+ // EXTRA_MESSAGING_PERSON is the same Person as the sender in last message in EXTRA_MESSAGES
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(Notification.EXTRA_MESSAGING_PERSON, person.build());
+ Bundle[] messagesBundle = new Bundle[]{new Notification.MessagingStyle.Message(
+ "text", 0, person.build()).toBundle()};
+ bundle.putParcelableArray(Notification.EXTRA_MESSAGES, messagesBundle);
+
+ Notification notification = new Notification.Builder(mContext, "test")
+ .addExtras(bundle)
+ .build();
+
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setPkg("pkg")
+ .setOpPkg("pkg")
+ .setTag("tag")
+ .setNotification(notification)
+ .setUser(mContext.getUser())
+ .setOverrideGroupKey("")
+ .build();
+ entry.setHasSentReply();
+
+ assertTrue(entry.isLastMessageFromReply());
+ }
+
+ private Notification.Action createContextualAction(String title) {
+ return new Notification.Action.Builder(
+ Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon),
+ title,
+ PendingIntent.getBroadcast(getContext(), 0, new Intent("Action"), 0))
+ .setContextual(true)
+ .build();
+ }
+
+ private Notification.Action createAction(String title) {
+ return new Notification.Action.Builder(
+ Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon),
+ title,
+ PendingIntent.getBroadcast(getContext(), 0, new Intent("Action"), 0)).build();
+ }
+
+ private ArrayList<Notification.Action> createActions(String... titles) {
+ ArrayList<Notification.Action> actions = new ArrayList<>();
+ for (String title : titles) {
+ actions.add(createAction(title));
+ }
+ return actions;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
new file mode 100644
index 000000000000..01b2f8932d9b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection
+
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager.IMPORTANCE_DEFAULT
+import android.app.NotificationManager.IMPORTANCE_LOW
+import android.app.Person
+import android.service.notification.NotificationListenerService.RankingMap
+import android.service.notification.StatusBarNotification
+import android.testing.AndroidTestingRunner
+
+import org.junit.runner.RunWith
+
+import androidx.test.filters.SmallTest
+
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.NotificationEntryBuilder
+import com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking
+import com.android.systemui.statusbar.NotificationMediaManager
+import com.android.systemui.statusbar.notification.NotificationFilter
+import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
+import com.android.systemui.statusbar.notification.logging.NotifLog
+import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING
+import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT
+import com.android.systemui.statusbar.phone.NotificationGroupManager
+import com.android.systemui.statusbar.policy.HeadsUpManager
+import dagger.Lazy
+import junit.framework.Assert.assertEquals
+import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertTrue
+
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.mock
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class NotificationRankingManagerTest
+ : SysuiTestCase() {
+
+ private var lazyMedia: Lazy<NotificationMediaManager> = Lazy {
+ mock<NotificationMediaManager>(NotificationMediaManager::class.java)
+ }
+
+ private val rankingManager = TestableNotificationRankingManager(
+ lazyMedia,
+ mock<NotificationGroupManager>(NotificationGroupManager::class.java),
+ mock<HeadsUpManager>(HeadsUpManager::class.java),
+ mock<NotificationFilter>(NotificationFilter::class.java),
+ mock<NotifLog>(NotifLog::class.java),
+ mock<NotificationSectionsFeatureManager>(NotificationSectionsFeatureManager::class.java)
+ )
+
+ @Before
+ fun setup() {
+ }
+
+ @Test
+ fun testPeopleNotification_isHighPriority() {
+ val person = Person.Builder()
+ .setName("name")
+ .setKey("abc")
+ .setUri("uri")
+ .setBot(true)
+ .build()
+
+ val notification = Notification.Builder(mContext, "test")
+ .addPerson(person)
+ .build()
+
+ val sbn = StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
+ notification, mContext.user, "", 0)
+
+ val e = NotificationEntryBuilder()
+ .setNotification(notification)
+ .setSbn(sbn)
+ .build()
+
+ assertTrue(rankingManager.isHighPriority2(e))
+ }
+
+ @Test
+ fun messagingStyleHighPriority() {
+
+ val notif = Notification.Builder(mContext, "test")
+ .setStyle(Notification.MessagingStyle(""))
+ .build()
+
+ val sbn = StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
+ notif, mContext.getUser(), "", 0)
+
+ val e = NotificationEntryBuilder()
+ .setNotification(notif)
+ .setSbn(sbn)
+ .build()
+
+ assertTrue(rankingManager.isHighPriority2(e))
+ }
+
+ @Test
+ fun lowForegroundHighPriority() {
+ val notification = mock(Notification::class.java)
+ `when`<Boolean>(notification.isForegroundService).thenReturn(true)
+
+ val sbn = StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
+ notification, mContext.user, "", 0)
+
+ val e = NotificationEntryBuilder()
+ .setNotification(notification)
+ .setSbn(sbn)
+ .build()
+
+ modifyRanking(e)
+ .setImportance(IMPORTANCE_LOW)
+ .build()
+
+ assertTrue(rankingManager.isHighPriority2(e))
+ }
+
+ @Test
+ fun userChangeTrumpsHighPriorityCharacteristics() {
+ val person = Person.Builder()
+ .setName("name")
+ .setKey("abc")
+ .setUri("uri")
+ .setBot(true)
+ .build()
+
+ val notification = Notification.Builder(mContext, "test")
+ .addPerson(person)
+ .setStyle(Notification.MessagingStyle(""))
+ .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
+ .build()
+
+ val sbn = StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
+ notification, mContext.user, "", 0)
+
+ val channel = NotificationChannel("a", "a", IMPORTANCE_LOW)
+ channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE)
+
+ val e = NotificationEntryBuilder()
+ .setSbn(sbn)
+ .setChannel(channel)
+ .build()
+
+ assertFalse(rankingManager.isHighPriority2(e))
+ }
+
+ @Test
+ fun testSort_highPriorityTrumpsNMSRank() {
+ // NMS rank says A and then B. But A is not high priority and B is, so B should sort in
+ // front
+ val aN = Notification.Builder(mContext, "test")
+ .setStyle(Notification.MessagingStyle(""))
+ .build()
+ val a = NotificationEntryBuilder()
+ .setPkg("pkg")
+ .setOpPkg("pkg")
+ .setTag("tag")
+ .setNotification(aN)
+ .setUser(mContext.getUser())
+ .setOverrideGroupKey("")
+ .build()
+
+ a.setIsHighPriority(false)
+
+ modifyRanking(a)
+ .setImportance(IMPORTANCE_LOW)
+ .setRank(1)
+ .build()
+
+ val bN = Notification.Builder(mContext, "test")
+ .setStyle(Notification.MessagingStyle(""))
+ .build()
+ val b = NotificationEntryBuilder()
+ .setPkg("pkg2")
+ .setOpPkg("pkg2")
+ .setTag("tag")
+ .setNotification(bN)
+ .setUser(mContext.getUser())
+ .setOverrideGroupKey("")
+ .build()
+ b.setIsHighPriority(true)
+
+ modifyRanking(b)
+ .setImportance(IMPORTANCE_LOW)
+ .setRank(2)
+ .build()
+
+ assertEquals(
+ listOf(b, a),
+ rankingManager.updateRanking(null, listOf(a, b), "test"))
+ }
+
+ @Test
+ fun testSort_samePriorityUsesNMSRank() {
+ // NMS rank says A and then B, and they are the same priority so use that rank
+ val aN = Notification.Builder(mContext, "test")
+ .setStyle(Notification.MessagingStyle(""))
+ .build()
+ val a = NotificationEntryBuilder()
+ .setPkg("pkg")
+ .setOpPkg("pkg")
+ .setTag("tag")
+ .setNotification(aN)
+ .setUser(mContext.getUser())
+ .setOverrideGroupKey("")
+ .build()
+ a.setIsHighPriority(false)
+
+ modifyRanking(a)
+ .setImportance(IMPORTANCE_LOW)
+ .setRank(1)
+ .build()
+
+ val bN = Notification.Builder(mContext, "test")
+ .setStyle(Notification.MessagingStyle(""))
+ .build()
+ val b = NotificationEntryBuilder()
+ .setPkg("pkg2")
+ .setOpPkg("pkg2")
+ .setTag("tag")
+ .setNotification(bN)
+ .setUser(mContext.getUser())
+ .setOverrideGroupKey("")
+ .build()
+ b.setIsHighPriority(false)
+
+ modifyRanking(b)
+ .setImportance(IMPORTANCE_LOW)
+ .setRank(2)
+ .build()
+
+ assertEquals(
+ listOf(a, b),
+ rankingManager.updateRanking(null, listOf(a, b), "test"))
+ }
+
+ @Test
+ fun testSort_properlySetsAlertingBucket() {
+ val notif = Notification.Builder(mContext, "test") .build()
+
+ val e = NotificationEntryBuilder()
+ .setPkg("pkg")
+ .setOpPkg("pkg")
+ .setTag("tag")
+ .setNotification(notif)
+ .setUser(mContext.user)
+ .setOverrideGroupKey("")
+ .build()
+
+ modifyRanking(e).setImportance(IMPORTANCE_DEFAULT) .build()
+
+ rankingManager.updateRanking(RankingMap(arrayOf(e.ranking)), listOf(e), "test")
+ assertEquals(e.bucket, BUCKET_ALERTING)
+ }
+
+ @Test
+ fun testSort_properlySetsSilentBucket() {
+ val notif = Notification.Builder(mContext, "test") .build()
+
+ val e = NotificationEntryBuilder()
+ .setPkg("pkg")
+ .setOpPkg("pkg")
+ .setTag("tag")
+ .setNotification(notif)
+ .setUser(mContext.user)
+ .setOverrideGroupKey("")
+ .build()
+
+ modifyRanking(e).setImportance(IMPORTANCE_LOW).build()
+
+ rankingManager.updateRanking(RankingMap(arrayOf(e.ranking)), listOf(e), "test")
+ assertEquals(e.bucket, BUCKET_SILENT)
+ }
+
+ internal class TestableNotificationRankingManager(
+ mediaManager: Lazy<NotificationMediaManager>,
+ groupManager: NotificationGroupManager,
+ headsUpManager: HeadsUpManager,
+ filter: NotificationFilter,
+ notifLog: NotifLog,
+ sectionsFeatureManager: NotificationSectionsFeatureManager
+ ) : NotificationRankingManager(
+ mediaManager,
+ groupManager,
+ headsUpManager,
+ filter,
+ notifLog,
+ sectionsFeatureManager
+ ) {
+
+ fun isHighPriority2(e: NotificationEntry): Boolean {
+ return isHighPriority(e)
+ }
+
+ fun applyTestRankingMap(r: RankingMap) {
+ rankingMap = r
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
index 47c17ad88fcb..d1398667f497 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
@@ -44,7 +44,6 @@ import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -71,7 +70,6 @@ public class NotificationLoggerTest extends SysuiTestCase {
@Mock private NotificationListContainer mListContainer;
@Mock private IStatusBarService mBarService;
- @Mock private NotificationData mNotificationData;
@Mock private ExpandableNotificationRow mRow;
@Mock private NotificationLogger.ExpansionStateLogger mExpansionStateLogger;
@@ -91,8 +89,6 @@ public class NotificationLoggerTest extends SysuiTestCase {
mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
mDependency.injectTestDependency(NotificationListener.class, mListener);
- when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
-
mEntry = new NotificationEntryBuilder()
.setPkg(TEST_PACKAGE_NAME)
.setOpPkg(TEST_PACKAGE_NAME)
@@ -131,7 +127,7 @@ public class NotificationLoggerTest extends SysuiTestCase {
any(NotificationVisibility[].class));
when(mListContainer.isInVisibleLocation(any())).thenReturn(true);
- when(mNotificationData.getActiveNotifications()).thenReturn(Lists.newArrayList(mEntry));
+ when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry));
mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
TestableLooper.get(this).processAllMessages();
waitForUiOffloadThread();
@@ -153,7 +149,7 @@ public class NotificationLoggerTest extends SysuiTestCase {
public void testStoppingNotificationLoggingReportsCurrentNotifications()
throws Exception {
when(mListContainer.isInVisibleLocation(any())).thenReturn(true);
- when(mNotificationData.getActiveNotifications()).thenReturn(Lists.newArrayList(mEntry));
+ when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry));
mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
TestableLooper.get(this).processAllMessages();
waitForUiOffloadThread();
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 6f52e4a6686b..5b624bc6e4b3 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
@@ -20,7 +20,6 @@ import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -56,7 +55,9 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.EmptyShadeView;
+import com.android.systemui.statusbar.NotificationEntryBuilder;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShelf;
@@ -65,9 +66,13 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationFilter;
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.TestableNotificationEntryManager;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
+import com.android.systemui.statusbar.notification.logging.NotifLog;
import com.android.systemui.statusbar.notification.people.PeopleHubSectionFooterViewAdapter;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.FooterView;
@@ -80,7 +85,6 @@ import com.android.systemui.statusbar.phone.NotificationIconAreaController;
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.StatusBarTest.TestableNotificationEntryManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.DeviceConfigProxyFake;
@@ -97,6 +101,7 @@ import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
+import java.util.List;
/**
* Tests for {@link NotificationStackScrollLayout}.
@@ -117,7 +122,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
@Mock private NotificationGroupManager mGroupManager;
@Mock private ExpandHelper mExpandHelper;
@Mock private EmptyShadeView mEmptyShadeView;
- @Mock private NotificationData mNotificationData;
@Mock private NotificationRemoteInputManager mRemoteInputManager;
@Mock private RemoteInputController mRemoteInputController;
@Mock private NotificationIconAreaController mNotificationIconAreaController;
@@ -140,6 +144,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
NOTIFICATION_NEW_INTERRUPTION_MODEL, 1);
// Inject dependencies before initializing the layout
+ mDependency.injectMockDependency(VisualStabilityManager.class);
mDependency.injectTestDependency(
NotificationBlockingHelperManager.class,
mBlockingHelperManager);
@@ -150,7 +155,18 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mDependency.injectMockDependency(ShadeController.class);
when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
- mEntryManager = new TestableNotificationEntryManager(mNotificationData);
+ mEntryManager = new TestableNotificationEntryManager(
+ mock(NotifLog.class),
+ mock(NotificationGroupManager.class),
+ new NotificationRankingManager(
+ () -> mock(NotificationMediaManager.class),
+ mGroupManager,
+ mHeadsUpManager,
+ mock(NotificationFilter.class),
+ mock(NotifLog.class),
+ mock(NotificationSectionsFeatureManager.class)
+ ),
+ mock(NotificationEntryManager.KeyguardEnvironment.class));
mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
Dependency.get(InitController.class).executePostInitTasks();
mEntryManager.setUpForTest(mock(NotificationPresenter.class), null, mHeadsUpManager);
@@ -161,8 +177,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
// The actual class under test. You may need to work with this class directly when
// testing anonymous class members of mStackScroller, like mMenuEventListener,
// which refer to members of NotificationStackScrollLayout. The spy
- // holds a copy of the CUT's instances of these classes, so they still refer to the CUT's
- // member variables, not the spy's member variables.
+ // holds a copy of the CUT's instances of these KeyguardBypassController, so they still
+ // refer to the CUT's member variables, not the spy's member variables.
mStackScrollerInternal = new NotificationStackScrollLayout(getContext(), null,
true /* allowLongPress */, mNotificationRoundnessManager,
mock(DynamicPrivacyController.class),
@@ -293,7 +309,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
@Test
public void testUpdateFooter_noNotifications() {
setBarStateForTest(StatusBarState.SHADE);
- assertEquals(0, mNotificationData.getActiveNotifications().size());
+ assertEquals(0, mEntryManager.getActiveNotificationsCount());
mStackScroller.updateFooter();
verify(mStackScroller, atLeastOnce()).updateFooterView(false, false);
@@ -303,8 +319,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
public void testUpdateFooter_remoteInput() {
setBarStateForTest(StatusBarState.SHADE);
ArrayList<NotificationEntry> entries = new ArrayList<>();
- entries.add(mock(NotificationEntry.class));
- when(mNotificationData.getActiveNotifications()).thenReturn(entries);
+ entries.add(new NotificationEntryBuilder().build());
+ addEntriesToEntryManager(entries);
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
when(row.canViewBeDismissed()).thenReturn(true);
@@ -319,9 +335,10 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
@Test
public void testUpdateFooter_oneClearableNotification() {
setBarStateForTest(StatusBarState.SHADE);
+
ArrayList<NotificationEntry> entries = new ArrayList<>();
- entries.add(mock(NotificationEntry.class));
- when(mNotificationData.getActiveNotifications()).thenReturn(entries);
+ entries.add(new NotificationEntryBuilder().build());
+ addEntriesToEntryManager(entries);
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
when(row.canViewBeDismissed()).thenReturn(true);
@@ -335,10 +352,10 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
@Test
public void testUpdateFooter_oneNonClearableNotification() {
setBarStateForTest(StatusBarState.SHADE);
+
ArrayList<NotificationEntry> entries = new ArrayList<>();
- entries.add(mock(NotificationEntry.class));
- when(mEntryManager.getNotificationData().getActiveNotifications()).thenReturn(entries);
- assertTrue(mEntryManager.getNotificationData().getActiveNotifications().size() != 0);
+ entries.add(new NotificationEntryBuilder().build());
+ addEntriesToEntryManager(entries);
mStackScroller.updateFooter();
verify(mStackScroller).updateFooterView(true, false);
@@ -460,4 +477,14 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
// rather than the mock because the spy just coppied the anonymous inner /shruggie.
mStackScroller.setStatusBarState(state);
}
+
+ private void addEntriesToEntryManager(List<NotificationEntry> entries) {
+ for (NotificationEntry e : entries) {
+ mEntryManager.addActiveNotificationForTest(e);
+ }
+ }
+
+ private void addActiveNotificationsToManager(List<NotificationEntry> entries) {
+ mEntryManager.setActiveNotificationList(entries);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
index 64c1b510cbf9..a02445487dc0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
@@ -40,7 +40,6 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import org.junit.Before;
@@ -51,8 +50,6 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.ArrayList;
-
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@@ -61,7 +58,6 @@ public class LightsOutNotifControllerTest extends SysuiTestCase {
private static final int LIGHTS_OUT = APPEARANCE_LOW_PROFILE_BARS;
@Mock private NotificationEntryManager mEntryManager;
- @Mock private NotificationData mNotificationData;
@Mock private CommandQueue mCommandQueue;
@Mock private WindowManager mWindowManager;
@Mock private Display mDisplay;
@@ -71,7 +67,6 @@ public class LightsOutNotifControllerTest extends SysuiTestCase {
private View mLightsOutView;
private LightsOutNotifController mLightsOutNotifController;
- private ArrayList<NotificationEntry> mActiveNotifications = new ArrayList<>();
private int mDisplayId;
private NotificationEntryListener mEntryListener;
private CommandQueue.Callbacks mCallbacks;
@@ -81,8 +76,6 @@ public class LightsOutNotifControllerTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
mDisplayId = mContext.getDisplayId();
mLightsOutView = new View(mContext);
- when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
- when(mNotificationData.getActiveNotifications()).thenReturn(mActiveNotifications);
when(mWindowManager.getDefaultDisplay()).thenReturn(mDisplay);
when(mDisplay.getDisplayId()).thenReturn(mDisplayId);
@@ -136,7 +129,7 @@ public class LightsOutNotifControllerTest extends SysuiTestCase {
@Test
public void testLightsOut_withNotifs_onSystemBarAppearanceChanged() {
// GIVEN active visible notifications
- mActiveNotifications.add(mock(NotificationEntry.class));
+ when(mEntryManager.hasActiveNotifications()).thenReturn(true);
// WHEN lights out
mCallbacks.onSystemBarAppearanceChanged(
@@ -153,7 +146,7 @@ public class LightsOutNotifControllerTest extends SysuiTestCase {
@Test
public void testLightsOut_withoutNotifs_onSystemBarAppearanceChanged() {
// GIVEN no active visible notifications
- mActiveNotifications.clear();
+ when(mEntryManager.hasActiveNotifications()).thenReturn(false);
// WHEN lights out
mCallbacks.onSystemBarAppearanceChanged(
@@ -170,7 +163,7 @@ public class LightsOutNotifControllerTest extends SysuiTestCase {
@Test
public void testLightsOn_afterLightsOut_onSystemBarAppearanceChanged() {
// GIVEN active visible notifications
- mActiveNotifications.add(mock(NotificationEntry.class));
+ when(mEntryManager.hasActiveNotifications()).thenReturn(true);
// WHEN lights on
mCallbacks.onSystemBarAppearanceChanged(
@@ -187,13 +180,13 @@ public class LightsOutNotifControllerTest extends SysuiTestCase {
@Test
public void testEntryAdded() {
// GIVEN no visible notifications and lights out
- mActiveNotifications.clear();
+ when(mEntryManager.hasActiveNotifications()).thenReturn(false);
mLightsOutNotifController.mAppearance = LIGHTS_OUT;
mLightsOutNotifController.updateLightsOutView();
assertIsShowingDot(false);
// WHEN an active notification is added
- mActiveNotifications.add(mock(NotificationEntry.class));
+ when(mEntryManager.hasActiveNotifications()).thenReturn(true);
assertTrue(mLightsOutNotifController.shouldShowDot());
mEntryListener.onNotificationAdded(mock(NotificationEntry.class));
@@ -204,13 +197,13 @@ public class LightsOutNotifControllerTest extends SysuiTestCase {
@Test
public void testEntryRemoved() {
// GIVEN a visible notification and lights out
- mActiveNotifications.add(mock(NotificationEntry.class));
+ when(mEntryManager.hasActiveNotifications()).thenReturn(true);
mLightsOutNotifController.mAppearance = LIGHTS_OUT;
mLightsOutNotifController.updateLightsOutView();
assertIsShowingDot(true);
// WHEN all active notifications are removed
- mActiveNotifications.clear();
+ when(mEntryManager.hasActiveNotifications()).thenReturn(false);
assertFalse(mLightsOutNotifController.shouldShowDot());
mEntryListener.onEntryRemoved(mock(NotificationEntry.class), null, false);
@@ -221,13 +214,13 @@ public class LightsOutNotifControllerTest extends SysuiTestCase {
@Test
public void testEntryUpdated() {
// GIVEN no visible notifications and lights out
- mActiveNotifications.clear();
+ when(mEntryManager.hasActiveNotifications()).thenReturn(false);
mLightsOutNotifController.mAppearance = LIGHTS_OUT;
mLightsOutNotifController.updateLightsOutView();
assertIsShowingDot(false);
// WHEN an active notification is added
- mActiveNotifications.add(mock(NotificationEntry.class));
+ when(mEntryManager.hasActiveNotifications()).thenReturn(true);
assertTrue(mLightsOutNotifController.shouldShowDot());
mEntryListener.onPostEntryUpdated(mock(NotificationEntry.class));
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 280cc90c0ca4..c165e561b393 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
@@ -54,11 +54,9 @@ import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
import com.android.systemui.statusbar.notification.logging.NotifLog;
-import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -239,11 +237,10 @@ public class NotificationPanelViewTest extends SysuiTestCase {
mock(ShadeController.class),
mock(NotificationLockscreenUserManager.class),
new NotificationEntryManager(
- new NotificationData(
- mock(NotificationSectionsFeatureManager.class),
- mock(NotifLog.class),
- mock(PeopleNotificationIdentifier.class)),
- mock(NotifLog.class)),
+ mock(NotifLog.class),
+ mock(NotificationGroupManager.class),
+ mock(NotificationRankingManager.class),
+ mock(NotificationEntryManager.KeyguardEnvironment.class)),
mock(KeyguardStateController.class),
statusBarStateController,
mock(DozeLog.class),
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 d8a68b0c230c..24a5d932fd85 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
@@ -68,7 +68,6 @@ import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -117,7 +116,6 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
@Mock
private Intent mContentIntentInner;
@Mock
- private NotificationData mNotificationData;
private NotificationActivityStarter mNotificationActivityStarter;
@@ -134,7 +132,6 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
- when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
when(mContentIntent.isActivity()).thenReturn(true);
when(mContentIntent.getCreatorUserHandle()).thenReturn(UserHandle.of(1));
@@ -157,7 +154,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
mActiveNotifications = new ArrayList<>();
mActiveNotifications.add(mNotificationRow.getEntry());
mActiveNotifications.add(mBubbleNotificationRow.getEntry());
- when(mNotificationData.getActiveNotifications()).thenReturn(mActiveNotifications);
+ when(mEntryManager.getVisibleNotifications()).thenReturn(mActiveNotifications);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
mNotificationActivityStarter = new StatusBarNotificationActivityStarter(getContext(),
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 de87d3197ef6..1c02b60902b8 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
@@ -52,7 +52,6 @@ import com.android.systemui.statusbar.notification.NotificationAlertingManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
@@ -100,11 +99,9 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
mDependency.injectMockDependency(NotificationGutsManager.class);
mDependency.injectMockDependency(StatusBarWindowController.class);
mDependency.injectMockDependency(InitController.class);
- NotificationData notificationData = mock(NotificationData.class);
- when(notificationData.getNotificationsForCurrentUser()).thenReturn(new ArrayList<>());
NotificationEntryManager entryManager =
mDependency.injectMockDependency(NotificationEntryManager.class);
- when(entryManager.getNotificationData()).thenReturn(notificationData);
+ when(entryManager.getActiveNotificationsForCurrentUser()).thenReturn(new ArrayList<>());
StatusBarWindowView statusBarWindowView = mock(StatusBarWindowView.class);
when(statusBarWindowView.getResources()).thenReturn(mContext.getResources());
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 f3273784c6ca..95929c3adcb8 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
@@ -101,7 +101,6 @@ import com.android.systemui.statusbar.NotificationEntryBuilder;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
import com.android.systemui.statusbar.PulseExpansionHandler;
@@ -120,12 +119,9 @@ import com.android.systemui.statusbar.notification.NotificationFilter;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.logging.NotifLog;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -177,7 +173,6 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private ArrayList<NotificationEntry> mNotificationList;
@Mock private Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
@Mock private BiometricUnlockController mBiometricUnlockController;
- @Mock private NotificationData mNotificationData;
@Mock private NotificationInterruptionStateProvider.HeadsUpSuppressor mHeadsUpSuppressor;
@Mock private VisualStabilityManager mVisualStabilityManager;
@Mock private NotificationListener mNotificationListener;
@@ -240,6 +235,7 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private ViewMediatorCallback mViewMediatorCallback;
@Mock private DismissCallbackRegistry mDismissCallbackRegistry;
@Mock private ScreenPinningRequest mScreenPinningRequest;
+ @Mock private NotificationEntryManager mEntryManager;
@Before
public void setup() throws Exception {
@@ -260,10 +256,8 @@ public class StatusBarTest extends SysuiTestCase {
mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class));
mMetricsLogger = new FakeMetricsLogger();
- TestableNotificationEntryManager entryManager = new TestableNotificationEntryManager(
- mNotificationData);
NotificationLogger notificationLogger = new NotificationLogger(mNotificationListener,
- Dependency.get(UiOffloadThread.class), entryManager, mStatusBarStateController,
+ Dependency.get(UiOffloadThread.class), mEntryManager, mStatusBarStateController,
mExpansionStateLogger);
notificationLogger.setVisibilityReporter(mock(Runnable.class));
@@ -332,7 +326,7 @@ public class StatusBarTest extends SysuiTestCase {
),
mNotificationGutsManager,
notificationLogger,
- entryManager,
+ mEntryManager,
mNotificationInterruptionStateProvider,
mNotificationViewHierarchyManager,
mKeyguardViewMediator,
@@ -407,9 +401,6 @@ public class StatusBarTest extends SysuiTestCase {
mStatusBar.mStatusBarWindowViewController = mStatusBarWindowViewController;
mStatusBar.startKeyguard();
Dependency.get(InitController.class).executePostInitTasks();
- entryManager.setUpForTest(mock(NotificationPresenter.class), mStackScroller,
- mHeadsUpManager);
- entryManager.addNotificationEntryListener(mEntryListener);
notificationLogger.setUpWithContainer(mStackScroller);
}
@@ -645,8 +636,7 @@ public class StatusBarTest extends SysuiTestCase {
public void testPanelOpenForHeadsUp() {
when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
- when(mNotificationData.getActiveNotifications()).thenReturn(mNotificationList);
- when(mNotificationList.size()).thenReturn(5);
+ when(mEntryManager.getActiveNotificationsCount()).thenReturn(5);
when(mNotificationPresenter.isPresenterFullyCollapsed()).thenReturn(true);
mStatusBar.setBarStateForTest(StatusBarState.SHADE);
@@ -664,8 +654,8 @@ public class StatusBarTest extends SysuiTestCase {
@Test
public void testPanelOpenAndClear() {
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
- when(mNotificationData.getActiveNotifications()).thenReturn(mNotificationList);
- when(mNotificationList.size()).thenReturn(5);
+ when(mEntryManager.getActiveNotificationsCount()).thenReturn(5);
+
when(mNotificationPresenter.isPresenterFullyCollapsed()).thenReturn(false);
mStatusBar.setBarStateForTest(StatusBarState.SHADE);
@@ -683,8 +673,7 @@ public class StatusBarTest extends SysuiTestCase {
@Test
public void testPanelOpenAndNoClear() {
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
- when(mNotificationData.getActiveNotifications()).thenReturn(mNotificationList);
- when(mNotificationList.size()).thenReturn(5);
+ when(mEntryManager.getActiveNotificationsCount()).thenReturn(5);
when(mNotificationPresenter.isPresenterFullyCollapsed()).thenReturn(false);
mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
@@ -842,19 +831,6 @@ public class StatusBarTest extends SysuiTestCase {
any(UserHandle.class));
}
- public static class TestableNotificationEntryManager extends NotificationEntryManager {
-
- public TestableNotificationEntryManager(NotificationData notificationData) {
- super(notificationData, mock(NotifLog.class));
- }
-
- public void setUpForTest(NotificationPresenter presenter,
- NotificationListContainer listContainer,
- HeadsUpManagerPhone headsUpManager) {
- super.setUpWithPresenter(presenter, listContainer, headsUpManager);
- }
- }
-
public static class TestableNotificationInterruptionStateProvider extends
NotificationInterruptionStateProvider {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeSensorManager.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeSensorManager.java
index 0bc7868aab9c..e15ca1da928d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeSensorManager.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeSensorManager.java
@@ -64,7 +64,8 @@ public class FakeSensorManager extends SensorManager {
.getDefaultSensor(Sensor.TYPE_PROXIMITY);
if (proxSensor == null) {
// No prox? Let's create a fake one!
- proxSensor = createSensor(Sensor.TYPE_PROXIMITY, null);
+ proxSensor =
+ createSensor(Sensor.TYPE_PROXIMITY, null, 1 /* SENSOR_FLAG_WAKE_UP_SENSOR */);
}
mSensors = new FakeGenericSensor[]{
@@ -92,18 +93,6 @@ public class FakeSensorManager extends SensorManager {
if (s != null) {
return s;
}
- switch(type) {
- case Sensor.TYPE_PROXIMITY:
- try {
- return createSensor(Sensor.TYPE_PROXIMITY, null);
- } catch (Exception e) {
- // fall through
- }
- break;
- default:
- break;
-
- }
// Our mock sensors aren't wakeup, and it's a pain to create them that way. Instead, just
// return non-wakeup sensors if we can't find a wakeup sensor.
return getDefaultSensor(type, false /* wakeup */);
@@ -208,6 +197,10 @@ public class FakeSensorManager extends SensorManager {
}
private Sensor createSensor(int type, @Nullable String stringType) throws Exception {
+ return createSensor(type, stringType, 0 /* flags */);
+ }
+
+ private Sensor createSensor(int type, @Nullable String stringType, int flags) throws Exception {
Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor();
constr.setAccessible(true);
Sensor sensor = constr.newInstance();
@@ -225,7 +218,7 @@ public class FakeSensorManager extends SensorManager {
setSensorField(sensor, "mPower", 1);
setSensorField(sensor, "mMinDelay", 1000);
setSensorField(sensor, "mMaxDelay", 1000000000);
- setSensorField(sensor, "mFlags", 0);
+ setSensorField(sensor, "mFlags", flags);
setSensorField(sensor, "mId", -1);
return sensor;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 6f435294268f..7fdd83bebbcd 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -98,6 +98,7 @@ import com.android.internal.util.DumpUtils;
import com.android.internal.util.IntPair;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
import org.xmlpull.v1.XmlPullParserException;
@@ -179,6 +180,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private final AccessibilityDisplayListener mA11yDisplayListener;
+ private final ActivityTaskManagerInternal mActivityTaskManagerService;
+
private final MainHandler mMainHandler;
private final SystemActionPerformer mSystemActionPerformer;
@@ -260,6 +263,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mWindowManagerService, this, mSecurityPolicy, this);
mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
mSecurityPolicy.setAccessibilityWindowManager(mA11yWindowManager);
+ mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class);
registerBroadcastReceivers();
new AccessibilityContentObserver(mMainHandler).register(
@@ -1435,7 +1439,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
service = new AccessibilityServiceConnection(userState, mContext, componentName,
installedService, sIdCounter++, mMainHandler, mLock, mSecurityPolicy,
this, mWindowManagerService, mSystemActionPerformer,
- mA11yWindowManager);
+ mA11yWindowManager, mActivityTaskManagerService);
} else if (userState.mBoundServices.contains(service)) {
continue;
}
@@ -2411,7 +2415,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
userState, mContext,
COMPONENT_NAME, info, sIdCounter++, mMainHandler, mLock, mSecurityPolicy,
AccessibilityManagerService.this, mWindowManagerService,
- mSystemActionPerformer, mA11yWindowManager) {
+ mSystemActionPerformer, mA11yWindowManager, mActivityTaskManagerService) {
@Override
public boolean supportsFlagForNotImportantViews(AccessibilityServiceInfo info) {
return true;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index a0a755a30cb3..6cadb6d0f31f 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -35,6 +35,7 @@ import android.provider.Settings;
import android.util.Slog;
import android.view.Display;
+import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
import java.lang.ref.WeakReference;
@@ -59,6 +60,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
*/
final WeakReference<AccessibilityUserState> mUserStateWeakReference;
final Intent mIntent;
+ final ActivityTaskManagerInternal mActivityTaskManagerService;
private final Handler mMainHandler;
@@ -67,7 +69,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler,
Object lock, AccessibilitySecurityPolicy securityPolicy, SystemSupport systemSupport,
WindowManagerInternal windowManagerInternal,
- SystemActionPerformer systemActionPerfomer, AccessibilityWindowManager awm) {
+ SystemActionPerformer systemActionPerfomer, AccessibilityWindowManager awm,
+ ActivityTaskManagerInternal activityTaskManagerService) {
super(context, componentName, accessibilityServiceInfo, id, mainHandler, lock,
securityPolicy, systemSupport, windowManagerInternal, systemActionPerfomer, awm);
mUserStateWeakReference = new WeakReference<AccessibilityUserState>(userState);
@@ -75,6 +78,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
mMainHandler = mainHandler;
mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
com.android.internal.R.string.accessibility_binding_label);
+ mActivityTaskManagerService = activityTaskManagerService;
final long identity = Binder.clearCallingIdentity();
try {
mIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, mSystemSupport.getPendingIntentActivity(
@@ -101,6 +105,9 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
} finally {
Binder.restoreCallingIdentity(identity);
}
+ mActivityTaskManagerService.setAllowAppSwitches(mComponentName.flattenToString(),
+ mAccessibilityServiceInfo.getResolveInfo().serviceInfo.applicationInfo.uid,
+ userState.mUserId);
}
public void unbindLocked() {
@@ -109,6 +116,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
if (userState == null) return;
userState.removeServiceLocked(this);
mSystemSupport.getMagnificationController().resetAllIfNeeded(mId);
+ mActivityTaskManagerService.setAllowAppSwitches(mComponentName.flattenToString(), -1,
+ userState.mUserId);
resetLocked();
}
@@ -207,6 +216,11 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
@Override
public void onServiceDisconnected(ComponentName componentName) {
binderDied();
+ AccessibilityUserState userState = mUserStateWeakReference.get();
+ if (userState != null) {
+ mActivityTaskManagerService.setAllowAppSwitches(mComponentName.flattenToString(), -1,
+ userState.mUserId);
+ }
}
@Override
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 770de09382fe..c6bc1068da39 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -91,6 +91,7 @@ java_library_static {
"android.hardware.light-V2.0-java",
"android.hardware.power-V1.0-java",
"android.hardware.tv.cec-V1.0-java",
+ "app-compat-annotations",
],
required: [
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 9fa572f9fe9a..09cbc5c67ab7 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -2919,11 +2919,14 @@ public class AudioService extends IAudioService.Stub
final boolean currentMute = AudioSystem.isMicrophoneMuted();
final long identity = Binder.clearCallingIdentity();
AudioSystem.muteMicrophone(muted);
- Binder.restoreCallingIdentity(identity);
- if (muted != currentMute) {
- mContext.sendBroadcastAsUser(
+ try {
+ if (muted != currentMute) {
+ mContext.sendBroadcastAsUser(
new Intent(AudioManager.ACTION_MICROPHONE_MUTE_CHANGED)
.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
}
}
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 4a62bc507d92..bc7307b3ee6c 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -394,6 +394,15 @@ public final class ContentService extends IContentService.Stub {
* allowed.
*/
@Override
+ public void notifyChange(Uri[] uris, IContentObserver observer,
+ boolean observerWantsSelfNotifications, int flags, int userHandle,
+ int targetSdkVersion, String callingPackage) {
+ for (Uri uri : uris) {
+ notifyChange(uri, observer, observerWantsSelfNotifications, flags, userHandle,
+ targetSdkVersion, callingPackage);
+ }
+ }
+
public void notifyChange(Uri uri, IContentObserver observer,
boolean observerWantsSelfNotifications, int flags, int userHandle,
int targetSdkVersion, String callingPackage) {
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
index 99b1ef4a1d2e..f05b2bf9711f 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
@@ -16,8 +16,15 @@
package com.android.server.notification;
+import android.app.AlarmManager;
import android.app.NotificationHistory;
import android.app.NotificationHistory.HistoricalNotification;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
import android.os.Handler;
import android.util.AtomicFile;
import android.util.Slog;
@@ -33,11 +40,15 @@ import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.attribute.BasicFileAttributes;
import java.util.Arrays;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.LinkedList;
+import java.util.concurrent.TimeUnit;
/**
* Provides an interface to write and query for notification history data for a user from a Protocol
@@ -52,32 +63,48 @@ public class NotificationHistoryDatabase {
private static final String TAG = "NotiHistoryDatabase";
private static final boolean DEBUG = NotificationManagerService.DBG;
private static final int HISTORY_RETENTION_DAYS = 2;
+ private static final int HISTORY_RETENTION_MS = 24 * 60 * 60 * 1000;
private static final long WRITE_BUFFER_INTERVAL_MS = 1000 * 60 * 20;
+ private static final String ACTION_HISTORY_DELETION =
+ NotificationHistoryDatabase.class.getSimpleName() + ".CLEANUP";
+ private static final int REQUEST_CODE_DELETION = 1;
+ private static final String SCHEME_DELETION = "delete";
+ private static final String EXTRA_KEY = "key";
+
+ private final Context mContext;
+ private final AlarmManager mAlarmManager;
private final Object mLock = new Object();
private Handler mFileWriteHandler;
@VisibleForTesting
// List of files holding history information, sorted newest to oldest
final LinkedList<AtomicFile> mHistoryFiles;
- private final GregorianCalendar mCal;
private final File mHistoryDir;
private final File mVersionFile;
// Current version of the database files schema
private int mCurrentVersion;
private final WriteBufferRunnable mWriteBufferRunnable;
+ private final FileAttrProvider mFileAttrProvider;
// Object containing posted notifications that have not yet been written to disk
@VisibleForTesting
NotificationHistory mBuffer;
- public NotificationHistoryDatabase(File dir) {
+ public NotificationHistoryDatabase(Context context, File dir,
+ FileAttrProvider fileAttrProvider) {
+ mContext = context;
+ mAlarmManager = context.getSystemService(AlarmManager.class);
mCurrentVersion = DEFAULT_CURRENT_VERSION;
mVersionFile = new File(dir, "version");
mHistoryDir = new File(dir, "history");
mHistoryFiles = new LinkedList<>();
- mCal = new GregorianCalendar();
mBuffer = new NotificationHistory();
mWriteBufferRunnable = new WriteBufferRunnable();
+ mFileAttrProvider = fileAttrProvider;
+
+ IntentFilter deletionFilter = new IntentFilter(ACTION_HISTORY_DELETION);
+ deletionFilter.addDataScheme(SCHEME_DELETION);
+ mContext.registerReceiver(mFileCleaupReceiver, deletionFilter);
}
public void init(Handler fileWriteHandler) {
@@ -105,7 +132,8 @@ public class NotificationHistoryDatabase {
}
// Sort with newest files first
- Arrays.sort(files, (lhs, rhs) -> Long.compare(rhs.lastModified(), lhs.lastModified()));
+ Arrays.sort(files, (lhs, rhs) -> Long.compare(mFileAttrProvider.getCreationTime(rhs),
+ mFileAttrProvider.getCreationTime(lhs)));
for (File file : files) {
mHistoryFiles.addLast(new AtomicFile(file));
@@ -197,31 +225,48 @@ public class NotificationHistoryDatabase {
}
/**
- * Remove any files that are too old.
+ * Remove any files that are too old and schedule jobs to clean up the rest
*/
public void prune(final int retentionDays, final long currentTimeMillis) {
synchronized (mLock) {
- mCal.setTimeInMillis(currentTimeMillis);
- mCal.add(Calendar.DATE, -1 * retentionDays);
-
- while (!mHistoryFiles.isEmpty()) {
- final AtomicFile currentOldestFile = mHistoryFiles.getLast();
- final long age = currentTimeMillis
- - currentOldestFile.getBaseFile().lastModified();
- if (age > mCal.getTimeInMillis()) {
+ GregorianCalendar retentionBoundary = new GregorianCalendar();
+ retentionBoundary.setTimeInMillis(currentTimeMillis);
+ retentionBoundary.add(Calendar.DATE, -1 * retentionDays);
+
+ for (int i = mHistoryFiles.size() - 1; i >= 0; i--) {
+ final AtomicFile currentOldestFile = mHistoryFiles.get(i);
+ final long creationTime =
+ mFileAttrProvider.getCreationTime(currentOldestFile.getBaseFile());
+ if (creationTime <= retentionBoundary.getTimeInMillis()) {
if (DEBUG) {
Slog.d(TAG, "Removed " + currentOldestFile.getBaseFile().getName());
}
currentOldestFile.delete();
mHistoryFiles.removeLast();
} else {
- // all remaining files are newer than the cut off
- return;
+ // all remaining files are newer than the cut off; schedule jobs to delete
+ final long deletionTime = creationTime + (retentionDays * HISTORY_RETENTION_MS);
+ scheduleDeletion(currentOldestFile.getBaseFile(), deletionTime);
}
}
}
}
+ void scheduleDeletion(File file, long deletionTime) {
+ if (DEBUG) {
+ Slog.d(TAG, "Scheduling deletion for " + file.getName() + " at " + deletionTime);
+ }
+ final PendingIntent pi = PendingIntent.getBroadcast(mContext,
+ REQUEST_CODE_DELETION,
+ new Intent(ACTION_HISTORY_DELETION)
+ .setData(new Uri.Builder().scheme(SCHEME_DELETION)
+ .appendPath(file.getAbsolutePath()).build())
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+ .putExtra(EXTRA_KEY, file.getAbsolutePath()),
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, deletionTime, pi);
+ }
+
private void writeLocked(AtomicFile file, NotificationHistory notifications)
throws IOException {
FileOutputStream fos = file.startWrite();
@@ -245,6 +290,25 @@ public class NotificationHistoryDatabase {
}
}
+ private final BroadcastReceiver mFileCleaupReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action == null) {
+ return;
+ }
+ if (ACTION_HISTORY_DELETION.equals(action)) {
+ try {
+ final String filePath = intent.getStringExtra(EXTRA_KEY);
+ AtomicFile fileToDelete = new AtomicFile(new File(filePath));
+ fileToDelete.delete();
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to delete notification history file", e);
+ }
+ }
+ }
+ };
+
private final class WriteBufferRunnable implements Runnable {
@Override
public void run() {
@@ -277,10 +341,7 @@ public class NotificationHistoryDatabase {
// Remove packageName entries from pending history
mBuffer.removeNotificationsFromWrite(mPkg);
- // Remove packageName entries from files on disk, and rewrite them to disk
- // Since we sort by modified date, we have to update the files oldest to newest to
- // maintain the original ordering
- Iterator<AtomicFile> historyFileItr = mHistoryFiles.descendingIterator();
+ Iterator<AtomicFile> historyFileItr = mHistoryFiles.iterator();
while (historyFileItr.hasNext()) {
final AtomicFile af = historyFileItr.next();
try {
@@ -297,4 +358,24 @@ public class NotificationHistoryDatabase {
}
}
}
+
+ public static final class NotificationHistoryFileAttrProvider implements
+ NotificationHistoryDatabase.FileAttrProvider {
+ final static String TAG = "NotifHistoryFileDate";
+
+ public long getCreationTime(File file) {
+ try {
+ BasicFileAttributes attr = Files.readAttributes(FileSystems.getDefault().getPath(
+ file.getAbsolutePath()), BasicFileAttributes.class);
+ return attr.creationTime().to(TimeUnit.MILLISECONDS);
+ } catch (Exception e) {
+ Slog.w(TAG, "Cannot read creation data for file; using file name");
+ return Long.valueOf(file.getName());
+ }
+ }
+ }
+
+ interface FileAttrProvider {
+ long getCreationTime(File file);
+ }
}
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 0a3c58150689..cbbf2a0ee04b 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -757,7 +757,7 @@ public class PreferencesHelper implements RankingConfig {
clearLockedFieldsLocked(channel);
channel.setImportanceLockedByOEM(r.oemLockedImportance);
if (!channel.isImportanceLockedByOEM()) {
- if (r.futureOemLockedChannels.remove(channel.getId())) {
+ if (r.oemLockedChannels.contains(channel.getId())) {
channel.setImportanceLockedByOEM(true);
}
}
@@ -952,11 +952,10 @@ public class PreferencesHelper implements RankingConfig {
NotificationChannel channel = r.channels.get(channelId);
if (channel != null) {
channel.setImportanceLockedByOEM(true);
- } else {
- // if this channel shows up in the future, make sure it'll
- // be locked immediately
- r.futureOemLockedChannels.add(channelId);
}
+ // Also store the locked channels on the record, so they aren't
+ // temporarily lost when data is cleared on the package
+ r.oemLockedChannels.add(channelId);
}
}
}
@@ -1528,9 +1527,9 @@ public class PreferencesHelper implements RankingConfig {
pw.print(" oemLocked=");
pw.print(r.oemLockedImportance);
}
- if (!r.futureOemLockedChannels.isEmpty()) {
+ if (!r.oemLockedChannels.isEmpty()) {
pw.print(" futureLockedChannels=");
- pw.print(r.futureOemLockedChannels);
+ pw.print(r.oemLockedChannels);
}
pw.println();
for (NotificationChannel channel : r.channels.values()) {
@@ -1940,7 +1939,7 @@ public class PreferencesHelper implements RankingConfig {
// these fields are loaded on boot from a different source of truth and so are not
// written to notification policy xml
boolean oemLockedImportance = DEFAULT_OEM_LOCKED_IMPORTANCE;
- List<String> futureOemLockedChannels = new ArrayList<>();
+ List<String> oemLockedChannels = new ArrayList<>();
boolean defaultAppLockedImportance = DEFAULT_APP_LOCKED_IMPORTANCE;
Delegate delegate = null;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 631df0ff9845..38649a772b8f 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2213,8 +2213,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED);
// Send broadcast to default launcher only if it's a new install
+ // TODO(b/144270665): Secure the usage of this broadcast.
final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING);
- if (success && isNewInstall && mPm.mInstallerService.okToSendBroadcasts()) {
+ if (success && isNewInstall && mPm.mInstallerService.okToSendBroadcasts()
+ && (params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
mPm.sendSessionCommitBroadcast(generateInfo(), userId);
}
diff --git a/services/core/java/com/android/server/policy/LegacyGlobalActions.java b/services/core/java/com/android/server/policy/LegacyGlobalActions.java
index c779ebf4a995..3aafd5e35dc4 100644
--- a/services/core/java/com/android/server/policy/LegacyGlobalActions.java
+++ b/services/core/java/com/android/server/policy/LegacyGlobalActions.java
@@ -403,7 +403,7 @@ class LegacyGlobalActions implements DialogInterface.OnDismissListener, DialogIn
public String getStatus() {
return mContext.getString(
com.android.internal.R.string.bugreport_status,
- Build.VERSION.RELEASE,
+ Build.VERSION.RELEASE_OR_CODENAME,
Build.ID);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 0a861ade2900..73034b020a76 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -548,7 +548,7 @@ class ActivityMetricsLogger {
private static boolean hasActivityToBeDrawn(Task t) {
for (int i = t.getChildCount() - 1; i >= 0; --i) {
final ActivityRecord r = t.getChildAt(i);
- if (r.mVisibleRequested && !r.mDrawn && !r.finishing) {
+ if (r.visible && !r.mDrawn && !r.finishing) {
return true;
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 2162bdeead05..3d41608763cb 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -121,7 +121,6 @@ import static com.android.server.am.ActivityRecordProto.PROC_ID;
import static com.android.server.am.ActivityRecordProto.STATE;
import static com.android.server.am.ActivityRecordProto.TRANSLUCENT;
import static com.android.server.am.ActivityRecordProto.VISIBLE;
-import static com.android.server.am.ActivityRecordProto.VISIBLE_REQUESTED;
import static com.android.server.am.EventLogTags.AM_RELAUNCH_ACTIVITY;
import static com.android.server.am.EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
@@ -173,10 +172,12 @@ import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_W
import static com.android.server.wm.ActivityTaskManagerService.getInputDispatchingTimeoutLocked;
import static com.android.server.wm.AppWindowTokenProto.ALL_DRAWN;
import static com.android.server.wm.AppWindowTokenProto.APP_STOPPED;
-import static com.android.server.wm.AppWindowTokenProto.CLIENT_VISIBLE;
+import static com.android.server.wm.AppWindowTokenProto.CLIENT_HIDDEN;
import static com.android.server.wm.AppWindowTokenProto.DEFER_HIDING_CLIENT;
import static com.android.server.wm.AppWindowTokenProto.FILLS_PARENT;
import static com.android.server.wm.AppWindowTokenProto.FROZEN_BOUNDS;
+import static com.android.server.wm.AppWindowTokenProto.HIDDEN_REQUESTED;
+import static com.android.server.wm.AppWindowTokenProto.HIDDEN_SET_FROM_TRANSFERRED_STARTING_WINDOW;
import static com.android.server.wm.AppWindowTokenProto.IS_ANIMATING;
import static com.android.server.wm.AppWindowTokenProto.IS_WAITING_FOR_TRANSITION_START;
import static com.android.server.wm.AppWindowTokenProto.LAST_ALL_DRAWN;
@@ -191,7 +192,6 @@ import static com.android.server.wm.AppWindowTokenProto.STARTING_DISPLAYED;
import static com.android.server.wm.AppWindowTokenProto.STARTING_MOVED;
import static com.android.server.wm.AppWindowTokenProto.STARTING_WINDOW;
import static com.android.server.wm.AppWindowTokenProto.THUMBNAIL;
-import static com.android.server.wm.AppWindowTokenProto.VISIBLE_SET_FROM_TRANSFERRED_STARTING_WINDOW;
import static com.android.server.wm.AppWindowTokenProto.WINDOW_TOKEN;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
@@ -462,16 +462,16 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
private boolean keysPaused; // has key dispatching been paused for it?
int launchMode; // the launch mode activity attribute.
int lockTaskLaunchMode; // the lockTaskMode manifest attribute, subject to override
- private boolean mVisible; // Should this token's windows be visible?
+ boolean visible; // does this activity's window need to be shown?
boolean visibleIgnoringKeyguard; // is this activity visible, ignoring the fact that Keyguard
// might hide this activity?
- // True if the visible state of this token was forced to true due to a transferred starting
+ // True if the hidden state of this token was forced to false due to a transferred starting
// window.
- private boolean mVisibleSetFromTransferredStartingWindow;
- // TODO: figure out how to consolidate with the same variable in ActivityRecord.
+ private boolean mHiddenSetFromTransferredStartingWindow;
+ // TODO: figureout how to consolidate with the same variable in ActivityRecord.
private boolean mDeferHidingClient; // If true we told WM to defer reporting to the client
// process that it is hidden.
- private boolean mLastDeferHidingClient; // If true we will defer setting mClientVisible to false
+ private boolean mLastDeferHidingClient; // If true we will defer setting mClientHidden to true
// and reporting to the client that it is hidden.
boolean sleeping; // have we told the activity to sleep?
boolean nowVisible; // is this activity's window visible?
@@ -536,8 +536,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
private Task mLastParent;
- // Have we told the window clients to show themselves?
- private boolean mClientVisible;
+ // Have we told the window clients to hide themselves?
+ private boolean mClientHidden;
boolean firstWindowDrawn;
// Last drawn state we reported to the app token.
@@ -622,11 +622,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// case do not clear allDrawn until the animation completes.
boolean deferClearAllDrawn;
- // Is this window's surface needed? This is almost like visible, except
- // it will sometimes be true a little earlier: when the activity record has
+ // Is this window's surface needed? This is almost like hidden, except
+ // it will sometimes be true a little earlier: when the token has
// been shown, but is still waiting for its app transition to execute
// before making its windows shown.
- boolean mVisibleRequested;
+ boolean hiddenRequested;
// Last visibility state we reported to the app token.
boolean reportedVisible;
@@ -836,6 +836,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
pw.print(" finishing="); pw.println(finishing);
pw.print(prefix); pw.print("keysPaused="); pw.print(keysPaused);
pw.print(" inHistory="); pw.print(inHistory);
+ pw.print(" visible="); pw.print(visible);
pw.print(" sleeping="); pw.print(sleeping);
pw.print(" idle="); pw.print(idle);
pw.print(" mStartingWindowState=");
@@ -854,14 +855,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
pw.println(requestedVrComponent);
}
super.dump(pw, prefix, dumpAll);
- pw.print(" visible="); pw.print(mVisible);
if (appToken != null) {
pw.println(prefix + "app=true mVoiceInteraction=" + mVoiceInteraction);
}
pw.print(prefix); pw.print(" mOccludesParent="); pw.print(mOccludesParent);
pw.print(" mOrientation="); pw.println(mOrientation);
- pw.println(prefix + "mVisibleRequested=" + mVisibleRequested
- + " mClientVisible=" + mClientVisible
+ pw.println(prefix + "hiddenRequested=" + hiddenRequested + " mClientHidden=" + mClientHidden
+ ((mDeferHidingClient) ? " mDeferHidingClient=" + mDeferHidingClient : "")
+ " reportedDrawn=" + reportedDrawn + " reportedVisible=" + reportedVisible);
if (paused) {
@@ -886,13 +885,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
pw.print(" mIsExiting="); pw.println(mIsExiting);
}
if (startingWindow != null || startingSurface != null
- || startingDisplayed || startingMoved || mVisibleSetFromTransferredStartingWindow) {
+ || startingDisplayed || startingMoved || mHiddenSetFromTransferredStartingWindow) {
pw.print(prefix); pw.print("startingWindow="); pw.print(startingWindow);
pw.print(" startingSurface="); pw.print(startingSurface);
pw.print(" startingDisplayed="); pw.print(startingDisplayed);
pw.print(" startingMoved="); pw.print(startingMoved);
pw.println(" mHiddenSetFromTransferredStartingWindow="
- + mVisibleSetFromTransferredStartingWindow);
+ + mHiddenSetFromTransferredStartingWindow);
}
if (!mFrozenBounds.isEmpty()) {
pw.print(prefix); pw.print("mFrozenBounds="); pw.println(mFrozenBounds);
@@ -1488,8 +1487,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
// Application tokens start out hidden.
- setVisible(false);
- mVisibleRequested = false;
+ setHidden(true);
+ hiddenRequested = true;
ColorDisplayService.ColorDisplayServiceInternal cds = LocalServices.getService(
ColorDisplayService.ColorDisplayServiceInternal.class);
@@ -1518,9 +1517,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
deferRelaunchUntilPaused = false;
keysPaused = false;
inHistory = false;
+ visible = false;
nowVisible = false;
mDrawn = false;
- mClientVisible = true;
idle = false;
hasBeenLaunched = false;
mStackSupervisor = supervisor;
@@ -2201,7 +2200,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* 2. App is delayed closing since it might enter PIP.
*/
boolean isClosingOrEnteringPip() {
- return (isAnimating(TRANSITION | PARENTS) && !mVisibleRequested) || mWillCloseOrEnterPip;
+ return (isAnimating(TRANSITION | PARENTS) && hiddenRequested) || mWillCloseOrEnterPip;
}
/**
* @return Whether AppOps allows this package to enter picture-in-picture.
@@ -2460,7 +2459,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mAtmService.getLockTaskController().clearLockedTask(task);
}
} else if (!isState(PAUSING)) {
- if (mVisibleRequested) {
+ if (visible) {
// Prepare and execute close transition.
prepareActivityHideTransitionAnimation(transit);
}
@@ -2539,13 +2538,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// TODO(b/137329632): find the next activity directly underneath this one, not just anywhere
final ActivityRecord next = getDisplay().topRunningActivity(
true /* considerKeyguardState */);
- final boolean isVisible = mVisibleRequested || nowVisible;
+ final boolean isVisible = visible || nowVisible;
// isNextNotYetVisible is to check if the next activity is invisible, or it has been
// requested to be invisible but its windows haven't reported as invisible. If so, it
// implied that the current finishing activity should be added into stopping list rather
// than destroy immediately.
- final boolean isNextNotYetVisible = next != null
- && (!next.nowVisible || !next.mVisibleRequested);
+ final boolean isNextNotYetVisible = next != null && (!next.nowVisible || !next.visible);
if (isVisible && isNextNotYetVisible) {
// Add this activity to the list of stopping activities. It will be processed and
// destroyed when the next activity reports idle.
@@ -3213,7 +3211,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
"Removing starting %s from %s", tStartingWindow, fromActivity);
fromActivity.removeChild(tStartingWindow);
fromActivity.postWindowRemoveStartingWindowCleanup(tStartingWindow);
- fromActivity.mVisibleSetFromTransferredStartingWindow = false;
+ fromActivity.mHiddenSetFromTransferredStartingWindow = false;
addWindow(tStartingWindow);
// Propagate other interesting state between the tokens. If the old token is displayed,
@@ -3226,12 +3224,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (fromActivity.firstWindowDrawn) {
firstWindowDrawn = true;
}
- if (fromActivity.isVisible()) {
- setVisible(true);
- mVisibleRequested = true;
- mVisibleSetFromTransferredStartingWindow = true;
+ if (!fromActivity.isHidden()) {
+ setHidden(false);
+ hiddenRequested = false;
+ mHiddenSetFromTransferredStartingWindow = true;
}
- setClientVisible(fromActivity.mClientVisible);
+ setClientHidden(fromActivity.mClientHidden);
transferAnimation(fromActivity);
@@ -3277,7 +3275,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (fromActivity == this) {
return;
}
- if (!fromActivity.mVisibleRequested && transferStartingWindow(fromActivity.token)) {
+ if (fromActivity.hiddenRequested && transferStartingWindow(fromActivity.token)) {
return;
}
}
@@ -3792,10 +3790,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return opts;
}
- boolean allowMoveToFront() {
- return pendingOptions == null || !pendingOptions.getAvoidMoveToFront();
- }
-
void removeUriPermissionsLocked() {
if (uriPermissions != null) {
uriPermissions.removeUriPermissions();
@@ -3832,7 +3826,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return;
}
mDeferHidingClient = deferHidingClient;
- if (!mDeferHidingClient && !mVisibleRequested) {
+ if (!mDeferHidingClient && !visible) {
// Hiding the client is no longer deferred and the app isn't visible still, go ahead and
// update the visibility.
setVisibility(false);
@@ -3843,14 +3837,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
boolean isVisible() {
// If the activity isn't hidden then it is considered visible and there is no need to check
// its children windows to see if they are visible.
- return mVisible;
- }
-
- void setVisible(boolean visible) {
- if (visible != mVisible) {
- mVisible = visible;
- scheduleAnimation();
- }
+ return !isHidden();
}
void setVisibility(boolean visible) {
@@ -3859,17 +3846,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
+ appToken);
return;
}
- if (visible) {
- mDeferHidingClient = false;
- }
setVisibility(visible, mDeferHidingClient);
mAtmService.addWindowLayoutReasons(
ActivityTaskManagerService.LAYOUT_REASON_VISIBILITY_CHANGED);
mStackSupervisor.getActivityMetricsLogger().notifyVisibilityChanged(this);
+ }
+
+ // TODO: Look into merging with #commitVisibility()
+ void setVisible(boolean newVisible) {
+ visible = newVisible;
+ mDeferHidingClient = !visible && mDeferHidingClient;
+ setVisibility(visible);
mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = true;
}
- @VisibleForTesting
void setVisibility(boolean visible, boolean deferHidingClient) {
final AppTransition appTransition = getDisplayContent().mAppTransition;
@@ -3880,20 +3870,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// transition can be selected.
// TODO: Probably a good idea to separate the concept of opening/closing apps from the
// concept of setting visibility...
- if (!visible && !mVisibleRequested) {
+ if (!visible && hiddenRequested) {
if (!deferHidingClient && mLastDeferHidingClient) {
// We previously deferred telling the client to hide itself when visibility was
// initially set to false. Now we would like it to hide, so go ahead and set it.
mLastDeferHidingClient = deferHidingClient;
- setClientVisible(false);
+ setClientHidden(true);
}
return;
}
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
- "setAppVisibility(%s, visible=%b): %s visible=%b mVisibleRequested=%b Callers=%s",
- appToken, visible, appTransition, isVisible(), mVisibleRequested,
+ "setAppVisibility(%s, visible=%b): %s hidden=%b hiddenRequested=%b Callers=%s",
+ appToken, visible, appTransition, isHidden(), hiddenRequested,
Debug.getCallers(6));
final DisplayContent displayContent = getDisplayContent();
@@ -3904,7 +3894,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
displayContent.mChangingApps.remove(this);
waitingToShow = false;
- mVisibleRequested = visible;
+ hiddenRequested = !visible;
mLastDeferHidingClient = deferHidingClient;
if (!visible) {
@@ -3923,15 +3913,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
startingMoved = false;
// If the token is currently hidden (should be the common case), or has been
// stopped, then we need to set up to wait for its windows to be ready.
- if (!isVisible() || mAppStopped) {
+ if (isHidden() || mAppStopped) {
clearAllDrawn();
// If the app was already visible, don't reset the waitingToShow state.
- if (!isVisible()) {
+ if (isHidden()) {
waitingToShow = true;
// If the client isn't hidden, we don't need to reset the drawing state.
- if (!isClientVisible()) {
+ if (isClientHidden()) {
// Let's reset the draw state in order to prevent the starting window to be
// immediately dismissed when the app still has the surface.
forAllWindows(w -> {
@@ -3951,7 +3941,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// we still need to tell the client to make its windows visible so they get drawn.
// Otherwise, we will wait on performing the transition until all windows have been
// drawn, they never will be, and we are sad.
- setClientVisible(true);
+ setClientHidden(false);
requestUpdateWallpaperIfNeeded();
@@ -3997,9 +3987,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
boolean visible, int transit, boolean performLayout, boolean isVoiceInteraction) {
boolean delayed = false;
- // Reset the state of mVisibleSetFromTransferredStartingWindow since visibility is actually
+ // Reset the state of mHiddenSetFromTransferredStartingWindow since visibility is actually
// been set by the app now.
- mVisibleSetFromTransferredStartingWindow = false;
+ mHiddenSetFromTransferredStartingWindow = false;
// Allow for state changes and animation to be applied if:
// * token is transitioning visibility state
@@ -4009,7 +3999,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// * or the token is the opening app and visible while opening task behind existing one.
final DisplayContent displayContent = getDisplayContent();
boolean visibilityChanged = false;
- if (isVisible() != visible || (!isVisible() && mIsExiting)
+ if (isHidden() == visible || (isHidden() && mIsExiting)
|| (visible && waitingForReplacement())
|| (visible && displayContent.mOpeningApps.contains(this)
&& displayContent.mAppTransition.getAppTransition() == TRANSIT_TASK_OPEN_BEHIND)) {
@@ -4017,7 +4007,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mWmService.mAccessibilityController;
boolean changed = false;
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
- "Changing app %s visible=%b performLayout=%b", this, isVisible(),
+ "Changing app %s hidden=%b performLayout=%b", this, isHidden(),
performLayout);
boolean runningAppAnimation = false;
@@ -4042,8 +4032,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
changed |= win.onAppVisibilityChanged(visible, runningAppAnimation);
}
- setVisible(visible);
- mVisibleRequested = visible;
+ setHidden(!visible);
+ hiddenRequested = !visible;
visibilityChanged = true;
if (!visible) {
stopFreezingScreen(true, true);
@@ -4061,8 +4051,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
- "commitVisibility: %s: visible=%b visibleRequested=%b", this,
- isVisible(), mVisibleRequested);
+ "commitVisibility: %s: hidden=%b hiddenRequested=%b", this,
+ isHidden(), hiddenRequested);
if (changed) {
displayContent.getInputMonitor().setUpdateInputWindowsNeededLw();
@@ -4099,7 +4089,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// If we're becoming invisible, update the client visibility if we are not running an
// animation. Otherwise, we'll update client visibility in onAnimationFinished.
if (visible || !isAnimating()) {
- setClientVisible(visible);
+ setClientHidden(!visible);
}
if (!displayContent.mClosingApps.contains(this)
@@ -4126,7 +4116,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// no animation but there will still be a transition set.
// We still need to delay hiding the surface such that it
// can be synchronized with showing the next surface in the transition.
- if (!isVisible() && !delayed && !displayContent.mAppTransition.isTransitionSet()) {
+ if (isHidden() && !delayed && !displayContent.mAppTransition.isTransitionSet()) {
SurfaceControl.openTransaction();
for (int i = mChildren.size() - 1; i >= 0; i--) {
mChildren.get(i).mWinAnimator.hide("immediately hidden");
@@ -4139,6 +4129,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
@Override
+ void setHidden(boolean hidden) {
+ super.setHidden(hidden);
+ scheduleAnimation();
+ }
+
+ @Override
void onAppTransitionDone() {
sendingToBottom = false;
}
@@ -4401,7 +4397,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
updateOptionsLocked(returningOptions);
stack.mUndrawnActivitiesBelowTopTranslucent.add(this);
}
- setVisibility(true);
+ setVisible(true);
sleeping = false;
app.postPendingUiCleanMsg(true);
if (reportToClient) {
@@ -4437,7 +4433,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
void makeInvisible() {
- if (!mVisibleRequested) {
+ if (!visible) {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Already invisible: " + this);
return;
}
@@ -4459,7 +4455,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final boolean deferHidingClient = canEnterPictureInPicture
&& !isState(STOPPING, STOPPED, PAUSED);
setDeferHidingClient(deferHidingClient);
- setVisibility(false);
+ setVisible(false);
switch (getState()) {
case STOPPING:
@@ -4646,8 +4642,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* state to match that fact.
*/
void completeResumeLocked() {
- final boolean wasVisible = mVisibleRequested;
- setVisibility(true);
+ final boolean wasVisible = visible;
+ setVisible(true);
if (!wasVisible) {
// Visibility has changed, so take a note of it so we call the TaskStackChangedListener
mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = true;
@@ -4731,16 +4727,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
setState(STOPPING, "stopIfPossible");
if (DEBUG_VISIBILITY) {
- Slog.v(TAG_VISIBILITY, "Stopping visibleRequested="
- + mVisibleRequested + " for " + this);
+ Slog.v(TAG_VISIBILITY, "Stopping visible=" + visible + " for " + this);
}
- if (!mVisibleRequested) {
- setVisibility(false);
+ if (!visible) {
+ setVisible(false);
}
EventLogTags.writeAmStopActivity(
mUserId, System.identityHashCode(this), shortComponentName);
mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
- StopActivityItem.obtain(mVisibleRequested, configChangeFlags));
+ StopActivityItem.obtain(visible, configChangeFlags));
if (stack.shouldSleepOrShutDownActivities()) {
setSleeping(true);
}
@@ -4903,10 +4898,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
void startFreezingScreen() {
ProtoLog.i(WM_DEBUG_ORIENTATION,
- "Set freezing of %s: visible=%b freezing=%b visibleRequested=%b. %s",
- appToken, isVisible(), mFreezingScreen, mVisibleRequested,
+ "Set freezing of %s: hidden=%b freezing=%b hiddenRequested=%b. %s",
+ appToken, isHidden(), mFreezingScreen, hiddenRequested,
new RuntimeException().fillInStackTrace());
- if (mVisibleRequested) {
+ if (!hiddenRequested) {
if (!mFreezingScreen) {
mFreezingScreen = true;
mWmService.registerAppFreezeListener(this);
@@ -4942,8 +4937,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return;
}
ProtoLog.v(WM_DEBUG_ORIENTATION,
- "Clear freezing of %s: visible=%b freezing=%b", appToken,
- isVisible(), isFreezingScreen());
+ "Clear freezing of %s: hidden=%b freezing=%b", appToken,
+ isHidden(), isFreezingScreen());
stopFreezingScreen(true, force);
}
}
@@ -5103,7 +5098,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
boolean nowGone = mReportedVisibilityResults.nowGone;
boolean nowDrawn = numInteresting > 0 && numDrawn >= numInteresting;
- boolean nowVisible = numInteresting > 0 && numVisible >= numInteresting && isVisible();
+ boolean nowVisible = numInteresting > 0 && numVisible >= numInteresting && !isHidden();
if (!nowGone) {
// If the app is not yet gone, then it can only become visible/drawn.
if (!nowDrawn) {
@@ -5131,18 +5126,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
- boolean isClientVisible() {
- return mClientVisible;
+ boolean isClientHidden() {
+ return mClientHidden;
}
- void setClientVisible(boolean clientVisible) {
- if (mClientVisible == clientVisible || (!clientVisible && mDeferHidingClient)) {
+ void setClientHidden(boolean hideClient) {
+ if (mClientHidden == hideClient || (hideClient && mDeferHidingClient)) {
return;
}
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
- "setClientVisible: %s clientVisible=%b Callers=%s", this, clientVisible,
+ "setClientHidden: %s clientHidden=%b Callers=%s", this, hideClient,
Debug.getCallers(5));
- mClientVisible = clientVisible;
+ mClientHidden = hideClient;
sendAppVisibilityToClients();
}
@@ -5185,7 +5180,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurfaceController
+ " pv=" + w.isVisibleByPolicy()
+ " mDrawState=" + winAnimator.drawStateToString()
- + " ph=" + w.isParentWindowHidden() + " th=" + mVisibleRequested
+ + " ph=" + w.isParentWindowHidden() + " th=" + hiddenRequested
+ " a=" + isAnimating(TRANSITION));
}
}
@@ -5293,7 +5288,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* currently pausing, or is resumed.
*/
public boolean isInterestingToUserLocked() {
- return mVisibleRequested || nowVisible || mState == PAUSING || mState == RESUMED;
+ return visible || nowVisible || mState == PAUSING || mState == RESUMED;
}
void setSleeping(boolean _sleeping) {
@@ -5367,7 +5362,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// We're not ready for this kind of thing.
return false;
}
- if (mVisibleRequested) {
+ if (visible) {
// The user would notice this!
return false;
}
@@ -5464,11 +5459,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// well there is no point now.
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Nulling last startingData");
mStartingData = null;
- if (mVisibleSetFromTransferredStartingWindow) {
- // We set the visible state to true for the token from a transferred starting
- // window. We now reset it back to false since the starting window was the last
- // window in the token.
- setVisible(false);
+ if (mHiddenSetFromTransferredStartingWindow) {
+ // We set the hidden state to false for the token from a transferred starting window.
+ // We now reset it back to true since the starting window was the last window in the
+ // token.
+ setHidden(true);
}
} else if (mChildren.size() == 1 && startingSurface != null && !isRelaunching()) {
// If this is the last window except for a starting transition window,
@@ -5806,7 +5801,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
@Override
void prepareSurfaces() {
- final boolean show = isVisible() || isAnimating();
+ final boolean show = !isHidden() || isAnimating();
if (mSurfaceControl != null) {
if (show && !mLastSurfaceShowing) {
@@ -5925,7 +5920,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
"AppWindowToken");
clearThumbnail();
- setClientVisible(isVisible() || mVisibleRequested);
+ setClientHidden(isHidden() && hiddenRequested);
getDisplayContent().computeImeTargetIfNeeded(this);
@@ -6522,7 +6517,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (display == null) {
return;
}
- if (mVisibleRequested) {
+ if (visible) {
// It may toggle the UI for user to restart the size compatibility mode activity.
display.handleActivitySizeCompatModeIfNeeded(this);
} else if (mCompatDisplayInsets != null) {
@@ -6819,7 +6814,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
} else {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Config is relaunching " + this);
- if (DEBUG_STATES && !mVisibleRequested) {
+ if (DEBUG_STATES && !visible) {
Slog.v(TAG_STATES, "Config is relaunching invisible activity " + this
+ " called by " + Debug.getCallers(4));
}
@@ -7005,7 +7000,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Reset the existing override configuration so it can be updated according to the latest
// configuration.
clearSizeCompatMode();
- if (mVisibleRequested) {
+ if (visible) {
// Configuration will be ensured when becoming visible, so if it is already visible,
// then the manual update is needed.
updateSizeCompatMode();
@@ -7018,7 +7013,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// The restarting state avoids removing this record when process is died.
setState(RESTARTING_PROCESS, "restartActivityProcess");
- if (!mVisibleRequested || mHaveState) {
+ if (!visible || mHaveState) {
// Kill its process immediately because the activity should be in background.
// The activity state will be update to {@link #DESTROYED} in
// {@link ActivityStack#cleanUp} when handling process died.
@@ -7309,13 +7304,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
writeToProto(proto, APP_WINDOW_TOKEN, WindowTraceLogLevel.ALL);
writeIdentifierToProto(proto, IDENTIFIER);
proto.write(STATE, mState.toString());
- proto.write(VISIBLE_REQUESTED, mVisibleRequested);
+ proto.write(VISIBLE, visible);
proto.write(FRONT_OF_TASK, isRootOfTask());
if (hasProcess()) {
proto.write(PROC_ID, app.getPid());
}
proto.write(TRANSLUCENT, !occludesParent());
- proto.write(VISIBLE, mVisible);
}
public void writeToProto(ProtoOutputStream proto, long fieldId) {
@@ -7346,8 +7340,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
proto.write(FILLS_PARENT, mOccludesParent);
proto.write(APP_STOPPED, mAppStopped);
- proto.write(com.android.server.wm.AppWindowTokenProto.VISIBLE_REQUESTED, mVisibleRequested);
- proto.write(CLIENT_VISIBLE, mClientVisible);
+ proto.write(HIDDEN_REQUESTED, hiddenRequested);
+ proto.write(CLIENT_HIDDEN, mClientHidden);
proto.write(DEFER_HIDING_CLIENT, mDeferHidingClient);
proto.write(REPORTED_DRAWN, reportedDrawn);
proto.write(REPORTED_VISIBLE, reportedVisible);
@@ -7361,12 +7355,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
proto.write(STARTING_DISPLAYED, startingDisplayed);
proto.write(STARTING_MOVED, startingMoved);
- proto.write(VISIBLE_SET_FROM_TRANSFERRED_STARTING_WINDOW,
- mVisibleSetFromTransferredStartingWindow);
+ proto.write(HIDDEN_SET_FROM_TRANSFERRED_STARTING_WINDOW,
+ mHiddenSetFromTransferredStartingWindow);
for (Rect bounds : mFrozenBounds) {
bounds.writeToProto(proto, FROZEN_BOUNDS);
}
- proto.write(com.android.server.wm.AppWindowTokenProto.VISIBLE, mVisible);
proto.end(token);
}
diff --git a/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
index 6e75f9c9167f..c56a9e2ac560 100644
--- a/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
+++ b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
@@ -73,7 +73,7 @@ public class ActivityServiceConnectionsHolder<T> {
public boolean isActivityVisible() {
synchronized (mService.mGlobalLock) {
- return mActivity.mVisibleRequested || mActivity.isState(RESUMED, PAUSING);
+ return mActivity.visible || mActivity.isState(RESUMED, PAUSING);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 2aea81724627..6f6e65933502 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -1629,8 +1629,7 @@ class ActivityStack extends TaskStack {
prev = prev.completeFinishing("completePausedLocked");
} else if (prev.hasProcess()) {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueue pending stop if needed: " + prev
- + " wasStopping=" + wasStopping
- + " visibleRequested=" + prev.mVisibleRequested);
+ + " wasStopping=" + wasStopping + " visible=" + prev.visible);
if (prev.deferRelaunchUntilPaused) {
// Complete the deferred relaunch that was waiting for pause to complete.
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Re-launching after pause: " + prev);
@@ -1640,7 +1639,7 @@ class ActivityStack extends TaskStack {
// We can't clobber it, because the stop confirmation will not be handled.
// We don't need to schedule another stop, we only need to let it happen.
prev.setState(STOPPING, "completePausedLocked");
- } else if (!prev.mVisibleRequested || shouldSleepOrShutDownActivities()) {
+ } else if (!prev.visible || shouldSleepOrShutDownActivities()) {
// Clear out any deferred client hide we might currently have.
prev.setDeferHidingClient(false);
// If we were visible then resumeTopActivities will release resources before
@@ -1761,7 +1760,7 @@ class ActivityStack extends TaskStack {
boolean isTopActivityVisible() {
final ActivityRecord topActivity = getTopActivity();
- return topActivity != null && topActivity.mVisibleRequested;
+ return topActivity != null && topActivity.visible;
}
/**
@@ -1902,7 +1901,7 @@ class ActivityStack extends TaskStack {
for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
final Task task = getChildAt(taskNdx);
ActivityRecord r = task.topRunningActivityLocked();
- if (r == null || r.finishing || !r.mVisibleRequested) {
+ if (r == null || r.finishing || !r.visible) {
task.mLayerRank = -1;
} else {
task.mLayerRank = baseLayer + layer++;
@@ -1991,7 +1990,7 @@ class ActivityStack extends TaskStack {
if (!r.attachedToProcess()) {
makeVisibleAndRestartIfNeeded(starting, configChanges, isTop,
resumeTopActivity && isTop, r);
- } else if (r.mVisibleRequested) {
+ } else if (r.visible) {
// If this activity is already visible, then there is nothing to do here.
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
"Skipping: already visible at " + r);
@@ -2168,16 +2167,16 @@ class ActivityStack extends TaskStack {
// invisible. If the app is already visible, it must have died while it was visible. In this
// case, we'll show the dead window but will not restart the app. Otherwise we could end up
// thrashing.
- if (isTop || !r.mVisibleRequested) {
+ if (isTop || !r.visible) {
// This activity needs to be visible, but isn't even running...
// get it started and resume if no other stack in this stack is resumed.
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Start and freeze screen for " + r);
if (r != starting) {
r.startFreezingScreenLocked(configChanges);
}
- if (!r.mVisibleRequested || r.mLaunchTaskBehind) {
+ if (!r.visible || r.mLaunchTaskBehind) {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Starting and making visible: " + r);
- r.setVisibility(true);
+ r.setVisible(true);
}
if (r != starting) {
mStackSupervisor.startSpecificActivityLocked(r, andResume, true /* checkConfig */);
@@ -2620,8 +2619,7 @@ class ActivityStack extends TaskStack {
if (next.attachedToProcess()) {
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resume running: " + next
- + " stopped=" + next.stopped
- + " visibleRequested=" + next.mVisibleRequested);
+ + " stopped=" + next.stopped + " visible=" + next.visible);
// If the previous activity is translucent, force a visibility update of
// the next activity, so that it's added to WM's opening app list, and
@@ -2636,7 +2634,7 @@ class ActivityStack extends TaskStack {
&& !lastFocusedStack.mLastPausedActivity.occludesParent()));
// This activity is now becoming visible.
- if (!next.mVisibleRequested || next.stopped || lastActivityTranslucent) {
+ if (!next.visible || next.stopped || lastActivityTranslucent) {
next.setVisibility(true);
}
@@ -2691,7 +2689,7 @@ class ActivityStack extends TaskStack {
// Do over!
mStackSupervisor.scheduleResumeTopActivities();
}
- if (!next.mVisibleRequested || next.stopped) {
+ if (!next.visible || next.stopped) {
next.setVisibility(true);
}
next.completeResumeLocked();
@@ -3360,7 +3358,7 @@ class ActivityStack extends TaskStack {
final ActivityRecord top = stack.topRunningActivityLocked();
- if (stack.isActivityTypeHome() && (top == null || !top.mVisibleRequested)) {
+ if (stack.isActivityTypeHome() && (top == null || !top.visible)) {
// If we will be focusing on the home stack next and its current top activity isn't
// visible, then use the move the home stack task to top to make the activity visible.
stack.getDisplay().moveHomeActivityToTop(reason);
@@ -3857,7 +3855,7 @@ class ActivityStack extends TaskStack {
"Record #" + targetIndex + " " + r + ": app=" + r.app);
if (r.app == app) {
- if (r.mVisibleRequested) {
+ if (r.visible) {
hasVisibleActivities = true;
}
final boolean remove;
@@ -3873,8 +3871,8 @@ class ActivityStack extends TaskStack {
// Don't currently have state for the activity, or
// it is finishing -- always remove it.
remove = true;
- } else if (!r.mVisibleRequested && r.launchCount > 2
- && r.lastLaunchTime > (SystemClock.uptimeMillis() - 60000)) {
+ } else if (!r.visible && r.launchCount > 2 &&
+ r.lastLaunchTime > (SystemClock.uptimeMillis() - 60000)) {
// We have launched this activity too many times since it was
// able to run, so give up and remove it.
// (Note if the activity is visible, we don't remove the record.
@@ -3910,7 +3908,7 @@ class ActivityStack extends TaskStack {
// it died, we leave the dead window on screen so it's basically visible.
// This is needed when user later tap on the dead window, we need to stop
// other apps when user transfers focus to the restarted activity.
- r.nowVisible = r.mVisibleRequested;
+ r.nowVisible = r.visible;
}
r.cleanUp(true /* cleanServices */, true /* setState */);
if (remove) {
@@ -4092,7 +4090,7 @@ class ActivityStack extends TaskStack {
* Ensures all visible activities at or below the input activity have the right configuration.
*/
void ensureVisibleActivitiesConfigurationLocked(ActivityRecord start, boolean preserveWindow) {
- if (start == null || !start.mVisibleRequested) {
+ if (start == null || !start.visible) {
return;
}
@@ -4487,7 +4485,7 @@ class ActivityStack extends TaskStack {
final ActivityRecord a = task.getChildAt(activityNdx);
if (a.info.packageName.equals(packageName)) {
a.forceNewConfig = true;
- if (starting != null && a == starting && a.mVisibleRequested) {
+ if (starting != null && a == starting && a.visible) {
a.startFreezingScreenLocked(CONFIG_SCREEN_LAYOUT);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 4828a8d864e9..572bf8381ffc 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -773,11 +773,12 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
}
if (r.getActivityStack().checkKeyguardVisibility(r, true /* shouldBeVisible */,
- true /* isTop */) && r.allowMoveToFront()) {
- // We only set the visibility to true if the activity is not being launched in
- // background, and is allowed to be visible based on keyguard state. This avoids
- // setting this into motion in window manager that is later cancelled due to later
- // calls to ensure visible activities that set visibility back to false.
+ true /* isTop */)) {
+ // We only set the visibility to true if the activity is allowed to be visible
+ // based on
+ // keyguard state. This avoids setting this into motion in window manager that is
+ // later cancelled due to later calls to ensure visible activities that set
+ // visibility back to false.
r.setVisibility(true);
}
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index ff1b42377f5f..bef6af350269 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -688,16 +688,15 @@ public class AppTransitionController {
* compare z-order.
*
* @param apps The list of apps to search.
- * @param ignoreInvisible If set to true, ignores apps that are not
- * {@link ActivityRecord#isVisible}.
+ * @param ignoreHidden If set to true, ignores apps that are {@link ActivityRecord#isHidden}.
* @return The top {@link ActivityRecord}.
*/
- private ActivityRecord getTopApp(ArraySet<ActivityRecord> apps, boolean ignoreInvisible) {
+ private ActivityRecord getTopApp(ArraySet<ActivityRecord> apps, boolean ignoreHidden) {
int topPrefixOrderIndex = Integer.MIN_VALUE;
ActivityRecord topApp = null;
for (int i = apps.size() - 1; i >= 0; i--) {
final ActivityRecord app = apps.valueAt(i);
- if (ignoreInvisible && !app.isVisible()) {
+ if (ignoreHidden && app.isHidden()) {
continue;
}
final int prefixOrderIndex = app.getPrefixOrderIndex();
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 6eb9dba254d8..cb868e1a0bd9 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -652,12 +652,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
+ " config reported=" + w.isLastConfigReportedToClient());
final ActivityRecord activity = w.mActivityRecord;
if (gone) Slog.v(TAG, " GONE: mViewVisibility=" + w.mViewVisibility
- + " mRelayoutCalled=" + w.mRelayoutCalled + " visible=" + w.mToken.isVisible()
- + " visibleRequested=" + (activity != null && activity.mVisibleRequested)
+ + " mRelayoutCalled=" + w.mRelayoutCalled + " hidden=" + w.mToken.isHidden()
+ + " hiddenRequested=" + (activity != null && activity.hiddenRequested)
+ " parentHidden=" + w.isParentWindowHidden());
else Slog.v(TAG, " VIS: mViewVisibility=" + w.mViewVisibility
- + " mRelayoutCalled=" + w.mRelayoutCalled + " visible=" + w.mToken.isVisible()
- + " visibleRequested=" + (activity != null && activity.mVisibleRequested)
+ + " mRelayoutCalled=" + w.mRelayoutCalled + " hidden=" + w.mToken.isHidden()
+ + " hiddenRequested=" + (activity != null && activity.hiddenRequested)
+ " parentHidden=" + w.isParentWindowHidden());
}
@@ -3061,7 +3061,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
wsa.destroySurface();
mWmService.mForceRemoves.add(w);
mTmpWindow = w;
- } else if (w.mActivityRecord != null && !w.mActivityRecord.isClientVisible()) {
+ } else if (w.mActivityRecord != null && w.mActivityRecord.isClientHidden()) {
Slog.w(TAG_WM, "LEAKED SURFACE (app token hidden): "
+ w + " surface=" + wsa.mSurfaceController
+ " token=" + w.mActivityRecord);
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index fc74d00e82db..caf95de75357 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -107,7 +107,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
mTargetActivityType);
ActivityRecord targetActivity = getTargetActivity(targetStack);
if (targetActivity != null) {
- if (targetActivity.mVisibleRequested || targetActivity.isTopRunningActivity()) {
+ if (targetActivity.visible || targetActivity.isTopRunningActivity()) {
// The activity is ready.
return;
}
@@ -189,7 +189,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
// Send launch hint if we are actually launching the target. If it's already visible
// (shouldn't happen in general) we don't need to send it.
- if (targetActivity == null || !targetActivity.mVisibleRequested) {
+ if (targetActivity == null || !targetActivity.visible) {
mService.mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded(
true /* forceSend */, targetActivity);
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 520c26e65fee..a4c7bcd36953 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -60,7 +60,9 @@ import static com.android.server.EventLogTags.WM_TASK_CREATED;
import static com.android.server.EventLogTags.WM_TASK_REMOVED;
import static com.android.server.am.TaskRecordProto.ACTIVITIES;
import static com.android.server.am.TaskRecordProto.ACTIVITY_TYPE;
+import static com.android.server.am.TaskRecordProto.BOUNDS;
import static com.android.server.am.TaskRecordProto.FULLSCREEN;
+import static com.android.server.am.TaskRecordProto.ID;
import static com.android.server.am.TaskRecordProto.LAST_NON_FULLSCREEN_BOUNDS;
import static com.android.server.am.TaskRecordProto.MIN_HEIGHT;
import static com.android.server.am.TaskRecordProto.MIN_WIDTH;
@@ -87,8 +89,10 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS
import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.server.wm.TaskProto.APP_WINDOW_TOKENS;
+import static com.android.server.wm.TaskProto.BOUNDS;
import static com.android.server.wm.TaskProto.DISPLAYED_BOUNDS;
import static com.android.server.wm.TaskProto.FILLS_PARENT;
+import static com.android.server.wm.TaskProto.ID;
import static com.android.server.wm.TaskProto.SURFACE_HEIGHT;
import static com.android.server.wm.TaskProto.SURFACE_WIDTH;
import static com.android.server.wm.TaskProto.WINDOW_CONTAINER;
@@ -2198,7 +2202,7 @@ class Task extends WindowContainer<ActivityRecord> implements ConfigurationConta
void addStartingWindowsForVisibleActivities(boolean taskSwitch) {
for (int activityNdx = getChildCount() - 1; activityNdx >= 0; --activityNdx) {
final ActivityRecord r = getChildAt(activityNdx);
- if (r.mVisibleRequested) {
+ if (r.visible) {
r.showStartingWindow(null /* prev */, false /* newTask */, taskSwitch);
}
}
@@ -2520,7 +2524,7 @@ class Task extends WindowContainer<ActivityRecord> implements ConfigurationConta
for (int i = mChildren.size() - 1; i >= 0; i--) {
final ActivityRecord token = mChildren.get(i);
// skip hidden (or about to hide) apps
- if (token.mIsExiting || !token.isClientVisible() || !token.mVisibleRequested) {
+ if (token.mIsExiting || token.isClientHidden() || token.hiddenRequested) {
continue;
}
final WindowState win = token.findMainWindow();
@@ -2738,9 +2742,10 @@ class Task extends WindowContainer<ActivityRecord> implements ConfigurationConta
ActivityRecord getTopVisibleActivity() {
for (int i = mChildren.size() - 1; i >= 0; i--) {
- final ActivityRecord activity = mChildren.get(i);
- if (!activity.mIsExiting && activity.isClientVisible() && activity.mVisibleRequested) {
- return activity;
+ final ActivityRecord token = mChildren.get(i);
+ // skip hidden (or about to hide) apps
+ if (!token.mIsExiting && !token.isClientHidden() && !token.hiddenRequested) {
+ return token;
}
}
return null;
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 0d4ec652f3bb..35f61a88522b 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+
import static com.android.server.wm.TaskSnapshotPersister.DISABLE_FULL_SIZED_BITMAPS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -248,6 +250,12 @@ class TaskSnapshotController {
@Nullable
SurfaceControl.ScreenshotGraphicBuffer createTaskSnapshot(@NonNull Task task,
float scaleFraction) {
+ return createTaskSnapshot(task, scaleFraction, PixelFormat.RGBA_8888);
+ }
+
+ @Nullable
+ SurfaceControl.ScreenshotGraphicBuffer createTaskSnapshot(@NonNull Task task,
+ float scaleFraction, int pixelFormat) {
if (task.getSurfaceControl() == null) {
if (DEBUG_SCREENSHOT) {
Slog.w(TAG_WM, "Failed to take screenshot. No surface control for " + task);
@@ -258,7 +266,7 @@ class TaskSnapshotController {
mTmpRect.offsetTo(0, 0);
final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer =
SurfaceControl.captureLayers(
- task.getSurfaceControl(), mTmpRect, scaleFraction);
+ task.getSurfaceControl(), mTmpRect, scaleFraction, pixelFormat);
final GraphicBuffer buffer = screenshotBuffer != null ? screenshotBuffer.getGraphicBuffer()
: null;
if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) {
@@ -299,8 +307,14 @@ class TaskSnapshotController {
Slog.w(TAG_WM, "Failed to take screenshot. No main window for " + task);
return null;
}
+ final boolean isWindowTranslucent = mainWindow.getAttrs().format != PixelFormat.OPAQUE;
+ final boolean isShowWallpaper = (mainWindow.getAttrs().flags & FLAG_SHOW_WALLPAPER) != 0;
+ final int pixelFormat = mPersister.use16BitFormat() && activity.fillsParent()
+ && !(isWindowTranslucent && isShowWallpaper)
+ ? PixelFormat.RGB_565
+ : PixelFormat.RGBA_8888;
final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer =
- createTaskSnapshot(task, scaleFraction);
+ createTaskSnapshot(task, scaleFraction, pixelFormat);
if (screenshotBuffer == null) {
if (DEBUG_SCREENSHOT) {
@@ -308,7 +322,8 @@ class TaskSnapshotController {
}
return null;
}
- final boolean isWindowTranslucent = mainWindow.getAttrs().format != PixelFormat.OPAQUE;
+ final boolean isTranslucent = PixelFormat.formatHasAlpha(pixelFormat)
+ && (!activity.fillsParent() || isWindowTranslucent);
return new TaskSnapshot(
System.currentTimeMillis() /* id */,
activity.mActivityComponent, screenshotBuffer.getGraphicBuffer(),
@@ -316,7 +331,7 @@ class TaskSnapshotController {
activity.getTask().getConfiguration().orientation,
getInsets(mainWindow), isLowRamDevice /* reduced */, scaleFraction /* scale */,
true /* isRealSnapshot */, task.getWindowingMode(), getSystemUiVisibility(task),
- !activity.fillsParent() || isWindowTranslucent);
+ isTranslucent);
}
private boolean shouldDisableSnapshots() {
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
index 696e1c3a2602..22c1ea59d176 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
@@ -75,25 +75,35 @@ class TaskSnapshotLoader {
final byte[] bytes = Files.readAllBytes(protoFile.toPath());
final TaskSnapshotProto proto = TaskSnapshotProto.parseFrom(bytes);
final Options options = new Options();
- options.inPreferredConfig = Config.HARDWARE;
+ options.inPreferredConfig = mPersister.use16BitFormat() && !proto.isTranslucent
+ ? Config.RGB_565
+ : Config.ARGB_8888;
final Bitmap bitmap = BitmapFactory.decodeFile(bitmapFile.getPath(), options);
if (bitmap == null) {
Slog.w(TAG, "Failed to load bitmap: " + bitmapFile.getPath());
return null;
}
- final GraphicBuffer buffer = bitmap.createGraphicBufferHandle();
+
+ final Bitmap hwBitmap = bitmap.copy(Config.HARDWARE, false);
+ bitmap.recycle();
+ if (hwBitmap == null) {
+ Slog.w(TAG, "Failed to create hardware bitmap: " + bitmapFile.getPath());
+ return null;
+ }
+ final GraphicBuffer buffer = hwBitmap.createGraphicBufferHandle();
if (buffer == null) {
Slog.w(TAG, "Failed to retrieve gralloc buffer for bitmap: "
+ bitmapFile.getPath());
return null;
}
+
final ComponentName topActivityComponent = ComponentName.unflattenFromString(
proto.topActivityComponent);
// For legacy snapshots, restore the scale based on the reduced resolution state
final float legacyScale = reducedResolution ? mPersister.getReducedScale() : 1f;
final float scale = Float.compare(proto.scale, 0f) != 0 ? proto.scale : legacyScale;
- return new TaskSnapshot(proto.id, topActivityComponent, buffer, bitmap.getColorSpace(),
- proto.orientation,
+ return new TaskSnapshot(proto.id, topActivityComponent, buffer,
+ hwBitmap.getColorSpace(), proto.orientation,
new Rect(proto.insetLeft, proto.insetTop, proto.insetRight, proto.insetBottom),
reducedResolution, scale, proto.isRealSnapshot, proto.windowingMode,
proto.systemUiVisibility, proto.isTranslucent);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index a156f5c240a8..59155907823b 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -74,6 +74,7 @@ class TaskSnapshotPersister {
private final Object mLock = new Object();
private final DirectoryResolver mDirectoryResolver;
private final float mReducedScale;
+ private final boolean mUse16BitFormat;
/**
* The list of ids of the tasks that have been persisted since {@link #removeObsoleteFiles} was
@@ -92,6 +93,8 @@ class TaskSnapshotPersister {
mReducedScale = ActivityManager.isLowRamDeviceStatic()
? LOW_RAM_REDUCED_SCALE : REDUCED_SCALE;
}
+ mUse16BitFormat = service.mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_use16BitTaskSnapshotPixelFormat);
}
/**
@@ -164,6 +167,15 @@ class TaskSnapshotPersister {
return mReducedScale;
}
+ /**
+ * Return if task snapshots are stored in 16 bit pixel format.
+ *
+ * @return true if task snapshots are stored in 16 bit pixel format.
+ */
+ boolean use16BitFormat() {
+ return mUse16BitFormat;
+ }
+
@TestApi
void waitForQueueEmpty() {
while (true) {
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 6ff4b2e504f1..3632284fdeb6 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -120,7 +120,7 @@ class WallpaperController {
}
mFindResults.resetTopWallpaper = true;
- if (w.mActivityRecord != null && !w.mActivityRecord.isVisible()
+ if (w.mActivityRecord != null && w.mActivityRecord.isHidden()
&& !w.mActivityRecord.isAnimating(TRANSITION)) {
// If this window's app token is hidden and not animating, it is of no interest to us.
@@ -278,11 +278,9 @@ class WallpaperController {
for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) {
final WallpaperWindowToken token = mWallpaperTokens.get(i);
token.hideWallpaperToken(wasDeferred, "hideWallpapers");
- if (DEBUG_WALLPAPER_LIGHT && token.isVisible()) {
- Slog.d(TAG, "Hiding wallpaper " + token
- + " from " + winGoingAway + " target=" + mWallpaperTarget + " prev="
- + mPrevWallpaperTarget + "\n" + Debug.getCallers(5, " "));
- }
+ if (DEBUG_WALLPAPER_LIGHT && !token.isHidden()) Slog.d(TAG, "Hiding wallpaper " + token
+ + " from " + winGoingAway + " target=" + mWallpaperTarget + " prev="
+ + mPrevWallpaperTarget + "\n" + Debug.getCallers(5, " "));
}
}
@@ -534,9 +532,9 @@ class WallpaperController {
}
final boolean newTargetHidden = wallpaperTarget.mActivityRecord != null
- && !wallpaperTarget.mActivityRecord.mVisibleRequested;
+ && wallpaperTarget.mActivityRecord.hiddenRequested;
final boolean oldTargetHidden = prevWallpaperTarget.mActivityRecord != null
- && !prevWallpaperTarget.mActivityRecord.mVisibleRequested;
+ && prevWallpaperTarget.mActivityRecord.hiddenRequested;
if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Animating wallpapers:" + " old: "
+ prevWallpaperTarget + " hidden=" + oldTargetHidden + " new: " + wallpaperTarget
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index d23bf978cbab..528cece9a78b 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -56,6 +56,7 @@ class WallpaperWindowToken extends WindowToken {
final WindowState wallpaper = mChildren.get(j);
wallpaper.hideWallpaperWindow(wasDeferred, reason);
}
+ setHidden(true);
}
void sendWindowWallpaperCommand(
@@ -87,7 +88,9 @@ class WallpaperWindowToken extends WindowToken {
final int dw = displayInfo.logicalWidth;
final int dh = displayInfo.logicalHeight;
- if (isVisible() != visible) {
+ if (isHidden() == visible) {
+ setHidden(!visible);
+
// Need to do a layout to ensure the wallpaper now has the correct size.
mDisplayContent.setLayoutNeeded();
}
@@ -115,9 +118,10 @@ class WallpaperWindowToken extends WindowToken {
void updateWallpaperWindows(boolean visible) {
- if (isVisible() != visible) {
+ if (isHidden() == visible) {
if (DEBUG_WALLPAPER_LIGHT) Slog.d(TAG,
- "Wallpaper token " + token + " visible=" + visible);
+ "Wallpaper token " + token + " hidden=" + !visible);
+ setHidden(!visible);
// Need to do a layout to ensure the wallpaper now has the correct size.
mDisplayContent.setLayoutNeeded();
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index cf3e2bbe1569..6fed2cbf03be 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -490,6 +490,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
mSurfaceControl = null;
+ mLastSurfacePosition.set(0, 0);
scheduleAnimation();
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 6e4f1ee29a64..88c0fad6a6c9 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1702,7 +1702,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (mInTouchMode) {
res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
}
- if (win.mActivityRecord == null || win.mActivityRecord.isClientVisible()) {
+ if (win.mActivityRecord == null || !win.mActivityRecord.isClientHidden()) {
res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;
}
@@ -2235,7 +2235,7 @@ public class WindowManagerService extends IWindowManager.Stub
// associated appToken is not hidden.
final boolean shouldRelayout = viewVisibility == View.VISIBLE &&
(win.mActivityRecord == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
- || win.mActivityRecord.isClientVisible());
+ || !win.mActivityRecord.isClientHidden());
// If we are not currently running the exit animation, we need to see about starting
// one.
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 2e188b7cc86d..d63fbc217e54 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -533,7 +533,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
synchronized (mAtm.mGlobalLockWithoutBoost) {
for (int i = mActivities.size() - 1; i >= 0; --i) {
final ActivityRecord r = mActivities.get(i);
- if (r.mVisibleRequested) {
+ if (r.visible) {
return true;
}
}
@@ -555,7 +555,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
continue;
}
ActivityRecord topActivity = task.getTopActivity();
- if (topActivity != null && topActivity.mVisibleRequested) {
+ if (topActivity != null && topActivity.visible) {
return true;
}
}
@@ -589,7 +589,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
// - no longer visible OR
// - not focusable (in PiP mode for instance)
if (topDisplay == null
- || !mPreQTopResumedActivity.mVisibleRequested
+ || !mPreQTopResumedActivity.visible
|| !mPreQTopResumedActivity.isFocusable()) {
canUpdate = true;
}
@@ -739,7 +739,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
}
// Don't consider any activities that are currently not in a state where they
// can be destroyed.
- if (r.mVisibleRequested || !r.stopped || !r.hasSavedState()
+ if (r.visible || !r.stopped || !r.hasSavedState()
|| r.isState(STARTED, RESUMED, PAUSING, PAUSED, STOPPING)) {
if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Not releasing in-use activity: " + r);
continue;
@@ -793,7 +793,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
continue;
}
}
- if (r.mVisibleRequested) {
+ if (r.visible) {
final Task task = r.getTask();
if (task != null && minTaskLayer > 0) {
final int layer = task.mLayerRank;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index b8168319dd29..62a3512c3cae 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1539,7 +1539,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
*/
// TODO: Can we consolidate this with #isVisible() or have a more appropriate name for this?
boolean isWinVisibleLw() {
- return (mActivityRecord == null || mActivityRecord.mVisibleRequested
+ return (mActivityRecord == null || !mActivityRecord.hiddenRequested
|| mActivityRecord.isAnimating(TRANSITION)) && isVisible();
}
@@ -1548,7 +1548,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
* not the pending requested hidden state.
*/
boolean isVisibleNow() {
- return (mToken.isVisible() || mAttrs.type == TYPE_APPLICATION_STARTING)
+ return (!mToken.isHidden() || mAttrs.type == TYPE_APPLICATION_STARTING)
&& isVisible();
}
@@ -1570,7 +1570,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final ActivityRecord atoken = mActivityRecord;
return (mHasSurface || (!mRelayoutCalled && mViewVisibility == View.VISIBLE))
&& isVisibleByPolicy() && !isParentWindowHidden()
- && (atoken == null || atoken.mVisibleRequested)
+ && (atoken == null || !atoken.hiddenRequested)
&& !mAnimatingExit && !mDestroying;
}
@@ -1585,7 +1585,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
final ActivityRecord atoken = mActivityRecord;
if (atoken != null) {
- return ((!isParentWindowHidden() && atoken.mVisibleRequested)
+ return ((!isParentWindowHidden() && !atoken.hiddenRequested)
|| isAnimating(TRANSITION | PARENTS));
}
return !isParentWindowHidden() || isAnimating(TRANSITION | PARENTS);
@@ -1621,7 +1621,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return false;
}
final boolean parentAndClientVisible = !isParentWindowHidden()
- && mViewVisibility == View.VISIBLE && mToken.isVisible();
+ && mViewVisibility == View.VISIBLE && !mToken.isHidden();
return mHasSurface && isVisibleByPolicy() && !mDestroying
&& (parentAndClientVisible || isAnimating(TRANSITION | PARENTS));
}
@@ -1640,7 +1640,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
} else {
final Task task = getTask();
final boolean canFromTask = task != null && task.canAffectSystemUiFlags();
- return canFromTask && mActivityRecord.isVisible();
+ return canFromTask && !mActivityRecord.isHidden();
}
}
@@ -1652,7 +1652,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
public boolean isDisplayedLw() {
final ActivityRecord atoken = mActivityRecord;
return isDrawnLw() && isVisibleByPolicy()
- && ((!isParentWindowHidden() && (atoken == null || atoken.mVisibleRequested))
+ && ((!isParentWindowHidden() && (atoken == null || !atoken.hiddenRequested))
|| isAnimating(TRANSITION | PARENTS));
}
@@ -1669,8 +1669,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final ActivityRecord atoken = mActivityRecord;
return mViewVisibility == View.GONE
|| !mRelayoutCalled
- || (atoken == null && !mToken.isVisible())
- || (atoken != null && !atoken.mVisibleRequested)
+ || (atoken == null && mToken.isHidden())
+ || (atoken != null && atoken.hiddenRequested)
|| isParentWindowGoneForLayout()
|| (mAnimatingExit && !isAnimatingLw())
|| mDestroying;
@@ -2162,8 +2162,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
+ " parentHidden=" + isParentWindowHidden()
+ " exiting=" + mAnimatingExit + " destroying=" + mDestroying);
if (mActivityRecord != null) {
- Slog.i(TAG_WM, " mActivityRecord.visibleRequested="
- + mActivityRecord.mVisibleRequested);
+ Slog.i(TAG_WM, " mActivityRecord.hiddenRequested=" + mActivityRecord.hiddenRequested);
}
}
}
@@ -2574,7 +2573,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
* interacts with it.
*/
private boolean shouldKeepVisibleDeadAppWindow() {
- if (!isWinVisibleLw() || mActivityRecord == null || !mActivityRecord.isClientVisible()) {
+ if (!isWinVisibleLw() || mActivityRecord == null || mActivityRecord.isClientHidden()) {
// Not a visible app window or the app isn't dead.
return false;
}
@@ -2611,14 +2610,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return showBecauseOfActivity || showBecauseOfWindow;
}
- /** @return {@code false} if this window desires touch events. */
+ /** @return false if this window desires touch events. */
boolean cantReceiveTouchInput() {
if (mActivityRecord == null || mActivityRecord.getTask() == null) {
return false;
}
return mActivityRecord.getTask().getTaskStack().shouldIgnoreInput()
- || !mActivityRecord.mVisibleRequested
+ || mActivityRecord.hiddenRequested
|| isAnimatingToRecents();
}
@@ -2886,13 +2885,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
void sendAppVisibilityToClients() {
super.sendAppVisibilityToClients();
- final boolean clientVisible = mActivityRecord.isClientVisible();
- if (mAttrs.type == TYPE_APPLICATION_STARTING && !clientVisible) {
+ final boolean clientHidden = mActivityRecord.isClientHidden();
+ if (mAttrs.type == TYPE_APPLICATION_STARTING && clientHidden) {
// Don't hide the starting window.
return;
}
- if (!clientVisible) {
+ if (clientHidden) {
// Once we are notifying the client that it's visibility has changed, we need to prevent
// it from destroying child surfaces until the animation has finished. We do this by
// detaching any surface control the client added from the client.
@@ -2906,8 +2905,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
try {
if (DEBUG_VISIBILITY) Slog.v(TAG,
- "Setting visibility of " + this + ": " + clientVisible);
- mClient.dispatchAppVisibility(clientVisible);
+ "Setting visibility of " + this + ": " + (!clientHidden));
+ mClient.dispatchAppVisibility(!clientHidden);
} catch (RemoteException e) {
}
}
@@ -4147,9 +4146,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
+ " starting=" + (mAttrs.type == TYPE_APPLICATION_STARTING)
+ " during animation: policyVis=" + isVisibleByPolicy()
+ " parentHidden=" + isParentWindowHidden()
- + " tok.visibleRequested="
- + (mActivityRecord != null && mActivityRecord.mVisibleRequested)
- + " tok.visible=" + (mActivityRecord != null && mActivityRecord.isVisible())
+ + " tok.hiddenRequested="
+ + (mActivityRecord != null && mActivityRecord.hiddenRequested)
+ + " tok.hidden=" + (mActivityRecord != null && mActivityRecord.isHidden())
+ " animating=" + isAnimating(TRANSITION | PARENTS)
+ " tok animating="
+ (mActivityRecord != null && mActivityRecord.isAnimating(TRANSITION))
@@ -4556,7 +4555,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
+ " pv=" + isVisibleByPolicy()
+ " mDrawState=" + mWinAnimator.mDrawState
+ " ph=" + isParentWindowHidden()
- + " th=" + (mActivityRecord != null && mActivityRecord.mVisibleRequested)
+ + " th=" + (mActivityRecord != null ? mActivityRecord.hiddenRequested : false)
+ " a=" + isAnimating(TRANSITION | PARENTS));
}
}
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 6480a15a4220..88a1458a783f 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -29,6 +29,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.WindowTokenProto.HASH_CODE;
+import static com.android.server.wm.WindowTokenProto.HIDDEN;
import static com.android.server.wm.WindowTokenProto.PAUSED;
import static com.android.server.wm.WindowTokenProto.WAITING_TO_SHOW;
import static com.android.server.wm.WindowTokenProto.WINDOWS;
@@ -71,6 +72,9 @@ class WindowToken extends WindowContainer<WindowState> {
// Is key dispatching paused for this token?
boolean paused = false;
+ // Should this token's windows be hidden?
+ private boolean mHidden;
+
// Temporary for finding which tokens no longer have visible windows.
boolean hasVisible;
@@ -124,6 +128,16 @@ class WindowToken extends WindowContainer<WindowState> {
}
}
+ void setHidden(boolean hidden) {
+ if (hidden != mHidden) {
+ mHidden = hidden;
+ }
+ }
+
+ boolean isHidden() {
+ return mHidden;
+ }
+
void removeAllWindowsIfPossible() {
for (int i = mChildren.size() - 1; i >= 0; --i) {
final WindowState win = mChildren.get(i);
@@ -142,7 +156,7 @@ class WindowToken extends WindowContainer<WindowState> {
// This token is exiting, so allow it to be removed when it no longer contains any windows.
mPersistOnEmpty = false;
- if (!isVisible()) {
+ if (mHidden) {
return;
}
@@ -155,10 +169,7 @@ class WindowToken extends WindowContainer<WindowState> {
changed |= win.onSetAppExiting();
}
- final ActivityRecord app = asActivityRecord();
- if (app != null) {
- app.setVisible(false);
- }
+ setHidden(true);
if (changed) {
mWmService.mWindowPlacerLocked.performSurfacePlacement();
@@ -275,6 +286,7 @@ class WindowToken extends WindowContainer<WindowState> {
final WindowState w = mChildren.get(i);
w.writeToProto(proto, WINDOWS, logLevel);
}
+ proto.write(HIDDEN, mHidden);
proto.write(WAITING_TO_SHOW, waitingToShow);
proto.write(PAUSED, paused);
proto.end(token);
@@ -284,7 +296,8 @@ class WindowToken extends WindowContainer<WindowState> {
super.dump(pw, prefix, dumpAll);
pw.print(prefix); pw.print("windows="); pw.println(mChildren);
pw.print(prefix); pw.print("windowType="); pw.print(windowType);
- pw.print(" hasVisible="); pw.println(hasVisible);
+ pw.print(" hidden="); pw.print(mHidden);
+ pw.print(" hasVisible="); pw.println(hasVisible);
if (waitingToShow || sendingToBottom) {
pw.print(prefix); pw.print("waitingToShow="); pw.print(waitingToShow);
pw.print(" sendingToBottom="); pw.print(sendingToBottom);
diff --git a/services/devicepolicy/Android.bp b/services/devicepolicy/Android.bp
index 91c05a858ce4..bffa44e868a7 100644
--- a/services/devicepolicy/Android.bp
+++ b/services/devicepolicy/Android.bp
@@ -4,6 +4,7 @@ java_library_static {
libs: [
"services.core",
+ "app-compat-annotations",
],
plugins: [
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index 597d337c2450..99dd9a12eb72 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -44,6 +44,7 @@ import android.os.UserHandle;
import android.testing.DexmakerShareClassLoaderRule;
import android.view.Display;
+import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
import org.junit.After;
@@ -80,6 +81,7 @@ public class AccessibilityServiceConnectionTest {
@Mock ResolveInfo mMockResolveInfo;
@Mock AccessibilitySecurityPolicy mMockSecurityPolicy;
@Mock AccessibilityWindowManager mMockA11yWindowManager;
+ @Mock ActivityTaskManagerInternal mMockActivityTaskManagerInternal;
@Mock AbstractAccessibilityServiceConnection.SystemSupport mMockSystemSupport;
@Mock WindowManagerInternal mMockWindowManagerInternal;
@Mock SystemActionPerformer mMockSystemActionPerformer;
@@ -111,7 +113,8 @@ public class AccessibilityServiceConnectionTest {
mConnection = new AccessibilityServiceConnection(mMockUserState, mMockContext,
COMPONENT_NAME, mMockServiceInfo, SERVICE_ID, mHandler, new Object(),
mMockSecurityPolicy, mMockSystemSupport, mMockWindowManagerInternal,
- mMockSystemActionPerformer, mMockA11yWindowManager);
+ mMockSystemActionPerformer, mMockA11yWindowManager,
+ mMockActivityTaskManagerInternal);
when(mMockSecurityPolicy.canPerformGestures(mConnection)).thenReturn(true);
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
index bcff2f81f805..608625f9fd10 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
@@ -18,6 +18,7 @@ package com.android.server.notification;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -25,7 +26,9 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.AlarmManager;
import android.app.NotificationHistory.HistoricalNotification;
+import android.content.Context;
import android.graphics.drawable.Icon;
import android.os.Handler;
import android.util.AtomicFile;
@@ -42,8 +45,17 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.io.File;
+import java.nio.file.FileSystem;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.FileTime;
+import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
@RunWith(AndroidJUnit4.class)
public class NotificationHistoryDatabaseTest extends UiServiceTestCase {
@@ -51,6 +63,11 @@ public class NotificationHistoryDatabaseTest extends UiServiceTestCase {
File mRootDir;
@Mock
Handler mFileWriteHandler;
+ @Mock
+ Context mContext;
+ @Mock
+ AlarmManager mAlarmManager;
+ TestFileAttrProvider mFileAttrProvider;
NotificationHistoryDatabase mDataBase;
@@ -85,36 +102,56 @@ public class NotificationHistoryDatabaseTest extends UiServiceTestCase {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ when(mContext.getSystemService(AlarmManager.class)).thenReturn(mAlarmManager);
+ when(mContext.getUser()).thenReturn(getContext().getUser());
+ when(mContext.getPackageName()).thenReturn(getContext().getPackageName());
+ mFileAttrProvider = new TestFileAttrProvider();
mRootDir = new File(mContext.getFilesDir(), "NotificationHistoryDatabaseTest");
- mDataBase = new NotificationHistoryDatabase(mRootDir);
+ mDataBase = new NotificationHistoryDatabase(mContext, mRootDir, mFileAttrProvider);
mDataBase.init(mFileWriteHandler);
}
@Test
- public void testPrune() {
+ public void testDeletionReceiver() {
+ verify(mContext, times(1)).registerReceiver(any(), any());
+ }
+
+ @Test
+ public void testPrune() throws Exception {
+ GregorianCalendar cal = new GregorianCalendar();
+ cal.setTimeInMillis(10);
int retainDays = 1;
- for (long i = 10; i >= 5; i--) {
+
+ List<AtomicFile> expectedFiles = new ArrayList<>();
+
+ // add 5 files with a creation date of "today"
+ for (long i = cal.getTimeInMillis(); i >= 5; i--) {
File file = mock(File.class);
- when(file.lastModified()).thenReturn(i);
+ mFileAttrProvider.creationDates.put(file, i);
AtomicFile af = new AtomicFile(file);
+ expectedFiles.add(af);
mDataBase.mHistoryFiles.addLast(af);
}
- GregorianCalendar cal = new GregorianCalendar();
- cal.setTimeInMillis(5);
+
cal.add(Calendar.DATE, -1 * retainDays);
+ // Add 5 more files more than retainDays old
for (int i = 5; i >= 0; i--) {
File file = mock(File.class);
- when(file.lastModified()).thenReturn(cal.getTimeInMillis() - i);
+ mFileAttrProvider.creationDates.put(file, cal.getTimeInMillis() - i);
AtomicFile af = new AtomicFile(file);
mDataBase.mHistoryFiles.addLast(af);
}
- mDataBase.prune(retainDays, 10);
- for (AtomicFile file : mDataBase.mHistoryFiles) {
- assertThat(file.getBaseFile().lastModified() > 0);
- }
+ // back to today; trim everything a day + old
+ cal.add(Calendar.DATE, 1 * retainDays);
+ mDataBase.prune(retainDays, cal.getTimeInMillis());
+
+ assertThat(mDataBase.mHistoryFiles).containsExactlyElementsIn(expectedFiles);
+
+ verify(mAlarmManager, times(6)).setExactAndAllowWhileIdle(anyInt(), anyLong(), any());
+
}
@Test
@@ -181,4 +218,12 @@ public class NotificationHistoryDatabaseTest extends UiServiceTestCase {
verify(af2, never()).openRead();
}
+ private class TestFileAttrProvider implements NotificationHistoryDatabase.FileAttrProvider {
+ public Map<File, Long> creationDates = new HashMap<>();
+
+ @Override
+ public long getCreationTime(File file) {
+ return creationDates.get(file);
+ }
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 776c00e75846..8961796ed617 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -2417,6 +2417,24 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
+ public void testLockChannelsForOEM_channelSpecific_clearData() {
+ NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
+ mHelper.getImportance(PKG_O, UID_O);
+ mHelper.lockChannelsForOEM(new String[] {PKG_O + ":" + a.getId()});
+ mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false);
+ assertTrue(mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false)
+ .isImportanceLockedByOEM());
+
+ mHelper.clearData(PKG_O, UID_O);
+
+ // it's back!
+ mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false);
+ // and still locked
+ assertTrue(mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false)
+ .isImportanceLockedByOEM());
+ }
+
+ @Test
public void testLockChannelsForOEM_channelDoesNotExistYet_appWide() {
NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
index e560cb9a6cf7..9df7b4576427 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
@@ -346,7 +346,7 @@ public class ActivityDisplayTests extends ActivityTestsBase {
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
activity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
activity.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
- activity.mVisibleRequested = true;
+ activity.visible = true;
activity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
final ArrayList<CompletableFuture<IBinder>> resultWrapper = new ArrayList<>();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 734761fd8048..3c619f73aa6f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -160,7 +160,7 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase {
public void testOnActivityLaunchCancelled_hasDrawn() {
onActivityLaunched();
- mTopActivity.mVisibleRequested = mTopActivity.mDrawn = true;
+ mTopActivity.visible = mTopActivity.mDrawn = true;
// Cannot time already-visible activities.
mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, mTopActivity);
@@ -171,7 +171,7 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase {
@Test
public void testOnActivityLaunchCancelled_finishedBeforeDrawn() {
- mTopActivity.mVisibleRequested = mTopActivity.mDrawn = true;
+ mTopActivity.visible = mTopActivity.mDrawn = true;
// Suppress resume when creating the record because we want to notify logger manually.
mSupervisor.beginDeferResume();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index e22c419f5919..c51a46a76f4c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -221,7 +221,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
@Test
public void testRestartProcessIfVisible() {
doNothing().when(mSupervisor).scheduleRestartTimeout(mActivity);
- mActivity.mVisibleRequested = true;
+ mActivity.visible = true;
mActivity.setSavedState(null /* savedState */);
mActivity.setState(ActivityStack.ActivityState.RESUMED, "testRestart");
prepareFixedAspectRatioUnresizableActivity();
@@ -502,7 +502,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
mTask.setBounds(100, 100, 400, 600);
mActivity.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
- mActivity.mVisibleRequested = true;
+ mActivity.visible = true;
ensureActivityConfiguration();
final Rect bounds = new Rect(mActivity.getBounds());
@@ -547,7 +547,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
.when(mActivity).getRequestedOrientation();
mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
mActivity.info.minAspectRatio = mActivity.info.maxAspectRatio = 1;
- mActivity.mVisibleRequested = true;
+ mActivity.visible = true;
ensureActivityConfiguration();
// The parent configuration doesn't change since the first resolved configuration, so the
// activity shouldn't be in the size compatibility mode.
@@ -589,7 +589,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
.setResizeMode(RESIZE_MODE_UNRESIZEABLE)
.setMaxAspectRatio(1.5f)
.build();
- mActivity.mVisibleRequested = true;
+ mActivity.visible = true;
final Rect originalBounds = new Rect(mActivity.getBounds());
final int originalDpi = mActivity.getConfiguration().densityDpi;
@@ -614,7 +614,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
mTask.getRequestedOverrideConfiguration().orientation = Configuration.ORIENTATION_PORTRAIT;
mActivity.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
- mActivity.mVisibleRequested = true;
+ mActivity.visible = true;
ensureActivityConfiguration();
final Rect originalBounds = new Rect(mActivity.getBounds());
@@ -661,7 +661,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
prepareFixedAspectRatioUnresizableActivity();
mActivity.setState(STOPPED, "testSizeCompatMode");
- mActivity.mVisibleRequested = false;
+ mActivity.visible = false;
mActivity.app.setReportedProcState(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY);
// Make the parent bounds to be different so the activity is in size compatibility mode.
setupDisplayAndParentSize(600, 1200);
@@ -829,7 +829,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
// Prepare the activity record to be ready for immediate removal. It should be invisible and
// have no process. Otherwise, request to finish it will send a message to client first.
mActivity.setState(STOPPED, "test");
- mActivity.mVisibleRequested = false;
+ mActivity.visible = false;
mActivity.nowVisible = false;
// Set process to 'null' to allow immediate removal, but don't call mActivity.setProcess() -
// this will cause NPE when updating task's process.
@@ -838,7 +838,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
// Put a visible activity on top, so the finishing activity doesn't have to wait until the
// next activity reports idle to destroy it.
final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
- topActivity.mVisibleRequested = true;
+ topActivity.visible = true;
topActivity.nowVisible = true;
topActivity.setState(RESUMED, "test");
@@ -924,7 +924,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
@Test
public void testFinishActivityIfPossible_visibleResumedPreparesAppTransition() {
mActivity.finishing = false;
- mActivity.mVisibleRequested = true;
+ mActivity.visible = true;
mActivity.setState(RESUMED, "test");
mActivity.finishIfPossible("test", false /* oomAdj */);
@@ -940,7 +940,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
@Test
public void testFinishActivityIfPossible_visibleNotResumedExecutesAppTransition() {
mActivity.finishing = false;
- mActivity.mVisibleRequested = true;
+ mActivity.visible = true;
mActivity.setState(PAUSED, "test");
mActivity.finishIfPossible("test", false /* oomAdj */);
@@ -958,7 +958,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
// Put an activity on top of test activity to make it invisible and prevent us from
// accidentally resuming the topmost one again.
new ActivityBuilder(mService).build();
- mActivity.mVisibleRequested = false;
+ mActivity.visible = false;
mActivity.setState(STOPPED, "test");
mActivity.finishIfPossible("test", false /* oomAdj */);
@@ -1010,7 +1010,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
@Test
public void testCompleteFinishing_keepStateOfNextInvisible() {
final ActivityRecord currentTop = mActivity;
- currentTop.mVisibleRequested = currentTop.nowVisible = true;
+ currentTop.visible = currentTop.nowVisible = true;
// Simulates that {@code currentTop} starts an existing activity from background (so its
// state is stopped) and the starting flow just goes to place it at top.
@@ -1036,13 +1036,13 @@ public class ActivityRecordTests extends ActivityTestsBase {
@Test
public void testCompleteFinishing_waitForNextVisible() {
final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
- topActivity.mVisibleRequested = true;
+ topActivity.visible = true;
topActivity.nowVisible = true;
topActivity.finishing = true;
topActivity.setState(PAUSED, "true");
// Mark the bottom activity as not visible, so that we will wait for it before removing
// the top one.
- mActivity.mVisibleRequested = false;
+ mActivity.visible = false;
mActivity.nowVisible = false;
mActivity.setState(STOPPED, "test");
@@ -1061,13 +1061,13 @@ public class ActivityRecordTests extends ActivityTestsBase {
@Test
public void testCompleteFinishing_noWaitForNextVisible_alreadyInvisible() {
final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
- topActivity.mVisibleRequested = false;
+ topActivity.visible = false;
topActivity.nowVisible = false;
topActivity.finishing = true;
topActivity.setState(PAUSED, "true");
// Mark the bottom activity as not visible, so that we would wait for it before removing
// the top one.
- mActivity.mVisibleRequested = false;
+ mActivity.visible = false;
mActivity.nowVisible = false;
mActivity.setState(STOPPED, "test");
@@ -1083,12 +1083,12 @@ public class ActivityRecordTests extends ActivityTestsBase {
@Test
public void testCompleteFinishing_waitForIdle() {
final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
- topActivity.mVisibleRequested = true;
+ topActivity.visible = true;
topActivity.nowVisible = true;
topActivity.finishing = true;
topActivity.setState(PAUSED, "true");
// Mark the bottom activity as already visible, so that there is no need to wait for it.
- mActivity.mVisibleRequested = true;
+ mActivity.visible = true;
mActivity.nowVisible = true;
mActivity.setState(RESUMED, "test");
@@ -1104,12 +1104,12 @@ public class ActivityRecordTests extends ActivityTestsBase {
@Test
public void testCompleteFinishing_noWaitForNextVisible_stopped() {
final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
- topActivity.mVisibleRequested = false;
+ topActivity.visible = false;
topActivity.nowVisible = false;
topActivity.finishing = true;
topActivity.setState(STOPPED, "true");
// Mark the bottom activity as already visible, so that there is no need to wait for it.
- mActivity.mVisibleRequested = true;
+ mActivity.visible = true;
mActivity.nowVisible = true;
mActivity.setState(RESUMED, "test");
@@ -1125,12 +1125,12 @@ public class ActivityRecordTests extends ActivityTestsBase {
@Test
public void testCompleteFinishing_noWaitForNextVisible_nonFocusedStack() {
final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
- topActivity.mVisibleRequested = true;
+ topActivity.visible = true;
topActivity.nowVisible = true;
topActivity.finishing = true;
topActivity.setState(PAUSED, "true");
// Mark the bottom activity as already visible, so that there is no need to wait for it.
- mActivity.mVisibleRequested = true;
+ mActivity.visible = true;
mActivity.nowVisible = true;
mActivity.setState(RESUMED, "test");
@@ -1139,7 +1139,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
final ActivityStack stack = new StackBuilder(mRootActivityContainer).build();
final ActivityRecord focusedActivity = stack.getChildAt(0).getChildAt(0);
focusedActivity.nowVisible = true;
- focusedActivity.mVisibleRequested = true;
+ focusedActivity.visible = true;
focusedActivity.setState(RESUMED, "test");
stack.mResumedActivity = focusedActivity;
@@ -1346,7 +1346,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
setupDisplayContentForCompatDisplayInsets();
mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
mActivity.info.maxAspectRatio = 1.5f;
- mActivity.mVisibleRequested = true;
+ mActivity.visible = true;
ensureActivityConfiguration();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index fc44652cc668..d0e07b619ad6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -1006,7 +1006,7 @@ public class ActivityStackTests extends ActivityTestsBase {
// There is still an activity1 in stack1 so the activity2 should be added to finishing list
// that will be destroyed until idle.
- stack2.getTopActivity().mVisibleRequested = true;
+ stack2.getTopActivity().visible = true;
final ActivityRecord activity2 = finishTopActivity(stack2);
assertEquals(STOPPING, activity2.getState());
assertThat(mSupervisor.mStoppingActivities).contains(activity2);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index 163268191df1..47b39b042fb5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -271,7 +271,7 @@ class ActivityTestsBase extends SystemServiceTestsBase {
doReturn(true).when(activity).occludesParent();
mTask.addChild(activity);
// Make visible by default...
- activity.setVisible(true);
+ activity.setHidden(false);
}
final WindowProcessController wpc = new WindowProcessController(mService,
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index d415f25baab9..60204539d178 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -62,7 +62,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
final ActivityRecord translucentOpening = createActivityRecord(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
translucentOpening.setOccludesParent(false);
- translucentOpening.setVisible(false);
+ translucentOpening.setHidden(true);
mDisplayContent.mOpeningApps.add(behind);
mDisplayContent.mOpeningApps.add(translucentOpening);
assertEquals(WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN,
@@ -90,7 +90,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
final ActivityRecord translucentOpening = createActivityRecord(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
translucentOpening.setOccludesParent(false);
- translucentOpening.setVisible(false);
+ translucentOpening.setHidden(true);
mDisplayContent.mOpeningApps.add(behind);
mDisplayContent.mOpeningApps.add(translucentOpening);
assertEquals(TRANSIT_TASK_CHANGE_WINDOWING_MODE,
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index d491569149a5..bd336ad2494f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -252,7 +252,7 @@ public class AppWindowTokenTests extends WindowTestsBase {
@Test
@Presubmit
public void testGetOrientation() {
- mActivity.setVisible(true);
+ mActivity.setHidden(false);
mActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
@@ -261,7 +261,7 @@ public class AppWindowTokenTests extends WindowTestsBase {
assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mActivity.getOrientation());
mActivity.setOccludesParent(true);
- mActivity.setVisible(false);
+ mActivity.setHidden(true);
mActivity.sendingToBottom = true;
// Can not specify orientation if app isn't visible even though it occludes parent.
assertEquals(SCREEN_ORIENTATION_UNSET, mActivity.getOrientation());
@@ -314,7 +314,7 @@ public class AppWindowTokenTests extends WindowTestsBase {
@Test
public void testSetOrientation() {
- mActivity.setVisible(true);
+ mActivity.setHidden(false);
// Assert orientation is unspecified to start.
assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, mActivity.getOrientation());
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 8db48584295a..9f4143ff95fa 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -394,7 +394,7 @@ public class DisplayContentTests extends WindowTestsBase {
assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
// Make sure top focused display not changed if there is a focused app.
- window1.mActivityRecord.mVisibleRequested = false;
+ window1.mActivityRecord.hiddenRequested = true;
window1.getDisplayContent().setFocusedApp(window1.mActivityRecord);
updateFocusedWindow();
assertTrue(!window1.isFocused());
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 1abd3662165e..702600402d8e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -149,7 +149,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
final ActivityRecord hiddenActivity = createActivityRecord(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- hiddenActivity.setVisible(false);
+ hiddenActivity.setHidden(true);
mDisplayContent.getConfiguration().windowConfiguration.setRotation(
mDisplayContent.getRotation());
mController.initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray(), homeActivity);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index 06d96fee3757..41cbd8137f5e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -106,12 +106,12 @@ public class RecentsAnimationTest extends ActivityTestsBase {
RecentsAnimationCallbacks recentsAnimation = startRecentsActivity(
mRecentsComponent, true /* getRecentsAnimation */);
// The launch-behind state should make the recents activity visible.
- assertTrue(recentActivity.mVisibleRequested);
+ assertTrue(recentActivity.visible);
// Simulate the animation is cancelled without changing the stack order.
recentsAnimation.onAnimationFinished(REORDER_KEEP_IN_PLACE, false /* sendUserLeaveHint */);
// The non-top recents activity should be invisible by the restored launch-behind state.
- assertFalse(recentActivity.mVisibleRequested);
+ assertFalse(recentActivity.visible);
}
@Test
@@ -158,7 +158,7 @@ public class RecentsAnimationTest extends ActivityTestsBase {
// The activity is started in background so it should be invisible and will be stopped.
assertThat(recentsActivity).isNotNull();
assertThat(mSupervisor.mStoppingActivities).contains(recentsActivity);
- assertFalse(recentsActivity.mVisibleRequested);
+ assertFalse(recentsActivity.visible);
// Assume it is stopped to test next use case.
recentsActivity.activityStoppedLocked(null /* newIcicle */, null /* newPersistentState */,
@@ -361,7 +361,7 @@ public class RecentsAnimationTest extends ActivityTestsBase {
true);
// Ensure we find the task for the right user and it is made visible
- assertTrue(otherUserHomeActivity.mVisibleRequested);
+ assertTrue(otherUserHomeActivity.visible);
}
private void startRecentsActivity() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 05457ccd7303..814004522b28 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -221,6 +221,32 @@ public class WindowContainerTests extends WindowTestsBase {
}
@Test
+ public void testRemoveImmediatelyClearsLastSurfacePosition() {
+ reset(mTransaction);
+ try (MockSurfaceBuildingContainer top = new MockSurfaceBuildingContainer(mWm)) {
+ final WindowContainer<WindowContainer> child1 = new WindowContainer(mWm);
+ child1.setBounds(1, 1, 10, 10);
+
+ top.addChild(child1, 0);
+ assertEquals(1, child1.getLastSurfacePosition().x);
+ assertEquals(1, child1.getLastSurfacePosition().y);
+
+ WindowContainer child11 = new WindowContainer(mWm);
+ child1.addChild(child11, 0);
+
+ child1.setBounds(2, 2, 20, 20);
+ assertEquals(2, child1.getLastSurfacePosition().x);
+ assertEquals(2, child1.getLastSurfacePosition().y);
+
+ child1.removeImmediately();
+ assertEquals(0, child1.getLastSurfacePosition().x);
+ assertEquals(0, child1.getLastSurfacePosition().y);
+ assertEquals(0, child11.getLastSurfacePosition().x);
+ assertEquals(0, child11.getLastSurfacePosition().y);
+ }
+ }
+
+ @Test
public void testAddChildByIndex() {
final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
final TestWindowContainer root = builder.setLayer(0).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 0b7cbceb8f95..e1f92dddf053 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -383,11 +383,11 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testCanAffectSystemUiFlags() {
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
- app.mActivityRecord.setVisible(true);
+ app.mToken.setHidden(false);
assertTrue(app.canAffectSystemUiFlags());
- app.mActivityRecord.setVisible(false);
+ app.mToken.setHidden(true);
assertFalse(app.canAffectSystemUiFlags());
- app.mActivityRecord.setVisible(true);
+ app.mToken.setHidden(false);
app.mAttrs.alpha = 0.0f;
assertFalse(app.canAffectSystemUiFlags());
}
@@ -395,7 +395,7 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testCanAffectSystemUiFlags_disallow() {
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
- app.mActivityRecord.setVisible(true);
+ app.mToken.setHidden(false);
assertTrue(app.canAffectSystemUiFlags());
app.getTask().setCanAffectSystemUiFlags(false);
assertFalse(app.canAffectSystemUiFlags());
@@ -569,7 +569,7 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testCantReceiveTouchWhenAppTokenHiddenRequested() {
final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0");
- win0.mActivityRecord.mVisibleRequested = false;
+ win0.mActivityRecord.hiddenRequested = true;
assertTrue(win0.cantReceiveTouchInput());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
index 26743c842122..797a6bc7e087 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
@@ -74,8 +74,8 @@ class WindowTestUtils {
private static void postCreateActivitySetup(ActivityRecord activity, DisplayContent dc) {
activity.onDisplayChanged(dc);
activity.setOccludesParent(true);
- activity.setVisible(true);
- activity.mVisibleRequested = true;
+ activity.setHidden(false);
+ activity.hiddenRequested = false;
}
static TestWindowToken createTestWindowToken(int type, DisplayContent dc) {
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index 047fcecd5a5b..9967bebf20b8 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -488,7 +488,7 @@ public class UsbHostManager {
* Opens the specified USB device
*/
public ParcelFileDescriptor openDevice(String deviceAddress,
- UsbUserPermissionManager permissions, String packageName, int uid) {
+ UsbUserPermissionManager permissions, String packageName, int pid, int uid) {
synchronized (mLock) {
if (isBlackListed(deviceAddress)) {
throw new SecurityException("USB device is on a restricted bus");
@@ -500,7 +500,7 @@ public class UsbHostManager {
"device " + deviceAddress + " does not exist or is restricted");
}
- permissions.checkPermission(device, packageName, uid);
+ permissions.checkPermission(device, packageName, pid, uid);
return nativeOpenDevice(deviceAddress);
}
}
diff --git a/services/usb/java/com/android/server/usb/UsbSerialReader.java b/services/usb/java/com/android/server/usb/UsbSerialReader.java
index 3151679eb545..86016bb6036f 100644
--- a/services/usb/java/com/android/server/usb/UsbSerialReader.java
+++ b/services/usb/java/com/android/server/usb/UsbSerialReader.java
@@ -93,7 +93,7 @@ class UsbSerialReader extends IUsbSerialReader.Stub {
int userId = UserHandle.getUserId(uid);
if (mDevice instanceof UsbDevice) {
mPermissionManager.getPermissionsForUser(userId)
- .checkPermission((UsbDevice) mDevice, packageName, uid);
+ .checkPermission((UsbDevice) mDevice, packageName, pid, uid);
} else {
mPermissionManager.getPermissionsForUser(userId)
.checkPermission((UsbAccessory) mDevice, uid);
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index 04936377bbfb..275319491e39 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -262,6 +262,7 @@ public class UsbService extends IUsbManager.Stub {
if (mHostManager != null) {
if (deviceName != null) {
int uid = Binder.getCallingUid();
+ int pid = Binder.getCallingPid();
int user = UserHandle.getUserId(uid);
long ident = clearCallingIdentity();
@@ -269,7 +270,7 @@ public class UsbService extends IUsbManager.Stub {
synchronized (mLock) {
if (mUserManager.isSameProfileGroup(user, mCurrentUserId)) {
fd = mHostManager.openDevice(deviceName, getPermissionsForUser(user),
- packageName, uid);
+ packageName, pid, uid);
} else {
Slog.w(TAG, "Cannot open " + deviceName + " for user " + user
+ " as user is not active.");
@@ -469,11 +470,12 @@ public class UsbService extends IUsbManager.Stub {
@Override
public boolean hasDevicePermission(UsbDevice device, String packageName) {
final int uid = Binder.getCallingUid();
+ final int pid = Binder.getCallingPid();
final int userId = UserHandle.getUserId(uid);
final long token = Binder.clearCallingIdentity();
try {
- return getPermissionsForUser(userId).hasPermission(device, packageName, uid);
+ return getPermissionsForUser(userId).hasPermission(device, packageName, pid, uid);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -495,11 +497,12 @@ public class UsbService extends IUsbManager.Stub {
@Override
public void requestDevicePermission(UsbDevice device, String packageName, PendingIntent pi) {
final int uid = Binder.getCallingUid();
+ final int pid = Binder.getCallingPid();
final int userId = UserHandle.getUserId(uid);
final long token = Binder.clearCallingIdentity();
try {
- getPermissionsForUser(userId).requestPermission(device, packageName, pi, uid);
+ getPermissionsForUser(userId).requestPermission(device, packageName, pi, pid, uid);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
index e700f19adbd4..58f5484657c5 100644
--- a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
+++ b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
@@ -186,12 +186,14 @@ class UsbUserPermissionManager {
* Returns true if package with uid has permission to access the device.
*
* @param device to check permission for
+ * @param pid to check permission for
* @param uid to check permission for
* @return {@code true} if package with uid has permission
*/
- boolean hasPermission(@NonNull UsbDevice device, @NonNull String packageName, int uid) {
+ boolean hasPermission(@NonNull UsbDevice device, @NonNull String packageName, int pid,
+ int uid) {
if (isCameraDevicePresent(device)) {
- if (!isCameraPermissionGranted(packageName, uid)) {
+ if (!isCameraPermissionGranted(packageName, pid, uid)) {
return false;
}
}
@@ -615,10 +617,11 @@ class UsbUserPermissionManager {
* Check for camera permission of the calling process.
*
* @param packageName Package name of the caller.
+ * @param pid Linux pid of the calling process.
* @param uid Linux uid of the calling process.
* @return True in case camera permission is available, False otherwise.
*/
- private boolean isCameraPermissionGranted(String packageName, int uid) {
+ private boolean isCameraPermissionGranted(String packageName, int pid, int uid) {
int targetSdkVersion = android.os.Build.VERSION_CODES.P;
try {
ApplicationInfo aInfo = mContext.getPackageManager().getApplicationInfo(packageName, 0);
@@ -634,7 +637,7 @@ class UsbUserPermissionManager {
}
if (targetSdkVersion >= android.os.Build.VERSION_CODES.P) {
- int allowed = mContext.checkCallingPermission(android.Manifest.permission.CAMERA);
+ int allowed = mContext.checkPermission(android.Manifest.permission.CAMERA, pid, uid);
if (android.content.pm.PackageManager.PERMISSION_DENIED == allowed) {
Slog.i(TAG, "Camera permission required for USB video class devices");
return false;
@@ -644,8 +647,8 @@ class UsbUserPermissionManager {
return true;
}
- public void checkPermission(UsbDevice device, String packageName, int uid) {
- if (!hasPermission(device, packageName, uid)) {
+ public void checkPermission(UsbDevice device, String packageName, int pid, int uid) {
+ if (!hasPermission(device, packageName, pid, uid)) {
throw new SecurityException("User has not given " + uid + "/" + packageName
+ " permission to access device " + device.getDeviceName());
}
@@ -678,11 +681,12 @@ class UsbUserPermissionManager {
requestPermissionDialog(device, accessory, canBeDefault, packageName, uid, mContext, pi);
}
- public void requestPermission(UsbDevice device, String packageName, PendingIntent pi, int uid) {
+ public void requestPermission(UsbDevice device, String packageName, PendingIntent pi, int pid,
+ int uid) {
Intent intent = new Intent();
// respond immediately if permission has already been granted
- if (hasPermission(device, packageName, uid)) {
+ if (hasPermission(device, packageName, pid, uid)) {
intent.putExtra(UsbManager.EXTRA_DEVICE, device);
intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
try {
@@ -693,7 +697,7 @@ class UsbUserPermissionManager {
return;
}
if (isCameraDevicePresent(device)) {
- if (!isCameraPermissionGranted(packageName, uid)) {
+ if (!isCameraPermissionGranted(packageName, pid, uid)) {
intent.putExtra(UsbManager.EXTRA_DEVICE, device);
intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false);
try {
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index dfcfed715523..1770671b2380 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -3182,13 +3182,14 @@ public class SubscriptionManager {
}
/**
- * Get active data subscription id.
- * See {@link PhoneStateListener#onActiveDataSubscriptionIdChanged(int)} for the details.
+ * Get active data subscription id. Active data subscription refers to the subscription
+ * currently chosen to provide cellular internet connection to the user. This may be
+ * different from getDefaultDataSubscriptionId(). Eg. Opportunistics data
*
- * @return Active data subscription id
+ * See {@link PhoneStateListener#onActiveDataSubscriptionIdChanged(int)} for the details.
*
- * //TODO: Refactor this API in b/134702460
- * @hide
+ * @return Active data subscription id if any is chosen, or
+ * SubscriptionManager.INVALID_SUBSCRIPTION_ID if not.
*/
public static int getActiveDataSubscriptionId() {
try {
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index 2f90a3dce724..cb66a9650f2f 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -863,7 +863,7 @@ public class EuiccManager {
* @param callbackIntent a PendingIntent to launch when the operation completes.
*
* @deprecated From R, callers should specify a flag for specific set of subscriptions to erase
- * and use {@link #eraseSubscriptionsWithOptions(int, PendingIntent)} instead
+ * and use {@link #eraseSubscriptions(int, PendingIntent)} instead
*
* @hide
*/
@@ -895,7 +895,7 @@ public class EuiccManager {
*/
@SystemApi
@RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
- public void eraseSubscriptionsWithOptions(
+ public void eraseSubscriptions(
@ResetOption int options, @NonNull PendingIntent callbackIntent) {
if (!isEnabled()) {
sendUnavailableError(callbackIntent);
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index eb0e2f7b8786..5fd0af564d34 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -979,25 +979,25 @@ public class ImsMmTelManager implements RegistrationManager {
/**
* Get the status of the MmTel Feature registered on this subscription.
+ * @param executor The executor that will be used to call the callback.
* @param callback A callback containing an Integer describing the current state of the
* MmTel feature, Which will be one of the following:
* {@link ImsFeature#STATE_UNAVAILABLE},
* {@link ImsFeature#STATE_INITIALIZING},
* {@link ImsFeature#STATE_READY}. Will be called using the executor
* specified when the service state has been retrieved from the IMS service.
- * @param executor The executor that will be used to call the callback.
* @throws ImsException if the IMS service associated with this subscription is not available or
* the IMS service is not available.
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public void getFeatureState(@NonNull @ImsFeature.ImsState Consumer<Integer> callback,
- @NonNull @CallbackExecutor Executor executor) throws ImsException {
- if (callback == null) {
- throw new IllegalArgumentException("Must include a non-null Consumer.");
- }
+ public void getFeatureState(@NonNull @CallbackExecutor Executor executor,
+ @NonNull @ImsFeature.ImsState Consumer<Integer> callback) throws ImsException {
if (executor == null) {
throw new IllegalArgumentException("Must include a non-null Executor.");
}
+ if (callback == null) {
+ throw new IllegalArgumentException("Must include a non-null Consumer.");
+ }
try {
getITelephony().getImsMmTelFeatureState(mSubId, new IIntegerConsumer.Stub() {
@Override
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index 0b5d4460612d..aa4174ad40f4 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -27,7 +27,10 @@ java_sdk_library {
":framework-core-sources-for-test-mock",
":framework_native_aidl",
],
- libs: ["framework-all"],
+ libs: [
+ "framework-all",
+ "app-compat-annotations",
+ ],
api_packages: [
"android.test.mock",
diff --git a/test-mock/src/android/test/mock/MockContentResolver.java b/test-mock/src/android/test/mock/MockContentResolver.java
index a70152c8b732..8283019a10ec 100644
--- a/test-mock/src/android/test/mock/MockContentResolver.java
+++ b/test-mock/src/android/test/mock/MockContentResolver.java
@@ -16,6 +16,8 @@
package android.test.mock;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
@@ -130,17 +132,47 @@ public class MockContentResolver extends ContentResolver {
}
/**
- * Overrides {@link android.content.ContentResolver#notifyChange(Uri, ContentObserver, boolean)
- * ContentResolver.notifChange(Uri, ContentObserver, boolean)}. All parameters are ignored.
- * The method hides providers linked to MockContentResolver from other observers in the system.
+ * Overrides the behavior from the parent class to completely ignore any
+ * content notifications sent to this object. This effectively hides clients
+ * from observers elsewhere in the system.
+ */
+ @Override
+ public void notifyChange(@NonNull Uri uri, @Nullable ContentObserver observer) {
+ }
+
+ /**
+ * Overrides the behavior from the parent class to completely ignore any
+ * content notifications sent to this object. This effectively hides clients
+ * from observers elsewhere in the system.
*
- * @param uri (Ignored) The uri of the content provider.
- * @param observer (Ignored) The observer that originated the change.
- * @param syncToNetwork (Ignored) If true, attempt to sync the change to the network.
+ * @deprecated callers should consider migrating to
+ * {@link #notifyChange(Uri, ContentObserver, int)}, as it
+ * offers support for many more options than just
+ * {@link #NOTIFY_SYNC_TO_NETWORK}.
*/
@Override
- public void notifyChange(Uri uri,
- ContentObserver observer,
+ @Deprecated
+ public void notifyChange(@NonNull Uri uri, @Nullable ContentObserver observer,
boolean syncToNetwork) {
}
+
+ /**
+ * Overrides the behavior from the parent class to completely ignore any
+ * content notifications sent to this object. This effectively hides clients
+ * from observers elsewhere in the system.
+ */
+ @Override
+ public void notifyChange(@NonNull Uri uri, @Nullable ContentObserver observer,
+ @NotifyFlags int flags) {
+ }
+
+ /**
+ * Overrides the behavior from the parent class to completely ignore any
+ * content notifications sent to this object. This effectively hides clients
+ * from observers elsewhere in the system.
+ */
+ @Override
+ public void notifyChange(@NonNull Iterable<Uri> uris, @Nullable ContentObserver observer,
+ @NotifyFlags int flags) {
+ }
}
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 1be4ea8eccda..6f442300bce7 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -210,6 +210,7 @@ genrule {
tools: [":soong_zip"],
srcs: [
"Configuration.proto",
+ "ResourcesInternal.proto",
"Resources.proto",
],
out: ["aapt2-protos.zip"],
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 30c24d37a494..90343d4798a7 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -2627,7 +2627,6 @@ public class WifiConfiguration implements Parcelable {
out.writeInt(apBand);
out.writeInt(apChannel);
BackupUtils.writeString(out, preSharedKey);
- BackupUtils.writeString(out, saePasswordId);
out.writeInt(getAuthType());
out.writeBoolean(hiddenSSID);
return baos.toByteArray();
@@ -2651,7 +2650,6 @@ public class WifiConfiguration implements Parcelable {
config.apBand = in.readInt();
config.apChannel = in.readInt();
config.preSharedKey = BackupUtils.readString(in);
- config.saePasswordId = BackupUtils.readString(in);
config.allowedKeyManagement.set(in.readInt());
if (version >= 3) {
config.hiddenSSID = in.readBoolean();