summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt11
-rw-r--r--api/system-current.txt11
-rw-r--r--api/test-current.txt12
-rw-r--r--core/java/android/app/ActivityManager.java24
-rw-r--r--core/java/android/app/INotificationManager.aidl3
-rw-r--r--core/java/android/app/Notification.java7
-rw-r--r--core/java/android/app/NotificationChannel.java38
-rw-r--r--core/java/android/app/NotificationChannelGroup.java121
-rw-r--r--core/java/android/app/NotificationManager.java5
-rw-r--r--core/java/android/app/ResourcesManager.java2
-rw-r--r--core/java/android/app/WindowConfiguration.java76
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java26
-rw-r--r--core/java/android/appwidget/AppWidgetHostView.java5
-rw-r--r--core/java/android/os/Build.java5
-rw-r--r--core/java/android/os/StrictMode.java1241
-rw-r--r--core/java/android/os/VibrationEffect.java52
-rwxr-xr-xcore/java/android/provider/Settings.java41
-rw-r--r--core/java/android/service/autofill/FillEventHistory.java27
-rw-r--r--core/java/android/service/autofill/FillResponse.java2
-rw-r--r--core/java/android/util/ExceptionUtils.java8
-rw-r--r--core/java/android/util/StatsLog.java76
-rw-r--r--core/java/android/view/View.java3
-rw-r--r--core/java/android/widget/RemoteViews.java187
-rw-r--r--core/java/android/widget/SmartSelectSprite.java194
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java3
-rw-r--r--core/java/com/android/internal/policy/DecorView.java2
-rw-r--r--core/java/com/android/internal/widget/FloatingToolbar.java6
-rw-r--r--core/jni/Android.bp1
-rw-r--r--core/jni/AndroidRuntime.cpp2
-rw-r--r--core/jni/android_util_StatsLog.cpp201
-rw-r--r--core/res/res/xml/sms_short_codes.xml2
-rw-r--r--core/tests/coretests/src/android/provider/SettingsBackupTest.java1
-rw-r--r--graphics/java/android/graphics/Color.java2
-rw-r--r--libs/hwui/BakedOpRenderer.cpp15
-rw-r--r--libs/hwui/VectorDrawable.cpp43
-rw-r--r--libs/hwui/VectorDrawable.h5
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.cpp5
-rw-r--r--libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp5
-rw-r--r--media/java/android/media/IMediaRouterService.aidl1
-rw-r--r--media/java/android/media/MediaRouter.java59
-rw-r--r--packages/SystemUI/res/drawable/car_qs_background_primary.xml20
-rw-r--r--packages/SystemUI/res/layout/battery_percentage_view.xml1
-rw-r--r--packages/SystemUI/res/layout/car_qs_panel.xml9
-rw-r--r--packages/SystemUI/res/values/colors_car.xml1
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java122
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozePauser.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java2
-rw-r--r--packages/SystemUI/tests/Android.mk3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/AlwaysOnDisplayPolicyTest.java86
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java6
-rw-r--r--proto/src/metrics_constants.proto7
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java3
-rw-r--r--services/accessibility/java/com/android/server/accessibility/GestureUtils.java20
-rw-r--r--services/accessibility/java/com/android/server/accessibility/MagnificationController.java40
-rw-r--r--services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java826
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java21
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java8
-rw-r--r--services/backup/java/com/android/server/backup/BackupManagerService.java2
-rw-r--r--services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java2
-rw-r--r--services/backup/java/com/android/server/backup/TransportManager.java44
-rw-r--r--services/core/java/com/android/server/VibratorService.java3
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java8
-rw-r--r--services/core/java/com/android/server/am/ActivityRecord.java75
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java147
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java95
-rw-r--r--services/core/java/com/android/server/am/ActivityStarter.java36
-rw-r--r--services/core/java/com/android/server/am/AppErrors.java3
-rw-r--r--services/core/java/com/android/server/am/PinnedActivityStack.java7
-rw-r--r--services/core/java/com/android/server/am/TaskRecord.java125
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java23
-rw-r--r--services/core/java/com/android/server/job/JobSchedulerService.java27
-rw-r--r--services/core/java/com/android/server/job/JobServiceContext.java29
-rw-r--r--services/core/java/com/android/server/media/MediaRouterService.java64
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java52
-rw-r--r--services/core/java/com/android/server/notification/RankingConfig.java1
-rw-r--r--services/core/java/com/android/server/notification/RankingHelper.java48
-rw-r--r--services/core/java/com/android/server/om/IdmapManager.java16
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerServiceImpl.java9
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java6
-rw-r--r--services/core/java/com/android/server/wm/AppWindowAnimator.java2
-rw-r--r--services/core/java/com/android/server/wm/AppWindowContainerController.java1
-rw-r--r--services/core/java/com/android/server/wm/AppWindowToken.java9
-rw-r--r--services/core/java/com/android/server/wm/BoundsAnimationController.java41
-rw-r--r--services/core/java/com/android/server/wm/BoundsAnimationTarget.java2
-rw-r--r--services/core/java/com/android/server/wm/ConfigurationContainer.java64
-rw-r--r--services/core/java/com/android/server/wm/DockedStackDividerController.java3
-rw-r--r--services/core/java/com/android/server/wm/PinnedStackWindowController.java6
-rw-r--r--services/core/java/com/android/server/wm/PinnedStackWindowListener.java3
-rw-r--r--services/core/java/com/android/server/wm/Task.java12
-rw-r--r--services/core/java/com/android/server/wm/TaskStack.java38
-rw-r--r--services/core/java/com/android/server/wm/TaskWindowContainerController.java15
-rw-r--r--services/core/jni/com_android_server_VibratorService.cpp6
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java80
-rw-r--r--services/robotests/Android.mk78
-rw-r--r--services/robotests/src/com/android/server/backup/TransportManagerTest.java513
-rw-r--r--services/robotests/src/com/android/server/backup/testing/BackupTransportStub.java179
-rw-r--r--services/robotests/src/com/android/server/backup/testing/DefaultPackageManagerWithQueryIntentServicesAsUser.java43
-rw-r--r--services/robotests/src/com/android/server/backup/testing/ShadowBackupTransportStub.java40
-rw-r--r--services/robotests/src/com/android/server/backup/testing/ShadowContextImplForBackup.java64
-rw-r--r--services/robotests/src/com/android/server/backup/testing/TransportBoundListenerStub.java64
-rw-r--r--services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java14
-rw-r--r--services/tests/notification/src/com/android/server/notification/RankingHelperTest.java106
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java535
-rw-r--r--services/tests/servicestests/src/com/android/server/backup/BackupPasswordManagerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/testutils/OffsettableClock.java79
-rw-r--r--services/tests/servicestests/src/com/android/server/testutils/TestHandler.java156
-rw-r--r--services/tests/servicestests/src/com/android/server/testutils/TestUtils.java (renamed from services/tests/servicestests/src/com/android/server/testutis/TestUtils.java)18
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java32
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/ConfigurationContainerTests.java80
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java19
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java13
-rw-r--r--tools/aapt/SdkConstants.h1
-rw-r--r--tools/aapt2/SdkConstants.h1
-rw-r--r--tools/aapt2/link/ManifestFixer.cpp1
122 files changed, 4905 insertions, 2005 deletions
diff --git a/api/current.txt b/api/current.txt
index 876c06ca6bf7..4c882315c3e0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5162,6 +5162,7 @@ package android.app {
field public static final java.lang.String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
field public static final java.lang.String EXTRA_BIG_TEXT = "android.bigText";
+ field public static final java.lang.String EXTRA_CHANNEL_GROUP_ID = "android.intent.extra.CHANNEL_GROUP_ID";
field public static final java.lang.String EXTRA_CHANNEL_ID = "android.intent.extra.CHANNEL_ID";
field public static final java.lang.String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
field public static final java.lang.String EXTRA_COLORIZED = "android.colorized";
@@ -5577,8 +5578,11 @@ package android.app {
method public android.app.NotificationChannelGroup clone();
method public int describeContents();
method public java.util.List<android.app.NotificationChannel> getChannels();
+ method public java.lang.String getDescription();
method public java.lang.String getId();
method public java.lang.CharSequence getName();
+ method public boolean isBlocked();
+ method public void setDescription(java.lang.String);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.NotificationChannelGroup> CREATOR;
}
@@ -6533,6 +6537,7 @@ package android.app.admin {
field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
field public static final int SKIP_SETUP_WIZARD = 1; // 0x1
+ field public static final int START_USER_IN_BACKGROUND = 8; // 0x8
field public static final int WIPE_EXTERNAL_STORAGE = 1; // 0x1
field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2
}
@@ -30679,6 +30684,7 @@ package android.os {
field public static final int N_MR1 = 25; // 0x19
field public static final int O = 26; // 0x1a
field public static final int O_MR1 = 27; // 0x1b
+ field public static final int P = 10000; // 0x2710
}
public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -34890,6 +34896,7 @@ package android.provider {
field public static final java.lang.String ACTION_BLUETOOTH_SETTINGS = "android.settings.BLUETOOTH_SETTINGS";
field public static final java.lang.String ACTION_CAPTIONING_SETTINGS = "android.settings.CAPTIONING_SETTINGS";
field public static final java.lang.String ACTION_CAST_SETTINGS = "android.settings.CAST_SETTINGS";
+ field public static final java.lang.String ACTION_CHANNEL_GROUP_NOTIFICATION_SETTINGS = "android.settings.CHANNEL_GROUP_NOTIFICATION_SETTINGS";
field public static final java.lang.String ACTION_CHANNEL_NOTIFICATION_SETTINGS = "android.settings.CHANNEL_NOTIFICATION_SETTINGS";
field public static final java.lang.String ACTION_DATA_ROAMING_SETTINGS = "android.settings.DATA_ROAMING_SETTINGS";
field public static final java.lang.String ACTION_DATE_SETTINGS = "android.settings.DATE_SETTINGS";
@@ -34949,6 +34956,7 @@ package android.provider {
field public static final java.lang.String EXTRA_APP_PACKAGE = "android.provider.extra.APP_PACKAGE";
field public static final java.lang.String EXTRA_AUTHORITIES = "authorities";
field public static final java.lang.String EXTRA_BATTERY_SAVER_MODE_ENABLED = "android.settings.extra.battery_saver_mode_enabled";
+ field public static final java.lang.String EXTRA_CHANNEL_GROUP_ID = "android.provider.extra.CHANNEL_GROUP_ID";
field public static final java.lang.String EXTRA_CHANNEL_ID = "android.provider.extra.CHANNEL_ID";
field public static final java.lang.String EXTRA_DO_NOT_DISTURB_MODE_ENABLED = "android.settings.extra.do_not_disturb_mode_enabled";
field public static final java.lang.String EXTRA_DO_NOT_DISTURB_MODE_MINUTES = "android.settings.extra.do_not_disturb_mode_minutes";
@@ -37081,13 +37089,14 @@ package android.service.autofill {
public final class FillEventHistory implements android.os.Parcelable {
method public int describeContents();
- method public android.os.Bundle getClientState();
+ method public deprecated android.os.Bundle getClientState();
method public java.util.List<android.service.autofill.FillEventHistory.Event> getEvents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.service.autofill.FillEventHistory> CREATOR;
}
public static final class FillEventHistory.Event {
+ method public android.os.Bundle getClientState();
method public java.lang.String getDatasetId();
method public int getType();
field public static final int TYPE_AUTHENTICATION_SELECTED = 2; // 0x2
diff --git a/api/system-current.txt b/api/system-current.txt
index 271093ea1817..9437dfe575fd 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5347,6 +5347,7 @@ package android.app {
field public static final java.lang.String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
field public static final java.lang.String EXTRA_BIG_TEXT = "android.bigText";
+ field public static final java.lang.String EXTRA_CHANNEL_GROUP_ID = "android.intent.extra.CHANNEL_GROUP_ID";
field public static final java.lang.String EXTRA_CHANNEL_ID = "android.intent.extra.CHANNEL_ID";
field public static final java.lang.String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
field public static final java.lang.String EXTRA_COLORIZED = "android.colorized";
@@ -5783,8 +5784,11 @@ package android.app {
method public android.app.NotificationChannelGroup clone();
method public int describeContents();
method public java.util.List<android.app.NotificationChannel> getChannels();
+ method public java.lang.String getDescription();
method public java.lang.String getId();
method public java.lang.CharSequence getName();
+ method public boolean isBlocked();
+ method public void setDescription(java.lang.String);
method public org.json.JSONObject toJson() throws org.json.JSONException;
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.NotificationChannelGroup> CREATOR;
@@ -6777,6 +6781,7 @@ package android.app.admin {
field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
field public static final int SKIP_SETUP_WIZARD = 1; // 0x1
+ field public static final int START_USER_IN_BACKGROUND = 8; // 0x8
field public static final int STATE_USER_PROFILE_COMPLETE = 4; // 0x4
field public static final int STATE_USER_SETUP_COMPLETE = 2; // 0x2
field public static final int STATE_USER_SETUP_FINALIZED = 3; // 0x3
@@ -33396,6 +33401,7 @@ package android.os {
field public static final int N_MR1 = 25; // 0x19
field public static final int O = 26; // 0x1a
field public static final int O_MR1 = 27; // 0x1b
+ field public static final int P = 10000; // 0x2710
}
public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -37942,6 +37948,7 @@ package android.provider {
field public static final java.lang.String ACTION_BLUETOOTH_SETTINGS = "android.settings.BLUETOOTH_SETTINGS";
field public static final java.lang.String ACTION_CAPTIONING_SETTINGS = "android.settings.CAPTIONING_SETTINGS";
field public static final java.lang.String ACTION_CAST_SETTINGS = "android.settings.CAST_SETTINGS";
+ field public static final java.lang.String ACTION_CHANNEL_GROUP_NOTIFICATION_SETTINGS = "android.settings.CHANNEL_GROUP_NOTIFICATION_SETTINGS";
field public static final java.lang.String ACTION_CHANNEL_NOTIFICATION_SETTINGS = "android.settings.CHANNEL_NOTIFICATION_SETTINGS";
field public static final java.lang.String ACTION_DATA_ROAMING_SETTINGS = "android.settings.DATA_ROAMING_SETTINGS";
field public static final java.lang.String ACTION_DATE_SETTINGS = "android.settings.DATE_SETTINGS";
@@ -38002,6 +38009,7 @@ package android.provider {
field public static final java.lang.String EXTRA_APP_PACKAGE = "android.provider.extra.APP_PACKAGE";
field public static final java.lang.String EXTRA_AUTHORITIES = "authorities";
field public static final java.lang.String EXTRA_BATTERY_SAVER_MODE_ENABLED = "android.settings.extra.battery_saver_mode_enabled";
+ field public static final java.lang.String EXTRA_CHANNEL_GROUP_ID = "android.provider.extra.CHANNEL_GROUP_ID";
field public static final java.lang.String EXTRA_CHANNEL_ID = "android.provider.extra.CHANNEL_ID";
field public static final java.lang.String EXTRA_DO_NOT_DISTURB_MODE_ENABLED = "android.settings.extra.do_not_disturb_mode_enabled";
field public static final java.lang.String EXTRA_DO_NOT_DISTURB_MODE_MINUTES = "android.settings.extra.do_not_disturb_mode_minutes";
@@ -40172,13 +40180,14 @@ package android.service.autofill {
public final class FillEventHistory implements android.os.Parcelable {
method public int describeContents();
- method public android.os.Bundle getClientState();
+ method public deprecated android.os.Bundle getClientState();
method public java.util.List<android.service.autofill.FillEventHistory.Event> getEvents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.service.autofill.FillEventHistory> CREATOR;
}
public static final class FillEventHistory.Event {
+ method public android.os.Bundle getClientState();
method public java.lang.String getDatasetId();
method public int getType();
field public static final int TYPE_AUTHENTICATION_SELECTED = 2; // 0x2
diff --git a/api/test-current.txt b/api/test-current.txt
index f16995378081..20a15f801790 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5175,6 +5175,7 @@ package android.app {
field public static final java.lang.String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
field public static final java.lang.String EXTRA_BIG_TEXT = "android.bigText";
+ field public static final java.lang.String EXTRA_CHANNEL_GROUP_ID = "android.intent.extra.CHANNEL_GROUP_ID";
field public static final java.lang.String EXTRA_CHANNEL_ID = "android.intent.extra.CHANNEL_ID";
field public static final java.lang.String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
field public static final java.lang.String EXTRA_COLORIZED = "android.colorized";
@@ -5590,8 +5591,12 @@ package android.app {
method public android.app.NotificationChannelGroup clone();
method public int describeContents();
method public java.util.List<android.app.NotificationChannel> getChannels();
+ method public java.lang.String getDescription();
method public java.lang.String getId();
method public java.lang.CharSequence getName();
+ method public boolean isBlocked();
+ method public void setBlocked(boolean);
+ method public void setDescription(java.lang.String);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.NotificationChannelGroup> CREATOR;
}
@@ -6564,6 +6569,7 @@ package android.app.admin {
field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
field public static final int SKIP_SETUP_WIZARD = 1; // 0x1
+ field public static final int START_USER_IN_BACKGROUND = 8; // 0x8
field public static final int WIPE_EXTERNAL_STORAGE = 1; // 0x1
field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2
}
@@ -30823,6 +30829,7 @@ package android.os {
field public static final int N_MR1 = 25; // 0x19
field public static final int O = 26; // 0x1a
field public static final int O_MR1 = 27; // 0x1b
+ field public static final int P = 10000; // 0x2710
}
public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -35067,6 +35074,7 @@ package android.provider {
field public static final java.lang.String ACTION_BLUETOOTH_SETTINGS = "android.settings.BLUETOOTH_SETTINGS";
field public static final java.lang.String ACTION_CAPTIONING_SETTINGS = "android.settings.CAPTIONING_SETTINGS";
field public static final java.lang.String ACTION_CAST_SETTINGS = "android.settings.CAST_SETTINGS";
+ field public static final java.lang.String ACTION_CHANNEL_GROUP_NOTIFICATION_SETTINGS = "android.settings.CHANNEL_GROUP_NOTIFICATION_SETTINGS";
field public static final java.lang.String ACTION_CHANNEL_NOTIFICATION_SETTINGS = "android.settings.CHANNEL_NOTIFICATION_SETTINGS";
field public static final java.lang.String ACTION_DATA_ROAMING_SETTINGS = "android.settings.DATA_ROAMING_SETTINGS";
field public static final java.lang.String ACTION_DATE_SETTINGS = "android.settings.DATE_SETTINGS";
@@ -35127,6 +35135,7 @@ package android.provider {
field public static final java.lang.String EXTRA_APP_PACKAGE = "android.provider.extra.APP_PACKAGE";
field public static final java.lang.String EXTRA_AUTHORITIES = "authorities";
field public static final java.lang.String EXTRA_BATTERY_SAVER_MODE_ENABLED = "android.settings.extra.battery_saver_mode_enabled";
+ field public static final java.lang.String EXTRA_CHANNEL_GROUP_ID = "android.provider.extra.CHANNEL_GROUP_ID";
field public static final java.lang.String EXTRA_CHANNEL_ID = "android.provider.extra.CHANNEL_ID";
field public static final java.lang.String EXTRA_DO_NOT_DISTURB_MODE_ENABLED = "android.settings.extra.do_not_disturb_mode_enabled";
field public static final java.lang.String EXTRA_DO_NOT_DISTURB_MODE_MINUTES = "android.settings.extra.do_not_disturb_mode_minutes";
@@ -37279,13 +37288,14 @@ package android.service.autofill {
public final class FillEventHistory implements android.os.Parcelable {
method public int describeContents();
- method public android.os.Bundle getClientState();
+ method public deprecated android.os.Bundle getClientState();
method public java.util.List<android.service.autofill.FillEventHistory.Event> getEvents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.service.autofill.FillEventHistory> CREATOR;
}
public static final class FillEventHistory.Event {
+ method public android.os.Bundle getClientState();
method public java.lang.String getDatasetId();
method public int getType();
field public static final int TYPE_AUTHENTICATION_SELECTED = 2; // 0x2
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 337219fd707e..e76e1903a529 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -16,6 +16,10 @@
package android.app;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_DOCKED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -874,6 +878,26 @@ public class ActivityManager {
}
return windowingMode;
}
+
+ /** Returns the activity type that should be used for this input stack id. */
+ // TODO: To be removed once we are not using stack id for stuff...
+ public static int getActivityTypeForStackId(int stackId) {
+ final int activityType;
+ switch (stackId) {
+ case HOME_STACK_ID:
+ activityType = ACTIVITY_TYPE_HOME;
+ break;
+ case RECENTS_STACK_ID:
+ activityType = ACTIVITY_TYPE_RECENTS;
+ break;
+ case ASSISTANT_STACK_ID:
+ activityType = ACTIVITY_TYPE_ASSISTANT;
+ break;
+ default :
+ activityType = ACTIVITY_TYPE_STANDARD;
+ }
+ return activityType;
+ }
}
/**
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 08821bebd57e..d4752a771492 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -61,6 +61,8 @@ interface INotificationManager
void createNotificationChannelsForPackage(String pkg, int uid, in ParceledListSlice channelsList);
ParceledListSlice getNotificationChannelGroupsForPackage(String pkg, int uid, boolean includeDeleted);
NotificationChannelGroup getNotificationChannelGroupForPackage(String groupId, String pkg, int uid);
+ NotificationChannelGroup getPopulatedNotificationChannelGroupForPackage(String pkg, int uid, String groupId, boolean includeDeleted);
+ void updateNotificationChannelGroupForPackage(String pkg, int uid, in NotificationChannelGroup group);
void updateNotificationChannelForPackage(String pkg, int uid, in NotificationChannel channel);
NotificationChannel getNotificationChannel(String pkg, String channelId);
NotificationChannel getNotificationChannelForPackage(String pkg, int uid, String channelId, boolean includeDeleted);
@@ -103,6 +105,7 @@ interface INotificationManager
void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim);
void setInterruptionFilter(String pkg, int interruptionFilter);
+ void updateNotificationChannelGroupFromPrivilegedListener(in INotificationListener token, String pkg, in UserHandle user, in NotificationChannelGroup group);
void updateNotificationChannelFromPrivilegedListener(in INotificationListener token, String pkg, in UserHandle user, in NotificationChannel channel);
ParceledListSlice getNotificationChannelsFromPrivilegedListener(in INotificationListener token, String pkg, in UserHandle user);
ParceledListSlice getNotificationChannelGroupsFromPrivilegedListener(in INotificationListener token, String pkg, in UserHandle user);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 9511f3fd7cef..841b9611fa20 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -124,6 +124,13 @@ public class Notification implements Parcelable
/**
* Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
+ * contain a {@link NotificationChannelGroup#getId() group id} that can be used to narrow down
+ * what settings should be shown in the target app.
+ */
+ public static final String EXTRA_CHANNEL_GROUP_ID = "android.intent.extra.CHANNEL_GROUP_ID";
+
+ /**
+ * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
* contain the tag provided to {@link NotificationManager#notify(String, int, Notification)}
* that can be used to narrow down what settings should be shown in the target app.
*/
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index d6e36914ac6c..163a8dcae2f4 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -830,24 +830,24 @@ public final class NotificationChannel implements Parcelable {
@Override
public String toString() {
- return "NotificationChannel{" +
- "mId='" + mId + '\'' +
- ", mName=" + mName +
- ", mDescription=" + (!TextUtils.isEmpty(mDesc) ? "hasDescription " : "") +
- ", mImportance=" + mImportance +
- ", mBypassDnd=" + mBypassDnd +
- ", mLockscreenVisibility=" + mLockscreenVisibility +
- ", mSound=" + mSound +
- ", mLights=" + mLights +
- ", mLightColor=" + mLightColor +
- ", mVibration=" + Arrays.toString(mVibration) +
- ", mUserLockedFields=" + mUserLockedFields +
- ", mVibrationEnabled=" + mVibrationEnabled +
- ", mShowBadge=" + mShowBadge +
- ", mDeleted=" + mDeleted +
- ", mGroup='" + mGroup + '\'' +
- ", mAudioAttributes=" + mAudioAttributes +
- ", mBlockableSystem=" + mBlockableSystem +
- '}';
+ return "NotificationChannel{"
+ + "mId='" + mId + '\''
+ + ", mName=" + mName
+ + ", mDescription=" + (!TextUtils.isEmpty(mDesc) ? "hasDescription " : "")
+ + ", mImportance=" + mImportance
+ + ", mBypassDnd=" + mBypassDnd
+ + ", mLockscreenVisibility=" + mLockscreenVisibility
+ + ", mSound=" + mSound
+ + ", mLights=" + mLights
+ + ", mLightColor=" + mLightColor
+ + ", mVibration=" + Arrays.toString(mVibration)
+ + ", mUserLockedFields=" + Integer.toHexString(mUserLockedFields)
+ + ", mVibrationEnabled=" + mVibrationEnabled
+ + ", mShowBadge=" + mShowBadge
+ + ", mDeleted=" + mDeleted
+ + ", mGroup='" + mGroup + '\''
+ + ", mAudioAttributes=" + mAudioAttributes
+ + ", mBlockableSystem=" + mBlockableSystem
+ + '}';
}
}
diff --git a/core/java/android/app/NotificationChannelGroup.java b/core/java/android/app/NotificationChannelGroup.java
index 18ad9cf3d8e3..51733114f8b9 100644
--- a/core/java/android/app/NotificationChannelGroup.java
+++ b/core/java/android/app/NotificationChannelGroup.java
@@ -16,6 +16,7 @@
package android.app;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.content.Intent;
import android.os.Parcel;
import android.os.Parcelable;
@@ -23,6 +24,7 @@ import android.text.TextUtils;
import org.json.JSONException;
import org.json.JSONObject;
+import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
@@ -42,10 +44,14 @@ public final class NotificationChannelGroup implements Parcelable {
private static final String TAG_GROUP = "channelGroup";
private static final String ATT_NAME = "name";
+ private static final String ATT_DESC = "desc";
private static final String ATT_ID = "id";
+ private static final String ATT_BLOCKED = "blocked";
private final String mId;
private CharSequence mName;
+ private String mDescription;
+ private boolean mBlocked;
private List<NotificationChannel> mChannels = new ArrayList<>();
/**
@@ -73,7 +79,13 @@ public final class NotificationChannelGroup implements Parcelable {
mId = null;
}
mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ if (in.readByte() != 0) {
+ mDescription = in.readString();
+ } else {
+ mDescription = null;
+ }
in.readParcelableList(mChannels, NotificationChannel.class.getClassLoader());
+ mBlocked = in.readBoolean();
}
private String getTrimmedString(String input) {
@@ -92,24 +104,38 @@ public final class NotificationChannelGroup implements Parcelable {
dest.writeByte((byte) 0);
}
TextUtils.writeToParcel(mName, dest, flags);
+ if (mDescription != null) {
+ dest.writeByte((byte) 1);
+ dest.writeString(mDescription);
+ } else {
+ dest.writeByte((byte) 0);
+ }
dest.writeParcelableList(mChannels, flags);
+ dest.writeBoolean(mBlocked);
}
/**
- * Returns the id of this channel.
+ * Returns the id of this group.
*/
public String getId() {
return mId;
}
/**
- * Returns the user visible name of this channel.
+ * Returns the user visible name of this group.
*/
public CharSequence getName() {
return mName;
}
/**
+ * Returns the user visible description of this group.
+ */
+ public String getDescription() {
+ return mDescription;
+ }
+
+ /**
* Returns the list of channels that belong to this group
*/
public List<NotificationChannel> getChannels() {
@@ -117,6 +143,32 @@ public final class NotificationChannelGroup implements Parcelable {
}
/**
+ * Returns whether or not notifications posted to {@link NotificationChannel channels} belonging
+ * to this group are blocked.
+ */
+ public boolean isBlocked() {
+ return mBlocked;
+ }
+
+ /**
+ * Sets the user visible description of this group.
+ *
+ * <p>The recommended maximum length is 300 characters; the value may be truncated if it is too
+ * long.
+ */
+ public void setDescription(String description) {
+ mDescription = getTrimmedString(description);
+ }
+
+ /**
+ * @hide
+ */
+ @TestApi
+ public void setBlocked(boolean blocked) {
+ mBlocked = blocked;
+ }
+
+ /**
* @hide
*/
public void addChannel(NotificationChannel channel) {
@@ -126,6 +178,28 @@ public final class NotificationChannelGroup implements Parcelable {
/**
* @hide
*/
+ public void setChannels(List<NotificationChannel> channels) {
+ mChannels = channels;
+ }
+
+ /**
+ * @hide
+ */
+ public void populateFromXml(XmlPullParser parser) {
+ // Name, id, and importance are set in the constructor.
+ setDescription(parser.getAttributeValue(null, ATT_DESC));
+ setBlocked(safeBool(parser, ATT_BLOCKED, false));
+ }
+
+ private static boolean safeBool(XmlPullParser parser, String att, boolean defValue) {
+ final String value = parser.getAttributeValue(null, att);
+ if (TextUtils.isEmpty(value)) return defValue;
+ return Boolean.parseBoolean(value);
+ }
+
+ /**
+ * @hide
+ */
public void writeXml(XmlSerializer out) throws IOException {
out.startTag(null, TAG_GROUP);
@@ -133,6 +207,10 @@ public final class NotificationChannelGroup implements Parcelable {
if (getName() != null) {
out.attribute(null, ATT_NAME, getName().toString());
}
+ if (getDescription() != null) {
+ out.attribute(null, ATT_DESC, getDescription().toString());
+ }
+ out.attribute(null, ATT_BLOCKED, Boolean.toString(isBlocked()));
out.endTag(null, TAG_GROUP);
}
@@ -145,6 +223,8 @@ public final class NotificationChannelGroup implements Parcelable {
JSONObject record = new JSONObject();
record.put(ATT_ID, getId());
record.put(ATT_NAME, getName());
+ record.put(ATT_DESC, getDescription());
+ record.put(ATT_BLOCKED, isBlocked());
return record;
}
@@ -173,31 +253,46 @@ public final class NotificationChannelGroup implements Parcelable {
NotificationChannelGroup that = (NotificationChannelGroup) o;
+ if (isBlocked() != that.isBlocked()) return false;
if (getId() != null ? !getId().equals(that.getId()) : that.getId() != null) return false;
if (getName() != null ? !getName().equals(that.getName()) : that.getName() != null) {
return false;
}
- return true;
- }
-
- @Override
- public NotificationChannelGroup clone() {
- return new NotificationChannelGroup(getId(), getName());
+ if (getDescription() != null ? !getDescription().equals(that.getDescription())
+ : that.getDescription() != null) {
+ return false;
+ }
+ return getChannels() != null ? getChannels().equals(that.getChannels())
+ : that.getChannels() == null;
}
@Override
public int hashCode() {
int result = getId() != null ? getId().hashCode() : 0;
result = 31 * result + (getName() != null ? getName().hashCode() : 0);
+ result = 31 * result + (getDescription() != null ? getDescription().hashCode() : 0);
+ result = 31 * result + (isBlocked() ? 1 : 0);
+ result = 31 * result + (getChannels() != null ? getChannels().hashCode() : 0);
return result;
}
@Override
+ public NotificationChannelGroup clone() {
+ NotificationChannelGroup cloned = new NotificationChannelGroup(getId(), getName());
+ cloned.setDescription(getDescription());
+ cloned.setBlocked(isBlocked());
+ cloned.setChannels(getChannels());
+ return cloned;
+ }
+
+ @Override
public String toString() {
- return "NotificationChannelGroup{" +
- "mId='" + mId + '\'' +
- ", mName=" + mName +
- ", mChannels=" + mChannels +
- '}';
+ return "NotificationChannelGroup{"
+ + "mId='" + mId + '\''
+ + ", mName=" + mName
+ + ", mDescription=" + (!TextUtils.isEmpty(mDescription) ? "hasDescription " : "")
+ + ", mBlocked=" + mBlocked
+ + ", mChannels=" + mChannels
+ + '}';
}
}
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 34343e9e106a..8fa7d6c3b4f7 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -421,7 +421,7 @@ public class NotificationManager {
* Creates a notification channel that notifications can be posted to.
*
* This can also be used to restore a deleted channel and to update an existing channel's
- * name, description, and/or importance.
+ * name, description, group, and/or importance.
*
* <p>The name and description should only be changed if the locale changes
* or in response to the user renaming this channel. For example, if a user has a channel
@@ -431,6 +431,9 @@ public class NotificationManager {
* <p>The importance of an existing channel will only be changed if the new importance is lower
* than the current value and the user has not altered any settings on this channel.
*
+ * <p>The group an existing channel will only be changed if the channel does not already
+ * belong to a group.
+ *
* All other fields are ignored for channels that already exist.
*
* @param channel the channel to create. Note that the created channel may differ from this
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 595ecd201e57..fb11272d7e62 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -964,7 +964,7 @@ public class ResourcesManager {
// TODO(adamlesinski): Make this accept more than just overlay directories.
final void applyNewResourceDirsLocked(@NonNull final String baseCodePath,
- @NonNull final String[] newResourceDirs) {
+ @Nullable final String[] newResourceDirs) {
try {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
"ResourcesManager#applyNewResourceDirsLocked");
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 11922ec804aa..187237d240f3 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -55,7 +55,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
/** Can be freely resized within its parent container. */
public static final int WINDOWING_MODE_FREEFORM = 4;
- @IntDef(value = {
+ @IntDef({
WINDOWING_MODE_UNDEFINED,
WINDOWING_MODE_FULLSCREEN,
WINDOWING_MODE_PINNED,
@@ -64,15 +64,41 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
})
public @interface WindowingMode {}
+ /** The current activity type of the configuration. */
+ private @ActivityType int mActivityType;
+
+ /** Activity type is currently not defined. */
+ public static final int ACTIVITY_TYPE_UNDEFINED = 0;
+ /** Standard activity type. Nothing special about the activity... */
+ public static final int ACTIVITY_TYPE_STANDARD = 1;
+ /** Home/Launcher activity type. */
+ public static final int ACTIVITY_TYPE_HOME = 2;
+ /** Recents/Overview activity type. */
+ public static final int ACTIVITY_TYPE_RECENTS = 3;
+ /** Assistant activity type. */
+ public static final int ACTIVITY_TYPE_ASSISTANT = 4;
+
+ @IntDef({
+ ACTIVITY_TYPE_UNDEFINED,
+ ACTIVITY_TYPE_STANDARD,
+ ACTIVITY_TYPE_HOME,
+ ACTIVITY_TYPE_RECENTS,
+ ACTIVITY_TYPE_ASSISTANT,
+ })
+ public @interface ActivityType {}
+
/** Bit that indicates that the {@link #mAppBounds} changed. */
public static final int WINDOW_CONFIG_APP_BOUNDS = 1 << 0;
/** Bit that indicates that the {@link #mWindowingMode} changed. */
public static final int WINDOW_CONFIG_WINDOWING_MODE = 1 << 1;
+ /** Bit that indicates that the {@link #mActivityType} changed. */
+ public static final int WINDOW_CONFIG_ACTIVITY_TYPE = 1 << 2;
@IntDef(flag = true,
value = {
WINDOW_CONFIG_APP_BOUNDS,
WINDOW_CONFIG_WINDOWING_MODE,
+ WINDOW_CONFIG_ACTIVITY_TYPE,
})
public @interface WindowConfig {}
@@ -92,11 +118,13 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(mAppBounds, flags);
dest.writeInt(mWindowingMode);
+ dest.writeInt(mActivityType);
}
private void readFromParcel(Parcel source) {
mAppBounds = source.readParcelable(Rect.class.getClassLoader());
mWindowingMode = source.readInt();
+ mActivityType = source.readInt();
}
@Override
@@ -158,9 +186,27 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
return mWindowingMode;
}
+ public void setActivityType(@ActivityType int activityType) {
+ if (mActivityType == activityType) {
+ return;
+ }
+ if (mActivityType != ACTIVITY_TYPE_UNDEFINED
+ && activityType != ACTIVITY_TYPE_UNDEFINED) {
+ throw new IllegalStateException("Can't change activity type once set: " + this
+ + " activityType=" + activityTypeToString(activityType));
+ }
+ mActivityType = activityType;
+ }
+
+ @ActivityType
+ public int getActivityType() {
+ return mActivityType;
+ }
+
public void setTo(WindowConfiguration other) {
setAppBounds(other.mAppBounds);
setWindowingMode(other.mWindowingMode);
+ setActivityType(other.mActivityType);
}
/** Set this object to completely undefined. */
@@ -171,6 +217,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
public void setToDefaults() {
setAppBounds(null);
setWindowingMode(WINDOWING_MODE_UNDEFINED);
+ setActivityType(ACTIVITY_TYPE_UNDEFINED);
}
/**
@@ -191,6 +238,11 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
changed |= WINDOW_CONFIG_WINDOWING_MODE;
setWindowingMode(delta.mWindowingMode);
}
+ if (delta.mActivityType != ACTIVITY_TYPE_UNDEFINED
+ && mActivityType != delta.mActivityType) {
+ changed |= WINDOW_CONFIG_ACTIVITY_TYPE;
+ setActivityType(delta.mActivityType);
+ }
return changed;
}
@@ -219,6 +271,11 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
changes |= WINDOW_CONFIG_WINDOWING_MODE;
}
+ if ((compareUndefined || other.mActivityType != ACTIVITY_TYPE_UNDEFINED)
+ && mActivityType != other.mActivityType) {
+ changes |= WINDOW_CONFIG_ACTIVITY_TYPE;
+ }
+
return changes;
}
@@ -241,6 +298,8 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
}
n = mWindowingMode - that.mWindowingMode;
if (n != 0) return n;
+ n = mActivityType - that.mActivityType;
+ if (n != 0) return n;
// if (n != 0) return n;
return n;
@@ -263,13 +322,15 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
result = 31 * result + mAppBounds.hashCode();
}
result = 31 * result + mWindowingMode;
+ result = 31 * result + mActivityType;
return result;
}
@Override
public String toString() {
return "{mAppBounds=" + mAppBounds
- + " mWindowingMode=" + windowingModeToString(mWindowingMode) + "}";
+ + " mWindowingMode=" + windowingModeToString(mWindowingMode)
+ + " mActivityType=" + activityTypeToString(mActivityType) + "}";
}
/**
@@ -366,4 +427,15 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
}
return String.valueOf(windowingMode);
}
+
+ public static String activityTypeToString(@ActivityType int applicationType) {
+ switch (applicationType) {
+ case ACTIVITY_TYPE_UNDEFINED: return "undefined";
+ case ACTIVITY_TYPE_STANDARD: return "standard";
+ case ACTIVITY_TYPE_HOME: return "home";
+ case ACTIVITY_TYPE_RECENTS: return "recents";
+ case ACTIVITY_TYPE_ASSISTANT: return "assistant";
+ }
+ return String.valueOf(applicationType);
+ }
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index dd2024178c94..833af1b538d0 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -5983,6 +5983,26 @@ public class DevicePolicyManager {
public static final int MAKE_USER_DEMO = 0x0004;
/**
+ * Flag used by {@link #createAndManageUser} to specificy that the newly created user should be
+ * started in the background as part of the user creation.
+ */
+ // TODO: Investigate solutions for the case where reboot happens before setup is completed.
+ public static final int START_USER_IN_BACKGROUND = 0x0008;
+
+ /**
+ * @hide
+ */
+ @IntDef(
+ flag = true,
+ prefix = {"SKIP_", "MAKE_USER_", "START_"},
+ value = {SKIP_SETUP_WIZARD, MAKE_USER_EPHEMERAL, MAKE_USER_DEMO,
+ START_USER_IN_BACKGROUND}
+ )
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CreateAndManageUserFlags {}
+
+
+ /**
* Called by a device owner to create a user with the specified name and a given component of
* the calling package as profile owner. The UserHandle returned by this method should not be
* persisted as user handles are recycled as users are removed and created. If you need to
@@ -6013,7 +6033,7 @@ public class DevicePolicyManager {
public @Nullable UserHandle createAndManageUser(@NonNull ComponentName admin,
@NonNull String name,
@NonNull ComponentName profileOwner, @Nullable PersistableBundle adminExtras,
- int flags) {
+ @CreateAndManageUserFlags int flags) {
throwIfParentInstance("createAndManageUser");
try {
return mService.createAndManageUser(admin, name, profileOwner, adminExtras, flags);
@@ -6772,6 +6792,10 @@ public class DevicePolicyManager {
* password, pin or pattern is set after the keyguard was disabled, the keyguard stops being
* disabled.
*
+ * <p>
+ * As of {@link android.os.Build.VERSION_CODES#P}, this call also dismisses the
+ * keyguard if it is currently shown.
+ *
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param disabled {@code true} disables the keyguard, {@code false} reenables it.
* @return {@code false} if attempting to disable the keyguard while a lock password was in
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 1242cb0fbdfa..8a1eae2da976 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -40,7 +40,6 @@ import android.util.SparseArray;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.Adapter;
import android.widget.AdapterView;
@@ -469,7 +468,9 @@ public class AppWidgetHostView extends FrameLayout {
// We've already done this -- nothing to do.
return ;
}
- Log.w(TAG, "updateAppWidget couldn't find any view, using error view", exception);
+ if (exception != null) {
+ Log.w(TAG, "Error inflating RemoteViews : " + exception.toString());
+ }
content = getErrorView();
mViewMode = VIEW_MODE_ERROR;
}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 062799891ffd..935f5f3b78a7 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -774,6 +774,11 @@ public class Build {
* O MR1.
*/
public static final int O_MR1 = 27;
+
+ /**
+ * P.
+ */
+ public static final int P = CUR_DEVELOPMENT; // STOPSHIP Replace with the real version.
}
/** The type of build, like "user" or "eng". */
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 3e071437b097..4703af06c06e 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -53,31 +53,25 @@ import java.util.HashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
- * <p>StrictMode is a developer tool which detects things you might be
- * doing by accident and brings them to your attention so you can fix
- * them.
+ * StrictMode is a developer tool which detects things you might be doing by accident and brings
+ * them to your attention so you can fix them.
*
- * <p>StrictMode is most commonly used to catch accidental disk or
- * network access on the application's main thread, where UI
- * operations are received and animations take place. Keeping disk
- * and network operations off the main thread makes for much smoother,
- * more responsive applications. By keeping your application's main thread
- * responsive, you also prevent
- * <a href="{@docRoot}guide/practices/design/responsiveness.html">ANR dialogs</a>
- * from being shown to users.
+ * <p>StrictMode is most commonly used to catch accidental disk or network access on the
+ * application's main thread, where UI operations are received and animations take place. Keeping
+ * disk and network operations off the main thread makes for much smoother, more responsive
+ * applications. By keeping your application's main thread responsive, you also prevent <a
+ * href="{@docRoot}guide/practices/design/responsiveness.html">ANR dialogs</a> from being shown to
+ * users.
*
- * <p class="note">Note that even though an Android device's disk is
- * often on flash memory, many devices run a filesystem on top of that
- * memory with very limited concurrency. It's often the case that
- * almost all disk accesses are fast, but may in individual cases be
- * dramatically slower when certain I/O is happening in the background
- * from other processes. If possible, it's best to assume that such
- * things are not fast.</p>
+ * <p class="note">Note that even though an Android device's disk is often on flash memory, many
+ * devices run a filesystem on top of that memory with very limited concurrency. It's often the case
+ * that almost all disk accesses are fast, but may in individual cases be dramatically slower when
+ * certain I/O is happening in the background from other processes. If possible, it's best to assume
+ * that such things are not fast.
*
- * <p>Example code to enable from early in your
- * {@link android.app.Application}, {@link android.app.Activity}, or
- * other application component's
- * {@link android.app.Application#onCreate} method:
+ * <p>Example code to enable from early in your {@link android.app.Application}, {@link
+ * android.app.Activity}, or other application component's {@link android.app.Application#onCreate}
+ * method:
*
* <pre>
* public void onCreate() {
@@ -99,36 +93,32 @@ import java.util.concurrent.atomic.AtomicInteger;
* }
* </pre>
*
- * <p>You can decide what should happen when a violation is detected.
- * For example, using {@link ThreadPolicy.Builder#penaltyLog} you can
- * watch the output of <code>adb logcat</code> while you use your
- * application to see the violations as they happen.
+ * <p>You can decide what should happen when a violation is detected. For example, using {@link
+ * ThreadPolicy.Builder#penaltyLog} you can watch the output of <code>adb logcat</code> while you
+ * use your application to see the violations as they happen.
*
- * <p>If you find violations that you feel are problematic, there are
- * a variety of tools to help solve them: threads, {@link android.os.Handler},
- * {@link android.os.AsyncTask}, {@link android.app.IntentService}, etc.
- * But don't feel compelled to fix everything that StrictMode finds. In particular,
- * many cases of disk access are often necessary during the normal activity lifecycle. Use
- * StrictMode to find things you did by accident. Network requests on the UI thread
+ * <p>If you find violations that you feel are problematic, there are a variety of tools to help
+ * solve them: threads, {@link android.os.Handler}, {@link android.os.AsyncTask}, {@link
+ * android.app.IntentService}, etc. But don't feel compelled to fix everything that StrictMode
+ * finds. In particular, many cases of disk access are often necessary during the normal activity
+ * lifecycle. Use StrictMode to find things you did by accident. Network requests on the UI thread
* are almost always a problem, though.
*
- * <p class="note">StrictMode is not a security mechanism and is not
- * guaranteed to find all disk or network accesses. While it does
- * propagate its state across process boundaries when doing
- * {@link android.os.Binder} calls, it's still ultimately a best
- * effort mechanism. Notably, disk or network access from JNI calls
- * won't necessarily trigger it. Future versions of Android may catch
- * more (or fewer) operations, so you should never leave StrictMode
- * enabled in applications distributed on Google Play.
+ * <p class="note">StrictMode is not a security mechanism and is not guaranteed to find all disk or
+ * network accesses. While it does propagate its state across process boundaries when doing {@link
+ * android.os.Binder} calls, it's still ultimately a best effort mechanism. Notably, disk or network
+ * access from JNI calls won't necessarily trigger it. Future versions of Android may catch more (or
+ * fewer) operations, so you should never leave StrictMode enabled in applications distributed on
+ * Google Play.
*/
public final class StrictMode {
private static final String TAG = "StrictMode";
private static final boolean LOG_V = Log.isLoggable(TAG, Log.VERBOSE);
/**
- * Boolean system property to disable strict mode checks outright.
- * Set this to 'true' to force disable; 'false' has no effect on other
- * enable/disable policy.
+ * Boolean system property to disable strict mode checks outright. Set this to 'true' to force
+ * disable; 'false' has no effect on other enable/disable policy.
+ *
* @hide
*/
public static final String DISABLE_PROPERTY = "persist.sys.strictmode.disable";
@@ -141,9 +131,9 @@ public final class StrictMode {
public static final String VISUAL_PROPERTY = "persist.sys.strictmode.visual";
/**
- * Temporary property used to include {@link #DETECT_VM_CLEARTEXT_NETWORK}
- * in {@link VmPolicy.Builder#detectAll()}. Apps can still always opt-into
- * detection using {@link VmPolicy.Builder#detectCleartextNetwork()}.
+ * Temporary property used to include {@link #DETECT_VM_CLEARTEXT_NETWORK} in {@link
+ * VmPolicy.Builder#detectAll()}. Apps can still always opt-into detection using {@link
+ * VmPolicy.Builder#detectCleartextNetwork()}.
*/
private static final String CLEARTEXT_PROPERTY = "persist.sys.strictmode.clear";
@@ -162,105 +152,96 @@ public final class StrictMode {
// Byte 1: Thread-policy
- /**
- * @hide
- */
- public static final int DETECT_DISK_WRITE = 0x01; // for ThreadPolicy
+ /** @hide */
+ public static final int DETECT_DISK_WRITE = 0x01; // for ThreadPolicy
- /**
- * @hide
- */
- public static final int DETECT_DISK_READ = 0x02; // for ThreadPolicy
+ /** @hide */
+ public static final int DETECT_DISK_READ = 0x02; // for ThreadPolicy
- /**
- * @hide
- */
- public static final int DETECT_NETWORK = 0x04; // for ThreadPolicy
+ /** @hide */
+ public static final int DETECT_NETWORK = 0x04; // for ThreadPolicy
/**
* For StrictMode.noteSlowCall()
*
* @hide
*/
- public static final int DETECT_CUSTOM = 0x08; // for ThreadPolicy
+ public static final int DETECT_CUSTOM = 0x08; // for ThreadPolicy
/**
* For StrictMode.noteResourceMismatch()
*
* @hide
*/
- public static final int DETECT_RESOURCE_MISMATCH = 0x10; // for ThreadPolicy
+ public static final int DETECT_RESOURCE_MISMATCH = 0x10; // for ThreadPolicy
- /**
- * @hide
- */
- public static final int DETECT_UNBUFFERED_IO = 0x20; // for ThreadPolicy
+ /** @hide */
+ public static final int DETECT_UNBUFFERED_IO = 0x20; // for ThreadPolicy
private static final int ALL_THREAD_DETECT_BITS =
- DETECT_DISK_WRITE | DETECT_DISK_READ | DETECT_NETWORK | DETECT_CUSTOM |
- DETECT_RESOURCE_MISMATCH | DETECT_UNBUFFERED_IO;
+ DETECT_DISK_WRITE
+ | DETECT_DISK_READ
+ | DETECT_NETWORK
+ | DETECT_CUSTOM
+ | DETECT_RESOURCE_MISMATCH
+ | DETECT_UNBUFFERED_IO;
// Byte 2: Process-policy
/**
* Note, a "VM_" bit, not thread.
+ *
* @hide
*/
- public static final int DETECT_VM_CURSOR_LEAKS = 0x01 << 8; // for VmPolicy
+ public static final int DETECT_VM_CURSOR_LEAKS = 0x01 << 8; // for VmPolicy
/**
* Note, a "VM_" bit, not thread.
+ *
* @hide
*/
- public static final int DETECT_VM_CLOSABLE_LEAKS = 0x02 << 8; // for VmPolicy
+ public static final int DETECT_VM_CLOSABLE_LEAKS = 0x02 << 8; // for VmPolicy
/**
* Note, a "VM_" bit, not thread.
+ *
* @hide
*/
- public static final int DETECT_VM_ACTIVITY_LEAKS = 0x04 << 8; // for VmPolicy
+ public static final int DETECT_VM_ACTIVITY_LEAKS = 0x04 << 8; // for VmPolicy
- /**
- * @hide
- */
- private static final int DETECT_VM_INSTANCE_LEAKS = 0x08 << 8; // for VmPolicy
+ /** @hide */
+ private static final int DETECT_VM_INSTANCE_LEAKS = 0x08 << 8; // for VmPolicy
- /**
- * @hide
- */
- public static final int DETECT_VM_REGISTRATION_LEAKS = 0x10 << 8; // for VmPolicy
+ /** @hide */
+ public static final int DETECT_VM_REGISTRATION_LEAKS = 0x10 << 8; // for VmPolicy
- /**
- * @hide
- */
- private static final int DETECT_VM_FILE_URI_EXPOSURE = 0x20 << 8; // for VmPolicy
+ /** @hide */
+ private static final int DETECT_VM_FILE_URI_EXPOSURE = 0x20 << 8; // for VmPolicy
- /**
- * @hide
- */
- private static final int DETECT_VM_CLEARTEXT_NETWORK = 0x40 << 8; // for VmPolicy
+ /** @hide */
+ private static final int DETECT_VM_CLEARTEXT_NETWORK = 0x40 << 8; // for VmPolicy
- /**
- * @hide
- */
- private static final int DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION = 0x80 << 8; // for VmPolicy
+ /** @hide */
+ private static final int DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION = 0x80 << 8; // for VmPolicy
- /**
- * @hide
- */
- private static final int DETECT_VM_UNTAGGED_SOCKET = 0x80 << 24; // for VmPolicy
+ /** @hide */
+ private static final int DETECT_VM_UNTAGGED_SOCKET = 0x80 << 24; // for VmPolicy
private static final int ALL_VM_DETECT_BITS =
- DETECT_VM_CURSOR_LEAKS | DETECT_VM_CLOSABLE_LEAKS |
- DETECT_VM_ACTIVITY_LEAKS | DETECT_VM_INSTANCE_LEAKS |
- DETECT_VM_REGISTRATION_LEAKS | DETECT_VM_FILE_URI_EXPOSURE |
- DETECT_VM_CLEARTEXT_NETWORK | DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION |
- DETECT_VM_UNTAGGED_SOCKET;
+ DETECT_VM_CURSOR_LEAKS
+ | DETECT_VM_CLOSABLE_LEAKS
+ | DETECT_VM_ACTIVITY_LEAKS
+ | DETECT_VM_INSTANCE_LEAKS
+ | DETECT_VM_REGISTRATION_LEAKS
+ | DETECT_VM_FILE_URI_EXPOSURE
+ | DETECT_VM_CLEARTEXT_NETWORK
+ | DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION
+ | DETECT_VM_UNTAGGED_SOCKET;
// Byte 3: Penalty
/** {@hide} */
- public static final int PENALTY_LOG = 0x01 << 16; // normal android.util.Log
+ public static final int PENALTY_LOG = 0x01 << 16; // normal android.util.Log
/** {@hide} */
public static final int PENALTY_DIALOG = 0x02 << 16;
/** {@hide} */
@@ -271,13 +252,11 @@ public final class StrictMode {
public static final int PENALTY_DROPBOX = 0x20 << 16;
/**
- * Non-public penalty mode which overrides all the other penalty
- * bits and signals that we're in a Binder call and we should
- * ignore the other penalty bits and instead serialize back all
- * our offending stack traces to the caller to ultimately handle
- * in the originating process.
+ * Non-public penalty mode which overrides all the other penalty bits and signals that we're in
+ * a Binder call and we should ignore the other penalty bits and instead serialize back all our
+ * offending stack traces to the caller to ultimately handle in the originating process.
*
- * This must be kept in sync with the constant in libs/binder/Parcel.cpp
+ * <p>This must be kept in sync with the constant in libs/binder/Parcel.cpp
*
* @hide
*/
@@ -308,18 +287,23 @@ public final class StrictMode {
// CAUTION: we started stealing the top bits of Byte 4 for VM above
- /**
- * Mask of all the penalty bits valid for thread policies.
- */
+ /** Mask of all the penalty bits valid for thread policies. */
private static final int THREAD_PENALTY_MASK =
- PENALTY_LOG | PENALTY_DIALOG | PENALTY_DEATH | PENALTY_DROPBOX | PENALTY_GATHER |
- PENALTY_DEATH_ON_NETWORK | PENALTY_FLASH;
-
- /**
- * Mask of all the penalty bits valid for VM policies.
- */
- private static final int VM_PENALTY_MASK = PENALTY_LOG | PENALTY_DEATH | PENALTY_DROPBOX
- | PENALTY_DEATH_ON_CLEARTEXT_NETWORK | PENALTY_DEATH_ON_FILE_URI_EXPOSURE;
+ PENALTY_LOG
+ | PENALTY_DIALOG
+ | PENALTY_DEATH
+ | PENALTY_DROPBOX
+ | PENALTY_GATHER
+ | PENALTY_DEATH_ON_NETWORK
+ | PENALTY_FLASH;
+
+ /** Mask of all the penalty bits valid for VM policies. */
+ private static final int VM_PENALTY_MASK =
+ PENALTY_LOG
+ | PENALTY_DEATH
+ | PENALTY_DROPBOX
+ | PENALTY_DEATH_ON_CLEARTEXT_NETWORK
+ | PENALTY_DEATH_ON_FILE_URI_EXPOSURE;
/** {@hide} */
public static final int NETWORK_POLICY_ACCEPT = 0;
@@ -330,14 +314,16 @@ public final class StrictMode {
// TODO: wrap in some ImmutableHashMap thing.
// Note: must be before static initialization of sVmPolicy.
- private static final HashMap<Class, Integer> EMPTY_CLASS_LIMIT_MAP = new HashMap<Class, Integer>();
+ private static final HashMap<Class, Integer> EMPTY_CLASS_LIMIT_MAP =
+ new HashMap<Class, Integer>();
/**
* The current VmPolicy in effect.
*
- * TODO: these are redundant (mask is in VmPolicy). Should remove sVmPolicyMask.
+ * <p>TODO: these are redundant (mask is in VmPolicy). Should remove sVmPolicyMask.
*/
private static volatile int sVmPolicyMask = 0;
+
private static volatile VmPolicy sVmPolicy = VmPolicy.LAX;
/** {@hide} */
@@ -355,8 +341,8 @@ public final class StrictMode {
}
/**
- * The number of threads trying to do an async dropbox write.
- * Just to limit ourselves out of paranoia.
+ * The number of threads trying to do an async dropbox write. Just to limit ourselves out of
+ * paranoia.
*/
private static final AtomicInteger sDropboxCallsInFlight = new AtomicInteger(0);
@@ -365,18 +351,15 @@ public final class StrictMode {
/**
* {@link StrictMode} policy applied to a certain thread.
*
- * <p>The policy is enabled by {@link #setThreadPolicy}. The current policy
- * can be retrieved with {@link #getThreadPolicy}.
+ * <p>The policy is enabled by {@link #setThreadPolicy}. The current policy can be retrieved
+ * with {@link #getThreadPolicy}.
*
- * <p>Note that multiple penalties may be provided and they're run
- * in order from least to most severe (logging before process
- * death, for example). There's currently no mechanism to choose
+ * <p>Note that multiple penalties may be provided and they're run in order from least to most
+ * severe (logging before process death, for example). There's currently no mechanism to choose
* different penalties for different detected actions.
*/
public static final class ThreadPolicy {
- /**
- * The default, lax policy which doesn't catch anything.
- */
+ /** The default, lax policy which doesn't catch anything. */
public static final ThreadPolicy LAX = new ThreadPolicy(0);
final int mask;
@@ -391,16 +374,15 @@ public final class StrictMode {
}
/**
- * Creates {@link ThreadPolicy} instances. Methods whose names start
- * with {@code detect} specify what problems we should look
- * for. Methods whose names start with {@code penalty} specify what
- * we should do when we detect a problem.
+ * Creates {@link ThreadPolicy} instances. Methods whose names start with {@code detect}
+ * specify what problems we should look for. Methods whose names start with {@code penalty}
+ * specify what we should do when we detect a problem.
*
- * <p>You can call as many {@code detect} and {@code penalty}
- * methods as you like. Currently order is insignificant: all
- * penalties apply to all detected problems.
+ * <p>You can call as many {@code detect} and {@code penalty} methods as you like. Currently
+ * order is insignificant: all penalties apply to all detected problems.
*
* <p>For example, detect everything and log anything that's found:
+ *
* <pre>
* StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder()
* .detectAll()
@@ -413,18 +395,15 @@ public final class StrictMode {
private int mMask = 0;
/**
- * Create a Builder that detects nothing and has no
- * violations. (but note that {@link #build} will default
- * to enabling {@link #penaltyLog} if no other penalties
- * are specified)
+ * Create a Builder that detects nothing and has no violations. (but note that {@link
+ * #build} will default to enabling {@link #penaltyLog} if no other penalties are
+ * specified)
*/
public Builder() {
mMask = 0;
}
- /**
- * Initialize a Builder from an existing ThreadPolicy.
- */
+ /** Initialize a Builder from an existing ThreadPolicy. */
public Builder(ThreadPolicy policy) {
mMask = policy.mask;
}
@@ -432,8 +411,8 @@ public final class StrictMode {
/**
* Detect everything that's potentially suspect.
*
- * <p>As of the Gingerbread release this includes network and
- * disk operations but will likely expand in future releases.
+ * <p>As of the Gingerbread release this includes network and disk operations but will
+ * likely expand in future releases.
*/
public Builder detectAll() {
detectDiskReads();
@@ -453,135 +432,106 @@ public final class StrictMode {
return this;
}
- /**
- * Disable the detection of everything.
- */
+ /** Disable the detection of everything. */
public Builder permitAll() {
return disable(ALL_THREAD_DETECT_BITS);
}
- /**
- * Enable detection of network operations.
- */
+ /** Enable detection of network operations. */
public Builder detectNetwork() {
return enable(DETECT_NETWORK);
}
- /**
- * Disable detection of network operations.
- */
+ /** Disable detection of network operations. */
public Builder permitNetwork() {
return disable(DETECT_NETWORK);
}
- /**
- * Enable detection of disk reads.
- */
+ /** Enable detection of disk reads. */
public Builder detectDiskReads() {
return enable(DETECT_DISK_READ);
}
- /**
- * Disable detection of disk reads.
- */
+ /** Disable detection of disk reads. */
public Builder permitDiskReads() {
return disable(DETECT_DISK_READ);
}
- /**
- * Enable detection of slow calls.
- */
+ /** Enable detection of slow calls. */
public Builder detectCustomSlowCalls() {
return enable(DETECT_CUSTOM);
}
- /**
- * Disable detection of slow calls.
- */
+ /** Disable detection of slow calls. */
public Builder permitCustomSlowCalls() {
return disable(DETECT_CUSTOM);
}
- /**
- * Disable detection of mismatches between defined resource types
- * and getter calls.
- */
+ /** Disable detection of mismatches between defined resource types and getter calls. */
public Builder permitResourceMismatches() {
return disable(DETECT_RESOURCE_MISMATCH);
}
- /**
- * Detect unbuffered input/output operations.
- */
+ /** Detect unbuffered input/output operations. */
public Builder detectUnbufferedIo() {
return enable(DETECT_UNBUFFERED_IO);
}
- /**
- * Disable detection of unbuffered input/output operations.
- */
+ /** Disable detection of unbuffered input/output operations. */
public Builder permitUnbufferedIo() {
return disable(DETECT_UNBUFFERED_IO);
}
/**
- * Enables detection of mismatches between defined resource types
- * and getter calls.
- * <p>
- * This helps detect accidental type mismatches and potentially
- * expensive type conversions when obtaining typed resources.
- * <p>
- * For example, a strict mode violation would be thrown when
- * calling {@link android.content.res.TypedArray#getInt(int, int)}
- * on an index that contains a String-type resource. If the string
- * value can be parsed as an integer, this method call will return
- * a value without crashing; however, the developer should format
- * the resource as an integer to avoid unnecessary type conversion.
+ * Enables detection of mismatches between defined resource types and getter calls.
+ *
+ * <p>This helps detect accidental type mismatches and potentially expensive type
+ * conversions when obtaining typed resources.
+ *
+ * <p>For example, a strict mode violation would be thrown when calling {@link
+ * android.content.res.TypedArray#getInt(int, int)} on an index that contains a
+ * String-type resource. If the string value can be parsed as an integer, this method
+ * call will return a value without crashing; however, the developer should format the
+ * resource as an integer to avoid unnecessary type conversion.
*/
public Builder detectResourceMismatches() {
return enable(DETECT_RESOURCE_MISMATCH);
}
- /**
- * Enable detection of disk writes.
- */
+ /** Enable detection of disk writes. */
public Builder detectDiskWrites() {
return enable(DETECT_DISK_WRITE);
}
- /**
- * Disable detection of disk writes.
- */
+ /** Disable detection of disk writes. */
public Builder permitDiskWrites() {
return disable(DETECT_DISK_WRITE);
}
/**
- * Show an annoying dialog to the developer on detected
- * violations, rate-limited to be only a little annoying.
+ * Show an annoying dialog to the developer on detected violations, rate-limited to be
+ * only a little annoying.
*/
public Builder penaltyDialog() {
return enable(PENALTY_DIALOG);
}
/**
- * Crash the whole process on violation. This penalty runs at
- * the end of all enabled penalties so you'll still get
- * see logging or other violations before the process dies.
+ * Crash the whole process on violation. This penalty runs at the end of all enabled
+ * penalties so you'll still get see logging or other violations before the process
+ * dies.
*
- * <p>Unlike {@link #penaltyDeathOnNetwork}, this applies
- * to disk reads, disk writes, and network usage if their
- * corresponding detect flags are set.
+ * <p>Unlike {@link #penaltyDeathOnNetwork}, this applies to disk reads, disk writes,
+ * and network usage if their corresponding detect flags are set.
*/
public Builder penaltyDeath() {
return enable(PENALTY_DEATH);
}
/**
- * Crash the whole process on any network usage. Unlike
- * {@link #penaltyDeath}, this penalty runs
- * <em>before</em> anything else. You must still have
- * called {@link #detectNetwork} to enable this.
+ * Crash the whole process on any network usage. Unlike {@link #penaltyDeath}, this
+ * penalty runs <em>before</em> anything else. You must still have called {@link
+ * #detectNetwork} to enable this.
*
* <p>In the Honeycomb or later SDKs, this is on by default.
*/
@@ -589,25 +539,20 @@ public final class StrictMode {
return enable(PENALTY_DEATH_ON_NETWORK);
}
- /**
- * Flash the screen during a violation.
- */
+ /** Flash the screen during a violation. */
public Builder penaltyFlashScreen() {
return enable(PENALTY_FLASH);
}
- /**
- * Log detected violations to the system log.
- */
+ /** Log detected violations to the system log. */
public Builder penaltyLog() {
return enable(PENALTY_LOG);
}
/**
- * Enable detected violations log a stacktrace and timing data
- * to the {@link android.os.DropBoxManager DropBox} on policy
- * violation. Intended mostly for platform integrators doing
- * beta user field data collection.
+ * Enable detected violations log a stacktrace and timing data to the {@link
+ * android.os.DropBoxManager DropBox} on policy violation. Intended mostly for platform
+ * integrators doing beta user field data collection.
*/
public Builder penaltyDropBox() {
return enable(PENALTY_DROPBOX);
@@ -626,16 +571,19 @@ public final class StrictMode {
/**
* Construct the ThreadPolicy instance.
*
- * <p>Note: if no penalties are enabled before calling
- * <code>build</code>, {@link #penaltyLog} is implicitly
- * set.
+ * <p>Note: if no penalties are enabled before calling <code>build</code>, {@link
+ * #penaltyLog} is implicitly set.
*/
public ThreadPolicy build() {
// If there are detection bits set but no violation bits
// set, enable simple logging.
- if (mMask != 0 &&
- (mMask & (PENALTY_DEATH | PENALTY_LOG |
- PENALTY_DROPBOX | PENALTY_DIALOG)) == 0) {
+ if (mMask != 0
+ && (mMask
+ & (PENALTY_DEATH
+ | PENALTY_LOG
+ | PENALTY_DROPBOX
+ | PENALTY_DIALOG))
+ == 0) {
penaltyLog();
}
return new ThreadPolicy(mMask);
@@ -649,9 +597,7 @@ public final class StrictMode {
* <p>The policy is enabled by {@link #setVmPolicy}.
*/
public static final class VmPolicy {
- /**
- * The default, lax policy which doesn't catch anything.
- */
+ /** The default, lax policy which doesn't catch anything. */
public static final VmPolicy LAX = new VmPolicy(0, EMPTY_CLASS_LIMIT_MAP);
final int mask;
@@ -673,16 +619,15 @@ public final class StrictMode {
}
/**
- * Creates {@link VmPolicy} instances. Methods whose names start
- * with {@code detect} specify what problems we should look
- * for. Methods whose names start with {@code penalty} specify what
- * we should do when we detect a problem.
+ * Creates {@link VmPolicy} instances. Methods whose names start with {@code detect} specify
+ * what problems we should look for. Methods whose names start with {@code penalty} specify
+ * what we should do when we detect a problem.
*
- * <p>You can call as many {@code detect} and {@code penalty}
- * methods as you like. Currently order is insignificant: all
- * penalties apply to all detected problems.
+ * <p>You can call as many {@code detect} and {@code penalty} methods as you like. Currently
+ * order is insignificant: all penalties apply to all detected problems.
*
* <p>For example, detect everything and log anything that's found:
+ *
* <pre>
* StrictMode.VmPolicy policy = new StrictMode.VmPolicy.Builder()
* .detectAll()
@@ -694,16 +639,14 @@ public final class StrictMode {
public static final class Builder {
private int mMask;
- private HashMap<Class, Integer> mClassInstanceLimit; // null until needed
- private boolean mClassInstanceLimitNeedCow = false; // need copy-on-write
+ private HashMap<Class, Integer> mClassInstanceLimit; // null until needed
+ private boolean mClassInstanceLimitNeedCow = false; // need copy-on-write
public Builder() {
mMask = 0;
}
- /**
- * Build upon an existing VmPolicy.
- */
+ /** Build upon an existing VmPolicy. */
public Builder(VmPolicy base) {
mMask = base.mask;
mClassInstanceLimitNeedCow = true;
@@ -711,16 +654,16 @@ public final class StrictMode {
}
/**
- * Set an upper bound on how many instances of a class can be in memory
- * at once. Helps to prevent object leaks.
+ * Set an upper bound on how many instances of a class can be in memory at once. Helps
+ * to prevent object leaks.
*/
public Builder setClassInstanceLimit(Class klass, int instanceLimit) {
if (klass == null) {
throw new NullPointerException("klass == null");
}
if (mClassInstanceLimitNeedCow) {
- if (mClassInstanceLimit.containsKey(klass) &&
- mClassInstanceLimit.get(klass) == instanceLimit) {
+ if (mClassInstanceLimit.containsKey(klass)
+ && mClassInstanceLimit.get(klass) == instanceLimit) {
// no-op; don't break COW
return this;
}
@@ -734,9 +677,7 @@ public final class StrictMode {
return this;
}
- /**
- * Detect leaks of {@link android.app.Activity} subclasses.
- */
+ /** Detect leaks of {@link android.app.Activity} subclasses. */
public Builder detectActivityLeaks() {
return enable(DETECT_VM_ACTIVITY_LEAKS);
}
@@ -744,9 +685,8 @@ public final class StrictMode {
/**
* Detect everything that's potentially suspect.
*
- * <p>In the Honeycomb release this includes leaks of
- * SQLite cursors, Activities, and other closable objects
- * but will likely expand in future releases.
+ * <p>In the Honeycomb release this includes leaks of SQLite cursors, Activities, and
+ * other closable objects but will likely expand in future releases.
*/
public Builder detectAll() {
detectLeakedSqlLiteObjects();
@@ -777,53 +717,46 @@ public final class StrictMode {
}
/**
- * Detect when an
- * {@link android.database.sqlite.SQLiteCursor} or other
- * SQLite object is finalized without having been closed.
+ * Detect when an {@link android.database.sqlite.SQLiteCursor} or other SQLite object is
+ * finalized without having been closed.
*
- * <p>You always want to explicitly close your SQLite
- * cursors to avoid unnecessary database contention and
- * temporary memory leaks.
+ * <p>You always want to explicitly close your SQLite cursors to avoid unnecessary
+ * database contention and temporary memory leaks.
*/
public Builder detectLeakedSqlLiteObjects() {
return enable(DETECT_VM_CURSOR_LEAKS);
}
/**
- * Detect when an {@link java.io.Closeable} or other
- * object with a explict termination method is finalized
- * without having been closed.
+ * Detect when an {@link java.io.Closeable} or other object with a explict termination
+ * method is finalized without having been closed.
*
- * <p>You always want to explicitly close such objects to
- * avoid unnecessary resources leaks.
+ * <p>You always want to explicitly close such objects to avoid unnecessary resources
+ * leaks.
*/
public Builder detectLeakedClosableObjects() {
return enable(DETECT_VM_CLOSABLE_LEAKS);
}
/**
- * Detect when a {@link BroadcastReceiver} or
- * {@link ServiceConnection} is leaked during {@link Context}
- * teardown.
+ * Detect when a {@link BroadcastReceiver} or {@link ServiceConnection} is leaked during
+ * {@link Context} teardown.
*/
public Builder detectLeakedRegistrationObjects() {
return enable(DETECT_VM_REGISTRATION_LEAKS);
}
/**
- * Detect when the calling application exposes a {@code file://}
- * {@link android.net.Uri} to another app.
- * <p>
- * This exposure is discouraged since the receiving app may not have
- * access to the shared path. For example, the receiving app may not
- * have requested the
- * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} runtime
- * permission, or the platform may be sharing the
- * {@link android.net.Uri} across user profile boundaries.
- * <p>
- * Instead, apps should use {@code content://} Uris so the platform
- * can extend temporary permission for the receiving app to access
- * the resource.
+ * Detect when the calling application exposes a {@code file://} {@link android.net.Uri}
+ * to another app.
+ *
+ * <p>This exposure is discouraged since the receiving app may not have access to the
+ * shared path. For example, the receiving app may not have requested the {@link
+ * android.Manifest.permission#READ_EXTERNAL_STORAGE} runtime permission, or the
+ * platform may be sharing the {@link android.net.Uri} across user profile boundaries.
+ *
+ * <p>Instead, apps should use {@code content://} Uris so the platform can extend
+ * temporary permission for the receiving app to access the resource.
*
* @see android.support.v4.content.FileProvider
* @see Intent#FLAG_GRANT_READ_URI_PERMISSION
@@ -833,34 +766,32 @@ public final class StrictMode {
}
/**
- * Detect any network traffic from the calling app which is not
- * wrapped in SSL/TLS. This can help you detect places that your app
- * is inadvertently sending cleartext data across the network.
- * <p>
- * Using {@link #penaltyDeath()} or
- * {@link #penaltyDeathOnCleartextNetwork()} will block further
- * traffic on that socket to prevent accidental data leakage, in
- * addition to crashing your process.
- * <p>
- * Using {@link #penaltyDropBox()} will log the raw contents of the
- * packet that triggered the violation.
- * <p>
- * This inspects both IPv4/IPv6 and TCP/UDP network traffic, but it
- * may be subject to false positives, such as when STARTTLS
- * protocols or HTTP proxies are used.
+ * Detect any network traffic from the calling app which is not wrapped in SSL/TLS. This
+ * can help you detect places that your app is inadvertently sending cleartext data
+ * across the network.
+ *
+ * <p>Using {@link #penaltyDeath()} or {@link #penaltyDeathOnCleartextNetwork()} will
+ * block further traffic on that socket to prevent accidental data leakage, in addition
+ * to crashing your process.
+ *
+ * <p>Using {@link #penaltyDropBox()} will log the raw contents of the packet that
+ * triggered the violation.
+ *
+ * <p>This inspects both IPv4/IPv6 and TCP/UDP network traffic, but it may be subject to
+ * false positives, such as when STARTTLS protocols or HTTP proxies are used.
*/
public Builder detectCleartextNetwork() {
return enable(DETECT_VM_CLEARTEXT_NETWORK);
}
/**
- * Detect when the calling application sends a {@code content://}
- * {@link android.net.Uri} to another app without setting
- * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION} or
- * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION}.
- * <p>
- * Forgetting to include one or more of these flags when sending an
- * intent is typically an app bug.
+ * Detect when the calling application sends a {@code content://} {@link
+ * android.net.Uri} to another app without setting {@link
+ * Intent#FLAG_GRANT_READ_URI_PERMISSION} or {@link
+ * Intent#FLAG_GRANT_WRITE_URI_PERMISSION}.
+ *
+ * <p>Forgetting to include one or more of these flags when sending an intent is
+ * typically an app bug.
*
* @see Intent#FLAG_GRANT_READ_URI_PERMISSION
* @see Intent#FLAG_GRANT_WRITE_URI_PERMISSION
@@ -870,12 +801,11 @@ public final class StrictMode {
}
/**
- * Detect any sockets in the calling app which have not been tagged
- * using {@link TrafficStats}. Tagging sockets can help you
- * investigate network usage inside your app, such as a narrowing
- * down heavy usage to a specific library or component.
- * <p>
- * This currently does not detect sockets created in native code.
+ * Detect any sockets in the calling app which have not been tagged using {@link
+ * TrafficStats}. Tagging sockets can help you investigate network usage inside your
+ * app, such as a narrowing down heavy usage to a specific library or component.
+ *
+ * <p>This currently does not detect sockets created in native code.
*
* @see TrafficStats#setThreadStatsTag(int)
* @see TrafficStats#tagSocket(java.net.Socket)
@@ -886,17 +816,16 @@ public final class StrictMode {
}
/**
- * Crashes the whole process on violation. This penalty runs at the
- * end of all enabled penalties so you'll still get your logging or
- * other violations before the process dies.
+ * Crashes the whole process on violation. This penalty runs at the end of all enabled
+ * penalties so you'll still get your logging or other violations before the process
+ * dies.
*/
public Builder penaltyDeath() {
return enable(PENALTY_DEATH);
}
/**
- * Crashes the whole process when cleartext network traffic is
- * detected.
+ * Crashes the whole process when cleartext network traffic is detected.
*
* @see #detectCleartextNetwork()
*/
@@ -905,8 +834,8 @@ public final class StrictMode {
}
/**
- * Crashes the whole process when a {@code file://}
- * {@link android.net.Uri} is exposed beyond this app.
+ * Crashes the whole process when a {@code file://} {@link android.net.Uri} is exposed
+ * beyond this app.
*
* @see #detectFileUriExposure()
*/
@@ -914,18 +843,15 @@ public final class StrictMode {
return enable(PENALTY_DEATH_ON_FILE_URI_EXPOSURE);
}
- /**
- * Log detected violations to the system log.
- */
+ /** Log detected violations to the system log. */
public Builder penaltyLog() {
return enable(PENALTY_LOG);
}
/**
- * Enable detected violations log a stacktrace and timing data
- * to the {@link android.os.DropBoxManager DropBox} on policy
- * violation. Intended mostly for platform integrators doing
- * beta user field data collection.
+ * Enable detected violations log a stacktrace and timing data to the {@link
+ * android.os.DropBoxManager DropBox} on policy violation. Intended mostly for platform
+ * integrators doing beta user field data collection.
*/
public Builder penaltyDropBox() {
return enable(PENALTY_DROPBOX);
@@ -944,48 +870,51 @@ public final class StrictMode {
/**
* Construct the VmPolicy instance.
*
- * <p>Note: if no penalties are enabled before calling
- * <code>build</code>, {@link #penaltyLog} is implicitly
- * set.
+ * <p>Note: if no penalties are enabled before calling <code>build</code>, {@link
+ * #penaltyLog} is implicitly set.
*/
public VmPolicy build() {
// If there are detection bits set but no violation bits
// set, enable simple logging.
- if (mMask != 0 &&
- (mMask & (PENALTY_DEATH | PENALTY_LOG |
- PENALTY_DROPBOX | PENALTY_DIALOG)) == 0) {
+ if (mMask != 0
+ && (mMask
+ & (PENALTY_DEATH
+ | PENALTY_LOG
+ | PENALTY_DROPBOX
+ | PENALTY_DIALOG))
+ == 0) {
penaltyLog();
}
- return new VmPolicy(mMask,
+ return new VmPolicy(
+ mMask,
mClassInstanceLimit != null ? mClassInstanceLimit : EMPTY_CLASS_LIMIT_MAP);
}
}
}
/**
- * Log of strict mode violation stack traces that have occurred
- * during a Binder call, to be serialized back later to the caller
- * via Parcel.writeNoException() (amusingly) where the caller can
- * choose how to react.
+ * Log of strict mode violation stack traces that have occurred during a Binder call, to be
+ * serialized back later to the caller via Parcel.writeNoException() (amusingly) where the
+ * caller can choose how to react.
*/
private static final ThreadLocal<ArrayList<ViolationInfo>> gatheredViolations =
new ThreadLocal<ArrayList<ViolationInfo>>() {
- @Override protected ArrayList<ViolationInfo> initialValue() {
- // Starts null to avoid unnecessary allocations when
- // checking whether there are any violations or not in
- // hasGatheredViolations() below.
- return null;
- }
- };
+ @Override
+ protected ArrayList<ViolationInfo> initialValue() {
+ // Starts null to avoid unnecessary allocations when
+ // checking whether there are any violations or not in
+ // hasGatheredViolations() below.
+ return null;
+ }
+ };
/**
- * Sets the policy for what actions on the current thread should
- * be detected, as well as the penalty if such actions occur.
+ * Sets the policy for what actions on the current thread should be detected, as well as the
+ * penalty if such actions occur.
*
- * <p>Internally this sets a thread-local variable which is
- * propagated across cross-process IPC calls, meaning you can
- * catch violations when a system service or another process
- * accesses the disk or network on your behalf.
+ * <p>Internally this sets a thread-local variable which is propagated across cross-process IPC
+ * calls, meaning you can catch violations when a system service or another process accesses the
+ * disk or network on your behalf.
*
* @param policy the policy to put into place
*/
@@ -1016,7 +945,7 @@ public final class StrictMode {
if (policy instanceof AndroidBlockGuardPolicy) {
androidPolicy = (AndroidBlockGuardPolicy) policy;
} else {
- androidPolicy = threadAndroidPolicy.get();
+ androidPolicy = THREAD_ANDROID_POLICY.get();
BlockGuard.setThreadPolicy(androidPolicy);
}
androidPolicy.setPolicyMask(policyMask);
@@ -1030,63 +959,49 @@ public final class StrictMode {
CloseGuard.setEnabled(enabled);
}
- /**
- * @hide
- */
+ /** @hide */
public static class StrictModeViolation extends BlockGuard.BlockGuardPolicyException {
public StrictModeViolation(int policyState, int policyViolated, String message) {
super(policyState, policyViolated, message);
}
}
- /**
- * @hide
- */
+ /** @hide */
public static class StrictModeNetworkViolation extends StrictModeViolation {
public StrictModeNetworkViolation(int policyMask) {
super(policyMask, DETECT_NETWORK, null);
}
}
- /**
- * @hide
- */
+ /** @hide */
private static class StrictModeDiskReadViolation extends StrictModeViolation {
public StrictModeDiskReadViolation(int policyMask) {
super(policyMask, DETECT_DISK_READ, null);
}
}
- /**
- * @hide
- */
- private static class StrictModeDiskWriteViolation extends StrictModeViolation {
+ /** @hide */
+ private static class StrictModeDiskWriteViolation extends StrictModeViolation {
public StrictModeDiskWriteViolation(int policyMask) {
super(policyMask, DETECT_DISK_WRITE, null);
}
}
- /**
- * @hide
- */
+ /** @hide */
private static class StrictModeCustomViolation extends StrictModeViolation {
public StrictModeCustomViolation(int policyMask, String name) {
super(policyMask, DETECT_CUSTOM, name);
}
}
- /**
- * @hide
- */
+ /** @hide */
private static class StrictModeResourceMismatchViolation extends StrictModeViolation {
public StrictModeResourceMismatchViolation(int policyMask, Object tag) {
super(policyMask, DETECT_RESOURCE_MISMATCH, tag != null ? tag.toString() : null);
}
}
- /**
- * @hide
- */
+ /** @hide */
private static class StrictModeUnbufferedIOViolation extends StrictModeViolation {
public StrictModeUnbufferedIOViolation(int policyMask) {
super(policyMask, DETECT_UNBUFFERED_IO, null);
@@ -1097,16 +1012,13 @@ public final class StrictMode {
* Returns the bitmask of the current thread's policy.
*
* @return the bitmask of all the DETECT_* and PENALTY_* bits currently enabled
- *
* @hide
*/
public static int getThreadPolicyMask() {
return BlockGuard.getThreadPolicy().getPolicyMask();
}
- /**
- * Returns the current thread's policy.
- */
+ /** Returns the current thread's policy. */
public static ThreadPolicy getThreadPolicy() {
// TODO: this was a last minute Gingerbread API change (to
// introduce VmPolicy cleanly) but this isn't particularly
@@ -1116,14 +1028,13 @@ public final class StrictMode {
}
/**
- * A convenience wrapper that takes the current
- * {@link ThreadPolicy} from {@link #getThreadPolicy}, modifies it
- * to permit both disk reads &amp; writes, and sets the new policy
- * with {@link #setThreadPolicy}, returning the old policy so you
- * can restore it at the end of a block.
+ * A convenience wrapper that takes the current {@link ThreadPolicy} from {@link
+ * #getThreadPolicy}, modifies it to permit both disk reads &amp; writes, and sets the new
+ * policy with {@link #setThreadPolicy}, returning the old policy so you can restore it at the
+ * end of a block.
*
- * @return the old policy, to be passed to {@link #setThreadPolicy} to
- * restore the policy at the end of a block
+ * @return the old policy, to be passed to {@link #setThreadPolicy} to restore the policy at the
+ * end of a block
*/
public static ThreadPolicy allowThreadDiskWrites() {
int oldPolicyMask = getThreadPolicyMask();
@@ -1135,14 +1046,11 @@ public final class StrictMode {
}
/**
- * A convenience wrapper that takes the current
- * {@link ThreadPolicy} from {@link #getThreadPolicy}, modifies it
- * to permit disk reads, and sets the new policy
- * with {@link #setThreadPolicy}, returning the old policy so you
- * can restore it at the end of a block.
+ * A convenience wrapper that takes the current {@link ThreadPolicy} from {@link
+ * #getThreadPolicy}, modifies it to permit disk reads, and sets the new policy with {@link
+ * #setThreadPolicy}, returning the old policy so you can restore it at the end of a block.
*
- * @return the old policy, to be passed to setThreadPolicy to
- * restore the policy.
+ * @return the old policy, to be passed to setThreadPolicy to restore the policy.
*/
public static ThreadPolicy allowThreadDiskReads() {
int oldPolicyMask = getThreadPolicyMask();
@@ -1182,8 +1090,8 @@ public final class StrictMode {
* @hide
*/
public static boolean conditionallyEnableDebugLogging() {
- boolean doFlashes = SystemProperties.getBoolean(VISUAL_PROPERTY, false)
- && !amTheSystemServerProcess();
+ boolean doFlashes =
+ SystemProperties.getBoolean(VISUAL_PROPERTY, false) && !amTheSystemServerProcess();
final boolean suppress = SystemProperties.getBoolean(DISABLE_PROPERTY, false);
// For debug builds, log event loop stalls to dropbox for analysis.
@@ -1201,9 +1109,10 @@ public final class StrictMode {
}
// Thread policy controls BlockGuard.
- int threadPolicyMask = StrictMode.DETECT_DISK_WRITE |
- StrictMode.DETECT_DISK_READ |
- StrictMode.DETECT_NETWORK;
+ int threadPolicyMask =
+ StrictMode.DETECT_DISK_WRITE
+ | StrictMode.DETECT_DISK_READ
+ | StrictMode.DETECT_NETWORK;
if (!Build.IS_USER) {
threadPolicyMask |= StrictMode.PENALTY_DROPBOX;
@@ -1243,8 +1152,7 @@ public final class StrictMode {
}
/**
- * Used by the framework to make network usage on the main
- * thread a fatal error.
+ * Used by the framework to make network usage on the main thread a fatal error.
*
* @hide
*/
@@ -1264,8 +1172,8 @@ public final class StrictMode {
}
/**
- * Used by lame internal apps that haven't done the hard work to get
- * themselves off file:// Uris yet.
+ * Used by lame internal apps that haven't done the hard work to get themselves off file:// Uris
+ * yet.
*
* @hide
*/
@@ -1274,17 +1182,14 @@ public final class StrictMode {
}
/**
- * Parses the BlockGuard policy mask out from the Exception's
- * getMessage() String value. Kinda gross, but least
- * invasive. :/
+ * Parses the BlockGuard policy mask out from the Exception's getMessage() String value. Kinda
+ * gross, but least invasive. :/
*
- * Input is of the following forms:
- * "policy=137 violation=64"
- * "policy=137 violation=64 msg=Arbitrary text"
+ * <p>Input is of the following forms: "policy=137 violation=64" "policy=137 violation=64
+ * msg=Arbitrary text"
*
- * Returns 0 on failure, which is a valid policy, but not a
- * valid policy during a violation (else there must've been
- * some policy in effect to violate).
+ * <p>Returns 0 on failure, which is a valid policy, but not a valid policy during a violation
+ * (else there must've been some policy in effect to violate).
*/
private static int parsePolicyFromMessage(String message) {
if (message == null || !message.startsWith("policy=")) {
@@ -1302,9 +1207,7 @@ public final class StrictMode {
}
}
- /**
- * Like parsePolicyFromMessage(), but returns the violation.
- */
+ /** Like parsePolicyFromMessage(), but returns the violation. */
private static int parseViolationFromMessage(String message) {
if (message == null) {
return 0;
@@ -1328,25 +1231,28 @@ public final class StrictMode {
private static final ThreadLocal<ArrayList<ViolationInfo>> violationsBeingTimed =
new ThreadLocal<ArrayList<ViolationInfo>>() {
- @Override protected ArrayList<ViolationInfo> initialValue() {
- return new ArrayList<ViolationInfo>();
- }
- };
+ @Override
+ protected ArrayList<ViolationInfo> initialValue() {
+ return new ArrayList<ViolationInfo>();
+ }
+ };
// Note: only access this once verifying the thread has a Looper.
- private static final ThreadLocal<Handler> threadHandler = new ThreadLocal<Handler>() {
- @Override protected Handler initialValue() {
- return new Handler();
- }
- };
+ private static final ThreadLocal<Handler> THREAD_HANDLER =
+ new ThreadLocal<Handler>() {
+ @Override
+ protected Handler initialValue() {
+ return new Handler();
+ }
+ };
- private static final ThreadLocal<AndroidBlockGuardPolicy>
- threadAndroidPolicy = new ThreadLocal<AndroidBlockGuardPolicy>() {
- @Override
- protected AndroidBlockGuardPolicy initialValue() {
- return new AndroidBlockGuardPolicy(0);
- }
- };
+ private static final ThreadLocal<AndroidBlockGuardPolicy> THREAD_ANDROID_POLICY =
+ new ThreadLocal<AndroidBlockGuardPolicy>() {
+ @Override
+ protected AndroidBlockGuardPolicy initialValue() {
+ return new AndroidBlockGuardPolicy(0);
+ }
+ };
private static boolean tooManyViolationsThisLoop() {
return violationsBeingTimed.get().size() >= MAX_OFFENSES_PER_LOOP;
@@ -1395,7 +1301,8 @@ public final class StrictMode {
if (tooManyViolationsThisLoop()) {
return;
}
- BlockGuard.BlockGuardPolicyException e = new StrictModeCustomViolation(mPolicyMask, name);
+ BlockGuard.BlockGuardPolicyException e =
+ new StrictModeCustomViolation(mPolicyMask, name);
e.fillInStackTrace();
startHandlingViolationException(e);
}
@@ -1496,9 +1403,8 @@ public final class StrictMode {
//
// TODO: if in gather mode, ignore Looper.myLooper() and always
// go into this immediate mode?
- if (looper == null ||
- (info.policy & THREAD_PENALTY_MASK) == PENALTY_DEATH) {
- info.durationMillis = -1; // unknown (redundant, already set)
+ if (looper == null || (info.policy & THREAD_PENALTY_MASK) == PENALTY_DEATH) {
+ info.durationMillis = -1; // unknown (redundant, already set)
handleViolation(info);
return;
}
@@ -1516,8 +1422,8 @@ public final class StrictMode {
return;
}
- final IWindowManager windowManager = (info.policy & PENALTY_FLASH) != 0 ?
- sWindowManager.get() : null;
+ final IWindowManager windowManager =
+ (info.policy & PENALTY_FLASH) != 0 ? sWindowManager.get() : null;
if (windowManager != null) {
try {
windowManager.showStrictModeViolation(true);
@@ -1534,31 +1440,34 @@ public final class StrictMode {
// throttled back to 60fps via SurfaceFlinger/View
// invalidates, _not_ by posting frame updates every 16
// milliseconds.
- threadHandler.get().postAtFrontOfQueue(new Runnable() {
- public void run() {
- long loopFinishTime = SystemClock.uptimeMillis();
-
- // Note: we do this early, before handling the
- // violation below, as handling the violation
- // may include PENALTY_DEATH and we don't want
- // to keep the red border on.
- if (windowManager != null) {
- try {
- windowManager.showStrictModeViolation(false);
- } catch (RemoteException unused) {
- }
- }
-
- for (int n = 0; n < records.size(); ++n) {
- ViolationInfo v = records.get(n);
- v.violationNumThisLoop = n + 1;
- v.durationMillis =
- (int) (loopFinishTime - v.violationUptimeMillis);
- handleViolation(v);
- }
- records.clear();
- }
- });
+ THREAD_HANDLER
+ .get()
+ .postAtFrontOfQueue(
+ new Runnable() {
+ public void run() {
+ long loopFinishTime = SystemClock.uptimeMillis();
+
+ // Note: we do this early, before handling the
+ // violation below, as handling the violation
+ // may include PENALTY_DEATH and we don't want
+ // to keep the red border on.
+ if (windowManager != null) {
+ try {
+ windowManager.showStrictModeViolation(false);
+ } catch (RemoteException unused) {
+ }
+ }
+
+ for (int n = 0; n < records.size(); ++n) {
+ ViolationInfo v = records.get(n);
+ v.violationNumThisLoop = n + 1;
+ v.durationMillis =
+ (int) (loopFinishTime - v.violationUptimeMillis);
+ handleViolation(v);
+ }
+ records.clear();
+ }
+ });
}
// Note: It's possible (even quite likely) that the
@@ -1603,17 +1512,21 @@ public final class StrictMode {
}
long now = SystemClock.uptimeMillis();
mLastViolationTime.put(crashFingerprint, now);
- long timeSinceLastViolationMillis = lastViolationTime == 0 ?
- Long.MAX_VALUE : (now - lastViolationTime);
+ long timeSinceLastViolationMillis =
+ lastViolationTime == 0 ? Long.MAX_VALUE : (now - lastViolationTime);
if ((info.policy & PENALTY_LOG) != 0 && sListener != null) {
sListener.onViolation(info.crashInfo.stackTrace);
}
- if ((info.policy & PENALTY_LOG) != 0 &&
- timeSinceLastViolationMillis > MIN_LOG_INTERVAL_MS) {
+ if ((info.policy & PENALTY_LOG) != 0
+ && timeSinceLastViolationMillis > MIN_LOG_INTERVAL_MS) {
if (info.durationMillis != -1) {
- Log.d(TAG, "StrictMode policy violation; ~duration=" +
- info.durationMillis + " ms: " + info.crashInfo.stackTrace);
+ Log.d(
+ TAG,
+ "StrictMode policy violation; ~duration="
+ + info.durationMillis
+ + " ms: "
+ + info.crashInfo.stackTrace);
} else {
Log.d(TAG, "StrictMode policy violation: " + info.crashInfo.stackTrace);
}
@@ -1625,8 +1538,8 @@ public final class StrictMode {
// by the ActivityManagerService remaining set.
int violationMaskSubset = 0;
- if ((info.policy & PENALTY_DIALOG) != 0 &&
- timeSinceLastViolationMillis > MIN_DIALOG_INTERVAL_MS) {
+ if ((info.policy & PENALTY_DIALOG) != 0
+ && timeSinceLastViolationMillis > MIN_DIALOG_INTERVAL_MS) {
violationMaskSubset |= PENALTY_DIALOG;
}
@@ -1659,10 +1572,9 @@ public final class StrictMode {
// We restore the current policy below, in the finally block.
setThreadPolicyMask(0);
- ActivityManager.getService().handleApplicationStrictModeViolation(
- RuntimeInit.getApplicationObject(),
- violationMaskSubset,
- info);
+ ActivityManager.getService()
+ .handleApplicationStrictModeViolation(
+ RuntimeInit.getApplicationObject(), violationMaskSubset, info);
} catch (RemoteException e) {
if (e instanceof DeadObjectException) {
// System process is dead; ignore
@@ -1687,12 +1599,10 @@ public final class StrictMode {
}
/**
- * In the common case, as set by conditionallyEnableDebugLogging,
- * we're just dropboxing any violations but not showing a dialog,
- * not loggging, and not killing the process. In these cases we
- * don't need to do a synchronous call to the ActivityManager.
- * This is used by both per-thread and vm-wide violations when
- * applicable.
+ * In the common case, as set by conditionallyEnableDebugLogging, we're just dropboxing any
+ * violations but not showing a dialog, not loggging, and not killing the process. In these
+ * cases we don't need to do a synchronous call to the ActivityManager. This is used by both
+ * per-thread and vm-wide violations when applicable.
*/
private static void dropboxViolationAsync(
final int violationMaskSubset, final ViolationInfo info) {
@@ -1715,9 +1625,7 @@ public final class StrictMode {
Log.d(TAG, "No activity manager; failed to Dropbox violation.");
} else {
am.handleApplicationStrictModeViolation(
- RuntimeInit.getApplicationObject(),
- violationMaskSubset,
- info);
+ RuntimeInit.getApplicationObject(), violationMaskSubset, info);
}
} catch (RemoteException e) {
if (e instanceof DeadObjectException) {
@@ -1738,25 +1646,20 @@ public final class StrictMode {
}
}
- /**
- * Called from Parcel.writeNoException()
- */
+ /** Called from Parcel.writeNoException() */
/* package */ static boolean hasGatheredViolations() {
return gatheredViolations.get() != null;
}
/**
- * Called from Parcel.writeException(), so we drop this memory and
- * don't incorrectly attribute it to the wrong caller on the next
- * Binder call on this thread.
+ * Called from Parcel.writeException(), so we drop this memory and don't incorrectly attribute
+ * it to the wrong caller on the next Binder call on this thread.
*/
/* package */ static void clearGatheredViolations() {
gatheredViolations.set(null);
}
- /**
- * @hide
- */
+ /** @hide */
public static void conditionallyCheckInstanceCounts() {
VmPolicy policy = getVmPolicy();
int policySize = policy.classInstanceLimit.size();
@@ -1784,7 +1687,7 @@ public final class StrictMode {
}
private static long sLastInstanceCountCheckMillis = 0;
- private static boolean sIsIdlerRegistered = false; // guarded by StrictMode.class
+ private static boolean sIsIdlerRegistered = false; // guarded by StrictMode.class
private static final MessageQueue.IdleHandler sProcessIdleHandler =
new MessageQueue.IdleHandler() {
public boolean queueIdle() {
@@ -1798,9 +1701,8 @@ public final class StrictMode {
};
/**
- * Sets the policy for what actions in the VM process (on any
- * thread) should be detected, as well as the penalty if such
- * actions occur.
+ * Sets the policy for what actions in the VM process (on any thread) should be detected, as
+ * well as the penalty if such actions occur.
*
* @param policy the policy to put into place
*/
@@ -1813,8 +1715,8 @@ public final class StrictMode {
Looper looper = Looper.getMainLooper();
if (looper != null) {
MessageQueue mq = looper.mQueue;
- if (policy.classInstanceLimit.size() == 0 ||
- (sVmPolicyMask & VM_PENALTY_MASK) == 0) {
+ if (policy.classInstanceLimit.size() == 0
+ || (sVmPolicyMask & VM_PENALTY_MASK) == 0) {
mq.removeIdleHandler(sProcessIdleHandler);
sIsIdlerRegistered = false;
} else if (!sIsIdlerRegistered) {
@@ -1833,8 +1735,9 @@ public final class StrictMode {
}
}
- final INetworkManagementService netd = INetworkManagementService.Stub.asInterface(
- ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
+ final INetworkManagementService netd =
+ INetworkManagementService.Stub.asInterface(
+ ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
if (netd != null) {
try {
netd.setUidCleartextNetworkPolicy(android.os.Process.myUid(), networkPolicy);
@@ -1846,9 +1749,7 @@ public final class StrictMode {
}
}
- /**
- * Gets the current VM policy.
- */
+ /** Gets the current VM policy. */
public static VmPolicy getVmPolicy() {
synchronized (StrictMode.class) {
return sVmPolicy;
@@ -1858,102 +1759,72 @@ public final class StrictMode {
/**
* Enable the recommended StrictMode defaults, with violations just being logged.
*
- * <p>This catches disk and network access on the main thread, as
- * well as leaked SQLite cursors and unclosed resources. This is
- * simply a wrapper around {@link #setVmPolicy} and {@link
+ * <p>This catches disk and network access on the main thread, as well as leaked SQLite cursors
+ * and unclosed resources. This is simply a wrapper around {@link #setVmPolicy} and {@link
* #setThreadPolicy}.
*/
public static void enableDefaults() {
- StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
- .detectAll()
- .penaltyLog()
- .build());
- StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
- .detectAll()
- .penaltyLog()
- .build());
+ StrictMode.setThreadPolicy(
+ new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
+ StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build());
}
- /**
- * @hide
- */
+ /** @hide */
public static boolean vmSqliteObjectLeaksEnabled() {
return (sVmPolicyMask & DETECT_VM_CURSOR_LEAKS) != 0;
}
- /**
- * @hide
- */
+ /** @hide */
public static boolean vmClosableObjectLeaksEnabled() {
return (sVmPolicyMask & DETECT_VM_CLOSABLE_LEAKS) != 0;
}
- /**
- * @hide
- */
+ /** @hide */
public static boolean vmRegistrationLeaksEnabled() {
return (sVmPolicyMask & DETECT_VM_REGISTRATION_LEAKS) != 0;
}
- /**
- * @hide
- */
+ /** @hide */
public static boolean vmFileUriExposureEnabled() {
return (sVmPolicyMask & DETECT_VM_FILE_URI_EXPOSURE) != 0;
}
- /**
- * @hide
- */
+ /** @hide */
public static boolean vmCleartextNetworkEnabled() {
return (sVmPolicyMask & DETECT_VM_CLEARTEXT_NETWORK) != 0;
}
- /**
- * @hide
- */
+ /** @hide */
public static boolean vmContentUriWithoutPermissionEnabled() {
return (sVmPolicyMask & DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION) != 0;
}
- /**
- * @hide
- */
+ /** @hide */
public static boolean vmUntaggedSocketEnabled() {
return (sVmPolicyMask & DETECT_VM_UNTAGGED_SOCKET) != 0;
}
- /**
- * @hide
- */
+ /** @hide */
public static void onSqliteObjectLeaked(String message, Throwable originStack) {
onVmPolicyViolation(message, originStack);
}
- /**
- * @hide
- */
+ /** @hide */
public static void onWebViewMethodCalledOnWrongThread(Throwable originStack) {
onVmPolicyViolation(null, originStack);
}
- /**
- * @hide
- */
+ /** @hide */
public static void onIntentReceiverLeaked(Throwable originStack) {
onVmPolicyViolation(null, originStack);
}
- /**
- * @hide
- */
+ /** @hide */
public static void onServiceConnectionLeaked(Throwable originStack) {
onVmPolicyViolation(null, originStack);
}
- /**
- * @hide
- */
+ /** @hide */
public static void onFileUriExposed(Uri uri, String location) {
final String message = uri + " exposed beyond app through " + location;
if ((sVmPolicyMask & PENALTY_DEATH_ON_FILE_URI_EXPOSURE) != 0) {
@@ -1963,19 +1834,18 @@ public final class StrictMode {
}
}
- /**
- * @hide
- */
+ /** @hide */
public static void onContentUriWithoutPermission(Uri uri, String location) {
- final String message = uri + " exposed beyond app through " + location
- + " without permission grant flags; did you forget"
- + " FLAG_GRANT_READ_URI_PERMISSION?";
+ final String message =
+ uri
+ + " exposed beyond app through "
+ + location
+ + " without permission grant flags; did you forget"
+ + " FLAG_GRANT_READ_URI_PERMISSION?";
onVmPolicyViolation(null, new Throwable(message));
}
- /**
- * @hide
- */
+ /** @hide */
public static void onCleartextNetworkDetected(byte[] firstPacket) {
byte[] rawAddr = null;
if (firstPacket != null) {
@@ -1994,40 +1864,40 @@ public final class StrictMode {
String msg = "Detected cleartext network traffic from UID " + uid;
if (rawAddr != null) {
try {
- msg = "Detected cleartext network traffic from UID " + uid + " to "
- + InetAddress.getByAddress(rawAddr);
+ msg =
+ "Detected cleartext network traffic from UID "
+ + uid
+ + " to "
+ + InetAddress.getByAddress(rawAddr);
} catch (UnknownHostException ignored) {
}
}
final boolean forceDeath = (sVmPolicyMask & PENALTY_DEATH_ON_CLEARTEXT_NETWORK) != 0;
- onVmPolicyViolation(HexDump.dumpHexString(firstPacket).trim(), new Throwable(msg),
- forceDeath);
+ onVmPolicyViolation(
+ HexDump.dumpHexString(firstPacket).trim(), new Throwable(msg), forceDeath);
}
- /**
- * @hide
- */
+ /** @hide */
public static void onUntaggedSocket() {
- onVmPolicyViolation(null, new Throwable("Untagged socket detected; use"
- + " TrafficStats.setThreadSocketTag() to track all network usage"));
+ onVmPolicyViolation(
+ null,
+ new Throwable(
+ "Untagged socket detected; use"
+ + " TrafficStats.setThreadSocketTag() to track all network usage"));
}
// Map from VM violation fingerprint to uptime millis.
private static final HashMap<Integer, Long> sLastVmViolationTime = new HashMap<Integer, Long>();
- /**
- * @hide
- */
+ /** @hide */
public static void onVmPolicyViolation(String message, Throwable originStack) {
onVmPolicyViolation(message, originStack, false);
}
- /**
- * @hide
- */
- public static void onVmPolicyViolation(String message, Throwable originStack,
- boolean forceDeath) {
+ /** @hide */
+ public static void onVmPolicyViolation(
+ String message, Throwable originStack, boolean forceDeath) {
final boolean penaltyDropbox = (sVmPolicyMask & PENALTY_DROPBOX) != 0;
final boolean penaltyDeath = ((sVmPolicyMask & PENALTY_DEATH) != 0) || forceDeath;
final boolean penaltyLog = (sVmPolicyMask & PENALTY_LOG) != 0;
@@ -2082,10 +1952,9 @@ public final class StrictMode {
// We restore the current policy below, in the finally block.
setThreadPolicyMask(0);
- ActivityManager.getService().handleApplicationStrictModeViolation(
- RuntimeInit.getApplicationObject(),
- violationMaskSubset,
- info);
+ ActivityManager.getService()
+ .handleApplicationStrictModeViolation(
+ RuntimeInit.getApplicationObject(), violationMaskSubset, info);
} catch (RemoteException e) {
if (e instanceof DeadObjectException) {
// System process is dead; ignore
@@ -2105,9 +1974,7 @@ public final class StrictMode {
}
}
- /**
- * Called from Parcel.writeNoException()
- */
+ /** Called from Parcel.writeNoException() */
/* package */ static void writeGatheredViolationsToParcel(Parcel p) {
ArrayList<ViolationInfo> violations = gatheredViolations.get();
if (violations == null) {
@@ -2128,8 +1995,8 @@ public final class StrictMode {
private static class LogStackTrace extends Exception {}
/**
- * Called from Parcel.readException() when the exception is EX_STRICT_MODE_VIOLATIONS,
- * we here read back all the encoded violations.
+ * Called from Parcel.readException() when the exception is EX_STRICT_MODE_VIOLATIONS, we here
+ * read back all the encoded violations.
*/
/* package */ static void readAndHandleBinderCallViolations(Parcel p) {
// Our own stack trace to append
@@ -2155,22 +2022,20 @@ public final class StrictMode {
}
/**
- * Called from android_util_Binder.cpp's
- * android_os_Parcel_enforceInterface when an incoming Binder call
- * requires changing the StrictMode policy mask. The role of this
- * function is to ask Binder for its current (native) thread-local
- * policy value and synchronize it to libcore's (Java)
- * thread-local policy value.
+ * Called from android_util_Binder.cpp's android_os_Parcel_enforceInterface when an incoming
+ * Binder call requires changing the StrictMode policy mask. The role of this function is to ask
+ * Binder for its current (native) thread-local policy value and synchronize it to libcore's
+ * (Java) thread-local policy value.
*/
private static void onBinderStrictModePolicyChange(int newPolicy) {
setBlockGuardPolicy(newPolicy);
}
/**
- * A tracked, critical time span. (e.g. during an animation.)
+ * A tracked, critical time span. (e.g. during an animation.)
*
- * The object itself is a linked list node, to avoid any allocations
- * during rapid span entries and exits.
+ * <p>The object itself is a linked list node, to avoid any allocations during rapid span
+ * entries and exits.
*
* @hide
*/
@@ -2178,7 +2043,7 @@ public final class StrictMode {
private String mName;
private long mCreateMillis;
private Span mNext;
- private Span mPrev; // not used when in freeList, only active
+ private Span mPrev; // not used when in freeList, only active
private final ThreadSpanState mContainerState;
Span(ThreadSpanState threadState) {
@@ -2191,12 +2056,10 @@ public final class StrictMode {
}
/**
- * To be called when the critical span is complete (i.e. the
- * animation is done animating). This can be called on any
- * thread (even a different one from where the animation was
- * taking place), but that's only a defensive implementation
- * measure. It really makes no sense for you to call this on
- * thread other than that where you created it.
+ * To be called when the critical span is complete (i.e. the animation is done animating).
+ * This can be called on any thread (even a different one from where the animation was
+ * taking place), but that's only a defensive implementation measure. It really makes no
+ * sense for you to call this on thread other than that where you created it.
*
* @hide
*/
@@ -2240,53 +2103,52 @@ public final class StrictMode {
}
// The no-op span that's used in user builds.
- private static final Span NO_OP_SPAN = new Span() {
- public void finish() {
- // Do nothing.
- }
- };
+ private static final Span NO_OP_SPAN =
+ new Span() {
+ public void finish() {
+ // Do nothing.
+ }
+ };
/**
* Linked lists of active spans and a freelist.
*
- * Locking notes: there's one of these structures per thread and
- * all members of this structure (as well as the Span nodes under
- * it) are guarded by the ThreadSpanState object instance. While
- * in theory there'd be no locking required because it's all local
- * per-thread, the finish() method above is defensive against
- * people calling it on a different thread from where they created
- * the Span, hence the locking.
+ * <p>Locking notes: there's one of these structures per thread and all members of this
+ * structure (as well as the Span nodes under it) are guarded by the ThreadSpanState object
+ * instance. While in theory there'd be no locking required because it's all local per-thread,
+ * the finish() method above is defensive against people calling it on a different thread from
+ * where they created the Span, hence the locking.
*/
private static class ThreadSpanState {
- public Span mActiveHead; // doubly-linked list.
+ public Span mActiveHead; // doubly-linked list.
public int mActiveSize;
- public Span mFreeListHead; // singly-linked list. only changes at head.
+ public Span mFreeListHead; // singly-linked list. only changes at head.
public int mFreeListSize;
}
private static final ThreadLocal<ThreadSpanState> sThisThreadSpanState =
new ThreadLocal<ThreadSpanState>() {
- @Override protected ThreadSpanState initialValue() {
- return new ThreadSpanState();
- }
- };
+ @Override
+ protected ThreadSpanState initialValue() {
+ return new ThreadSpanState();
+ }
+ };
- private static Singleton<IWindowManager> sWindowManager = new Singleton<IWindowManager>() {
- protected IWindowManager create() {
- return IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
- }
- };
+ private static Singleton<IWindowManager> sWindowManager =
+ new Singleton<IWindowManager>() {
+ protected IWindowManager create() {
+ return IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
+ }
+ };
/**
* Enter a named critical span (e.g. an animation)
*
- * <p>The name is an arbitary label (or tag) that will be applied
- * to any strictmode violation that happens while this span is
- * active. You must call finish() on the span when done.
+ * <p>The name is an arbitary label (or tag) that will be applied to any strictmode violation
+ * that happens while this span is active. You must call finish() on the span when done.
*
- * <p>This will never return null, but on devices without debugging
- * enabled, this may return a dummy object on which the finish()
- * method is a no-op.
+ * <p>This will never return null, but on devices without debugging enabled, this may return a
+ * dummy object on which the finish() method is a no-op.
*
* <p>TODO: add CloseGuard to this, verifying callers call finish.
*
@@ -2325,13 +2187,11 @@ public final class StrictMode {
}
/**
- * For code to note that it's slow. This is a no-op unless the
- * current thread's {@link android.os.StrictMode.ThreadPolicy} has
- * {@link android.os.StrictMode.ThreadPolicy.Builder#detectCustomSlowCalls}
- * enabled.
+ * For code to note that it's slow. This is a no-op unless the current thread's {@link
+ * android.os.StrictMode.ThreadPolicy} has {@link
+ * android.os.StrictMode.ThreadPolicy.Builder#detectCustomSlowCalls} enabled.
*
- * @param name a short string for the exception stack trace that's
- * built if when this fires.
+ * @param name a short string for the exception stack trace that's built if when this fires.
*/
public static void noteSlowCall(String name) {
BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
@@ -2343,14 +2203,11 @@ public final class StrictMode {
}
/**
- * For code to note that a resource was obtained using a type other than
- * its defined type. This is a no-op unless the current thread's
- * {@link android.os.StrictMode.ThreadPolicy} has
- * {@link android.os.StrictMode.ThreadPolicy.Builder#detectResourceMismatches()}
- * enabled.
+ * For code to note that a resource was obtained using a type other than its defined type. This
+ * is a no-op unless the current thread's {@link android.os.StrictMode.ThreadPolicy} has {@link
+ * android.os.StrictMode.ThreadPolicy.Builder#detectResourceMismatches()} enabled.
*
- * @param tag an object for the exception stack trace that's
- * built if when this fires.
+ * @param tag an object for the exception stack trace that's built if when this fires.
* @hide
*/
public static void noteResourceMismatch(Object tag) {
@@ -2362,9 +2219,7 @@ public final class StrictMode {
((AndroidBlockGuardPolicy) policy).onResourceMismatch(tag);
}
- /**
- * @hide
- */
+ /** @hide */
public static void noteUnbufferedIO() {
BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
if (!(policy instanceof AndroidBlockGuardPolicy)) {
@@ -2374,9 +2229,7 @@ public final class StrictMode {
((AndroidBlockGuardPolicy) policy).onUnbufferedIO();
}
- /**
- * @hide
- */
+ /** @hide */
public static void noteDiskRead() {
BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
if (!(policy instanceof AndroidBlockGuardPolicy)) {
@@ -2386,9 +2239,7 @@ public final class StrictMode {
((AndroidBlockGuardPolicy) policy).onReadFromDisk();
}
- /**
- * @hide
- */
+ /** @hide */
public static void noteDiskWrite() {
BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
if (!(policy instanceof AndroidBlockGuardPolicy)) {
@@ -2403,17 +2254,16 @@ public final class StrictMode {
new HashMap<Class, Integer>();
/**
- * Returns an object that is used to track instances of activites.
- * The activity should store a reference to the tracker object in one of its fields.
+ * Returns an object that is used to track instances of activites. The activity should store a
+ * reference to the tracker object in one of its fields.
+ *
* @hide
*/
public static Object trackActivity(Object instance) {
return new InstanceTracker(instance);
}
- /**
- * @hide
- */
+ /** @hide */
public static void incrementExpectedActivityCount(Class klass) {
if (klass == null) {
return;
@@ -2430,9 +2280,7 @@ public final class StrictMode {
}
}
- /**
- * @hide
- */
+ /** @hide */
public static void decrementExpectedActivityCount(Class klass) {
if (klass == null) {
return;
@@ -2483,70 +2331,49 @@ public final class StrictMode {
}
/**
- * Parcelable that gets sent in Binder call headers back to callers
- * to report violations that happened during a cross-process call.
+ * Parcelable that gets sent in Binder call headers back to callers to report violations that
+ * happened during a cross-process call.
*
* @hide
*/
public static class ViolationInfo implements Parcelable {
public final String message;
- /**
- * Stack and other stuff info.
- */
+ /** Stack and other stuff info. */
public final ApplicationErrorReport.CrashInfo crashInfo;
- /**
- * The strict mode policy mask at the time of violation.
- */
+ /** The strict mode policy mask at the time of violation. */
public final int policy;
- /**
- * The wall time duration of the violation, when known. -1 when
- * not known.
- */
+ /** The wall time duration of the violation, when known. -1 when not known. */
public int durationMillis = -1;
- /**
- * The number of animations currently running.
- */
+ /** The number of animations currently running. */
public int numAnimationsRunning = 0;
- /**
- * List of tags from active Span instances during this
- * violation, or null for none.
- */
+ /** List of tags from active Span instances during this violation, or null for none. */
public String[] tags;
/**
- * Which violation number this was (1-based) since the last Looper loop,
- * from the perspective of the root caller (if it crossed any processes
- * via Binder calls). The value is 0 if the root caller wasn't on a Looper
- * thread.
+ * Which violation number this was (1-based) since the last Looper loop, from the
+ * perspective of the root caller (if it crossed any processes via Binder calls). The value
+ * is 0 if the root caller wasn't on a Looper thread.
*/
public int violationNumThisLoop;
- /**
- * The time (in terms of SystemClock.uptimeMillis()) that the
- * violation occurred.
- */
+ /** The time (in terms of SystemClock.uptimeMillis()) that the violation occurred. */
public long violationUptimeMillis;
/**
- * The action of the Intent being broadcast to somebody's onReceive
- * on this thread right now, or null.
+ * The action of the Intent being broadcast to somebody's onReceive on this thread right
+ * now, or null.
*/
public String broadcastIntentAction;
- /**
- * If this is a instance count violation, the number of instances in memory,
- * else -1.
- */
+ /** If this is a instance count violation, the number of instances in memory, else -1. */
public long numInstances = -1;
- /**
- * Create an uninitialized instance of ViolationInfo
- */
+ /** Create an uninitialized instance of ViolationInfo */
public ViolationInfo() {
message = null;
crashInfo = null;
@@ -2557,9 +2384,7 @@ public final class StrictMode {
this(null, tr, policy);
}
- /**
- * Create an instance of ViolationInfo initialized from an exception.
- */
+ /** Create an instance of ViolationInfo initialized from an exception. */
public ViolationInfo(String message, Throwable tr, int policy) {
this.message = message;
crashInfo = new ApplicationErrorReport.CrashInfo(tr);
@@ -2612,9 +2437,7 @@ public final class StrictMode {
return result;
}
- /**
- * Create an instance of ViolationInfo initialized from a Parcel.
- */
+ /** Create an instance of ViolationInfo initialized from a Parcel. */
public ViolationInfo(Parcel in) {
this(in, false);
}
@@ -2622,8 +2445,8 @@ public final class StrictMode {
/**
* Create an instance of ViolationInfo initialized from a Parcel.
*
- * @param unsetGatheringBit if true, the caller is the root caller
- * and the gathering penalty should be removed.
+ * @param unsetGatheringBit if true, the caller is the root caller and the gathering penalty
+ * should be removed.
*/
public ViolationInfo(Parcel in, boolean unsetGatheringBit) {
message = in.readString();
@@ -2647,9 +2470,7 @@ public final class StrictMode {
tags = in.readStringArray();
}
- /**
- * Save a ViolationInfo instance to a parcel.
- */
+ /** Save a ViolationInfo instance to a parcel. */
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(message);
@@ -2668,23 +2489,29 @@ public final class StrictMode {
dest.writeLong(numInstances);
dest.writeString(broadcastIntentAction);
dest.writeStringArray(tags);
- int total = dest.dataPosition()-start;
- if (Binder.CHECK_PARCEL_SIZE && total > 10*1024) {
- Slog.d(TAG, "VIO: policy=" + policy + " dur=" + durationMillis
- + " numLoop=" + violationNumThisLoop
- + " anim=" + numAnimationsRunning
- + " uptime=" + violationUptimeMillis
- + " numInst=" + numInstances);
+ int total = dest.dataPosition() - start;
+ if (Binder.CHECK_PARCEL_SIZE && total > 10 * 1024) {
+ Slog.d(
+ TAG,
+ "VIO: policy="
+ + policy
+ + " dur="
+ + durationMillis
+ + " numLoop="
+ + violationNumThisLoop
+ + " anim="
+ + numAnimationsRunning
+ + " uptime="
+ + violationUptimeMillis
+ + " numInst="
+ + numInstances);
Slog.d(TAG, "VIO: action=" + broadcastIntentAction);
Slog.d(TAG, "VIO: tags=" + Arrays.toString(tags));
- Slog.d(TAG, "VIO: TOTAL BYTES WRITTEN: " + (dest.dataPosition()-start));
+ Slog.d(TAG, "VIO: TOTAL BYTES WRITTEN: " + (dest.dataPosition() - start));
}
}
-
- /**
- * Dump a ViolationInfo instance to a Printer.
- */
+ /** Dump a ViolationInfo instance to a Printer. */
public void dump(Printer pw, String prefix) {
if (crashInfo != null) {
crashInfo.dump(pw, prefix);
@@ -2743,8 +2570,8 @@ public final class StrictMode {
final int mLimit;
private static final StackTraceElement[] FAKE_STACK = {
- new StackTraceElement("android.os.StrictMode", "setClassInstanceLimit",
- "StrictMode.java", 1)
+ new StackTraceElement(
+ "android.os.StrictMode", "setClassInstanceLimit", "StrictMode.java", 1)
};
public InstanceCountViolation(Class klass, long instances, int limit) {
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index fe9e8c67e566..da0ed54e003e 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -149,14 +149,43 @@ public abstract class VibrationEffect implements Parcelable {
* provide a better experience than you could otherwise build using the generic building
* blocks.
*
+ * This will fallback to a generic pattern if one exists and there does not exist a
+ * hardware-specific implementation of the effect.
+ *
* @param effectId The ID of the effect to perform:
- * {@link #EFFECT_CLICK}, {@link #EFFECT_DOUBLE_CLICK}.
+ * {@link #EFFECT_CLICK}, {@link #EFFECT_DOUBLE_CLICK}, {@link #EFFECT_TICK}
*
* @return The desired effect.
* @hide
*/
public static VibrationEffect get(int effectId) {
- VibrationEffect effect = new Prebaked(effectId);
+ return get(effectId, true);
+ }
+
+ /**
+ * Get a predefined vibration effect.
+ *
+ * Predefined effects are a set of common vibration effects that should be identical, regardless
+ * of the app they come from, in order to provide a cohesive experience for users across
+ * the entire device. They also may be custom tailored to the device hardware in order to
+ * provide a better experience than you could otherwise build using the generic building
+ * blocks.
+ *
+ * Some effects you may only want to play if there's a hardware specific implementation because
+ * they may, for example, be too disruptive to the user without tuning. The {@code fallback}
+ * parameter allows you to decide whether you want to fallback to the generic implementation or
+ * only play if there's a tuned, hardware specific one available.
+ *
+ * @param effectId The ID of the effect to perform:
+ * {@link #EFFECT_CLICK}, {@link #EFFECT_DOUBLE_CLICK}, {@link #EFFECT_TICK}
+ * @param fallback Whether to fallback to a generic pattern if a hardware specific
+ * implementation doesn't exist.
+ *
+ * @return The desired effect.
+ * @hide
+ */
+ public static VibrationEffect get(int effectId, boolean fallback) {
+ VibrationEffect effect = new Prebaked(effectId, fallback);
effect.validate();
return effect;
}
@@ -374,19 +403,29 @@ public abstract class VibrationEffect implements Parcelable {
/** @hide */
public static class Prebaked extends VibrationEffect implements Parcelable {
private int mEffectId;
+ private boolean mFallback;
public Prebaked(Parcel in) {
- this(in.readInt());
+ this(in.readInt(), in.readByte() != 0);
}
- public Prebaked(int effectId) {
+ public Prebaked(int effectId, boolean fallback) {
mEffectId = effectId;
+ mFallback = fallback;
}
public int getId() {
return mEffectId;
}
+ /**
+ * Whether the effect should fall back to a generic pattern if there's no hardware specific
+ * implementation of it.
+ */
+ public boolean shouldFallback() {
+ return mFallback;
+ }
+
@Override
public void validate() {
switch (mEffectId) {
@@ -406,7 +445,7 @@ public abstract class VibrationEffect implements Parcelable {
return false;
}
VibrationEffect.Prebaked other = (VibrationEffect.Prebaked) o;
- return mEffectId == other.mEffectId;
+ return mEffectId == other.mEffectId && mFallback == other.mFallback;
}
@Override
@@ -416,7 +455,7 @@ public abstract class VibrationEffect implements Parcelable {
@Override
public String toString() {
- return "Prebaked{mEffectId=" + mEffectId + "}";
+ return "Prebaked{mEffectId=" + mEffectId + ", mFallback=" + mFallback + "}";
}
@@ -424,6 +463,7 @@ public abstract class VibrationEffect implements Parcelable {
public void writeToParcel(Parcel out, int flags) {
out.writeInt(PARCEL_TOKEN_EFFECT);
out.writeInt(mEffectId);
+ out.writeByte((byte) (mFallback ? 1 : 0));
}
public static final Parcelable.Creator<Prebaked> CREATOR =
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index fa523f36a113..c44b0bcf9acc 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -31,6 +31,7 @@ import android.app.ActivityThread;
import android.app.AppOpsManager;
import android.app.Application;
import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
import android.app.SearchManager;
import android.app.WallpaperManager;
@@ -1311,6 +1312,18 @@ public final class Settings {
= "android.settings.CHANNEL_NOTIFICATION_SETTINGS";
/**
+ * Activity Action: Show notification settings for a single {@link NotificationChannelGroup}.
+ * <p>
+ * Input: {@link #EXTRA_APP_PACKAGE}, the package containing the channel group to display.
+ * Input: {@link #EXTRA_CHANNEL_GROUP_ID}, the id of the channel group to display.
+ * <p>
+ * Output: Nothing.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_CHANNEL_GROUP_NOTIFICATION_SETTINGS =
+ "android.settings.CHANNEL_GROUP_NOTIFICATION_SETTINGS";
+
+ /**
* Activity Extra: The package owner of the notification channel settings to display.
* <p>
* This must be passed as an extra field to the {@link #ACTION_CHANNEL_NOTIFICATION_SETTINGS}.
@@ -1326,6 +1339,15 @@ public final class Settings {
public static final String EXTRA_CHANNEL_ID = "android.provider.extra.CHANNEL_ID";
/**
+ * Activity Extra: The {@link NotificationChannelGroup#getId()} of the notification channel
+ * group settings to display.
+ * <p>
+ * This must be passed as an extra field to the
+ * {@link #ACTION_CHANNEL_GROUP_NOTIFICATION_SETTINGS}.
+ */
+ public static final String EXTRA_CHANNEL_GROUP_ID = "android.provider.extra.CHANNEL_GROUP_ID";
+
+ /**
* Activity Action: Show notification redaction settings.
*
* @hide
@@ -9334,6 +9356,25 @@ public final class Settings {
public static final String ANOMALY_DETECTION_CONSTANTS = "anomaly_detection_constants";
/**
+ * Always on display(AOD) specific settings
+ * This is encoded as a key=value list, separated by commas. Ex:
+ *
+ * "prox_screen_off_delay=10000,screen_brightness_array=0:1:2:3:4"
+ *
+ * The following keys are supported:
+ *
+ * <pre>
+ * screen_brightness_array (string)
+ * dimming_scrim_array (string)
+ * prox_screen_off_delay (long)
+ * prox_cooldown_trigger (long)
+ * prox_cooldown_period (long)
+ * </pre>
+ * @hide
+ */
+ public static final String ALWAYS_ON_DISPLAY_CONSTANTS = "always_on_display_constants";
+
+ /**
* App standby (app idle) specific settings.
* This is encoded as a key=value list, separated by commas. Ex:
*
diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java
index f7dc1c58ade1..60c1c9a7e87a 100644
--- a/core/java/android/service/autofill/FillEventHistory.java
+++ b/core/java/android/service/autofill/FillEventHistory.java
@@ -81,10 +81,13 @@ public final class FillEventHistory implements Parcelable {
/**
* Returns the client state set in the previous {@link FillResponse}.
*
- * <p><b>NOTE: </b>the state is associated with the app that was autofilled in the previous
+ * <p><b>Note: </b>the state is associated with the app that was autofilled in the previous
* {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)}
* , which is not necessary the same app being autofilled now.
+ *
+ * @deprecated use {@link #getEvents()} then {@link Event#getClientState()} instead.
*/
+ @Deprecated
@Nullable public Bundle getClientState() {
return mClientState;
}
@@ -126,7 +129,6 @@ public final class FillEventHistory implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeBundle(mClientState);
-
if (mEvents == null) {
dest.writeInt(0);
} else {
@@ -137,6 +139,7 @@ public final class FillEventHistory implements Parcelable {
Event event = mEvents.get(i);
dest.writeInt(event.getType());
dest.writeString(event.getDatasetId());
+ dest.writeBundle(event.getClientState());
}
}
}
@@ -177,6 +180,7 @@ public final class FillEventHistory implements Parcelable {
@EventIds private final int mEventType;
@Nullable private final String mDatasetId;
+ @Nullable private final Bundle mClientState;
/**
* Returns the type of the event.
@@ -197,18 +201,32 @@ public final class FillEventHistory implements Parcelable {
}
/**
+ * Returns the client state from the {@link FillResponse} used to generate this event.
+ *
+ * <p><b>Note: </b>the state is associated with the app that was autofilled in the previous
+ * {@link
+ * AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)},
+ * which is not necessary the same app being autofilled now.
+ */
+ @Nullable public Bundle getClientState() {
+ return mClientState;
+ }
+
+ /**
* Creates a new event.
*
* @param eventType The type of the event
* @param datasetId The dataset the event was on, or {@code null} if the event was on the
* whole response.
+ * @param clientState The client state associated with the event.
*
* @hide
*/
- public Event(int eventType, String datasetId) {
+ public Event(int eventType, @Nullable String datasetId, @Nullable Bundle clientState) {
mEventType = Preconditions.checkArgumentInRange(eventType, 0, TYPE_SAVE_SHOWN,
"eventType");
mDatasetId = datasetId;
+ mClientState = clientState;
}
}
@@ -220,7 +238,8 @@ public final class FillEventHistory implements Parcelable {
int numEvents = parcel.readInt();
for (int i = 0; i < numEvents; i++) {
- selection.addEvent(new Event(parcel.readInt(), parcel.readString()));
+ selection.addEvent(new Event(parcel.readInt(), parcel.readString(),
+ parcel.readBundle()));
}
return selection;
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index b6a9a2648470..3b09c678ba8e 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -302,7 +302,7 @@ public final class FillResponse implements Parcelable {
// TODO: create a dump() method instead
return new StringBuilder(
"FillResponse : [mRequestId=" + mRequestId)
- .append(", datasets=").append(mDatasets)
+ .append(", datasets=").append(mDatasets == null ? "N/A" : mDatasets.getList())
.append(", saveInfo=").append(mSaveInfo)
.append(", clientState=").append(mClientState != null)
.append(", hasPresentation=").append(mPresentation != null)
diff --git a/core/java/android/util/ExceptionUtils.java b/core/java/android/util/ExceptionUtils.java
index 44019c32560d..da7387fcae70 100644
--- a/core/java/android/util/ExceptionUtils.java
+++ b/core/java/android/util/ExceptionUtils.java
@@ -78,4 +78,12 @@ public class ExceptionUtils {
propagateIfInstanceOf(t, RuntimeException.class);
throw new RuntimeException(t);
}
+
+ /**
+ * Gets the root {@link Throwable#getCause() cause} of {@code t}
+ */
+ public static @NonNull Throwable getRootCause(@NonNull Throwable t) {
+ while (t.getCause() != null) t = t.getCause();
+ return t;
+ }
}
diff --git a/core/java/android/util/StatsLog.java b/core/java/android/util/StatsLog.java
new file mode 100644
index 000000000000..0be1a8cfabae
--- /dev/null
+++ b/core/java/android/util/StatsLog.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+/**
+ * Logging access for platform metrics.
+ *
+ * <p>This is <b>not</b> the main "logcat" debugging log ({@link android.util.Log})!
+ * These diagnostic stats are for system integrators, not application authors.
+ *
+ * <p>Stats use integer tag codes.
+ * They carry a payload of one or more int, long, or String values.
+ * @hide
+ */
+public class StatsLog {
+ /** @hide */ public StatsLog() {}
+
+ private static final String TAG = "StatsLog";
+
+ // We assume that the native methods deal with any concurrency issues.
+
+ /**
+ * Records an stats log message.
+ * @param tag The stats type tag code
+ * @param value A value to log
+ * @return The number of bytes written
+ */
+ public static native int writeInt(int tag, int value);
+
+ /**
+ * Records an stats log message.
+ * @param tag The stats type tag code
+ * @param value A value to log
+ * @return The number of bytes written
+ */
+ public static native int writeLong(int tag, long value);
+
+ /**
+ * Records an stats log message.
+ * @param tag The stats type tag code
+ * @param value A value to log
+ * @return The number of bytes written
+ */
+ public static native int writeFloat(int tag, float value);
+
+ /**
+ * Records an stats log message.
+ * @param tag The stats type tag code
+ * @param str A value to log
+ * @return The number of bytes written
+ */
+ public static native int writeString(int tag, String str);
+
+ /**
+ * Records an stats log message.
+ * @param tag The stats type tag code
+ * @param list A list of values to log. All values should
+ * be of type int, long, float or String.
+ * @return The number of bytes written
+ */
+ public static native int writeArray(int tag, Object... list);
+}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ffb3203688eb..e5bd5ac076f3 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7621,6 +7621,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* <li>Call
* {@link android.view.autofill.AutofillManager#notifyValueChanged(View, int, AutofillValue)}
* when the value of a virtual child changed.
+ * <li>Call {@link
+ * android.view.autofill.AutofillManager#notifyViewVisibilityChanged(View, int, boolean)}
+ * when the visibility of a virtual child changed.
* <li>Call {@link AutofillManager#commit()} when the autofill context of the view structure
* changed and the current context should be committed (for example, when the user tapped
* a {@code SUBMIT} button in an HTML page).
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 5adbdbe8ab4f..46742b8852ff 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -73,6 +73,9 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
@@ -188,17 +191,12 @@ public class RemoteViews implements Parcelable, Filter {
private static final OnClickHandler DEFAULT_ON_CLICK_HANDLER = new OnClickHandler();
- private static final Object[] sMethodsLock = new Object[0];
- private static final ArrayMap<Class<? extends View>, ArrayMap<MutablePair<String, Class<?>>, Method>> sMethods =
- new ArrayMap<Class<? extends View>, ArrayMap<MutablePair<String, Class<?>>, Method>>();
- private static final ArrayMap<Method, Method> sAsyncMethods = new ArrayMap<>();
+ private static final ArrayMap<MethodKey, MethodArgs> sMethods = new ArrayMap<>();
- private static final ThreadLocal<Object[]> sInvokeArgsTls = new ThreadLocal<Object[]>() {
- @Override
- protected Object[] initialValue() {
- return new Object[1];
- }
- };
+ /**
+ * This key is used to perform lookups in sMethods without causing allocations.
+ */
+ private static final MethodKey sLookupKey = new MethodKey();
/**
* @hide
@@ -255,37 +253,47 @@ public class RemoteViews implements Parcelable, Filter {
}
/**
- * Handle with care!
+ * Stores information related to reflection method lookup.
*/
- static class MutablePair<F, S> {
- F first;
- S second;
-
- MutablePair(F first, S second) {
- this.first = first;
- this.second = second;
- }
+ static class MethodKey {
+ public Class targetClass;
+ public Class paramClass;
+ public String methodName;
@Override
public boolean equals(Object o) {
- if (!(o instanceof MutablePair)) {
+ if (!(o instanceof MethodKey)) {
return false;
}
- MutablePair<?, ?> p = (MutablePair<?, ?>) o;
- return Objects.equal(p.first, first) && Objects.equal(p.second, second);
+ MethodKey p = (MethodKey) o;
+ return Objects.equal(p.targetClass, targetClass)
+ && Objects.equal(p.paramClass, paramClass)
+ && Objects.equal(p.methodName, methodName);
}
@Override
public int hashCode() {
- return (first == null ? 0 : first.hashCode()) ^ (second == null ? 0 : second.hashCode());
+ return Objects.hashCode(targetClass) ^ Objects.hashCode(paramClass)
+ ^ Objects.hashCode(methodName);
+ }
+
+ public void set(Class targetClass, Class paramClass, String methodName) {
+ this.targetClass = targetClass;
+ this.paramClass = paramClass;
+ this.methodName = methodName;
}
}
+
/**
- * This pair is used to perform lookups in sMethods without causing allocations.
+ * Stores information related to reflection method lookup result.
*/
- private final MutablePair<String, Class<?>> mPair =
- new MutablePair<String, Class<?>>(null, null);
+ static class MethodArgs {
+ public MethodHandle syncMethod;
+ public MethodHandle asyncMethod;
+ public String asyncMethodName;
+ }
+
/**
* This annotation indicates that a subclass of View is allowed to be used
@@ -307,6 +315,12 @@ public class RemoteViews implements Parcelable, Filter {
public ActionException(String message) {
super(message);
}
+ /**
+ * @hide
+ */
+ public ActionException(Throwable t) {
+ super(t);
+ }
}
/** @hide */
@@ -943,73 +957,66 @@ public class RemoteViews implements Parcelable, Filter {
return rect;
}
- private Method getMethod(View view, String methodName, Class<?> paramType) {
- Method method;
+ private MethodHandle getMethod(View view, String methodName, Class<?> paramType,
+ boolean async) {
+ MethodArgs result;
Class<? extends View> klass = view.getClass();
- synchronized (sMethodsLock) {
- ArrayMap<MutablePair<String, Class<?>>, Method> methods = sMethods.get(klass);
- if (methods == null) {
- methods = new ArrayMap<MutablePair<String, Class<?>>, Method>();
- sMethods.put(klass, methods);
- }
-
- mPair.first = methodName;
- mPair.second = paramType;
+ synchronized (sMethods) {
+ // The key is defined by the view class, param class and method name.
+ sLookupKey.set(klass, paramType, methodName);
+ result = sMethods.get(sLookupKey);
- method = methods.get(mPair);
- if (method == null) {
+ if (result == null) {
+ Method method;
try {
if (paramType == null) {
method = klass.getMethod(methodName);
} else {
method = klass.getMethod(methodName, paramType);
}
- } catch (NoSuchMethodException ex) {
- throw new ActionException("view: " + klass.getName() + " doesn't have method: "
- + methodName + getParameters(paramType));
- }
+ if (!method.isAnnotationPresent(RemotableViewMethod.class)) {
+ throw new ActionException("view: " + klass.getName()
+ + " can't use method with RemoteViews: "
+ + methodName + getParameters(paramType));
+ }
- if (!method.isAnnotationPresent(RemotableViewMethod.class)) {
- throw new ActionException("view: " + klass.getName()
- + " can't use method with RemoteViews: "
+ result = new MethodArgs();
+ result.syncMethod = MethodHandles.publicLookup().unreflect(method);
+ result.asyncMethodName =
+ method.getAnnotation(RemotableViewMethod.class).asyncImpl();
+ } catch (NoSuchMethodException | IllegalAccessException ex) {
+ throw new ActionException("view: " + klass.getName() + " doesn't have method: "
+ methodName + getParameters(paramType));
}
- methods.put(new MutablePair<String, Class<?>>(methodName, paramType), method);
+ MethodKey key = new MethodKey();
+ key.set(klass, paramType, methodName);
+ sMethods.put(key, result);
}
- }
-
- return method;
- }
- /**
- * @return the async implementation of the provided method.
- */
- private Method getAsyncMethod(Method method) {
- synchronized (sAsyncMethods) {
- int valueIndex = sAsyncMethods.indexOfKey(method);
- if (valueIndex >= 0) {
- return sAsyncMethods.valueAt(valueIndex);
+ if (!async) {
+ return result.syncMethod;
}
-
- RemotableViewMethod annotation = method.getAnnotation(RemotableViewMethod.class);
- Method asyncMethod = null;
- if (!annotation.asyncImpl().isEmpty()) {
+ // Check this so see if async method is implemented or not.
+ if (result.asyncMethodName.isEmpty()) {
+ return null;
+ }
+ // Async method is lazily loaded. If it is not yet loaded, load now.
+ if (result.asyncMethod == null) {
+ MethodType asyncType = result.syncMethod.type()
+ .dropParameterTypes(0, 1).changeReturnType(Runnable.class);
try {
- asyncMethod = method.getDeclaringClass()
- .getMethod(annotation.asyncImpl(), method.getParameterTypes());
- if (!asyncMethod.getReturnType().equals(Runnable.class)) {
- throw new ActionException("Async implementation for " + method.getName() +
- " does not return a Runnable");
- }
- } catch (NoSuchMethodException ex) {
- throw new ActionException("Async implementation declared but not defined for " +
- method.getName());
+ result.asyncMethod = MethodHandles.publicLookup().findVirtual(
+ klass, result.asyncMethodName, asyncType);
+ } catch (NoSuchMethodException | IllegalAccessException ex) {
+ throw new ActionException("Async implementation declared as "
+ + result.asyncMethodName + " but not defined for " + methodName
+ + ": public Runnable " + result.asyncMethodName + " ("
+ + TextUtils.join(",", asyncType.parameterArray()) + ")");
}
}
- sAsyncMethods.put(method, asyncMethod);
- return asyncMethod;
+ return result.asyncMethod;
}
}
@@ -1018,12 +1025,6 @@ public class RemoteViews implements Parcelable, Filter {
return "(" + paramType + ")";
}
- private static Object[] wrapArg(Object value) {
- Object[] args = sInvokeArgsTls.get();
- args[0] = value;
- return args;
- }
-
/**
* Equivalent to calling a combination of {@link Drawable#setAlpha(int)},
* {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
@@ -1140,10 +1141,8 @@ public class RemoteViews implements Parcelable, Filter {
if (view == null) return;
try {
- getMethod(view, this.methodName, null).invoke(view);
- } catch (ActionException e) {
- throw e;
- } catch (Exception ex) {
+ getMethod(view, this.methodName, null, false /* async */).invoke(view);
+ } catch (Throwable ex) {
throw new ActionException(ex);
}
}
@@ -1516,12 +1515,9 @@ public class RemoteViews implements Parcelable, Filter {
if (param == null) {
throw new ActionException("bad type: " + this.type);
}
-
try {
- getMethod(view, this.methodName, param).invoke(view, wrapArg(this.value));
- } catch (ActionException e) {
- throw e;
- } catch (Exception ex) {
+ getMethod(view, this.methodName, param, false /* async */).invoke(view, this.value);
+ } catch (Throwable ex) {
throw new ActionException(ex);
}
}
@@ -1537,11 +1533,10 @@ public class RemoteViews implements Parcelable, Filter {
}
try {
- Method method = getMethod(view, this.methodName, param);
- Method asyncMethod = getAsyncMethod(method);
+ MethodHandle method = getMethod(view, this.methodName, param, true /* async */);
- if (asyncMethod != null) {
- Runnable endAction = (Runnable) asyncMethod.invoke(view, wrapArg(this.value));
+ if (method != null) {
+ Runnable endAction = (Runnable) method.invoke(view, this.value);
if (endAction == null) {
return ACTION_NOOP;
} else {
@@ -1555,9 +1550,7 @@ public class RemoteViews implements Parcelable, Filter {
return new RunnableAction(endAction);
}
}
- } catch (ActionException e) {
- throw e;
- } catch (Exception ex) {
+ } catch (Throwable ex) {
throw new ActionException(ex);
}
@@ -2672,7 +2665,7 @@ public class RemoteViews implements Parcelable, Filter {
* given {@link RemoteViews}.
*
* @param viewId The id of the parent {@link ViewGroup} to add the child into.
- * @param nestedView {@link RemoveViews} of the child to add.
+ * @param nestedView {@link RemoteViews} of the child to add.
* @param index The position at which to add the child.
*
* @hide
diff --git a/core/java/android/widget/SmartSelectSprite.java b/core/java/android/widget/SmartSelectSprite.java
index 45466c225e14..8d06f5fdfaf2 100644
--- a/core/java/android/widget/SmartSelectSprite.java
+++ b/core/java/android/widget/SmartSelectSprite.java
@@ -44,11 +44,8 @@ import android.view.animation.Interpolator;
import java.lang.annotation.Retention;
import java.util.Collections;
import java.util.Comparator;
-import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
-import java.util.Set;
-import java.util.Stack;
/**
* A utility class for creating and animating the Smart Select animation.
@@ -59,7 +56,6 @@ final class SmartSelectSprite {
private static final int EXPAND_DURATION = 300;
private static final int CORNER_DURATION = 150;
private static final float STROKE_WIDTH_DP = 1.5F;
- private static final int POINTS_PER_LINE = 4;
// GBLUE700
@ColorInt
@@ -73,40 +69,13 @@ final class SmartSelectSprite {
private Animator mActiveAnimator = null;
@ColorInt
private final int mStrokeColor;
- private Set<Drawable> mExistingAnimationDrawables = new HashSet<>();
static final Comparator<RectF> RECTANGLE_COMPARATOR = Comparator
.<RectF>comparingDouble(e -> e.bottom)
.thenComparingDouble(e -> e.left);
- /**
- * Represents a set of points connected by lines.
- */
- private static final class PolygonShape extends Shape {
-
- private final float[] mLineCoordinates;
-
- private PolygonShape(final List<PointF> points) {
- mLineCoordinates = new float[points.size() * POINTS_PER_LINE];
-
- int index = 0;
- PointF currentPoint = points.get(0);
- for (final PointF nextPoint : points) {
- mLineCoordinates[index] = currentPoint.x;
- mLineCoordinates[index + 1] = currentPoint.y;
- mLineCoordinates[index + 2] = nextPoint.x;
- mLineCoordinates[index + 3] = nextPoint.y;
-
- index += POINTS_PER_LINE;
- currentPoint = nextPoint;
- }
- }
-
- @Override
- public void draw(Canvas canvas, Paint paint) {
- canvas.drawLines(mLineCoordinates, paint);
- }
- }
+ private Drawable mExistingDrawable = null;
+ private RectangleList mExistingRectangleList = null;
/**
* A rounded rectangle with a configurable corner radius and the ability to expand outside of
@@ -262,16 +231,27 @@ final class SmartSelectSprite {
*/
private static final class RectangleList extends Shape {
+ @Retention(SOURCE)
+ @IntDef({DisplayType.RECTANGLES, DisplayType.POLYGON})
+ private @interface DisplayType {
+ int RECTANGLES = 0;
+ int POLYGON = 1;
+ }
+
private static final String PROPERTY_RIGHT_BOUNDARY = "rightBoundary";
private static final String PROPERTY_LEFT_BOUNDARY = "leftBoundary";
private final List<RoundedRectangleShape> mRectangles;
private final List<RoundedRectangleShape> mReversedRectangles;
- private RectangleList(List<RoundedRectangleShape> rectangles) {
+ private final Path mOutlinePolygonPath;
+ private @DisplayType int mDisplayType = DisplayType.RECTANGLES;
+
+ private RectangleList(final List<RoundedRectangleShape> rectangles) {
mRectangles = new LinkedList<>(rectangles);
mReversedRectangles = new LinkedList<>(rectangles);
Collections.reverse(mReversedRectangles);
+ mOutlinePolygonPath = generateOutlinePolygonPath(rectangles);
}
private void setLeftBoundary(final float leftBoundary) {
@@ -307,6 +287,10 @@ final class SmartSelectSprite {
}
}
+ void setDisplayType(@DisplayType int displayType) {
+ mDisplayType = displayType;
+ }
+
private int getTotalWidth() {
int sum = 0;
for (RoundedRectangleShape rectangle : mRectangles) {
@@ -317,11 +301,34 @@ final class SmartSelectSprite {
@Override
public void draw(Canvas canvas, Paint paint) {
+ if (mDisplayType == DisplayType.POLYGON) {
+ drawPolygon(canvas, paint);
+ } else {
+ drawRectangles(canvas, paint);
+ }
+ }
+
+ private void drawRectangles(final Canvas canvas, final Paint paint) {
for (RoundedRectangleShape rectangle : mRectangles) {
rectangle.draw(canvas, paint);
}
}
+ private void drawPolygon(final Canvas canvas, final Paint paint) {
+ canvas.drawPath(mOutlinePolygonPath, paint);
+ }
+
+ private static Path generateOutlinePolygonPath(
+ final List<RoundedRectangleShape> rectangles) {
+ final Path path = new Path();
+ for (final RoundedRectangleShape shape : rectangles) {
+ final Path rectanglePath = new Path();
+ rectanglePath.addRect(shape.mBoundingRectangle, Path.Direction.CW);
+ path.op(rectanglePath, Path.Op.UNION);
+ }
+ return path;
+ }
+
}
SmartSelectSprite(final View view) {
@@ -337,84 +344,6 @@ final class SmartSelectSprite {
mView = view;
}
- private static boolean intersectsOrTouches(RectF a, RectF b) {
- return a.left <= b.right && b.left <= a.right && a.top <= b.bottom && b.top <= a.bottom;
- }
-
- private List<Drawable> mergeRectanglesToPolygonShape(
- final List<RectF> rectangles,
- final int color) {
- final List<Drawable> drawables = new LinkedList<>();
- final Set<List<PointF>> mergedPaths = calculateMergedPolygonPoints(rectangles);
-
- for (List<PointF> path : mergedPaths) {
- // Add the starting point to the end of the polygon so that it ends up closed.
- path.add(path.get(0));
-
- final PolygonShape shape = new PolygonShape(path);
- final ShapeDrawable drawable = new ShapeDrawable(shape);
-
- drawable.getPaint().setColor(color);
- drawable.getPaint().setStyle(Paint.Style.STROKE);
- drawable.getPaint().setStrokeWidth(mStrokeWidth);
-
- drawables.add(drawable);
- }
-
- return drawables;
- }
-
- private static Set<List<PointF>> calculateMergedPolygonPoints(
- List<RectF> rectangles) {
- final Set<List<RectF>> partitions = new HashSet<>();
- final LinkedList<RectF> listOfRects = new LinkedList<>(rectangles);
-
- while (!listOfRects.isEmpty()) {
- final RectF candidate = listOfRects.removeFirst();
- final List<RectF> partition = new LinkedList<>();
- partition.add(candidate);
-
- final LinkedList<RectF> otherCandidates = new LinkedList<>();
- otherCandidates.addAll(listOfRects);
-
- while (!otherCandidates.isEmpty()) {
- final RectF otherCandidate = otherCandidates.removeFirst();
- for (RectF partitionElement : partition) {
- if (intersectsOrTouches(partitionElement, otherCandidate)) {
- partition.add(otherCandidate);
- listOfRects.remove(otherCandidate);
- break;
- }
- }
- }
-
- partition.sort(Comparator.comparing(o -> o.top));
- partitions.add(partition);
- }
-
- final Set<List<PointF>> result = new HashSet<>();
- for (List<RectF> partition : partitions) {
- final List<PointF> points = new LinkedList<>();
-
- final Stack<RectF> rects = new Stack<>();
- for (RectF rect : partition) {
- points.add(new PointF(rect.right, rect.top));
- points.add(new PointF(rect.right, rect.bottom));
- rects.add(rect);
- }
- while (!rects.isEmpty()) {
- final RectF rect = rects.pop();
- points.add(new PointF(rect.left, rect.bottom));
- points.add(new PointF(rect.left, rect.top));
- }
-
- result.add(points);
- }
-
- return result;
-
- }
-
/**
* Performs the Smart Select animation on the view bound to this SmartSelectSprite.
*
@@ -490,17 +419,17 @@ final class SmartSelectSprite {
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(mStrokeWidth);
- addToOverlay(shapeDrawable);
+ mExistingRectangleList = rectangleList;
+ mExistingDrawable = shapeDrawable;
+ mView.getOverlay().add(shapeDrawable);
- mActiveAnimator = createAnimator(mStrokeColor, destinationRectangles, rectangleList,
- startingOffsetLeft, startingOffsetRight, cornerAnimators, updateListener,
+ mActiveAnimator = createAnimator(rectangleList, startingOffsetLeft, startingOffsetRight,
+ cornerAnimators, updateListener,
onAnimationEnd);
mActiveAnimator.start();
}
private Animator createAnimator(
- final @ColorInt int color,
- final List<RectF> destinationRectangles,
final RectangleList rectangleList,
final float startingOffsetLeft,
final float startingOffsetRight,
@@ -537,15 +466,12 @@ final class SmartSelectSprite {
final AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playSequentially(boundaryAnimator, cornerAnimator);
- setUpAnimatorListener(animatorSet, destinationRectangles, color, onAnimationEnd);
+ setUpAnimatorListener(animatorSet, onAnimationEnd);
return animatorSet;
}
- private void setUpAnimatorListener(final Animator animator,
- final List<RectF> destinationRectangles,
- final @ColorInt int color,
- final Runnable onAnimationEnd) {
+ private void setUpAnimatorListener(final Animator animator, final Runnable onAnimationEnd) {
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
@@ -553,15 +479,8 @@ final class SmartSelectSprite {
@Override
public void onAnimationEnd(Animator animator) {
- removeExistingDrawables();
-
- final List<Drawable> polygonShapes = mergeRectanglesToPolygonShape(
- destinationRectangles,
- color);
-
- for (Drawable drawable : polygonShapes) {
- addToOverlay(drawable);
- }
+ mExistingRectangleList.setDisplayType(RectangleList.DisplayType.POLYGON);
+ mExistingDrawable.invalidateSelf();
onAnimationEnd.run();
}
@@ -661,17 +580,12 @@ final class SmartSelectSprite {
&& y <= rectangle.bottom;
}
- private void addToOverlay(final Drawable drawable) {
- mView.getOverlay().add(drawable);
- mExistingAnimationDrawables.add(drawable);
- }
-
private void removeExistingDrawables() {
final ViewOverlay overlay = mView.getOverlay();
- for (Drawable drawable : mExistingAnimationDrawables) {
- overlay.remove(drawable);
- }
- mExistingAnimationDrawables.clear();
+ overlay.remove(mExistingDrawable);
+
+ mExistingDrawable = null;
+ mExistingRectangleList = null;
}
/**
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 80f4b99c8113..ceb06f511108 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1941,7 +1941,8 @@ public class ResolverActivity extends Activity {
final int checkedPos = mAdapterView.getCheckedItemPosition();
final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION;
if (!useLayoutWithDefault()
- && (!hasValidSelection || mLastSelected != checkedPos)) {
+ && (!hasValidSelection || mLastSelected != checkedPos)
+ && mAlwaysButton != null) {
setAlwaysButtonEnabled(hasValidSelection, checkedPos, true);
mOnceButton.setEnabled(hasValidSelection);
if (hasValidSelection) {
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index bd94fc7b2112..8ea0242b3549 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -1807,7 +1807,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
mFloatingActionMode.finish();
}
cleanupFloatingActionModeViews();
- mFloatingToolbar = new FloatingToolbar(mContext, mWindow);
+ mFloatingToolbar = new FloatingToolbar(mWindow);
final FloatingActionMode mode =
new FloatingActionMode(mContext, callback, originatingView, mFloatingToolbar);
mFloatingActionModeOriginatingView = originatingView;
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index 1d56e1ad3e03..f63b5a213528 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -120,8 +120,10 @@ public final class FloatingToolbar {
/**
* Initializes a floating toolbar.
*/
- public FloatingToolbar(Context context, Window window) {
- mContext = applyDefaultTheme(Preconditions.checkNotNull(context));
+ public FloatingToolbar(Window window) {
+ // TODO(b/65172902): Pass context in constructor when DecorView (and other callers)
+ // supports multi-display.
+ mContext = applyDefaultTheme(window.getContext());
mWindow = Preconditions.checkNotNull(window);
mPopup = new FloatingToolbarPopup(mContext, window.getDecorView());
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index c62934100540..d63e22c189f8 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -103,6 +103,7 @@ cc_library_shared {
"android_nio_utils.cpp",
"android_util_AssetManager.cpp",
"android_util_Binder.cpp",
+ "android_util_StatsLog.cpp",
"android_util_EventLog.cpp",
"android_util_MemoryIntArray.cpp",
"android_util_Log.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 5afd06750601..02c9848ea149 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -111,6 +111,7 @@ namespace android {
extern int register_android_app_admin_SecurityLog(JNIEnv* env);
extern int register_android_content_AssetManager(JNIEnv* env);
extern int register_android_util_EventLog(JNIEnv* env);
+extern int register_android_util_StatsLog(JNIEnv* env);
extern int register_android_util_Log(JNIEnv* env);
extern int register_android_util_MemoryIntArray(JNIEnv* env);
extern int register_android_util_PathParser(JNIEnv* env);
@@ -1311,6 +1312,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_com_android_internal_os_ZygoteInit_nativeZygoteInit),
REG_JNI(register_android_os_SystemClock),
REG_JNI(register_android_util_EventLog),
+ REG_JNI(register_android_util_StatsLog),
REG_JNI(register_android_util_Log),
REG_JNI(register_android_util_MemoryIntArray),
REG_JNI(register_android_util_PathParser),
diff --git a/core/jni/android_util_StatsLog.cpp b/core/jni/android_util_StatsLog.cpp
new file mode 100644
index 000000000000..c992365094f7
--- /dev/null
+++ b/core/jni/android_util_StatsLog.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2007-2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <fcntl.h>
+#include <log/log_event_list.h>
+
+#include <log/log.h>
+
+#include <nativehelper/JNIHelp.h>
+#include "core_jni_helpers.h"
+#include "jni.h"
+
+#define UNUSED __attribute__((__unused__))
+
+namespace android {
+
+static jclass gCollectionClass;
+static jmethodID gCollectionAddID;
+
+static jclass gIntegerClass;
+static jfieldID gIntegerValueID;
+
+static jclass gLongClass;
+static jfieldID gLongValueID;
+
+static jclass gFloatClass;
+static jfieldID gFloatValueID;
+
+static jclass gStringClass;
+
+/*
+ * In class android.util.StatsLog:
+ * static native int writeInt(int tag, int value)
+ */
+static jint android_util_StatsLog_write_Integer(JNIEnv* env UNUSED,
+ jobject clazz UNUSED,
+ jint tag, jint value)
+{
+ android_log_event_list ctx(tag);
+ ctx << (int32_t)value;
+ return ctx.write(LOG_ID_STATS);
+}
+
+/*
+ * In class android.util.StatsLog:
+ * static native int writeLong(long tag, long value)
+ */
+static jint android_util_StatsLog_write_Long(JNIEnv* env UNUSED,
+ jobject clazz UNUSED,
+ jint tag, jlong value)
+{
+ android_log_event_list ctx(tag);
+ ctx << (int64_t)value;
+ return ctx.write(LOG_ID_STATS);
+}
+
+/*
+ * In class android.util.StatsLog:
+ * static native int writeFloat(long tag, float value)
+ */
+static jint android_util_StatsLog_write_Float(JNIEnv* env UNUSED,
+ jobject clazz UNUSED,
+ jint tag, jfloat value)
+{
+ android_log_event_list ctx(tag);
+ ctx << (float)value;
+ return ctx.write(LOG_ID_STATS);
+}
+
+/*
+ * In class android.util.StatsLog:
+ * static native int writeString(int tag, String value)
+ */
+static jint android_util_StatsLog_write_String(JNIEnv* env,
+ jobject clazz UNUSED,
+ jint tag, jstring value) {
+ android_log_event_list ctx(tag);
+ // Don't throw NPE -- I feel like it's sort of mean for a logging function
+ // to be all crashy if you pass in NULL -- but make the NULL value explicit.
+ if (value != NULL) {
+ const char *str = env->GetStringUTFChars(value, NULL);
+ ctx << str;
+ env->ReleaseStringUTFChars(value, str);
+ } else {
+ ctx << "NULL";
+ }
+ return ctx.write(LOG_ID_STATS);
+}
+
+/*
+ * In class android.util.StatsLog:
+ * static native int writeArray(long tag, Object... value)
+ */
+static jint android_util_StatsLog_write_Array(JNIEnv* env, jobject clazz,
+ jint tag, jobjectArray value) {
+ android_log_event_list ctx(tag);
+
+ if (value == NULL) {
+ ctx << "[NULL]";
+ return ctx.write(LOG_ID_STATS);
+ }
+
+ jsize copied = 0, num = env->GetArrayLength(value);
+ for (; copied < num && copied < 255; ++copied) {
+ if (ctx.status()) break;
+ jobject item = env->GetObjectArrayElement(value, copied);
+ if (item == NULL) {
+ ctx << "NULL";
+ } else if (env->IsInstanceOf(item, gStringClass)) {
+ const char *str = env->GetStringUTFChars((jstring) item, NULL);
+ ctx << str;
+ env->ReleaseStringUTFChars((jstring) item, str);
+ } else if (env->IsInstanceOf(item, gIntegerClass)) {
+ ctx << (int32_t)env->GetIntField(item, gIntegerValueID);
+ } else if (env->IsInstanceOf(item, gLongClass)) {
+ ctx << (int64_t)env->GetLongField(item, gLongValueID);
+ } else if (env->IsInstanceOf(item, gFloatClass)) {
+ ctx << (float)env->GetFloatField(item, gFloatValueID);
+ } else {
+ jniThrowException(env,
+ "java/lang/IllegalArgumentException",
+ "Invalid payload item type");
+ return -1;
+ }
+ env->DeleteLocalRef(item);
+ }
+ return ctx.write(LOG_ID_STATS);
+}
+
+/*
+ * JNI registration.
+ */
+static const JNINativeMethod gRegisterMethods[] = {
+ /* name, signature, funcPtr */
+ { "writeInt", "(II)I", (void*) android_util_StatsLog_write_Integer },
+ { "writeLong", "(IJ)I", (void*) android_util_StatsLog_write_Long },
+ { "writeFloat", "(IF)I", (void*) android_util_StatsLog_write_Float },
+ { "writeString",
+ "(ILjava/lang/String;)I",
+ (void*) android_util_StatsLog_write_String
+ },
+ { "writeArray",
+ "(I[Ljava/lang/Object;)I",
+ (void*) android_util_StatsLog_write_Array
+ },
+};
+
+static struct { const char *name; jclass *clazz; } gClasses[] = {
+ { "java/lang/Integer", &gIntegerClass },
+ { "java/lang/Long", &gLongClass },
+ { "java/lang/Float", &gFloatClass },
+ { "java/lang/String", &gStringClass },
+ { "java/util/Collection", &gCollectionClass },
+};
+
+static struct { jclass *c; const char *name, *ft; jfieldID *id; } gFields[] = {
+ { &gIntegerClass, "value", "I", &gIntegerValueID },
+ { &gLongClass, "value", "J", &gLongValueID },
+ { &gFloatClass, "value", "F", &gFloatValueID },
+};
+
+static struct { jclass *c; const char *name, *mt; jmethodID *id; } gMethods[] = {
+ { &gCollectionClass, "add", "(Ljava/lang/Object;)Z", &gCollectionAddID },
+};
+
+int register_android_util_StatsLog(JNIEnv* env) {
+ for (int i = 0; i < NELEM(gClasses); ++i) {
+ jclass clazz = FindClassOrDie(env, gClasses[i].name);
+ *gClasses[i].clazz = MakeGlobalRefOrDie(env, clazz);
+ }
+
+ for (int i = 0; i < NELEM(gFields); ++i) {
+ *gFields[i].id = GetFieldIDOrDie(env,
+ *gFields[i].c, gFields[i].name, gFields[i].ft);
+ }
+
+ for (int i = 0; i < NELEM(gMethods); ++i) {
+ *gMethods[i].id = GetMethodIDOrDie(env,
+ *gMethods[i].c, gMethods[i].name, gMethods[i].mt);
+ }
+
+ return RegisterMethodsOrDie(
+ env,
+ "android/util/StatsLog",
+ gRegisterMethods, NELEM(gRegisterMethods));
+}
+
+}; // namespace android
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 7e9f561e68d5..f5b350b053d4 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -181,7 +181,7 @@
<shortcode country="mk" pattern="\\d{1,6}" free="129005|122" />
<!-- Mexico: 4-5 digits (not confirmed), known premium codes listed -->
- <shortcode country="mx" pattern="\\d{4,5}" premium="53035|7766" free="46645|5050|26259|50025|50052|9963" />
+ <shortcode country="mx" pattern="\\d{4,5}" premium="53035|7766" free="46645|5050|26259|50025|50052|9963|76551" />
<!-- Malaysia: 5 digits: http://www.skmm.gov.my/attachment/Consumer_Regulation/Mobile_Content_Services_FAQs.pdf -->
<shortcode country="my" pattern="\\d{5}" premium="32298|33776" free="22099|28288" />
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index b41e892adae9..8a57ea9a9c13 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -99,6 +99,7 @@ public class SettingsBackupTest {
Settings.Global.ALARM_MANAGER_CONSTANTS,
Settings.Global.ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED,
Settings.Global.ALWAYS_FINISH_ACTIVITIES,
+ Settings.Global.ALWAYS_ON_DISPLAY_CONSTANTS,
Settings.Global.ANIMATOR_DURATION_SCALE,
Settings.Global.ANOMALY_DETECTION_CONSTANTS,
Settings.Global.APN_DB_UPDATE_CONTENT_URL,
diff --git a/graphics/java/android/graphics/Color.java b/graphics/java/android/graphics/Color.java
index bdd828fd5127..c4bf9d3123bf 100644
--- a/graphics/java/android/graphics/Color.java
+++ b/graphics/java/android/graphics/Color.java
@@ -73,7 +73,7 @@ import java.util.function.DoubleUnaryOperator;
* <h4>Encoding</h4>
* <p>The four components of a color int are encoded in the following way:</p>
* <pre class="prettyprint">
- * int color = (A & 0xff) << 24 | (R & 0xff) << 16 | (G & 0xff) << 16 | (B & 0xff);
+ * int color = (A & 0xff) << 24 | (R & 0xff) << 16 | (G & 0xff) << 8 | (B & 0xff);
* </pre>
*
* <p>Because of this encoding, color ints can easily be described as an integer
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
index 4e59baa48983..3c3b3177159b 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -216,10 +216,7 @@ void BakedOpRenderer::drawRects(const float* rects, int count, const SkPaint* pa
.setTransform(Matrix4::identity(), TransformFlags::None)
.setModelViewIdentityEmptyBounds()
.build();
- // Disable blending if this is the first draw to the main framebuffer, in case app has defined
- // transparency where it doesn't make sense - as first draw in opaque window.
- bool overrideDisableBlending = !mHasDrawn && mOpaque && !mRenderTarget.frameBufferId;
- mRenderState.render(glop, mRenderTarget.orthoMatrix, overrideDisableBlending);
+ mRenderState.render(glop, mRenderTarget.orthoMatrix, false);
mHasDrawn = true;
}
@@ -350,8 +347,14 @@ void BakedOpRenderer::renderGlopImpl(const Rect* dirtyBounds, const ClipBase* cl
const Glop& glop) {
prepareRender(dirtyBounds, clip);
// Disable blending if this is the first draw to the main framebuffer, in case app has defined
- // transparency where it doesn't make sense - as first draw in opaque window.
- bool overrideDisableBlending = !mHasDrawn && mOpaque && !mRenderTarget.frameBufferId;
+ // transparency where it doesn't make sense - as first draw in opaque window. Note that we only
+ // apply this improvement when the blend mode is SRC_OVER - other modes (e.g. CLEAR) can be
+ // valid draws that affect other content (e.g. draw CLEAR, then draw DST_OVER)
+ bool overrideDisableBlending = !mHasDrawn
+ && mOpaque
+ && !mRenderTarget.frameBufferId
+ && glop.blend.src == GL_ONE
+ && glop.blend.dst == GL_ONE_MINUS_SRC_ALPHA;
mRenderState.render(glop, mRenderTarget.orthoMatrix, overrideDisableBlending);
if (!mRenderTarget.frameBufferId) mHasDrawn = true;
}
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index f4ce864e83e1..e0373cae9923 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -511,25 +511,19 @@ void Tree::updateCache(sp<skiapipeline::VectorDrawableAtlas>& atlas, GrContext*
}
}
if (!canReuseSurface || mCache.dirty) {
- draw(surface.get(), dst);
+ if (surface) {
+ Bitmap& bitmap = getBitmapUpdateIfDirty();
+ SkBitmap skiaBitmap;
+ bitmap.getSkBitmap(&skiaBitmap);
+ if (!surface->getCanvas()->writePixels(skiaBitmap, dst.fLeft, dst.fTop)) {
+ ALOGD("VectorDrawable caching failed to efficiently upload");
+ surface->getCanvas()->drawBitmap(skiaBitmap, dst.fLeft, dst.fTop);
+ }
+ }
mCache.dirty = false;
}
}
-void Tree::draw(SkSurface* surface, const SkRect& dst) {
- if (surface) {
- SkCanvas* canvas = surface->getCanvas();
- float scaleX = dst.width() / mProperties.getViewportWidth();
- float scaleY = dst.height() / mProperties.getViewportHeight();
- SkAutoCanvasRestore acr(canvas, true);
- canvas->translate(dst.fLeft, dst.fTop);
- canvas->clipRect(SkRect::MakeWH(dst.width(), dst.height()));
- canvas->clear(SK_ColorTRANSPARENT);
- canvas->scale(scaleX, scaleY);
- mRootNode->draw(canvas, false);
- }
-}
-
void Tree::Cache::setAtlas(sp<skiapipeline::VectorDrawableAtlas> newAtlas,
skiapipeline::AtlasKey newAtlasKey) {
LOG_ALWAYS_FATAL_IF(newAtlasKey == INVALID_ATLAS_KEY);
@@ -570,22 +564,15 @@ void Tree::draw(SkCanvas* canvas) {
// Handle the case when VectorDrawableAtlas has been destroyed, because of memory pressure.
// We render the VD into a temporary standalone buffer and mark the frame as dirty. Next
// frame will be cached into the atlas.
+ Bitmap& bitmap = getBitmapUpdateIfDirty();
+ SkBitmap skiaBitmap;
+ bitmap.getSkBitmap(&skiaBitmap);
+
int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth());
int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight());
- SkRect src = SkRect::MakeWH(scaledWidth, scaledHeight);
-#ifndef ANDROID_ENABLE_LINEAR_BLENDING
- sk_sp<SkColorSpace> colorSpace = nullptr;
-#else
- sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeSRGB();
-#endif
- SkImageInfo info = SkImageInfo::MakeN32(scaledWidth, scaledHeight, kPremul_SkAlphaType,
- colorSpace);
- sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(canvas->getGrContext(),
- SkBudgeted::kYes, info);
- draw(surface.get(), src);
+ canvas->drawBitmapRect(skiaBitmap, SkRect::MakeWH(scaledWidth, scaledHeight),
+ mutateProperties()->getBounds(), getPaint(), SkCanvas::kFast_SrcRectConstraint);
mCache.clear();
- canvas->drawImageRect(surface->makeImageSnapshot().get(), mutateProperties()->getBounds(),
- getPaint(), SkCanvas::kFast_SrcRectConstraint);
markDirty();
}
}
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index efbb695a14dd..10d3e05c067f 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -738,11 +738,6 @@ private:
bool canReuseBitmap(Bitmap*, int width, int height);
void updateBitmapCache(Bitmap& outCache, bool useStagingData);
- /**
- * Draws the root node into "surface" at a given "dst" position.
- */
- void draw(SkSurface* surface, const SkRect& dst);
-
// Cap the bitmap size, such that it won't hurt the performance too much
// and it won't crash due to a very large scale.
// The drawable will look blurry above this size.
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index a8463ecc44d8..742f14d04db4 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -118,6 +118,8 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers,
return;
}
+ ATRACE_FORMAT("drawLayer [%s] %.1f x %.1f", layerNode->getName(), bounds.width(), bounds.height());
+
layerNode->getSkiaLayer()->hasRenderedSinceRepaint = false;
layerCanvas->clear(SK_ColorTRANSPARENT);
@@ -143,7 +145,6 @@ bool SkiaPipeline::createOrUpdateLayer(RenderNode* node,
}
SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
SkASSERT(mRenderThread.getGrContext() != nullptr);
- // TODO: Handle wide color gamut requests
node->setLayerSurface(
SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes,
info, 0, &props));
@@ -194,10 +195,10 @@ void SkiaPipeline::renderVectorDrawableCache() {
sp<VectorDrawableAtlas> atlas = mRenderThread.cacheManager().acquireVectorDrawableAtlas();
auto grContext = mRenderThread.getGrContext();
atlas->prepareForDraw(grContext);
+ ATRACE_NAME("Update VectorDrawables");
for (auto vd : mVectorDrawables) {
vd->updateCache(atlas, grContext);
}
- grContext->flush();
mVectorDrawables.clear();
}
}
diff --git a/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp b/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp
index 23969908ff4d..9c9e17d600bf 100644
--- a/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp
+++ b/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp
@@ -270,7 +270,10 @@ sk_sp<SkSurface> VectorDrawableAtlas::createSurface(int width, int height, GrCon
sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeSRGB();
#endif
SkImageInfo info = SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType, colorSpace);
- return SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, info);
+ // This must have a top-left origin so that calls to surface->canvas->writePixels
+ // performs a basic texture upload instead of a more complex drawing operation
+ return SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, info, 0,
+ kTopLeft_GrSurfaceOrigin, nullptr);
}
void VectorDrawableAtlas::setStorageMode(StorageMode mode) {
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index 3308fc929b03..dc7fa8c00f82 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -28,6 +28,7 @@ interface IMediaRouterService {
MediaRouterClientState getState(IMediaRouterClient client);
boolean isPlaybackActive(IMediaRouterClient client);
+ boolean isGlobalBluetoothA2doOn();
void setDiscoveryRequest(IMediaRouterClient client, int routeTypes, boolean activeScan);
void setSelectedRoute(IMediaRouterClient client, String routeId, boolean explicit);
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 29b88a28294c..2894e8956c1c 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -88,7 +88,6 @@ public class MediaRouter {
RouteInfo mBluetoothA2dpRoute;
RouteInfo mSelectedRoute;
- RouteInfo mSystemAudioRoute;
final boolean mCanConfigureWifiDisplays;
boolean mActivelyScanningWifiDisplays;
@@ -150,7 +149,6 @@ public class MediaRouter {
}
addRouteStatic(mDefaultAudioVideo);
- mSystemAudioRoute = mDefaultAudioVideo;
// This will select the active wifi display route if there is one.
updateWifiDisplayStatus(mDisplayService.getWifiDisplayStatus());
@@ -185,7 +183,7 @@ public class MediaRouter {
}
void updateAudioRoutes(AudioRoutesInfo newRoutes) {
- boolean updated = false;
+ boolean audioRoutesChanged = false;
if (newRoutes.mainType != mCurAudioRoutesInfo.mainType) {
mCurAudioRoutesInfo.mainType = newRoutes.mainType;
int name;
@@ -201,11 +199,10 @@ public class MediaRouter {
}
mDefaultAudioVideo.mNameResId = name;
dispatchRouteChanged(mDefaultAudioVideo);
- updated = true;
+ audioRoutesChanged = true;
}
final int mainType = mCurAudioRoutesInfo.mainType;
-
if (!TextUtils.equals(newRoutes.bluetoothName, mCurAudioRoutesInfo.bluetoothName)) {
mCurAudioRoutesInfo.bluetoothName = newRoutes.bluetoothName;
if (mCurAudioRoutesInfo.bluetoothName != null) {
@@ -219,8 +216,6 @@ public class MediaRouter {
info.mDeviceType = RouteInfo.DEVICE_TYPE_BLUETOOTH;
mBluetoothA2dpRoute = info;
addRouteStatic(mBluetoothA2dpRoute);
- mSystemAudioRoute = mBluetoothA2dpRoute;
- selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mSystemAudioRoute, false);
} else {
mBluetoothA2dpRoute.mName = mCurAudioRoutesInfo.bluetoothName;
dispatchRouteChanged(mBluetoothA2dpRoute);
@@ -229,30 +224,32 @@ public class MediaRouter {
// BT disconnected
removeRouteStatic(mBluetoothA2dpRoute);
mBluetoothA2dpRoute = null;
- mSystemAudioRoute = mDefaultAudioVideo;
- selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mSystemAudioRoute, false);
- }
- updated = true;
- }
-
- if (mBluetoothA2dpRoute != null) {
- final boolean a2dpEnabled = isBluetoothA2dpOn();
- if (mSelectedRoute == mBluetoothA2dpRoute && !a2dpEnabled) {
- // A2DP off
- mSystemAudioRoute = mDefaultAudioVideo;
- updated = true;
- } else if ((mSelectedRoute == mDefaultAudioVideo || mSelectedRoute == null) &&
- a2dpEnabled) {
- // A2DP on or BT connected
- mSystemAudioRoute = mBluetoothA2dpRoute;
- updated = true;
}
+ audioRoutesChanged = true;
}
- if (updated) {
+
+ if (audioRoutesChanged) {
+ selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, getDefaultSystemAudioRoute(), false);
Log.v(TAG, "Audio routes updated: " + newRoutes + ", a2dp=" + isBluetoothA2dpOn());
}
}
+ RouteInfo getDefaultSystemAudioRoute() {
+ boolean globalBluetoothA2doOn = false;
+ try {
+ globalBluetoothA2doOn = mMediaRouterService.isGlobalBluetoothA2doOn();
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Unable to call isSystemBluetoothA2doOn.", ex);
+ }
+ return (globalBluetoothA2doOn && mBluetoothA2dpRoute != null)
+ ? mBluetoothA2dpRoute : mDefaultAudioVideo;
+ }
+
+ RouteInfo getCurrentSystemAudioRoute() {
+ return (isBluetoothA2dpOn() && mBluetoothA2dpRoute != null)
+ ? mBluetoothA2dpRoute : mDefaultAudioVideo;
+ }
+
boolean isBluetoothA2dpOn() {
try {
return mAudioService.isBluetoothA2dpOn();
@@ -603,15 +600,13 @@ public class MediaRouter {
@Override
public void onRestoreRoute() {
+ // Skip restoring route if the selected route is not a system audio route, or
+ // MediaRouter is initializing.
if ((mSelectedRoute != mDefaultAudioVideo && mSelectedRoute != mBluetoothA2dpRoute)
- || mSelectedRoute == mSystemAudioRoute) {
+ || mSelectedRoute == null) {
return;
}
- try {
- sStatic.mAudioService.setBluetoothA2dpOn(mSelectedRoute == mBluetoothA2dpRoute);
- } catch (RemoteException e) {
- Log.e(TAG, "Error changing Bluetooth A2DP state", e);
- }
+ mSelectedRoute.select();
}
}
}
@@ -946,7 +941,7 @@ public class MediaRouter {
boolean wasDefaultOrBluetoothRoute = (oldRoute == sStatic.mDefaultAudioVideo
|| oldRoute == sStatic.mBluetoothA2dpRoute);
if (oldRoute == route
- && (!wasDefaultOrBluetoothRoute || oldRoute == sStatic.mSystemAudioRoute)) {
+ && (!wasDefaultOrBluetoothRoute || route == sStatic.getCurrentSystemAudioRoute())) {
return;
}
if (!route.matchesTypes(types)) {
diff --git a/packages/SystemUI/res/drawable/car_qs_background_primary.xml b/packages/SystemUI/res/drawable/car_qs_background_primary.xml
deleted file mode 100644
index 0f77987bb7ce..000000000000
--- a/packages/SystemUI/res/drawable/car_qs_background_primary.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<inset xmlns:android="http://schemas.android.com/apk/res/android">
- <shape>
- <solid android:color="?android:attr/colorPrimaryDark"/>
- </shape>
-</inset>
diff --git a/packages/SystemUI/res/layout/battery_percentage_view.xml b/packages/SystemUI/res/layout/battery_percentage_view.xml
index deb494fdefbc..59c0957d98cb 100644
--- a/packages/SystemUI/res/layout/battery_percentage_view.xml
+++ b/packages/SystemUI/res/layout/battery_percentage_view.xml
@@ -26,4 +26,5 @@
android:textColor="?android:attr/textColorPrimary"
android:gravity="center_vertical|start"
android:paddingEnd="@dimen/battery_level_padding_start"
+ android:importantForAccessibility="no"
/>
diff --git a/packages/SystemUI/res/layout/car_qs_panel.xml b/packages/SystemUI/res/layout/car_qs_panel.xml
index 0b46b0bdaa1c..4cb0fd5fecfb 100644
--- a/packages/SystemUI/res/layout/car_qs_panel.xml
+++ b/packages/SystemUI/res/layout/car_qs_panel.xml
@@ -18,12 +18,13 @@
android:id="@+id/quick_settings_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@drawable/car_qs_background_primary"
+ android:background="@color/car_qs_background_primary"
android:orientation="vertical"
- android:elevation="4dp">
+ android:elevation="4dp"
+ android:theme="@android:style/Theme">
- <include layout="@layout/car_status_bar_header" />
- <include layout="@layout/car_qs_footer" />
+ <include layout="@layout/car_status_bar_header"/>
+ <include layout="@layout/car_qs_footer"/>
<com.android.systemui.statusbar.car.UserGridView
android:id="@+id/user_grid"
diff --git a/packages/SystemUI/res/values/colors_car.xml b/packages/SystemUI/res/values/colors_car.xml
index 9593fe51917c..1b8c2fa68244 100644
--- a/packages/SystemUI/res/values/colors_car.xml
+++ b/packages/SystemUI/res/values/colors_car.xml
@@ -17,6 +17,7 @@
*/
-->
<resources>
+ <color name="car_qs_background_primary">#263238</color> <!-- Blue Gray 900 -->
<color name="car_user_switcher_progress_bgcolor">#00000000</color> <!-- Transparent -->
<color name="car_user_switcher_progress_fgcolor">#80CBC4</color> <!-- Teal 200 -->
<color name="car_user_switcher_no_user_image_bgcolor">#FAFAFA</color> <!-- Grey 50 -->
diff --git a/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java b/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
new file mode 100644
index 000000000000..d1d180819eef
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
@@ -0,0 +1,122 @@
+/*
+ * 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.doze;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.provider.Settings;
+import android.text.format.DateUtils;
+import android.util.KeyValueListParser;
+import android.util.Log;
+
+import com.android.systemui.R;
+
+import java.util.Arrays;
+
+/**
+ * Class to store the policy for AOD, which comes from
+ * {@link android.provider.Settings.Global}
+ */
+public class AlwaysOnDisplayPolicy {
+ public static final String TAG = "AlwaysOnDisplayPolicy";
+
+ static final String KEY_SCREEN_BRIGHTNESS_ARRAY = "screen_brightness_array";
+ static final String KEY_DIMMING_SCRIM_ARRAY = "dimming_scrim_array";
+ static final String KEY_PROX_SCREEN_OFF_DELAY_MS = "prox_screen_off_delay";
+ static final String KEY_PROX_COOLDOWN_TRIGGER_MS = "prox_cooldown_trigger";
+ static final String KEY_PROX_COOLDOWN_PERIOD_MS = "prox_cooldown_period";
+
+ /**
+ * Integer array to map ambient brightness type to real screen brightness.
+ *
+ * @see Settings.Global#ALWAYS_ON_DISPLAY_CONSTANTS
+ * @see #KEY_SCREEN_BRIGHTNESS_ARRAY
+ */
+ public final int[] screenBrightnessArray;
+
+ /**
+ * Integer array to map ambient brightness type to dimming scrim.
+ *
+ * @see Settings.Global#ALWAYS_ON_DISPLAY_CONSTANTS
+ * @see #KEY_DIMMING_SCRIM_ARRAY
+ */
+ public final int[] dimmingScrimArray;
+
+ /**
+ * Delay time(ms) from covering the prox to turning off the screen.
+ *
+ * @see Settings.Global#ALWAYS_ON_DISPLAY_CONSTANTS
+ * @see #KEY_PROX_SCREEN_OFF_DELAY_MS
+ */
+ public final long proxScreenOffDelayMs;
+
+ /**
+ * The threshold time(ms) to trigger the cooldown timer, which will
+ * turn off prox sensor for a period.
+ *
+ * @see Settings.Global#ALWAYS_ON_DISPLAY_CONSTANTS
+ * @see #KEY_PROX_COOLDOWN_TRIGGER_MS
+ */
+ public final long proxCooldownTriggerMs;
+
+ /**
+ * The period(ms) to turning off the prox sensor if
+ * {@link #KEY_PROX_COOLDOWN_TRIGGER_MS} is triggered.
+ *
+ * @see Settings.Global#ALWAYS_ON_DISPLAY_CONSTANTS
+ * @see #KEY_PROX_COOLDOWN_PERIOD_MS
+ */
+ public final long proxCooldownPeriodMs;
+
+ private final KeyValueListParser mParser;
+
+ public AlwaysOnDisplayPolicy(Context context) {
+ final Resources resources = context.getResources();
+ mParser = new KeyValueListParser(',');
+
+ final String value = Settings.Global.getString(context.getContentResolver(),
+ Settings.Global.ALWAYS_ON_DISPLAY_CONSTANTS);
+
+ try {
+ mParser.setString(value);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Bad AOD constants");
+ }
+
+ proxScreenOffDelayMs = mParser.getLong(KEY_PROX_SCREEN_OFF_DELAY_MS,
+ 10 * DateUtils.MINUTE_IN_MILLIS);
+ proxCooldownTriggerMs = mParser.getLong(KEY_PROX_COOLDOWN_TRIGGER_MS,
+ 2 * DateUtils.MINUTE_IN_MILLIS);
+ proxCooldownPeriodMs = mParser.getLong(KEY_PROX_COOLDOWN_PERIOD_MS,
+ 5 * DateUtils.MINUTE_IN_MILLIS);
+ screenBrightnessArray = parseIntArray(KEY_SCREEN_BRIGHTNESS_ARRAY,
+ resources.getIntArray(R.array.config_doze_brightness_sensor_to_brightness));
+ dimmingScrimArray = parseIntArray(KEY_DIMMING_SCRIM_ARRAY,
+ resources.getIntArray(R.array.config_doze_brightness_sensor_to_scrim_opacity));
+ }
+
+ private int[] parseIntArray(final String key, final int[] defaultArray) {
+ final String value = mParser.getString(key, null);
+ if (value != null) {
+ return Arrays.stream(value.split(":")).map(String::trim).mapToInt(
+ Integer::parseInt).toArray();
+ } else {
+ return defaultArray;
+ }
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index d374d68a456b..6f8bcff16a83 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -59,7 +59,7 @@ public class DozeFactory {
DozeMachine machine = new DozeMachine(wrappedService, config, wakeLock);
machine.setParts(new DozeMachine.Part[]{
- new DozePauser(handler, machine, alarmManager),
+ new DozePauser(handler, machine, alarmManager, new AlwaysOnDisplayPolicy(context)),
new DozeFalsingManagerAdapter(FalsingManager.getInstance(context)),
createDozeTriggers(context, sensorManager, host, alarmManager, config, params,
handler, wakeLock, machine),
@@ -76,7 +76,8 @@ public class DozeFactory {
Handler handler) {
Sensor sensor = DozeSensors.findSensorWithType(sensorManager,
context.getString(R.string.doze_brightness_sensor_type));
- return new DozeScreenBrightness(context, service, sensorManager, sensor, host, handler);
+ return new DozeScreenBrightness(context, service, sensorManager, sensor, host, handler,
+ new AlwaysOnDisplayPolicy(context));
}
private DozeTriggers createDozeTriggers(Context context, SensorManager sensorManager,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java b/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java
index a33b454c6430..76a190213ba3 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java
@@ -26,20 +26,22 @@ import com.android.systemui.util.AlarmTimeout;
*/
public class DozePauser implements DozeMachine.Part {
public static final String TAG = DozePauser.class.getSimpleName();
- private static final long TIMEOUT = 10 * 1000;
private final AlarmTimeout mPauseTimeout;
private final DozeMachine mMachine;
+ private final long mTimeoutMs;
- public DozePauser(Handler handler, DozeMachine machine, AlarmManager alarmManager) {
+ public DozePauser(Handler handler, DozeMachine machine, AlarmManager alarmManager,
+ AlwaysOnDisplayPolicy policy) {
mMachine = machine;
mPauseTimeout = new AlarmTimeout(alarmManager, this::onTimeout, TAG, handler);
+ mTimeoutMs = policy.proxScreenOffDelayMs;
}
@Override
public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
switch (newState) {
case DOZE_AOD_PAUSING:
- mPauseTimeout.schedule(TIMEOUT, AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
+ mPauseTimeout.schedule(mTimeoutMs, AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
break;
default:
mPauseTimeout.cancel();
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index 30420529df56..11b4b0ef8294 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -42,7 +42,7 @@ public class DozeScreenBrightness implements DozeMachine.Part, SensorEventListen
public DozeScreenBrightness(Context context, DozeMachine.Service service,
SensorManager sensorManager, Sensor lightSensor, DozeHost host,
- Handler handler) {
+ Handler handler, AlwaysOnDisplayPolicy policy) {
mContext = context;
mDozeService = service;
mSensorManager = sensorManager;
@@ -50,10 +50,8 @@ public class DozeScreenBrightness implements DozeMachine.Part, SensorEventListen
mDozeHost = host;
mHandler = handler;
- mSensorToBrightness = context.getResources().getIntArray(
- R.array.config_doze_brightness_sensor_to_brightness);
- mSensorToScrimOpacity = context.getResources().getIntArray(
- R.array.config_doze_brightness_sensor_to_scrim_opacity);
+ mSensorToBrightness = policy.screenBrightnessArray;
+ mSensorToScrimOpacity = policy.dimmingScrimArray;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 566353c74b57..91cde378c41b 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -72,7 +72,7 @@ public class DozeSensors {
public DozeSensors(Context context, AlarmManager alarmManager, SensorManager sensorManager,
DozeParameters dozeParameters,
AmbientDisplayConfiguration config, WakeLock wakeLock, Callback callback,
- Consumer<Boolean> proxCallback) {
+ Consumer<Boolean> proxCallback, AlwaysOnDisplayPolicy policy) {
mContext = context;
mAlarmManager = alarmManager;
mSensorManager = sensorManager;
@@ -112,7 +112,7 @@ public class DozeSensors {
true /* touchscreen */),
};
- mProxSensor = new ProxSensor();
+ mProxSensor = new ProxSensor(policy);
mCallback = callback;
}
@@ -206,17 +206,16 @@ public class DozeSensors {
private class ProxSensor implements SensorEventListener {
- static final long COOLDOWN_TRIGGER = 2 * 1000;
- static final long COOLDOWN_PERIOD = 5 * 1000;
-
boolean mRequested;
boolean mRegistered;
Boolean mCurrentlyFar;
long mLastNear;
final AlarmTimeout mCooldownTimer;
+ final AlwaysOnDisplayPolicy mPolicy;
- public ProxSensor() {
+ public ProxSensor(AlwaysOnDisplayPolicy policy) {
+ mPolicy = policy;
mCooldownTimer = new AlarmTimeout(mAlarmManager, this::updateRegistered,
"prox_cooldown", mHandler);
}
@@ -264,11 +263,12 @@ public class DozeSensors {
// Sensor has been unregistered by the proxCallback. Do nothing.
} else if (!mCurrentlyFar) {
mLastNear = now;
- } else if (mCurrentlyFar && now - mLastNear < COOLDOWN_TRIGGER) {
+ } else if (mCurrentlyFar && now - mLastNear < mPolicy.proxCooldownTriggerMs) {
// If the last near was very recent, we might be using more power for prox
// wakeups than we're saving from turning of the screen. Instead, turn it off
// for a while.
- mCooldownTimer.schedule(COOLDOWN_PERIOD, AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
+ mCooldownTimer.schedule(mPolicy.proxCooldownPeriodMs,
+ AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
updateRegistered();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 45831601a0f2..f7a258a2c959 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -84,7 +84,8 @@ public class DozeTriggers implements DozeMachine.Part {
mWakeLock = wakeLock;
mAllowPulseTriggers = allowPulseTriggers;
mDozeSensors = new DozeSensors(context, alarmManager, mSensorManager, dozeParameters,
- config, wakeLock, this::onSensor, this::onProximityFar);
+ config, wakeLock, this::onSensor, this::onProximityFar,
+ new AlwaysOnDisplayPolicy(context));
mUiModeManager = mContext.getSystemService(UiModeManager.class);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index c2a7ed3fdad4..aecf95fc677f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -132,6 +132,11 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
// Preloads the next task
RecentsConfiguration config = Recents.getConfiguration();
if (config.svelteLevel == RecentsConfiguration.SVELTE_NONE) {
+ Rect windowRect = getWindowRect(null /* windowRectOverride */);
+ if (windowRect.isEmpty()) {
+ return;
+ }
+
// Load the next task only if we aren't svelte
SystemServicesProxy ssp = Recents.getSystemServices();
ActivityManager.RunningTaskInfo runningTaskInfo = ssp.getRunningTask();
@@ -146,8 +151,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
// This callback is made when a new activity is launched and the old one is
// paused so ignore the current activity and try and preload the thumbnail for
// the previous one.
- updateDummyStackViewLayout(mBackgroundLayoutAlgorithm, stack,
- getWindowRect(null /* windowRectOverride */));
+ updateDummyStackViewLayout(mBackgroundLayoutAlgorithm, stack, windowRect);
// Launched from app is always the worst case (in terms of how many
// thumbnails/tasks visible)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 46f9c04aa42e..afe5c917a856 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -404,8 +404,8 @@ public abstract class PanelView extends FrameLayout {
false /* collapseWhenFinished */);
notifyBarPanelExpansionChanged();
if (mVibrateOnOpening && !isHapticFeedbackDisabled(mContext)) {
- AsyncTask.execute(
- () -> mVibrator.vibrate(VibrationEffect.get(VibrationEffect.EFFECT_TICK)));
+ AsyncTask.execute(() ->
+ mVibrator.vibrate(VibrationEffect.get(VibrationEffect.EFFECT_TICK, false)));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 03f42a6f760d..d7f11f710501 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -422,7 +422,7 @@ public class StatusBarWindowView extends FrameLayout {
mFloatingActionMode.finish();
}
cleanupFloatingActionModeViews();
- mFloatingToolbar = new FloatingToolbar(mContext, mFakeWindow);
+ mFloatingToolbar = new FloatingToolbar(mFakeWindow);
final FloatingActionMode mode =
new FloatingActionMode(mContext, callback, originatingView, mFloatingToolbar);
mFloatingActionModeOriginatingView = originatingView;
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index 5e71dd4684c5..27c16d53ce78 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -54,7 +54,8 @@ LOCAL_STATIC_JAVA_LIBRARIES := \
SystemUI-proto \
SystemUI-tags \
legacy-android-test \
- testables
+ testables \
+ truth-prebuilt \
LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common android.car
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/AlwaysOnDisplayPolicyTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/AlwaysOnDisplayPolicyTest.java
new file mode 100644
index 000000000000..abc2d0e5c845
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/AlwaysOnDisplayPolicyTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.doze;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.provider.Settings;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.text.format.DateUtils;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class AlwaysOnDisplayPolicyTest extends SysuiTestCase {
+ private static final String ALWAYS_ON_DISPLAY_CONSTANTS_VALUE = "prox_screen_off_delay=1000"
+ + ",prox_cooldown_trigger=2000"
+ + ",prox_cooldown_period=3000"
+ + ",screen_brightness_array=1:2:3:4:5"
+ + ",dimming_scrim_array=5:4:3:2:1";
+
+ private String mPreviousConfig;
+
+ @Before
+ public void setUp() {
+ mPreviousConfig = Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.ALWAYS_ON_DISPLAY_CONSTANTS);
+ }
+
+ @After
+ public void tearDown() {
+ Settings.Global.putString(mContext.getContentResolver(),
+ Settings.Global.ALWAYS_ON_DISPLAY_CONSTANTS, mPreviousConfig);
+ }
+
+ @Test
+ public void testPolicy_valueNull_containsDefaultValue() throws Exception {
+ Settings.Global.putString(mContext.getContentResolver(),
+ Settings.Global.ALWAYS_ON_DISPLAY_CONSTANTS, null);
+
+ AlwaysOnDisplayPolicy policy = new AlwaysOnDisplayPolicy(mContext);
+
+ assertThat(policy.proxScreenOffDelayMs).isEqualTo(10 * DateUtils.MINUTE_IN_MILLIS);
+ assertThat(policy.proxCooldownTriggerMs).isEqualTo(2 * DateUtils.MINUTE_IN_MILLIS);
+ assertThat(policy.proxCooldownPeriodMs).isEqualTo(5 * DateUtils.MINUTE_IN_MILLIS);
+ assertThat(policy.screenBrightnessArray).isEqualTo(mContext.getResources().getIntArray(
+ R.array.config_doze_brightness_sensor_to_brightness));
+ assertThat(policy.dimmingScrimArray).isEqualTo(mContext.getResources().getIntArray(
+ R.array.config_doze_brightness_sensor_to_scrim_opacity));
+ }
+
+ @Test
+ public void testPolicy_valueNotNull_containsValue() throws Exception {
+ Settings.Global.putString(mContext.getContentResolver(),
+ Settings.Global.ALWAYS_ON_DISPLAY_CONSTANTS, ALWAYS_ON_DISPLAY_CONSTANTS_VALUE);
+
+ AlwaysOnDisplayPolicy policy = new AlwaysOnDisplayPolicy(mContext);
+
+ assertThat(policy.proxScreenOffDelayMs).isEqualTo(1000);
+ assertThat(policy.proxCooldownTriggerMs).isEqualTo(2000);
+ assertThat(policy.proxCooldownPeriodMs).isEqualTo(3000);
+ assertThat(policy.screenBrightnessArray).isEqualTo(new int[]{1, 2, 3, 4, 5});
+ assertThat(policy.dimmingScrimArray).isEqualTo(new int[]{5, 4, 3, 2, 1});
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index c2758068a4ed..46e1d5562714 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -60,7 +60,8 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
mSensorManager = new FakeSensorManager(mContext);
mSensor = mSensorManager.getFakeLightSensor();
mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
- mSensor.getSensor(), mHostFake, null /* handler */);
+ mSensor.getSensor(), mHostFake, null /* handler */,
+ new AlwaysOnDisplayPolicy(mContext));
}
@Test
@@ -135,7 +136,8 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
@Test
public void testNullSensor() throws Exception {
mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
- null /* sensor */, mHostFake, null /* handler */);
+ null /* sensor */, mHostFake, null /* handler */,
+ new AlwaysOnDisplayPolicy(mContext));
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 7690f6d2853c..8e782c0dd32b 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -1516,7 +1516,7 @@ message MetricsEvent {
// OS: N
ACTION_ZEN_ALLOW_LIGHTS = 264;
- // OPEN: Settings > Notifications > [App] > Topic Notifications
+ // OPEN: Settings > Notifications > [App] > Channel Notifications
// CATEGORY: SETTINGS
// OS: N
NOTIFICATION_TOPIC_NOTIFICATION = 265;
@@ -4360,6 +4360,11 @@ message MetricsEvent {
// CATEGORY: SETTINGS
SETTINGS_FEATURE_FLAGS_DASHBOARD = 1156;
+ // OPEN: Settings > Notifications > [App] > Topic Notifications
+ // CATEGORY: SETTINGS
+ // OS: P
+ NOTIFICATION_CHANNEL_GROUP = 1157;
+
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 7324b82351f6..c60647fada09 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -417,7 +417,8 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
final boolean triggerable = (mEnabledFeatures
& FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0;
mMagnificationGestureHandler = new MagnificationGestureHandler(
- mContext, mAms, detectControlGestures, triggerable);
+ mContext, mAms.getMagnificationController(),
+ detectControlGestures, triggerable);
addFirstEventHandler(mMagnificationGestureHandler);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/GestureUtils.java b/services/accessibility/java/com/android/server/accessibility/GestureUtils.java
index bc761914caf2..abfdb683c04c 100644
--- a/services/accessibility/java/com/android/server/accessibility/GestureUtils.java
+++ b/services/accessibility/java/com/android/server/accessibility/GestureUtils.java
@@ -12,32 +12,27 @@ final class GestureUtils {
/* cannot be instantiated */
}
- public static boolean isTap(MotionEvent down, MotionEvent up, int tapTimeSlop,
- int tapDistanceSlop, int actionIndex) {
- return eventsWithinTimeAndDistanceSlop(down, up, tapTimeSlop, tapDistanceSlop, actionIndex);
- }
-
public static boolean isMultiTap(MotionEvent firstUp, MotionEvent secondUp,
- int multiTapTimeSlop, int multiTapDistanceSlop, int actionIndex) {
+ int multiTapTimeSlop, int multiTapDistanceSlop) {
+ if (firstUp == null || secondUp == null) return false;
return eventsWithinTimeAndDistanceSlop(firstUp, secondUp, multiTapTimeSlop,
- multiTapDistanceSlop, actionIndex);
+ multiTapDistanceSlop);
}
private static boolean eventsWithinTimeAndDistanceSlop(MotionEvent first, MotionEvent second,
- int timeout, int distance, int actionIndex) {
+ int timeout, int distance) {
if (isTimedOut(first, second, timeout)) {
return false;
}
- final double deltaMove = computeDistance(first, second, actionIndex);
+ final double deltaMove = distance(first, second);
if (deltaMove >= distance) {
return false;
}
return true;
}
- public static double computeDistance(MotionEvent first, MotionEvent second, int pointerIndex) {
- return MathUtils.dist(first.getX(pointerIndex), first.getY(pointerIndex),
- second.getX(pointerIndex), second.getY(pointerIndex));
+ public static double distance(MotionEvent first, MotionEvent second) {
+ return MathUtils.dist(first.getX(), first.getY(), second.getX(), second.getY());
}
public static boolean isTimedOut(MotionEvent firstUp, MotionEvent secondUp, int timeout) {
@@ -54,7 +49,6 @@ final class GestureUtils {
/**
* Determines whether a two pointer gesture is a dragging one.
*
- * @param event The event with the pointer data.
* @return True if the gesture is a dragging one.
*/
public static boolean isDraggingGesture(float firstPtrDownX, float firstPtrDownY,
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
index caa74b9512d1..98b8e6b723ac 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
@@ -16,11 +16,6 @@
package com.android.server.accessibility;
-import com.android.internal.R;
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.os.SomeArgs;
-import com.android.server.LocalServices;
-
import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.content.BroadcastReceiver;
@@ -42,6 +37,12 @@ import android.view.View;
import android.view.WindowManagerInternal;
import android.view.animation.DecelerateInterpolator;
+import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.SomeArgs;
+import com.android.server.LocalServices;
+
import java.util.Locale;
/**
@@ -138,7 +139,7 @@ class MagnificationController implements Handler.Callback {
private final WindowManagerInternal mWindowManager;
// Flag indicating that we are registered with window manager.
- private boolean mRegistered;
+ @VisibleForTesting boolean mRegistered;
private boolean mUnregisterPending;
@@ -148,9 +149,14 @@ class MagnificationController implements Handler.Callback {
mHandler = new Handler(context.getMainLooper(), this);
}
- public MagnificationController(Context context, AccessibilityManagerService ams, Object lock,
- Handler handler, WindowManagerInternal windowManagerInternal,
- ValueAnimator valueAnimator, SettingsBridge settingsBridge) {
+ public MagnificationController(
+ Context context,
+ AccessibilityManagerService ams,
+ Object lock,
+ Handler handler,
+ WindowManagerInternal windowManagerInternal,
+ ValueAnimator valueAnimator,
+ SettingsBridge settingsBridge) {
mHandler = handler;
mWindowManager = windowManagerInternal;
mMainThreadId = context.getMainLooper().getThread().getId();
@@ -672,8 +678,7 @@ class MagnificationController implements Handler.Callback {
* Resets magnification if magnification and auto-update are both enabled.
*
* @param animate whether the animate the transition
- * @return {@code true} if magnification was reset to the disabled state,
- * {@code false} if magnification is still active
+ * @return whether was {@link #isMagnifying magnifying}
*/
boolean resetIfNeeded(boolean animate) {
synchronized (mLock) {
@@ -790,6 +795,19 @@ class MagnificationController implements Handler.Callback {
return true;
}
+ @Override
+ public String toString() {
+ return "MagnificationController{" +
+ "mCurrentMagnificationSpec=" + mCurrentMagnificationSpec +
+ ", mMagnificationRegion=" + mMagnificationRegion +
+ ", mMagnificationBounds=" + mMagnificationBounds +
+ ", mUserId=" + mUserId +
+ ", mIdOfLastServiceToMagnify=" + mIdOfLastServiceToMagnify +
+ ", mRegistered=" + mRegistered +
+ ", mUnregisterPending=" + mUnregisterPending +
+ '}';
+ }
+
/**
* Class responsible for animating spec on the main thread and sending spec
* updates to the window manager.
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
index b1ac5891dafa..d6452f87d155 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
@@ -16,6 +16,21 @@
package com.android.server.accessibility;
+import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_POINTER_DOWN;
+import static android.view.MotionEvent.ACTION_POINTER_UP;
+import static android.view.MotionEvent.ACTION_UP;
+
+import static com.android.server.accessibility.GestureUtils.distance;
+
+import static java.lang.Math.abs;
+import static java.util.Arrays.asList;
+import static java.util.Arrays.copyOfRange;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -27,7 +42,6 @@ import android.util.Slog;
import android.util.TypedValue;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
-import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.MotionEvent.PointerCoords;
@@ -37,6 +51,8 @@ import android.view.ScaleGestureDetector.OnScaleGestureListener;
import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityEvent;
+import com.android.internal.annotations.VisibleForTesting;
+
/**
* This class handles magnification in response to touch events.
*
@@ -85,91 +101,109 @@ import android.view.accessibility.AccessibilityEvent;
*
* 7. The magnification scale will be persisted in settings and in the cloud.
*/
+@SuppressWarnings("WeakerAccess")
class MagnificationGestureHandler implements EventStreamTransformation {
private static final String LOG_TAG = "MagnificationEventHandler";
- private static final boolean DEBUG_STATE_TRANSITIONS = false;
- private static final boolean DEBUG_DETECTING = false;
- private static final boolean DEBUG_PANNING = false;
+ private static final boolean DEBUG_ALL = false;
+ private static final boolean DEBUG_STATE_TRANSITIONS = false || DEBUG_ALL;
+ private static final boolean DEBUG_DETECTING = false || DEBUG_ALL;
+ private static final boolean DEBUG_PANNING = false || DEBUG_ALL;
- private static final int STATE_DELEGATING = 1;
- private static final int STATE_DETECTING = 2;
- private static final int STATE_VIEWPORT_DRAGGING = 3;
- private static final int STATE_MAGNIFIED_INTERACTION = 4;
+ /** @see #handleMotionEventStateDelegating */
+ @VisibleForTesting static final int STATE_DELEGATING = 1;
+ /** @see DetectingStateHandler */
+ @VisibleForTesting static final int STATE_DETECTING = 2;
+ /** @see ViewportDraggingStateHandler */
+ @VisibleForTesting static final int STATE_VIEWPORT_DRAGGING = 3;
+ /** @see PanningScalingStateHandler */
+ @VisibleForTesting static final int STATE_PANNING_SCALING = 4;
private static final float MIN_SCALE = 2.0f;
private static final float MAX_SCALE = 5.0f;
- private final MagnificationController mMagnificationController;
- private final DetectingStateHandler mDetectingStateHandler;
- private final MagnifiedContentInteractionStateHandler mMagnifiedContentInteractionStateHandler;
- private final StateViewportDraggingHandler mStateViewportDraggingHandler;
+ @VisibleForTesting final MagnificationController mMagnificationController;
+
+ @VisibleForTesting final DetectingStateHandler mDetectingStateHandler;
+ @VisibleForTesting final PanningScalingStateHandler mPanningScalingStateHandler;
+ @VisibleForTesting final ViewportDraggingStateHandler mViewportDraggingStateHandler;
private final ScreenStateReceiver mScreenStateReceiver;
- private final boolean mDetectTripleTap;
- private final boolean mTriggerable;
+ /**
+ * {@code true} if this detector should detect and respond to triple-tap
+ * gestures for engaging and disengaging magnification,
+ * {@code false} if it should ignore such gestures
+ */
+ final boolean mDetectTripleTap;
+
+ /**
+ * Whether {@link #mShortcutTriggered shortcut} is enabled
+ */
+ final boolean mDetectShortcutTrigger;
- private EventStreamTransformation mNext;
+ EventStreamTransformation mNext;
- private int mCurrentState;
- private int mPreviousState;
+ @VisibleForTesting int mCurrentState;
+ @VisibleForTesting int mPreviousState;
- private boolean mTranslationEnabledBeforePan;
+ @VisibleForTesting boolean mShortcutTriggered;
- private boolean mShortcutTriggered;
+ /**
+ * Time of last {@link MotionEvent#ACTION_DOWN} while in {@link #STATE_DELEGATING}
+ */
+ long mDelegatingStateDownTime;
private PointerCoords[] mTempPointerCoords;
private PointerProperties[] mTempPointerProperties;
- private long mDelegatingStateDownTime;
-
/**
* @param context Context for resolving various magnification-related resources
- * @param ams AccessibilityManagerService used to obtain a {@link MagnificationController}
+ * @param magnificationController the {@link MagnificationController}
+ *
* @param detectTripleTap {@code true} if this detector should detect and respond to triple-tap
- * gestures for engaging and disengaging magnification,
- * {@code false} if it should ignore such gestures
- * @param triggerable {@code true} if this detector should be "triggerable" by some external
- * shortcut invoking {@link #notifyShortcutTriggered}, {@code
- * false} if it should ignore such triggers.
+ * gestures for engaging and disengaging magnification,
+ * {@code false} if it should ignore such gestures
+ * @param detectShortcutTrigger {@code true} if this detector should be "triggerable" by some
+ * external shortcut invoking {@link #notifyShortcutTriggered},
+ * {@code false} if it should ignore such triggers.
*/
- public MagnificationGestureHandler(Context context, AccessibilityManagerService ams,
- boolean detectTripleTap, boolean triggerable) {
- mMagnificationController = ams.getMagnificationController();
+ public MagnificationGestureHandler(Context context,
+ MagnificationController magnificationController,
+ boolean detectTripleTap,
+ boolean detectShortcutTrigger) {
+ mMagnificationController = magnificationController;
+
mDetectingStateHandler = new DetectingStateHandler(context);
- mStateViewportDraggingHandler = new StateViewportDraggingHandler();
- mMagnifiedContentInteractionStateHandler =
- new MagnifiedContentInteractionStateHandler(context);
+ mViewportDraggingStateHandler = new ViewportDraggingStateHandler();
+ mPanningScalingStateHandler =
+ new PanningScalingStateHandler(context);
+
mDetectTripleTap = detectTripleTap;
- mTriggerable = triggerable;
+ mDetectShortcutTrigger = detectShortcutTrigger;
- if (triggerable) {
+ if (mDetectShortcutTrigger) {
mScreenStateReceiver = new ScreenStateReceiver(context, this);
mScreenStateReceiver.register();
} else {
mScreenStateReceiver = null;
}
- transitionToState(STATE_DETECTING);
+ transitionTo(STATE_DETECTING);
}
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
- if (!event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) {
- if (mNext != null) {
- mNext.onMotionEvent(event, rawEvent, policyFlags);
- }
- return;
- }
- if (!mDetectTripleTap && !mTriggerable) {
- if (mNext != null) {
- dispatchTransformedEvent(event, rawEvent, policyFlags);
- }
+ if ((!mDetectTripleTap && !mDetectShortcutTrigger)
+ || !event.isFromSource(SOURCE_TOUCHSCREEN)) {
+ dispatchTransformedEvent(event, rawEvent, policyFlags);
return;
}
- mMagnifiedContentInteractionStateHandler.onMotionEvent(event, rawEvent, policyFlags);
- switch (mCurrentState) {
+ // Local copy to avoid dispatching the same event to more than one state handler
+ // in case mPanningScalingStateHandler changes mCurrentState
+ int currentState = mCurrentState;
+ mPanningScalingStateHandler.onMotionEvent(event, rawEvent, policyFlags);
+ switch (currentState) {
case STATE_DELEGATING: {
handleMotionEventStateDelegating(event, rawEvent, policyFlags);
}
@@ -179,17 +213,17 @@ class MagnificationGestureHandler implements EventStreamTransformation {
}
break;
case STATE_VIEWPORT_DRAGGING: {
- mStateViewportDraggingHandler.onMotionEvent(event, rawEvent, policyFlags);
+ mViewportDraggingStateHandler.onMotionEvent(event, rawEvent, policyFlags);
}
break;
- case STATE_MAGNIFIED_INTERACTION: {
- // mMagnifiedContentInteractionStateHandler handles events only
+ case STATE_PANNING_SCALING: {
+ // mPanningScalingStateHandler handles events only
// if this is the current state since it uses ScaleGestureDetector
// and a GestureDetector which need well formed event stream.
}
break;
default: {
- throw new IllegalStateException("Unknown state: " + mCurrentState);
+ throw new IllegalStateException("Unknown state: " + currentState);
}
}
}
@@ -215,8 +249,8 @@ class MagnificationGestureHandler implements EventStreamTransformation {
@Override
public void clearEvents(int inputSource) {
- if (inputSource == InputDevice.SOURCE_TOUCHSCREEN) {
- clear();
+ if (inputSource == SOURCE_TOUCHSCREEN) {
+ clearAndTransitionToStateDetecting();
}
if (mNext != null) {
@@ -229,20 +263,25 @@ class MagnificationGestureHandler implements EventStreamTransformation {
if (mScreenStateReceiver != null) {
mScreenStateReceiver.unregister();
}
- clear();
+ clearAndTransitionToStateDetecting();
}
void notifyShortcutTriggered() {
- if (mTriggerable) {
- if (mMagnificationController.resetIfNeeded(true)) {
- clear();
+ if (mDetectShortcutTrigger) {
+ boolean wasMagnifying = mMagnificationController.resetIfNeeded(/* animate */ true);
+ if (wasMagnifying) {
+ clearAndTransitionToStateDetecting();
} else {
- setMagnificationShortcutTriggered(!mShortcutTriggered);
+ toggleShortcutTriggered();
}
}
}
- private void setMagnificationShortcutTriggered(boolean state) {
+ private void toggleShortcutTriggered() {
+ setShortcutTriggered(!mShortcutTriggered);
+ }
+
+ private void setShortcutTriggered(boolean state) {
if (mShortcutTriggered == state) {
return;
}
@@ -251,27 +290,25 @@ class MagnificationGestureHandler implements EventStreamTransformation {
mMagnificationController.setForceShowMagnifiableBounds(state);
}
- private void clear() {
+ void clearAndTransitionToStateDetecting() {
+ setShortcutTriggered(false);
mCurrentState = STATE_DETECTING;
- setMagnificationShortcutTriggered(false);
mDetectingStateHandler.clear();
- mStateViewportDraggingHandler.clear();
- mMagnifiedContentInteractionStateHandler.clear();
+ mViewportDraggingStateHandler.clear();
+ mPanningScalingStateHandler.clear();
}
private void handleMotionEventStateDelegating(MotionEvent event,
MotionEvent rawEvent, int policyFlags) {
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_DOWN: {
- mDelegatingStateDownTime = event.getDownTime();
- }
- break;
- case MotionEvent.ACTION_UP: {
- if (mDetectingStateHandler.mDelayedEventQueue == null) {
- transitionToState(STATE_DETECTING);
- }
- }
- break;
+ if (event.getActionMasked() == ACTION_UP) {
+ transitionTo(STATE_DETECTING);
+ }
+ delegateEvent(event, rawEvent, policyFlags);
+ }
+
+ void delegateEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ mDelegatingStateDownTime = event.getDownTime();
}
if (mNext != null) {
// We cache some events to see if the user wants to trigger magnification.
@@ -287,13 +324,15 @@ class MagnificationGestureHandler implements EventStreamTransformation {
private void dispatchTransformedEvent(MotionEvent event, MotionEvent rawEvent,
int policyFlags) {
- // If the event is within the magnified portion of the screen we have
+ if (mNext == null) return; // Nowhere to dispatch to
+
+ // If the touchscreen event is within the magnified portion of the screen we have
// to change its location to be where the user thinks he is poking the
// UI which may have been magnified and panned.
- final float eventX = event.getX();
- final float eventY = event.getY();
if (mMagnificationController.isMagnifying()
- && mMagnificationController.magnificationRegionContains(eventX, eventY)) {
+ && event.isFromSource(SOURCE_TOUCHSCREEN)
+ && mMagnificationController.magnificationRegionContains(
+ event.getX(), event.getY())) {
final float scale = mMagnificationController.getScale();
final float scaledOffsetX = mMagnificationController.getOffsetX();
final float scaledOffsetY = mMagnificationController.getOffsetY();
@@ -347,34 +386,27 @@ class MagnificationGestureHandler implements EventStreamTransformation {
return mTempPointerProperties;
}
- private void transitionToState(int state) {
+ private void transitionTo(int state) {
if (DEBUG_STATE_TRANSITIONS) {
- switch (state) {
- case STATE_DELEGATING: {
- Slog.i(LOG_TAG, "mCurrentState: STATE_DELEGATING");
- }
- break;
- case STATE_DETECTING: {
- Slog.i(LOG_TAG, "mCurrentState: STATE_DETECTING");
- }
- break;
- case STATE_VIEWPORT_DRAGGING: {
- Slog.i(LOG_TAG, "mCurrentState: STATE_VIEWPORT_DRAGGING");
- }
- break;
- case STATE_MAGNIFIED_INTERACTION: {
- Slog.i(LOG_TAG, "mCurrentState: STATE_MAGNIFIED_INTERACTION");
- }
- break;
- default: {
- throw new IllegalArgumentException("Unknown state: " + state);
- }
- }
+ Slog.i(LOG_TAG, (stateToString(mCurrentState) + " -> " + stateToString(state)
+ + " at " + asList(copyOfRange(new RuntimeException().getStackTrace(), 1, 5)))
+ .replace(getClass().getName(), ""));
}
mPreviousState = mCurrentState;
mCurrentState = state;
}
+ private static String stateToString(int state) {
+ switch (state) {
+ case STATE_DELEGATING: return "STATE_DELEGATING";
+ case STATE_DETECTING: return "STATE_DETECTING";
+ case STATE_VIEWPORT_DRAGGING: return "STATE_VIEWPORT_DRAGGING";
+ case STATE_PANNING_SCALING: return "STATE_PANNING_SCALING";
+ case 0: return "0";
+ default: throw new IllegalArgumentException("Unknown state: " + state);
+ }
+ }
+
private interface MotionEventHandler {
void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags);
@@ -384,21 +416,20 @@ class MagnificationGestureHandler implements EventStreamTransformation {
/**
* This class determines if the user is performing a scale or pan gesture.
+ *
+ * @see #STATE_PANNING_SCALING
*/
- private final class MagnifiedContentInteractionStateHandler extends SimpleOnGestureListener
+ final class PanningScalingStateHandler extends SimpleOnGestureListener
implements OnScaleGestureListener, MotionEventHandler {
private final ScaleGestureDetector mScaleGestureDetector;
-
private final GestureDetector mGestureDetector;
+ final float mScalingThreshold;
- private final float mScalingThreshold;
+ float mInitialScaleFactor = -1;
+ boolean mScaling;
- private float mInitialScaleFactor = -1;
-
- private boolean mScaling;
-
- public MagnifiedContentInteractionStateHandler(Context context) {
+ public PanningScalingStateHandler(Context context) {
final TypedValue scaleValue = new TypedValue();
context.getResources().getValue(
com.android.internal.R.dimen.config_screen_magnification_scaling_threshold,
@@ -411,26 +442,39 @@ class MagnificationGestureHandler implements EventStreamTransformation {
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ // Dispatches #onScaleBegin, #onScale, #onScaleEnd
mScaleGestureDetector.onTouchEvent(event);
+ // Dispatches #onScroll
mGestureDetector.onTouchEvent(event);
- if (mCurrentState != STATE_MAGNIFIED_INTERACTION) {
+
+ if (mCurrentState != STATE_PANNING_SCALING) {
return;
}
- if (event.getActionMasked() == MotionEvent.ACTION_UP) {
- clear();
- mMagnificationController.persistScale();
- if (mPreviousState == STATE_VIEWPORT_DRAGGING) {
- transitionToState(STATE_VIEWPORT_DRAGGING);
- } else {
- transitionToState(STATE_DETECTING);
- }
+
+ int action = event.getActionMasked();
+ if (action == ACTION_POINTER_UP
+ && event.getPointerCount() == 2 // includes the pointer currently being released
+ && mPreviousState == STATE_VIEWPORT_DRAGGING) {
+
+ persistScaleAndTransitionTo(STATE_VIEWPORT_DRAGGING);
+
+ } else if (action == ACTION_UP) {
+
+ persistScaleAndTransitionTo(STATE_DETECTING);
+
}
}
+ public void persistScaleAndTransitionTo(int state) {
+ mMagnificationController.persistScale();
+ clear();
+ transitionTo(state);
+ }
+
@Override
- public boolean onScroll(MotionEvent first, MotionEvent second, float distanceX,
- float distanceY) {
- if (mCurrentState != STATE_MAGNIFIED_INTERACTION) {
+ public boolean onScroll(MotionEvent first, MotionEvent second,
+ float distanceX, float distanceY) {
+ if (mCurrentState != STATE_PANNING_SCALING) {
return true;
}
if (DEBUG_PANNING) {
@@ -447,14 +491,15 @@ class MagnificationGestureHandler implements EventStreamTransformation {
if (!mScaling) {
if (mInitialScaleFactor < 0) {
mInitialScaleFactor = detector.getScaleFactor();
+ return false;
+ }
+ final float deltaScale = detector.getScaleFactor() - mInitialScaleFactor;
+ if (abs(deltaScale) > mScalingThreshold) {
+ mScaling = true;
+ return true;
} else {
- final float deltaScale = detector.getScaleFactor() - mInitialScaleFactor;
- if (Math.abs(deltaScale) > mScalingThreshold) {
- mScaling = true;
- return true;
- }
+ return false;
}
- return false;
}
final float initialScale = mMagnificationController.getScale();
@@ -485,7 +530,7 @@ class MagnificationGestureHandler implements EventStreamTransformation {
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
- return (mCurrentState == STATE_MAGNIFIED_INTERACTION);
+ return (mCurrentState == STATE_PANNING_SCALING);
}
@Override
@@ -498,60 +543,65 @@ class MagnificationGestureHandler implements EventStreamTransformation {
mInitialScaleFactor = -1;
mScaling = false;
}
+
+ @Override
+ public String toString() {
+ return "MagnifiedContentInteractionStateHandler{" +
+ "mInitialScaleFactor=" + mInitialScaleFactor +
+ ", mScaling=" + mScaling +
+ '}';
+ }
}
/**
* This class handles motion events when the event dispatcher has
* determined that the user is performing a single-finger drag of the
* magnification viewport.
+ *
+ * @see #STATE_VIEWPORT_DRAGGING
*/
- private final class StateViewportDraggingHandler implements MotionEventHandler {
+ final class ViewportDraggingStateHandler implements MotionEventHandler {
+ /** Whether to disable zoom after dragging ends */
+ boolean mZoomedInBeforeDrag;
private boolean mLastMoveOutsideMagnifiedRegion;
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
final int action = event.getActionMasked();
switch (action) {
- case MotionEvent.ACTION_DOWN: {
- throw new IllegalArgumentException("Unexpected event type: ACTION_DOWN");
- }
- case MotionEvent.ACTION_POINTER_DOWN: {
+ case ACTION_POINTER_DOWN: {
clear();
- transitionToState(STATE_MAGNIFIED_INTERACTION);
+ transitionTo(STATE_PANNING_SCALING);
}
break;
- case MotionEvent.ACTION_MOVE: {
+ case ACTION_MOVE: {
if (event.getPointerCount() != 1) {
throw new IllegalStateException("Should have one pointer down.");
}
final float eventX = event.getX();
final float eventY = event.getY();
if (mMagnificationController.magnificationRegionContains(eventX, eventY)) {
- if (mLastMoveOutsideMagnifiedRegion) {
- mLastMoveOutsideMagnifiedRegion = false;
- mMagnificationController.setCenter(eventX, eventY, true,
- AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
- } else {
- mMagnificationController.setCenter(eventX, eventY, false,
- AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
- }
+ mMagnificationController.setCenter(eventX, eventY,
+ /* animate */ mLastMoveOutsideMagnifiedRegion,
+ AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+ mLastMoveOutsideMagnifiedRegion = false;
} else {
mLastMoveOutsideMagnifiedRegion = true;
}
}
break;
- case MotionEvent.ACTION_UP: {
- if (!mTranslationEnabledBeforePan) {
- mMagnificationController.reset(true);
- }
+ case ACTION_UP: {
+ if (!mZoomedInBeforeDrag) zoomOff();
clear();
- transitionToState(STATE_DETECTING);
+ transitionTo(STATE_DETECTING);
}
break;
- case MotionEvent.ACTION_POINTER_UP: {
+
+ case ACTION_DOWN:
+ case ACTION_POINTER_UP: {
throw new IllegalArgumentException(
- "Unexpected event type: ACTION_POINTER_UP");
+ "Unexpected event type: " + MotionEvent.actionToString(action));
}
}
}
@@ -560,211 +610,224 @@ class MagnificationGestureHandler implements EventStreamTransformation {
public void clear() {
mLastMoveOutsideMagnifiedRegion = false;
}
+
+ @Override
+ public String toString() {
+ return "ViewportDraggingStateHandler{" +
+ "mZoomedInBeforeDrag=" + mZoomedInBeforeDrag +
+ ", mLastMoveOutsideMagnifiedRegion=" + mLastMoveOutsideMagnifiedRegion +
+ '}';
+ }
}
/**
* This class handles motion events when the event dispatch has not yet
* determined what the user is doing. It watches for various tap events.
+ *
+ * @see #STATE_DETECTING
*/
- private final class DetectingStateHandler implements MotionEventHandler {
-
- private static final int MESSAGE_ON_ACTION_TAP_AND_HOLD = 1;
+ final class DetectingStateHandler implements MotionEventHandler, Handler.Callback {
+ private static final int MESSAGE_ON_TRIPLE_TAP_AND_HOLD = 1;
private static final int MESSAGE_TRANSITION_TO_DELEGATING_STATE = 2;
- private static final int ACTION_TAP_COUNT = 3;
-
- private final int mTapTimeSlop = ViewConfiguration.getJumpTapTimeout();
-
- private final int mMultiTapTimeSlop;
-
- private final int mTapDistanceSlop;
-
- private final int mMultiTapDistanceSlop;
+ final int mLongTapMinDelay = ViewConfiguration.getJumpTapTimeout();
+ final int mSwipeMinDistance;
+ final int mMultiTapMaxDelay;
+ final int mMultiTapMaxDistance;
private MotionEventInfo mDelayedEventQueue;
+ MotionEvent mLastDown;
+ private MotionEvent mPreLastDown;
+ private MotionEvent mLastUp;
+ private MotionEvent mPreLastUp;
- private MotionEvent mLastDownEvent;
-
- private MotionEvent mLastTapUpEvent;
-
- private int mTapCount;
+ Handler mHandler = new Handler(this);
public DetectingStateHandler(Context context) {
- mMultiTapTimeSlop = ViewConfiguration.getDoubleTapTimeout()
+ mMultiTapMaxDelay = ViewConfiguration.getDoubleTapTimeout()
+ context.getResources().getInteger(
com.android.internal.R.integer.config_screen_magnification_multi_tap_adjustment);
- mTapDistanceSlop = ViewConfiguration.get(context).getScaledTouchSlop();
- mMultiTapDistanceSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop();
- }
-
- private final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- final int type = message.what;
- switch (type) {
- case MESSAGE_ON_ACTION_TAP_AND_HOLD: {
- MotionEvent event = (MotionEvent) message.obj;
- final int policyFlags = message.arg1;
- onActionTapAndHold(event, policyFlags);
- }
- break;
- case MESSAGE_TRANSITION_TO_DELEGATING_STATE: {
- transitionToState(STATE_DELEGATING);
- sendDelayedMotionEvents();
- clear();
- }
- break;
- default: {
- throw new IllegalArgumentException("Unknown message type: " + type);
- }
+ mSwipeMinDistance = ViewConfiguration.get(context).getScaledTouchSlop();
+ mMultiTapMaxDistance = ViewConfiguration.get(context).getScaledDoubleTapSlop();
+ }
+
+ @Override
+ public boolean handleMessage(Message message) {
+ final int type = message.what;
+ switch (type) {
+ case MESSAGE_ON_TRIPLE_TAP_AND_HOLD: {
+ onTripleTapAndHold(/* down */ (MotionEvent) message.obj);
+ }
+ break;
+ case MESSAGE_TRANSITION_TO_DELEGATING_STATE: {
+ transitionToDelegatingState(/* andClear */ true);
+ }
+ break;
+ default: {
+ throw new IllegalArgumentException("Unknown message type: " + type);
}
}
- };
+ return true;
+ }
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
cacheDelayedMotionEvent(event, rawEvent, policyFlags);
- final int action = event.getActionMasked();
- switch (action) {
+ switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
+
mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
+
if (!mMagnificationController.magnificationRegionContains(
event.getX(), event.getY())) {
- transitionToDelegatingState(!mShortcutTriggered);
- return;
- }
- if (mShortcutTriggered) {
- Message message = mHandler.obtainMessage(MESSAGE_ON_ACTION_TAP_AND_HOLD,
- policyFlags, 0, event);
- mHandler.sendMessageDelayed(message,
- ViewConfiguration.getLongPressTimeout());
- return;
- }
- if (mDetectTripleTap) {
- if ((mTapCount == ACTION_TAP_COUNT - 1) && (mLastDownEvent != null)
- && GestureUtils.isMultiTap(mLastDownEvent, event, mMultiTapTimeSlop,
- mMultiTapDistanceSlop, 0)) {
- Message message = mHandler.obtainMessage(MESSAGE_ON_ACTION_TAP_AND_HOLD,
- policyFlags, 0, event);
- mHandler.sendMessageDelayed(message,
- ViewConfiguration.getLongPressTimeout());
- } else if (mTapCount < ACTION_TAP_COUNT) {
- Message message = mHandler.obtainMessage(
- MESSAGE_TRANSITION_TO_DELEGATING_STATE);
- mHandler.sendMessageDelayed(message, mMultiTapTimeSlop);
- }
- clearLastDownEvent();
- mLastDownEvent = MotionEvent.obtain(event);
- } else if (mMagnificationController.isMagnifying()) {
- // If magnified, consume an ACTION_DOWN until mMultiTapTimeSlop or
- // mTapDistanceSlop is reached to ensure MAGNIFIED_INTERACTION is reachable.
- Message message = mHandler.obtainMessage(
- MESSAGE_TRANSITION_TO_DELEGATING_STATE);
- mHandler.sendMessageDelayed(message, mMultiTapTimeSlop);
- return;
+
+ transitionToDelegatingState(/* andClear */ !mShortcutTriggered);
+
+ } else if (isMultiTapTriggered(2 /* taps */)) {
+
+ // 3tap and hold
+ delayedTransitionToDraggingState(event);
+
+ } else if (mDetectTripleTap
+ // If magnified, delay an ACTION_DOWN for mMultiTapMaxDelay
+ // to ensure reachability of
+ // STATE_PANNING_SCALING(triggerable with ACTION_POINTER_DOWN)
+ || mMagnificationController.isMagnifying()) {
+
+ delayedTransitionToDelegatingState();
+
} else {
- transitionToDelegatingState(true);
- return;
+
+ // Delegate pending events without delay
+ transitionToDelegatingState(/* andClear */ true);
}
}
break;
- case MotionEvent.ACTION_POINTER_DOWN: {
+ case ACTION_POINTER_DOWN: {
if (mMagnificationController.isMagnifying()) {
- mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
- transitionToState(STATE_MAGNIFIED_INTERACTION);
+ transitionTo(STATE_PANNING_SCALING);
clear();
} else {
- transitionToDelegatingState(true);
+ transitionToDelegatingState(/* andClear */ true);
}
}
break;
- case MotionEvent.ACTION_MOVE: {
- if (mLastDownEvent != null && mTapCount < ACTION_TAP_COUNT - 1) {
- final double distance = GestureUtils.computeDistance(mLastDownEvent,
- event, 0);
- if (Math.abs(distance) > mTapDistanceSlop) {
- transitionToDelegatingState(true);
- }
+ case ACTION_MOVE: {
+ if (isFingerDown()
+ && distance(mLastDown, /* move */ event) > mSwipeMinDistance
+ // For convenience, viewport dragging on 3tap&hold takes precedence
+ // over insta-delegating on 3tap&swipe
+ // (which is a rare combo to be used aside from magnification)
+ && !isMultiTapTriggered(2 /* taps */)) {
+
+ // Swipe detected - delegate skipping timeout
+ transitionToDelegatingState(/* andClear */ true);
}
}
break;
- case MotionEvent.ACTION_UP: {
- mHandler.removeMessages(MESSAGE_ON_ACTION_TAP_AND_HOLD);
+ case ACTION_UP: {
+
+ mHandler.removeMessages(MESSAGE_ON_TRIPLE_TAP_AND_HOLD);
+
if (!mMagnificationController.magnificationRegionContains(
event.getX(), event.getY())) {
- transitionToDelegatingState(!mShortcutTriggered);
- return;
- }
- if (mShortcutTriggered) {
- clear();
- onActionTap(event, policyFlags);
- return;
- }
- if (mLastDownEvent == null) {
- return;
- }
- if (!GestureUtils.isTap(mLastDownEvent, event, mTapTimeSlop,
- mTapDistanceSlop, 0)) {
- transitionToDelegatingState(true);
- return;
- }
- if (mLastTapUpEvent != null && !GestureUtils.isMultiTap(
- mLastTapUpEvent, event, mMultiTapTimeSlop, mMultiTapDistanceSlop, 0)) {
- transitionToDelegatingState(true);
- return;
- }
- mTapCount++;
- if (DEBUG_DETECTING) {
- Slog.i(LOG_TAG, "Tap count:" + mTapCount);
- }
- if (mTapCount == ACTION_TAP_COUNT) {
- clear();
- onActionTap(event, policyFlags);
- return;
+
+ transitionToDelegatingState(/* andClear */ !mShortcutTriggered);
+
+ } else if (isMultiTapTriggered(3 /* taps */)) {
+
+ onTripleTap(/* up */ event);
+
+ } else if (
+ // Possible to be false on: 3tap&drag -> scale -> PTR_UP -> UP
+ isFingerDown()
+ //TODO long tap should never happen here
+ && (timeBetween(mLastDown, /* mLastUp */ event) >= mLongTapMinDelay)
+ || distance(mLastDown, /* mLastUp */ event)
+ >= mSwipeMinDistance) {
+
+ transitionToDelegatingState(/* andClear */ true);
+
}
- clearLastTapUpEvent();
- mLastTapUpEvent = MotionEvent.obtain(event);
- }
- break;
- case MotionEvent.ACTION_POINTER_UP: {
- /* do nothing */
}
break;
}
}
- @Override
- public void clear() {
- setMagnificationShortcutTriggered(false);
- mHandler.removeMessages(MESSAGE_ON_ACTION_TAP_AND_HOLD);
- mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
- clearTapDetectionState();
- clearDelayedMotionEvents();
+ public boolean isMultiTapTriggered(int numTaps) {
+
+ // Shortcut acts as the 2 initial taps
+ if (mShortcutTriggered) return tapCount() + 2 >= numTaps;
+
+ return mDetectTripleTap
+ && tapCount() >= numTaps
+ && isMultiTap(mPreLastDown, mLastDown)
+ && isMultiTap(mPreLastUp, mLastUp);
}
- private void clearTapDetectionState() {
- mTapCount = 0;
- clearLastTapUpEvent();
- clearLastDownEvent();
+ private boolean isMultiTap(MotionEvent first, MotionEvent second) {
+ return GestureUtils.isMultiTap(first, second, mMultiTapMaxDelay, mMultiTapMaxDistance);
}
- private void clearLastTapUpEvent() {
- if (mLastTapUpEvent != null) {
- mLastTapUpEvent.recycle();
- mLastTapUpEvent = null;
- }
+ public boolean isFingerDown() {
+ return mLastDown != null;
}
- private void clearLastDownEvent() {
- if (mLastDownEvent != null) {
- mLastDownEvent.recycle();
- mLastDownEvent = null;
- }
+ private long timeBetween(@Nullable MotionEvent a, @Nullable MotionEvent b) {
+ if (a == null && b == null) return 0;
+ return abs(timeOf(a) - timeOf(b));
+ }
+
+ /**
+ * Nullsafe {@link MotionEvent#getEventTime} that interprets null event as something that
+ * has happened long enough ago to be gone from the event queue.
+ * Thus the time for a null event is a small number, that is below any other non-null
+ * event's time.
+ *
+ * @return {@link MotionEvent#getEventTime}, or {@link Long#MIN_VALUE} if the event is null
+ */
+ private long timeOf(@Nullable MotionEvent event) {
+ return event != null ? event.getEventTime() : Long.MIN_VALUE;
+ }
+
+ public int tapCount() {
+ return MotionEventInfo.countOf(mDelayedEventQueue, ACTION_UP);
}
+ /** -> {@link #STATE_DELEGATING} */
+ public void delayedTransitionToDelegatingState() {
+ mHandler.sendEmptyMessageDelayed(
+ MESSAGE_TRANSITION_TO_DELEGATING_STATE,
+ mMultiTapMaxDelay);
+ }
+
+ /** -> {@link #STATE_VIEWPORT_DRAGGING} */
+ public void delayedTransitionToDraggingState(MotionEvent event) {
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(MESSAGE_ON_TRIPLE_TAP_AND_HOLD, event),
+ ViewConfiguration.getLongPressTimeout());
+ }
+
+ @Override
+ public void clear() {
+ setShortcutTriggered(false);
+ mHandler.removeMessages(MESSAGE_ON_TRIPLE_TAP_AND_HOLD);
+ mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
+ clearDelayedMotionEvents();
+ }
+
+
private void cacheDelayedMotionEvent(MotionEvent event, MotionEvent rawEvent,
int policyFlags) {
+ if (event.getActionMasked() == ACTION_DOWN) {
+ mPreLastDown = mLastDown;
+ mLastDown = event;
+ } else if (event.getActionMasked() == ACTION_UP) {
+ mPreLastUp = mLastUp;
+ mLastUp = event;
+ }
+
MotionEventInfo info = MotionEventInfo.obtain(event, rawEvent,
policyFlags);
if (mDelayedEventQueue == null) {
@@ -782,8 +845,13 @@ class MagnificationGestureHandler implements EventStreamTransformation {
while (mDelayedEventQueue != null) {
MotionEventInfo info = mDelayedEventQueue;
mDelayedEventQueue = info.mNext;
- MagnificationGestureHandler.this.onMotionEvent(info.mEvent, info.mRawEvent,
- info.mPolicyFlags);
+
+ // Because MagnifiedInteractionStateHandler requires well-formed event stream
+ mPanningScalingStateHandler.onMotionEvent(
+ info.event, info.rawEvent, info.policyFlags);
+
+ delegateEvent(info.event, info.rawEvent, info.policyFlags);
+
info.recycle();
}
}
@@ -794,91 +862,136 @@ class MagnificationGestureHandler implements EventStreamTransformation {
mDelayedEventQueue = info.mNext;
info.recycle();
}
+ mPreLastDown = null;
+ mPreLastUp = null;
+ mLastDown = null;
+ mLastUp = null;
}
- private void transitionToDelegatingState(boolean andClear) {
- transitionToState(STATE_DELEGATING);
+ void transitionToDelegatingState(boolean andClear) {
+ transitionTo(STATE_DELEGATING);
sendDelayedMotionEvents();
- if (andClear) {
- clear();
- }
+ if (andClear) clear();
}
- private void onActionTap(MotionEvent up, int policyFlags) {
+ private void onTripleTap(MotionEvent up) {
+
if (DEBUG_DETECTING) {
- Slog.i(LOG_TAG, "onActionTap()");
+ Slog.i(LOG_TAG, "onTripleTap(); delayed: "
+ + MotionEventInfo.toString(mDelayedEventQueue));
}
+ clear();
- if (!mMagnificationController.isMagnifying()) {
- final float targetScale = mMagnificationController.getPersistedScale();
- final float scale = MathUtils.constrain(targetScale, MIN_SCALE, MAX_SCALE);
- mMagnificationController.setScaleAndCenter(scale, up.getX(), up.getY(), true,
- AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+ // Toggle zoom
+ if (mMagnificationController.isMagnifying()) {
+ zoomOff();
} else {
- mMagnificationController.reset(true);
+ zoomOn(up.getX(), up.getY());
}
}
- private void onActionTapAndHold(MotionEvent down, int policyFlags) {
- if (DEBUG_DETECTING) {
- Slog.i(LOG_TAG, "onActionTapAndHold()");
- }
+ void onTripleTapAndHold(MotionEvent down) {
+ if (DEBUG_DETECTING) Slog.i(LOG_TAG, "onTripleTapAndHold()");
clear();
- mTranslationEnabledBeforePan = mMagnificationController.isMagnifying();
- final float targetScale = mMagnificationController.getPersistedScale();
- final float scale = MathUtils.constrain(targetScale, MIN_SCALE, MAX_SCALE);
- mMagnificationController.setScaleAndCenter(scale, down.getX(), down.getY(), true,
- AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+ mViewportDraggingStateHandler.mZoomedInBeforeDrag =
+ mMagnificationController.isMagnifying();
+
+ zoomOn(down.getX(), down.getY());
- transitionToState(STATE_VIEWPORT_DRAGGING);
+ transitionTo(STATE_VIEWPORT_DRAGGING);
+ }
+
+ @Override
+ public String toString() {
+ return "DetectingStateHandler{" +
+ "tapCount()=" + tapCount() +
+ ", mDelayedEventQueue=" + MotionEventInfo.toString(mDelayedEventQueue) +
+ '}';
}
}
+ private void zoomOn(float centerX, float centerY) {
+ final float scale = MathUtils.constrain(
+ mMagnificationController.getPersistedScale(),
+ MIN_SCALE, MAX_SCALE);
+ mMagnificationController.setScaleAndCenter(
+ scale, centerX, centerY,
+ /* animate */ true,
+ AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+ }
+
+ private void zoomOff() {
+ mMagnificationController.reset(/* animate */ true);
+ }
+
+ private static MotionEvent recycleAndNullify(@Nullable MotionEvent event) {
+ if (event != null) {
+ event.recycle();
+ }
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return "MagnificationGestureHandler{" +
+ "mDetectingStateHandler=" + mDetectingStateHandler +
+ ", mMagnifiedInteractionStateHandler=" + mPanningScalingStateHandler +
+ ", mViewportDraggingStateHandler=" + mViewportDraggingStateHandler +
+ ", mDetectTripleTap=" + mDetectTripleTap +
+ ", mDetectShortcutTrigger=" + mDetectShortcutTrigger +
+ ", mCurrentState=" + stateToString(mCurrentState) +
+ ", mPreviousState=" + stateToString(mPreviousState) +
+ ", mShortcutTriggered=" + mShortcutTriggered +
+ ", mDelegatingStateDownTime=" + mDelegatingStateDownTime +
+ ", mMagnificationController=" + mMagnificationController +
+ '}';
+ }
+
private static final class MotionEventInfo {
private static final int MAX_POOL_SIZE = 10;
-
private static final Object sLock = new Object();
-
private static MotionEventInfo sPool;
-
private static int sPoolSize;
private MotionEventInfo mNext;
-
private boolean mInPool;
- public MotionEvent mEvent;
-
- public MotionEvent mRawEvent;
-
- public int mPolicyFlags;
+ public MotionEvent event;
+ public MotionEvent rawEvent;
+ public int policyFlags;
public static MotionEventInfo obtain(MotionEvent event, MotionEvent rawEvent,
int policyFlags) {
synchronized (sLock) {
- MotionEventInfo info;
- if (sPoolSize > 0) {
- sPoolSize--;
- info = sPool;
- sPool = info.mNext;
- info.mNext = null;
- info.mInPool = false;
- } else {
- info = new MotionEventInfo();
- }
+ MotionEventInfo info = obtainInternal();
info.initialize(event, rawEvent, policyFlags);
return info;
}
}
+ @NonNull
+ private static MotionEventInfo obtainInternal() {
+ MotionEventInfo info;
+ if (sPoolSize > 0) {
+ sPoolSize--;
+ info = sPool;
+ sPool = info.mNext;
+ info.mNext = null;
+ info.mInPool = false;
+ } else {
+ info = new MotionEventInfo();
+ }
+ return info;
+ }
+
private void initialize(MotionEvent event, MotionEvent rawEvent,
int policyFlags) {
- mEvent = MotionEvent.obtain(event);
- mRawEvent = MotionEvent.obtain(rawEvent);
- mPolicyFlags = policyFlags;
+ this.event = MotionEvent.obtain(event);
+ this.rawEvent = MotionEvent.obtain(rawEvent);
+ this.policyFlags = policyFlags;
}
public void recycle() {
@@ -897,11 +1010,22 @@ class MagnificationGestureHandler implements EventStreamTransformation {
}
private void clear() {
- mEvent.recycle();
- mEvent = null;
- mRawEvent.recycle();
- mRawEvent = null;
- mPolicyFlags = 0;
+ event = recycleAndNullify(event);
+ rawEvent = recycleAndNullify(rawEvent);
+ policyFlags = 0;
+ }
+
+ static int countOf(MotionEventInfo info, int eventType) {
+ if (info == null) return 0;
+ return (info.event.getAction() == eventType ? 1 : 0)
+ + countOf(info.mNext, eventType);
+ }
+
+ public static String toString(MotionEventInfo info) {
+ return info == null
+ ? ""
+ : MotionEvent.actionToString(info.event.getAction()).replace("ACTION_", "")
+ + " " + MotionEventInfo.toString(info.mNext);
}
}
@@ -927,7 +1051,7 @@ class MagnificationGestureHandler implements EventStreamTransformation {
@Override
public void onReceive(Context context, Intent intent) {
- mGestureHandler.setMagnificationShortcutTriggered(false);
+ mGestureHandler.setShortcutTriggered(false);
}
}
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 20ccee286fbc..2b8b25ef0fb4 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -522,10 +522,11 @@ final class AutofillManagerServiceImpl {
/**
* Updates the last fill selection when an authentication was selected.
*/
- void setAuthenticationSelected(int sessionId) {
+ void setAuthenticationSelected(int sessionId, @Nullable Bundle clientState) {
synchronized (mLock) {
if (isValidEventLocked("setAuthenticationSelected()", sessionId)) {
- mEventHistory.addEvent(new Event(Event.TYPE_AUTHENTICATION_SELECTED, null));
+ mEventHistory
+ .addEvent(new Event(Event.TYPE_AUTHENTICATION_SELECTED, null, clientState));
}
}
}
@@ -533,11 +534,13 @@ final class AutofillManagerServiceImpl {
/**
* Updates the last fill selection when an dataset authentication was selected.
*/
- void setDatasetAuthenticationSelected(@Nullable String selectedDataset, int sessionId) {
+ void setDatasetAuthenticationSelected(@Nullable String selectedDataset, int sessionId,
+ @Nullable Bundle clientState) {
synchronized (mLock) {
if (isValidEventLocked("setDatasetAuthenticationSelected()", sessionId)) {
mEventHistory.addEvent(
- new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset));
+ new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset,
+ clientState));
}
}
}
@@ -545,10 +548,10 @@ final class AutofillManagerServiceImpl {
/**
* Updates the last fill selection when an save Ui is shown.
*/
- void setSaveShown(int sessionId) {
+ void setSaveShown(int sessionId, @Nullable Bundle clientState) {
synchronized (mLock) {
if (isValidEventLocked("setSaveShown()", sessionId)) {
- mEventHistory.addEvent(new Event(Event.TYPE_SAVE_SHOWN, null));
+ mEventHistory.addEvent(new Event(Event.TYPE_SAVE_SHOWN, null, clientState));
}
}
}
@@ -556,10 +559,12 @@ final class AutofillManagerServiceImpl {
/**
* Updates the last fill response when a dataset was selected.
*/
- void setDatasetSelected(@Nullable String selectedDataset, int sessionId) {
+ void setDatasetSelected(@Nullable String selectedDataset, int sessionId,
+ @Nullable Bundle clientState) {
synchronized (mLock) {
if (isValidEventLocked("setDatasetSelected()", sessionId)) {
- mEventHistory.addEvent(new Event(Event.TYPE_DATASET_SELECTED, selectedDataset));
+ mEventHistory.addEvent(
+ new Event(Event.TYPE_DATASET_SELECTED, selectedDataset, clientState));
}
}
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 95db6039b696..8d9f0aa2f49b 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -597,7 +597,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
getFillContextByRequestIdLocked(requestId).getStructure(), extras);
}
- mService.setAuthenticationSelected(id);
+ mService.setAuthenticationSelected(id, mClientState);
final int authenticationId = AutofillManager.makeAuthenticationId(requestId, datasetIndex);
mHandlerCaller.getHandler().post(() -> startAuthentication(authenticationId,
@@ -970,7 +970,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
if (sDebug) Slog.d(TAG, "Good news, everyone! All checks passed, show save UI!");
- mService.setSaveShown(id);
+ mService.setSaveShown(id, mClientState);
final IAutoFillManagerClient client = getClient();
mPendingSaveUi = new PendingUi(mActivityToken);
getUiForShowing().showSaveUi(mService.getServiceLabel(), saveInfo,
@@ -1532,14 +1532,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
// Autofill it directly...
if (dataset.getAuthentication() == null) {
- mService.setDatasetSelected(dataset.getId(), id);
+ mService.setDatasetSelected(dataset.getId(), id, mClientState);
autoFillApp(dataset);
return;
}
// ...or handle authentication.
- mService.setDatasetAuthenticationSelected(dataset.getId(), id);
+ mService.setDatasetAuthenticationSelected(dataset.getId(), id, mClientState);
setViewStatesLocked(null, dataset, ViewState.STATE_WAITING_DATASET_AUTH, false);
final Intent fillInIntent = createAuthFillInIntent(
getFillContextByRequestIdLocked(requestId).getStructure(), mClientState);
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 29d562b17deb..c1a7f6882d18 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -10475,7 +10475,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
public ComponentName[] listAllTransportComponents() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"listAllTransportComponents");
- return mTransportManager.getAllTransportCompenents();
+ return mTransportManager.getAllTransportComponents();
}
@Override
diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
index b01cfc572432..83b6693e7a70 100644
--- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
@@ -2762,7 +2762,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
public ComponentName[] listAllTransportComponents() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"listAllTransportComponents");
- return mTransportManager.getAllTransportCompenents();
+ return mTransportManager.getAllTransportComponents();
}
@Override
diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/java/com/android/server/backup/TransportManager.java
index fb2982eb0baa..098bc0778ac1 100644
--- a/services/backup/java/com/android/server/backup/TransportManager.java
+++ b/services/backup/java/com/android/server/backup/TransportManager.java
@@ -41,10 +41,12 @@ import android.util.Log;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.backup.IBackupTransport;
import com.android.server.EventLogTags;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -57,7 +59,9 @@ public class TransportManager {
private static final String TAG = "BackupTransportManager";
- private static final String SERVICE_ACTION_TRANSPORT_HOST = "android.backup.TRANSPORT_HOST";
+ @VisibleForTesting
+ /* package */ static final String SERVICE_ACTION_TRANSPORT_HOST =
+ "android.backup.TRANSPORT_HOST";
private static final long REBINDING_TIMEOUT_UNPROVISIONED_MS = 30 * 1000; // 30 sec
private static final long REBINDING_TIMEOUT_PROVISIONED_MS = 5 * 60 * 1000; // 5 mins
@@ -95,7 +99,11 @@ public class TransportManager {
TransportBoundListener listener, Looper looper) {
mContext = context;
mPackageManager = context.getPackageManager();
- mTransportWhitelist = (whitelist != null) ? whitelist : new ArraySet<>();
+ if (whitelist != null) {
+ mTransportWhitelist = whitelist;
+ } else {
+ mTransportWhitelist = new ArraySet<>();
+ }
mCurrentTransportName = defaultTransport;
mTransportBoundListener = listener;
mHandler = new RebindOnTimeoutHandler(looper);
@@ -186,7 +194,7 @@ public class TransportManager {
}
}
- ComponentName[] getAllTransportCompenents() {
+ ComponentName[] getAllTransportComponents() {
synchronized (mTransportLock) {
return mValidTransports.keySet().toArray(new ComponentName[mValidTransports.size()]);
}
@@ -208,7 +216,8 @@ public class TransportManager {
}
}
- void ensureTransportReady(ComponentName transportComponent, SelectBackupTransportCallback listener) {
+ void ensureTransportReady(ComponentName transportComponent,
+ SelectBackupTransportCallback listener) {
synchronized (mTransportLock) {
TransportConnection conn = mValidTransports.get(transportComponent);
if (conn == null) {
@@ -252,7 +261,7 @@ public class TransportManager {
intent, 0, UserHandle.USER_SYSTEM);
if (hosts != null) {
for (ResolveInfo host : hosts) {
- final ComponentName infoComponentName = host.serviceInfo.getComponentName();
+ final ComponentName infoComponentName = getComponentName(host.serviceInfo);
boolean shouldBind = false;
if (components != null && packageName != null) {
for (String component : components) {
@@ -310,7 +319,7 @@ public class TransportManager {
Intent intent = new Intent(mTransportServiceIntent)
.setComponent(componentName);
return mContext.bindServiceAsUser(intent, connection, Context.BIND_AUTO_CREATE,
- UserHandle.SYSTEM);
+ createSystemUserHandle());
}
private class TransportConnection implements ServiceConnection {
@@ -333,7 +342,7 @@ public class TransportManager {
boolean success = false;
EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE,
- component.flattenToShortString(), 1);
+ component.flattenToShortString(), 1);
try {
mTransportName = mBinder.name();
@@ -437,7 +446,8 @@ public class TransportManager {
}
private long getRebindTimeout() {
- final boolean isDeviceProvisioned = Settings.Global.getInt(mContext.getContentResolver(),
+ final boolean isDeviceProvisioned = Settings.Global.getInt(
+ mContext.getContentResolver(),
Settings.Global.DEVICE_PROVISIONED, 0) != 0;
return isDeviceProvisioned
? REBINDING_TIMEOUT_PROVISIONED_MS
@@ -445,7 +455,7 @@ public class TransportManager {
}
}
- interface TransportBoundListener {
+ public interface TransportBoundListener {
/** Should return true if this is a valid transport. */
boolean onTransportBound(IBackupTransport binder);
}
@@ -465,7 +475,7 @@ public class TransportManager {
synchronized (mTransportLock) {
if (mBoundTransports.containsValue(transportComponent)) {
Slog.d(TAG, "Explicit rebinding timeout passed, but already bound to "
- + componentShortString + " so not attempting to rebind");
+ + componentShortString + " so not attempting to rebind");
return;
}
Slog.d(TAG, "Explicit rebinding timeout passed, attempting rebinding to: "
@@ -492,4 +502,18 @@ public class TransportManager {
Slog.v(TAG, message);
}
}
+
+ // These only exists to make it testable with Robolectric, which is not updated to API level 24
+ // yet.
+ // TODO: Get rid of this once Robolectric is updated.
+ private static ComponentName getComponentName(ServiceInfo serviceInfo) {
+ return new ComponentName(serviceInfo.packageName, serviceInfo.name);
+ }
+
+ // These only exists to make it testable with Robolectric, which is not updated to API level 24
+ // yet.
+ // TODO: Get rid of this once Robolectric is updated.
+ private static UserHandle createSystemUserHandle() {
+ return new UserHandle(UserHandle.USER_SYSTEM);
+ }
}
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 473384081656..046eb761d1c0 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -728,6 +728,9 @@ public class VibratorService extends IVibratorService.Stub
return timeout;
}
}
+ if (!prebaked.shouldFallback()) {
+ return 0;
+ }
final int id = prebaked.getId();
if (id < 0 || id >= mFallbackEffects.length || mFallbackEffects[id] == null) {
Slog.w(TAG, "Failed to play prebaked effect, no fallback");
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f3bc62f90c5c..8f68bd6c2781 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3093,7 +3093,7 @@ public class ActivityManagerService extends IActivityManager.Stub
*/
void setResumedActivityUncheckLocked(ActivityRecord r, String reason) {
final TaskRecord task = r.getTask();
- if (task.isApplicationTask()) {
+ if (task.isActivityTypeStandard()) {
if (mCurAppTimeTracker != r.appTimeTracker) {
// We are switching app tracking. Complete the current one.
if (mCurAppTimeTracker != null) {
@@ -9934,7 +9934,7 @@ public class ActivityManagerService extends IActivityManager.Stub
if (!allowed) {
// If the caller doesn't have the GET_TASKS permission, then only
// allow them to see a small subset of tasks -- their own and home.
- if (!tr.isHomeTask() && tr.effectiveUid != callingUid) {
+ if (!tr.isActivityTypeHome() && tr.effectiveUid != callingUid) {
if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not allowed: " + tr);
continue;
}
@@ -12945,7 +12945,7 @@ public class ActivityManagerService extends IActivityManager.Stub
int userId;
synchronized (this) {
final ActivityStack focusedStack = getFocusedStack();
- if (focusedStack == null || focusedStack.isAssistantStack()) {
+ if (focusedStack == null || focusedStack.isActivityTypeAssistant()) {
return false;
}
@@ -13050,7 +13050,7 @@ public class ActivityManagerService extends IActivityManager.Stub
pae = new PendingAssistExtras(activity, extras, intent, hint, receiver, receiverExtras,
userHandle);
- pae.isHome = activity.isHomeActivity();
+ pae.isHome = activity.isActivityTypeHome();
// Increment the sessionId if necessary
if (newSessionId) {
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 4433b84af68d..651d3a6a044e 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -33,6 +33,12 @@ import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.OP_PICTURE_IN_PICTURE;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.activityTypeToString;
import static android.content.Intent.ACTION_MAIN;
import static android.content.Intent.CATEGORY_HOME;
import static android.content.Intent.CATEGORY_LAUNCHER;
@@ -226,12 +232,6 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
private final boolean componentSpecified; // did caller specify an explicit component?
final boolean rootVoiceInteraction; // was this the root activity of a voice interaction?
- static final int APPLICATION_ACTIVITY_TYPE = 0;
- static final int HOME_ACTIVITY_TYPE = 1;
- static final int RECENTS_ACTIVITY_TYPE = 2;
- static final int ASSISTANT_ACTIVITY_TYPE = 3;
- int mActivityType;
-
private CharSequence nonLocalizedLabel; // the label information from the package mgr.
private int labelRes; // the label information from the package mgr.
private int icon; // resource identifier of activity's icon.
@@ -388,7 +388,8 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
}
pw.print(prefix); pw.print("stateNotNeeded="); pw.print(stateNotNeeded);
pw.print(" componentSpecified="); pw.print(componentSpecified);
- pw.print(" mActivityType="); pw.println(mActivityType);
+ pw.print(" mActivityType="); pw.println(
+ activityTypeToString(getActivityType()));
if (rootVoiceInteraction) {
pw.print(prefix); pw.print("rootVoiceInteraction="); pw.println(rootVoiceInteraction);
}
@@ -495,7 +496,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
pw.print(prefix); pw.print("frozenBeforeDestroy="); pw.print(frozenBeforeDestroy);
pw.print(" forceNewConfig="); pw.println(forceNewConfig);
pw.print(prefix); pw.print("mActivityType=");
- pw.println(activityTypeToString(mActivityType));
+ pw.println(activityTypeToString(getActivityType()));
if (requestedVrComponent != null) {
pw.print(prefix);
pw.print("requestedVrComponent=");
@@ -652,14 +653,14 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
}
}
- void updatePictureInPictureMode(Rect targetStackBounds) {
+ void updatePictureInPictureMode(Rect targetStackBounds, boolean forceUpdate) {
if (task == null || task.getStack() == null || app == null || app.thread == null) {
return;
}
final boolean inPictureInPictureMode = (task.getStackId() == PINNED_STACK_ID) &&
(targetStackBounds != null);
- if (inPictureInPictureMode != mLastReportedPictureInPictureMode) {
+ if (inPictureInPictureMode != mLastReportedPictureInPictureMode || forceUpdate) {
// Picture-in-picture mode changes also trigger a multi-window mode change as well, so
// update that here in order
mLastReportedPictureInPictureMode = inPictureInPictureMode;
@@ -674,8 +675,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
private void schedulePictureInPictureModeChanged(Configuration overrideConfig) {
try {
app.thread.schedulePictureInPictureModeChanged(appToken,
- mLastReportedPictureInPictureMode,
- overrideConfig);
+ mLastReportedPictureInPictureMode, overrideConfig);
} catch (Exception e) {
// If process died, no one cares.
}
@@ -938,7 +938,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
task.voiceSession != null, mLaunchTaskBehind, isAlwaysFocusable(),
appInfo.targetSdkVersion, mRotationAnimationHint,
ActivityManagerService.getInputDispatchingTimeoutLocked(this) * 1000000L,
- getOverrideConfiguration(), mBounds);
+ new Configuration(getOverrideConfiguration()), mBounds);
task.addActivityToTop(this);
@@ -1028,10 +1028,11 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
private void setActivityType(boolean componentSpecified, int launchedFromUid, Intent intent,
ActivityOptions options, ActivityRecord sourceRecord) {
+ int activityType = ACTIVITY_TYPE_UNDEFINED;
if ((!componentSpecified || canLaunchHomeActivity(launchedFromUid, sourceRecord))
&& isHomeIntent(intent) && !isResolverActivity()) {
// This sure looks like a home activity!
- mActivityType = HOME_ACTIVITY_TYPE;
+ activityType = ACTIVITY_TYPE_HOME;
if (info.resizeMode == RESIZE_MODE_FORCE_RESIZEABLE
|| info.resizeMode == RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) {
@@ -1039,13 +1040,12 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
}
} else if (realActivity.getClassName().contains(RECENTS_PACKAGE_NAME)) {
- mActivityType = RECENTS_ACTIVITY_TYPE;
+ activityType = ACTIVITY_TYPE_RECENTS;
} else if (options != null && options.getLaunchStackId() == ASSISTANT_STACK_ID
&& canLaunchAssistActivity(launchedFromPackage)) {
- mActivityType = ASSISTANT_ACTIVITY_TYPE;
- } else {
- mActivityType = APPLICATION_ACTIVITY_TYPE;
+ activityType = ACTIVITY_TYPE_ASSISTANT;
}
+ setActivityType(activityType);
}
void setTaskToAffiliateWith(TaskRecord taskToAffiliateWith) {
@@ -1096,18 +1096,6 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
return stack != null && stack.isInStackLocked(this) != null;
}
- boolean isHomeActivity() {
- return mActivityType == HOME_ACTIVITY_TYPE;
- }
-
- boolean isRecentsActivity() {
- return mActivityType == RECENTS_ACTIVITY_TYPE;
- }
-
- boolean isAssistantActivity() {
- return mActivityType == ASSISTANT_ACTIVITY_TYPE;
- }
-
boolean isPersistable() {
return (info.persistableMode == PERSIST_ROOT_ONLY ||
info.persistableMode == PERSIST_ACROSS_REBOOTS) &&
@@ -1134,7 +1122,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
* @return whether this activity supports PiP multi-window and can be put in the pinned stack.
*/
boolean supportsPictureInPicture() {
- return service.mSupportsPictureInPicture && !isHomeActivity()
+ return service.mSupportsPictureInPicture && isActivityTypeStandard()
&& info.supportsPictureInPicture();
}
@@ -1160,7 +1148,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
* @return whether this activity supports non-PiP multi-window.
*/
private boolean supportsResizeableMultiWindow() {
- return service.mSupportsMultiWindow && !isHomeActivity()
+ return service.mSupportsMultiWindow && !isActivityTypeHome()
&& (ActivityInfo.isResizeableMode(info.resizeMode)
|| service.mForceResizableActivities);
}
@@ -1521,7 +1509,12 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
}
void notifyUnknownVisibilityLaunched() {
- mWindowContainerController.notifyUnknownVisibilityLaunched();
+
+ // No display activities never add a window, so there is no point in waiting them for
+ // relayout.
+ if (!noDisplay) {
+ mWindowContainerController.notifyUnknownVisibilityLaunched();
+ }
}
/**
@@ -1537,7 +1530,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
boolean isVisible = !behindFullscreenActivity || mLaunchTaskBehind;
- if (service.mSupportsLeanbackOnly && isVisible && isRecentsActivity()) {
+ if (service.mSupportsLeanbackOnly && isVisible && isActivityTypeRecents()) {
// On devices that support leanback only (Android TV), Recents activity can only be
// visible if the home stack is the focused stack or we are in split-screen mode.
isVisible = mStackSupervisor.getStack(DOCKED_STACK_ID) != null
@@ -1615,7 +1608,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
newIntents = null;
stopped = false;
- if (isHomeActivity()) {
+ if (isActivityTypeHome()) {
ProcessRecord app = task.mActivities.get(0).app;
if (app != null && app != service.mHomeProcess) {
service.mHomeProcess = app;
@@ -2145,7 +2138,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
config.getOverrideConfiguration());
}
- void setLastReportedConfiguration(Configuration global, Configuration override) {
+ private void setLastReportedConfiguration(Configuration global, Configuration override) {
mLastReportedConfiguration.setConfiguration(global, override);
}
@@ -2718,16 +2711,6 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
return r;
}
- private static String activityTypeToString(int type) {
- switch (type) {
- case APPLICATION_ACTIVITY_TYPE: return "APPLICATION_ACTIVITY_TYPE";
- case HOME_ACTIVITY_TYPE: return "HOME_ACTIVITY_TYPE";
- case RECENTS_ACTIVITY_TYPE: return "RECENTS_ACTIVITY_TYPE";
- case ASSISTANT_ACTIVITY_TYPE: return "ASSISTANT_ACTIVITY_TYPE";
- default: return Integer.toString(type);
- }
- }
-
private static boolean isInVrUiMode(Configuration config) {
return (config.uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_VR_HEADSET;
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 2da437769ed0..4560d3a6e4f7 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -23,8 +23,12 @@ import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.HOME_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static android.app.ActivityManager.StackId.RECENTS_STACK_ID;
+import static android.app.ActivityManager.StackId.getActivityTypeForStackId;
import static android.app.ActivityManager.StackId.getWindowingModeForStackId;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
@@ -66,9 +70,6 @@ import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_USER_LEAV
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
-import static com.android.server.am.ActivityRecord.ASSISTANT_ACTIVITY_TYPE;
-import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
import static com.android.server.am.ActivityStack.ActivityState.STOPPED;
import static com.android.server.am.ActivityStack.ActivityState.STOPPING;
import static com.android.server.am.ActivityStackSupervisor.FindTaskResult;
@@ -486,6 +487,10 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
if (windowingMode != WINDOWING_MODE_UNDEFINED) {
setWindowingMode(windowingMode);
}
+ final int activityType = getActivityTypeForStackId(mStackId);
+ if (activityType != ACTIVITY_TYPE_UNDEFINED) {
+ setActivityType(activityType);
+ }
}
/** Adds the stack to specified display and calls WindowManager to do the same. */
@@ -831,14 +836,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
return hadit;
}
- final boolean isHomeStack() {
- return mStackId == HOME_STACK_ID;
- }
-
- final boolean isRecentsStack() {
- return mStackId == RECENTS_STACK_ID;
- }
-
final boolean isHomeOrRecentsStack() {
return StackId.isHomeOrRecentsStack(mStackId);
}
@@ -851,10 +848,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
return mStackId == PINNED_STACK_ID;
}
- final boolean isAssistantStack() {
- return mStackId == ASSISTANT_STACK_ID;
- }
-
final boolean isOnHomeDisplay() {
return isAttached() && mDisplayId == DEFAULT_DISPLAY;
}
@@ -975,7 +968,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": mismatch root " + r);
continue;
}
- if (r.mActivityType != target.mActivityType) {
+ if (!r.hasCompatibleActivityType(target)) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": mismatch activity type");
continue;
}
@@ -1656,7 +1649,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
if (!isHomeOrRecentsStack() && r.frontOfTask && task.isOverHomeStack()
- && !StackId.isHomeOrRecentsStack(stackBehindId) && !isAssistantStack()) {
+ && !StackId.isHomeOrRecentsStack(stackBehindId)
+ && !isActivityTypeAssistant()) {
// Stack isn't translucent if it's top activity should have the home stack
// behind it and the stack currently behind it isn't the home or recents stack
// or the assistant stack.
@@ -1703,7 +1697,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
if (mStackId == DOCKED_STACK_ID) {
// If the assistant stack is focused and translucent, then the docked stack is always
// visible
- if (topStack.isAssistantStack()) {
+ if (topStack.isActivityTypeAssistant()) {
return (topStack.isStackTranslucent(starting, DOCKED_STACK_ID)) ? STACK_VISIBLE
: STACK_INVISIBLE;
}
@@ -1905,39 +1899,22 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// status of an activity in a previous task affects other.
behindFullscreenActivity = stackVisibility == STACK_INVISIBLE;
} else if (mStackId == HOME_STACK_ID) {
- if (task.isHomeTask()) {
- if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Home task: at " + task
- + " stackInvisible=" + stackInvisible
- + " behindFullscreenActivity=" + behindFullscreenActivity);
- // No other task in the home stack should be visible behind the home activity.
- // Home activities is usually a translucent activity with the wallpaper behind
- // them. However, when they don't have the wallpaper behind them, we want to
- // show activities in the next application stack behind them vs. another
- // task in the home stack like recents.
- behindFullscreenActivity = true;
- } else if (task.isRecentsTask()
- && task.getTaskToReturnTo() == APPLICATION_ACTIVITY_TYPE) {
- if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
- "Recents task returning to app: at " + task
- + " stackInvisible=" + stackInvisible
- + " behindFullscreenActivity=" + behindFullscreenActivity);
- // We don't want any other tasks in the home stack visible if the recents
- // activity is going to be returning to an application activity type.
- // We do this to preserve the visible order the user used to get into the
- // recents activity. The recents activity is normally translucent and if it
- // doesn't have the wallpaper behind it the next activity in the home stack
- // shouldn't be visible when the home stack is brought to the front to display
- // the recents activity from an app.
- behindFullscreenActivity = true;
- }
+ if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Home task: at " + task
+ + " stackInvisible=" + stackInvisible
+ + " behindFullscreenActivity=" + behindFullscreenActivity);
+ // No other task in the home stack should be visible behind the home activity.
+ // Home activities is usually a translucent activity with the wallpaper behind
+ // them. However, when they don't have the wallpaper behind them, we want to
+ // show activities in the next application stack behind them vs. another
+ // task in the home stack like recents.
+ behindFullscreenActivity = true;
} else if (mStackId == FULLSCREEN_WORKSPACE_STACK_ID) {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Skipping after task=" + task
+ " returning to non-application type=" + task.getTaskToReturnTo());
// Once we reach a fullscreen stack task that has a running activity and should
// return to another stack task, then no other activities behind that one should
// be visible.
- if (task.topRunningActivityLocked() != null &&
- task.getTaskToReturnTo() != APPLICATION_ACTIVITY_TYPE) {
+ if (task.topRunningActivityLocked() != null && !task.returnsToStandardTask()) {
behindFullscreenActivity = true;
}
}
@@ -2334,10 +2311,10 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// This task is going away but it was supposed to return to the home stack.
// Now the task above it has to return to the home task instead.
final int taskNdx = mTaskHistory.indexOf(prevTask) + 1;
- mTaskHistory.get(taskNdx).setTaskToReturnTo(HOME_ACTIVITY_TYPE);
+ mTaskHistory.get(taskNdx).setTaskToReturnTo(ACTIVITY_TYPE_HOME);
} else if (!isOnHomeDisplay()) {
return false;
- } else if (!isHomeStack()){
+ } else if (!isActivityTypeHome()){
if (DEBUG_STATES) Slog.d(TAG_STATES,
"resumeTopActivityLocked: Launching home next");
return isOnHomeDisplay() &&
@@ -2820,7 +2797,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// If this is not on the default display, then just set the return type to application
if (!isOnHomeDisplay()) {
- task.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE);
+ task.setTaskToReturnTo(ACTIVITY_TYPE_STANDARD);
return;
}
@@ -2832,8 +2809,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
// If the task was launched from the assistant stack, set the return type to assistant
- if (lastStack.isAssistantStack()) {
- task.setTaskToReturnTo(ASSISTANT_ACTIVITY_TYPE);
+ if (lastStack.isActivityTypeAssistant()) {
+ task.setTaskToReturnTo(ACTIVITY_TYPE_ASSISTANT);
return;
}
@@ -2845,9 +2822,10 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// If it's a last task over home - we default to keep its return to type not to
// make underlying task focused when this one will be finished.
int returnToType = isLastTaskOverHome
- ? task.getTaskToReturnTo() : APPLICATION_ACTIVITY_TYPE;
+ ? task.getTaskToReturnTo() : ACTIVITY_TYPE_STANDARD;
if (fromHomeOrRecents && StackId.allowTopTaskToReturnHome(mStackId)) {
- returnToType = topTask == null ? HOME_ACTIVITY_TYPE : topTask.taskType;
+ returnToType = topTask == null
+ ? ACTIVITY_TYPE_HOME : topTask.getActivityType();
}
task.setTaskToReturnTo(returnToType);
}
@@ -3099,7 +3077,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
} else {
targetTask = createTaskRecord(
mStackSupervisor.getNextTaskIdForUserLocked(target.userId),
- target.info, null, null, null, false, target.mActivityType);
+ target.info, null, null, null, false);
targetTask.affinityIntent = target.intent;
if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " + target
+ " out to new task " + targetTask);
@@ -3410,8 +3388,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
throw new IllegalStateException("activity no longer associated with task:" + r);
}
- final boolean isAssistantOrOverAssistant = task.getStack().isAssistantStack() ||
- task.isOverAssistantStack();
+ final boolean isAssistantOrOverAssistant =
+ task.getStack().isActivityTypeAssistant() || task.isOverAssistantStack();
if (r.frontOfTask && isATopFinishingTask(task)
&& (task.isOverHomeStack() || isAssistantOrOverAssistant)) {
// For non-fullscreen or assistant stack, we want to move the focus to the next
@@ -3446,8 +3424,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
* @param allowFocusSelf Is the focus allowed to remain on the same stack.
*/
private boolean adjustFocusToNextFocusableStackLocked(String reason, boolean allowFocusSelf) {
- if (isAssistantStack() && bottomTask() != null &&
- bottomTask().getTaskToReturnTo() == HOME_ACTIVITY_TYPE) {
+ if (isActivityTypeAssistant() && bottomTask() != null
+ && bottomTask().returnsToHomeTask()) {
// If the current stack is the assistant stack, then use the return-to type to determine
// whether to return to the home screen. This is needed to workaround an issue where
// launching a fullscreen task (and subequently returning from that task) will cause
@@ -3472,8 +3450,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
return mStackSupervisor.moveHomeStackTaskToTop(reason);
}
- if (stack.isAssistantStack() && top != null
- && top.getTask().getTaskToReturnTo() == HOME_ACTIVITY_TYPE) {
+ if (stack.isActivityTypeAssistant() && top != null
+ && top.getTask().returnsToHomeTask()) {
// It is possible for the home stack to not be directly underneath the assistant stack.
// For example, the assistant may start an activity in the fullscreen stack. Upon
// returning to the assistant stack, we must ensure that the home stack is underneath
@@ -3611,7 +3589,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
if (r.state == ActivityState.RESUMED
|| r.state == ActivityState.PAUSING
|| r.state == ActivityState.PAUSED) {
- if (!r.isHomeActivity() || mService.mHomeProcess != r.app) {
+ if (!r.isActivityTypeHome() || mService.mHomeProcess != r.app) {
Slog.w(TAG, " Force finishing activity "
+ r.intent.getComponent().flattenToShortString());
finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false);
@@ -3928,7 +3906,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
if (srec.frontOfTask && task != null && task.getBaseIntent() != null
&& task.getBaseIntent().isDocument()) {
// Okay, this activity is at the root of its task. What to do, what to do...
- if (task.getTaskToReturnTo() != ActivityRecord.APPLICATION_ACTIVITY_TYPE) {
+ if (!task.returnsToStandardTask()) {
// Finishing won't return to an application, so we need to recreate.
return true;
}
@@ -3938,11 +3916,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
Slog.w(TAG, "shouldUpRecreateTask: task not in history for " + srec);
return false;
}
- if (taskIdx == 0) {
- // At the bottom of the stack, nothing to go back to.
- return true;
- }
- TaskRecord prevTask = mTaskHistory.get(taskIdx);
+ final TaskRecord prevTask = mTaskHistory.get(taskIdx);
if (!task.affinity.equals(prevTask.affinity)) {
// These are different apps, so need to recreate.
return true;
@@ -4527,17 +4501,18 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
void moveHomeStackTaskToTop() {
+ if (!isActivityTypeHome()) {
+ throw new IllegalStateException("Calling moveHomeStackTaskToTop() on non-home stack: "
+ + this);
+ }
final int top = mTaskHistory.size() - 1;
- for (int taskNdx = top; taskNdx >= 0; --taskNdx) {
- final TaskRecord task = mTaskHistory.get(taskNdx);
- if (task.taskType == HOME_ACTIVITY_TYPE) {
- if (DEBUG_TASKS || DEBUG_STACK) Slog.d(TAG_STACK,
- "moveHomeStackTaskToTop: moving " + task);
- mTaskHistory.remove(taskNdx);
- mTaskHistory.add(top, task);
- updateTaskMovement(task, true);
- return;
- }
+ if (top >= 0) {
+ final TaskRecord task = mTaskHistory.get(top);
+ if (DEBUG_TASKS || DEBUG_STACK) Slog.d(TAG_STACK,
+ "moveHomeStackTaskToTop: moving " + task);
+ mTaskHistory.remove(top);
+ mTaskHistory.add(top, task);
+ updateTaskMovement(task, true);
}
}
@@ -4660,7 +4635,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// If true, we should resume the home activity next if the task we are moving to the
// back is over the home stack. We force to false if the task we are moving to back
// is the home task and we don't want it resumed after moving to the back.
- final boolean canGoHome = !tr.isHomeTask() && tr.isOverHomeStack();
+ final boolean canGoHome = !tr.isActivityTypeHome() && tr.isOverHomeStack();
if (canGoHome) {
final TaskRecord nextTask = getNextTask(tr);
if (nextTask != null) {
@@ -4695,7 +4670,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
if (taskNdx == 1) {
// Set the last task before tr to go to home.
- task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
+ task.setTaskToReturnTo(ACTIVITY_TYPE_HOME);
}
}
@@ -4705,7 +4680,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// Not ready yet!
return false;
}
- tr.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE);
+ tr.setTaskToReturnTo(ACTIVITY_TYPE_STANDARD);
return mStackSupervisor.resumeHomeStackTask(null, "moveTaskToBack");
}
@@ -4919,7 +4894,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
return true;
}
- if (r.isHomeActivity()) {
+ if (r.isActivityTypeHome()) {
if (homeActivity != null && homeActivity.equals(r.realActivity)) {
Slog.i(TAG, "Skip force-stop again " + r);
continue;
@@ -4962,7 +4937,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
int numActivities = 0;
int numRunning = 0;
final ArrayList<ActivityRecord> activities = task.mActivities;
- if (!allowed && !task.isHomeTask() && task.effectiveUid != callingUid) {
+ if (!allowed && !task.isActivityTypeHome() && task.effectiveUid != callingUid) {
continue;
}
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
@@ -5158,7 +5133,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
if (task.isOverHomeStack() && taskNdx < topTaskNdx) {
final TaskRecord nextTask = mTaskHistory.get(taskNdx + 1);
if (!nextTask.isOverHomeStack() && !nextTask.isOverAssistantStack()) {
- nextTask.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
+ nextTask.setTaskToReturnTo(ACTIVITY_TYPE_HOME);
}
}
mTaskHistory.remove(task);
@@ -5214,9 +5189,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
TaskRecord createTaskRecord(int taskId, ActivityInfo info, Intent intent,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
- boolean toTop, int type) {
- TaskRecord task = new TaskRecord(mService, taskId, info, intent, voiceSession,
- voiceInteractor, type);
+ boolean toTop) {
+ final TaskRecord task = new TaskRecord(mService, taskId, info, intent, voiceSession,
+ voiceInteractor);
// add the task to stack first, mTaskPositioner might need the stack association
addTask(task, toTop, "createTaskRecord");
final boolean isLockscreenShown =
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 64cbe2bacbf1..e4a2273387be 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -33,6 +33,9 @@ import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.app.ActivityManager.StackId.RECENTS_STACK_ID;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
@@ -67,8 +70,6 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityManagerService.ANIMATE;
import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG;
-import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
-import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
import static com.android.server.am.ActivityStack.ActivityState.DESTROYED;
import static com.android.server.am.ActivityStack.ActivityState.DESTROYING;
import static com.android.server.am.ActivityStack.ActivityState.INITIALIZING;
@@ -689,7 +690,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
if (prev != null) {
- prev.getTask().setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE);
+ prev.getTask().setTaskToReturnTo(ACTIVITY_TYPE_STANDARD);
}
mHomeStack.moveHomeStackTaskToTop();
@@ -1268,6 +1269,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
r.app = app;
+ if (mKeyguardController.isKeyguardLocked()) {
+ r.notifyUnknownVisibilityLaunched();
+ }
+
// Have the window manager re-evaluate the orientation of the screen based on the new
// activity order. Note that as a result of this, it can call back into the activity
// manager with a new orientation. We don't care about that, because the activity is
@@ -1294,9 +1299,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
r.setVisibility(true);
}
- if (mKeyguardController.isKeyguardLocked()) {
- r.notifyUnknownVisibilityLaunched();
- }
final int applicationInfoUid =
(r.info.applicationInfo != null) ? r.info.applicationInfo.uid : -1;
if ((r.userId != app.userId) || (r.appInfo.uid != applicationInfoUid)) {
@@ -1342,7 +1344,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
+ " newIntents=" + newIntents + " andResume=" + andResume);
EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY, r.userId,
System.identityHashCode(r), task.taskId, r.shortComponentName);
- if (r.isHomeActivity()) {
+ if (r.isActivityTypeHome()) {
// Home process is the root process of the task.
mService.mHomeProcess = task.mActivities.get(0).app;
}
@@ -2077,7 +2079,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
if ((flags & ActivityManager.MOVE_TASK_WITH_HOME) != 0) {
// Caller wants the home activity moved with it. To accomplish this,
// we'll just indicate that this task returns to the home task.
- task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
+ task.setTaskToReturnTo(ACTIVITY_TYPE_HOME);
}
ActivityStack currentStack = task.getStack();
if (currentStack == null) {
@@ -2263,11 +2265,11 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
final ArrayList<TaskRecord> tasks = mHomeStack.getAllTasks();
for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = tasks.get(taskNdx);
- if (task.isHomeTask()) {
+ if (task.isActivityTypeHome()) {
final ArrayList<ActivityRecord> activities = task.mActivities;
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
final ActivityRecord r = activities.get(activityNdx);
- if (r.isHomeActivity()
+ if (r.isActivityTypeHome()
&& ((userId == UserHandle.USER_ALL) || (r.userId == userId))) {
return r;
}
@@ -2400,8 +2402,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// Update the return-to to reflect where the pinned stack task was moved
// from so that we retain the stack that was previously visible if the
// pinned stack is recreated. See moveActivityToPinnedStackLocked().
- task.setTaskToReturnTo(isFullscreenStackVisible && onTop ?
- APPLICATION_ACTIVITY_TYPE : HOME_ACTIVITY_TYPE);
+ task.setTaskToReturnTo(isFullscreenStackVisible ?
+ ACTIVITY_TYPE_STANDARD : ACTIVITY_TYPE_HOME);
}
// Defer resume until all the tasks have been moved to the fullscreen stack
task.reparent(FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP,
@@ -2921,7 +2923,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// move the home stack forward if we are currently entering picture-in-picture
// while pausing because that changes the focused stack and may prevent the new
// starting activity from resuming.
- if (moveHomeStackToFront && task.getTaskToReturnTo() == HOME_ACTIVITY_TYPE
+ if (moveHomeStackToFront && task.returnsToHomeTask()
&& (r.state == RESUMED || !r.supportsEnterPipOnTaskSwitch)) {
// Move the home stack forward if the task we just moved to the pinned stack
// was launched from home so home should be visible behind it.
@@ -2940,8 +2942,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// ensures that all the necessary work to migrate states in the old and new stacks
// is also done.
final TaskRecord newTask = task.getStack().createTaskRecord(
- getNextTaskIdForUserLocked(r.userId), r.info, r.intent, null, null, true,
- r.mActivityType);
+ getNextTaskIdForUserLocked(r.userId), r.info, r.intent, null, null, true);
r.reparent(newTask, MAX_VALUE, "moveActivityToStack");
// Defer resume until below, and do not schedule PiP changes until we animate below
@@ -3013,7 +3014,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
- if (!checkActivityBelongsInStack(r, stack)) {
+ if (!r.hasCompatibleActivityType(stack)) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping stack: (mismatch activity/stack) "
+ stack);
continue;
@@ -3041,21 +3042,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
return affinityMatch;
}
- /**
- * Checks that for the given activity {@param r}, its activity type matches the {@param stack}
- * type.
- */
- private boolean checkActivityBelongsInStack(ActivityRecord r, ActivityStack stack) {
- if (r.isHomeActivity()) {
- return stack.isHomeStack();
- } else if (r.isRecentsActivity()) {
- return stack.isRecentsStack();
- } else if (r.isAssistantActivity()) {
- return stack.isAssistantStack();
- }
- return true;
- }
-
ActivityRecord findActivityLocked(Intent intent, ActivityInfo info,
boolean compareIntentFilters) {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
@@ -3438,7 +3424,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
if (stack == null) {
stack = mHomeStack;
}
- final boolean homeInFront = stack.isHomeStack();
+ final boolean homeInFront = stack.isActivityTypeHome();
if (stack.isOnHomeDisplay()) {
stack.moveToFront("switchUserOnHomeDisplay");
} else {
@@ -4038,7 +4024,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
(preferredDisplayId != DEFAULT_DISPLAY && preferredDisplayId != INVALID_DISPLAY)
|| StackId.isDynamicStack(preferredStackId);
if (((!isStackDockedInEffect(actualStackId) && preferredStackId != DOCKED_STACK_ID)
- && !isSecondaryDisplayPreferred) || task.isHomeTask()) {
+ && !isSecondaryDisplayPreferred) || task.isActivityTypeHome()) {
return;
}
@@ -4135,31 +4121,29 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
return;
}
- scheduleUpdatePictureInPictureModeIfNeeded(task, stack.mBounds, false /* immediate */);
+ scheduleUpdatePictureInPictureModeIfNeeded(task, stack.mBounds);
}
- void scheduleUpdatePictureInPictureModeIfNeeded(TaskRecord task, Rect targetStackBounds,
- boolean immediate) {
-
- if (immediate) {
- mHandler.removeMessages(REPORT_PIP_MODE_CHANGED_MSG);
- for (int i = task.mActivities.size() - 1; i >= 0; i--) {
- final ActivityRecord r = task.mActivities.get(i);
- if (r.app != null && r.app.thread != null) {
- r.updatePictureInPictureMode(targetStackBounds);
- }
- }
- } else {
- for (int i = task.mActivities.size() - 1; i >= 0; i--) {
- final ActivityRecord r = task.mActivities.get(i);
- if (r.app != null && r.app.thread != null) {
- mPipModeChangedActivities.add(r);
- }
+ void scheduleUpdatePictureInPictureModeIfNeeded(TaskRecord task, Rect targetStackBounds) {
+ for (int i = task.mActivities.size() - 1; i >= 0; i--) {
+ final ActivityRecord r = task.mActivities.get(i);
+ if (r.app != null && r.app.thread != null) {
+ mPipModeChangedActivities.add(r);
}
- mPipModeChangedTargetStackBounds = targetStackBounds;
+ }
+ mPipModeChangedTargetStackBounds = targetStackBounds;
+
+ if (!mHandler.hasMessages(REPORT_PIP_MODE_CHANGED_MSG)) {
+ mHandler.sendEmptyMessage(REPORT_PIP_MODE_CHANGED_MSG);
+ }
+ }
- if (!mHandler.hasMessages(REPORT_PIP_MODE_CHANGED_MSG)) {
- mHandler.sendEmptyMessage(REPORT_PIP_MODE_CHANGED_MSG);
+ void updatePictureInPictureMode(TaskRecord task, Rect targetStackBounds, boolean forceUpdate) {
+ mHandler.removeMessages(REPORT_PIP_MODE_CHANGED_MSG);
+ for (int i = task.mActivities.size() - 1; i >= 0; i--) {
+ final ActivityRecord r = task.mActivities.get(i);
+ if (r.app != null && r.app.thread != null) {
+ r.updatePictureInPictureMode(targetStackBounds, forceUpdate);
}
}
}
@@ -4221,7 +4205,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
synchronized (mService) {
for (int i = mPipModeChangedActivities.size() - 1; i >= 0; i--) {
final ActivityRecord r = mPipModeChangedActivities.remove(i);
- r.updatePictureInPictureMode(mPipModeChangedTargetStackBounds);
+ r.updatePictureInPictureMode(mPipModeChangedTargetStackBounds,
+ false /* forceUpdate */);
}
}
} break;
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 235477e24a27..16abcfb620d9 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -36,6 +36,9 @@ import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.app.ActivityManager.StackId.RECENTS_STACK_ID;
import static android.app.ActivityManager.StackId.isDynamicStack;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
@@ -72,9 +75,6 @@ import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_USER_LEAV
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityManagerService.ANIMATE;
-import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
-import static com.android.server.am.ActivityRecord.ASSISTANT_ACTIVITY_TYPE;
-import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
import static com.android.server.am.ActivityStack.STACK_INVISIBLE;
import static com.android.server.am.ActivityStackSupervisor.CREATE_IF_NEEDED;
@@ -1507,7 +1507,7 @@ class ActivityStarter {
// There can be one and only one instance of single instance activity in the
// history, and it is always in its own unique task, so we do a special search.
intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info,
- mStartActivity.isHomeActivity());
+ mStartActivity.isActivityTypeHome());
} else if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
// For the launch adjacent case we only want to put the activity in an existing
// task if the activity already exists in the history.
@@ -1669,21 +1669,21 @@ class ActivityStarter {
if ((launchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME))
== (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) {
// Caller wants to appear on home activity.
- task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
+ task.setTaskToReturnTo(ACTIVITY_TYPE_HOME);
return;
- } else if (focusedStack == null || focusedStack.isHomeStack()) {
+ } else if (focusedStack == null || focusedStack.isActivityTypeHome()) {
// Task will be launched over the home stack, so return home.
- task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
+ task.setTaskToReturnTo(ACTIVITY_TYPE_HOME);
return;
} else if (focusedStack != null && focusedStack != task.getStack() &&
- focusedStack.isAssistantStack()) {
+ focusedStack.isActivityTypeAssistant()) {
// Task was launched over the assistant stack, so return there
- task.setTaskToReturnTo(ASSISTANT_ACTIVITY_TYPE);
+ task.setTaskToReturnTo(ACTIVITY_TYPE_ASSISTANT);
return;
}
// Else we are coming from an application stack so return to an application.
- task.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE);
+ task.setTaskToReturnTo(ACTIVITY_TYPE_STANDARD);
}
private void setTaskFromIntentActivity(ActivityRecord intentActivity) {
@@ -1791,7 +1791,7 @@ class ActivityStarter {
mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId),
mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info,
mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession,
- mVoiceInteractor, !mLaunchTaskBehind /* toTop */, mStartActivity.mActivityType);
+ mVoiceInteractor, !mLaunchTaskBehind /* toTop */);
addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask - mReuseTask");
if (mLaunchBounds != null) {
final int stackId = mTargetStack.mStackId;
@@ -1995,7 +1995,7 @@ class ActivityStarter {
final ActivityRecord prev = mTargetStack.topActivity();
final TaskRecord task = (prev != null) ? prev.getTask() : mTargetStack.createTaskRecord(
mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId), mStartActivity.info,
- mIntent, null, null, true, mStartActivity.mActivityType);
+ mIntent, null, null, true);
addOrReparentStartingActivity(task, "setTaskToCurrentTopOrCreateNewTask");
mTargetStack.positionChildWindowContainerAtTop(task);
if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
@@ -2124,7 +2124,7 @@ class ActivityStarter {
canUseFocusedStack = true;
break;
case ASSISTANT_STACK_ID:
- canUseFocusedStack = r.isAssistantActivity();
+ canUseFocusedStack = r.isActivityTypeAssistant();
break;
case DOCKED_STACK_ID:
// Any activity which supports split screen can go in the docked stack.
@@ -2155,13 +2155,13 @@ class ActivityStarter {
// If the activity is of a specific type, return the associated stack, creating it if
// necessary
- if (r.isHomeActivity()) {
+ if (r.isActivityTypeHome()) {
return mSupervisor.mHomeStack;
}
- if (r.isRecentsActivity()) {
+ if (r.isActivityTypeRecents()) {
return mSupervisor.getStack(RECENTS_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
}
- if (r.isAssistantActivity()) {
+ if (r.isActivityTypeAssistant()) {
return mSupervisor.getStack(ASSISTANT_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
}
@@ -2254,9 +2254,9 @@ class ActivityStarter {
case PINNED_STACK_ID:
return r.supportsPictureInPicture();
case RECENTS_STACK_ID:
- return r.isRecentsActivity();
+ return r.isActivityTypeRecents();
case ASSISTANT_STACK_ID:
- return r.isAssistantActivity();
+ return r.isActivityTypeAssistant();
default:
if (StackId.isDynamicStack(stackId)) {
return r.canBeLaunchedOnDisplay(displayId);
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index fc03db1203bd..d1424c8d43f2 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -55,7 +55,6 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
-import java.util.List;
import java.util.Set;
import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
@@ -676,7 +675,7 @@ class AppErrors {
&& (mService.mHomeProcess.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
final ActivityRecord r = activities.get(activityNdx);
- if (r.isHomeActivity()) {
+ if (r.isActivityTypeHome()) {
Log.i(TAG, "Clearing package preferred activities from " + r.packageName);
try {
ActivityThread.getPackageManager()
diff --git a/services/core/java/com/android/server/am/PinnedActivityStack.java b/services/core/java/com/android/server/am/PinnedActivityStack.java
index a1b95f9de05a..86ee3f4ba215 100644
--- a/services/core/java/com/android/server/am/PinnedActivityStack.java
+++ b/services/core/java/com/android/server/am/PinnedActivityStack.java
@@ -92,15 +92,16 @@ class PinnedActivityStack extends ActivityStack<PinnedStackWindowController>
return mWindowContainerController.deferScheduleMultiWindowModeChanged();
}
- public void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds) {
+ public void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds,
+ boolean forceUpdate) {
// It is guaranteed that the activities requiring the update will be in the pinned stack at
// this point (either reparented before the animation into PiP, or before reparenting after
// the animation out of PiP)
synchronized(this) {
ArrayList<TaskRecord> tasks = getAllTasks();
for (int i = 0; i < tasks.size(); i++ ) {
- mStackSupervisor.scheduleUpdatePictureInPictureModeIfNeeded(tasks.get(i),
- targetStackBounds, true /* immediate */);
+ mStackSupervisor.updatePictureInPictureMode(tasks.get(i), targetStackBounds,
+ forceUpdate);
}
}
}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 15789309cf7a..5c0d587a1202 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -26,6 +26,11 @@ import static android.app.ActivityManager.StackId.HOME_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.app.ActivityManager.StackId.RECENTS_STACK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
@@ -44,6 +49,7 @@ import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
import static android.view.Display.DEFAULT_DISPLAY;
+
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
@@ -54,15 +60,12 @@ import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_TASKS;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
-import static com.android.server.am.ActivityRecord.ASSISTANT_ACTIVITY_TYPE;
-import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
-import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
import static com.android.server.am.ActivityRecord.STARTING_WINDOW_SHOWN;
import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING;
import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING_TO_TOP;
import static com.android.server.am.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
+
import static java.lang.Integer.MAX_VALUE;
import android.annotation.IntDef;
@@ -75,6 +78,7 @@ import android.app.ActivityManager.TaskSnapshot;
import android.app.ActivityOptions;
import android.app.AppGlobals;
import android.app.IActivityManager;
+import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -135,6 +139,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
private static final String ATTR_USERID = "user_id";
private static final String ATTR_USER_SETUP_COMPLETE = "user_setup_complete";
private static final String ATTR_EFFECTIVE_UID = "effective_uid";
+ @Deprecated
private static final String ATTR_TASKTYPE = "task_type";
private static final String ATTR_FIRSTACTIVETIME = "first_active_time";
private static final String ATTR_LASTACTIVETIME = "last_active_time";
@@ -248,9 +253,6 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
/** Current stack. Setter must always be used to update the value. */
private ActivityStack mStack;
- /** Takes on same set of values as ActivityRecord.mActivityType */
- int taskType;
-
/** Takes on same value as first root activity */
boolean isPersistable = false;
int maxRecents;
@@ -260,10 +262,11 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
* (positive) or back (negative). Absolute value indicates time. */
long mLastTimeMoved = System.currentTimeMillis();
- /** Indication of what to run next when task exits. Use ActivityRecord types.
- * ActivityRecord.APPLICATION_ACTIVITY_TYPE indicates to resume the task below this one in the
- * task stack. */
- private int mTaskToReturnTo = APPLICATION_ACTIVITY_TYPE;
+ /** Indication of what to run next when task exits. */
+ // TODO: Shouldn't be needed if we have things in visual order. I.e. we stop using stacks or
+ // have a stack per standard application type...
+ /*@WindowConfiguration.ActivityType*/
+ private int mTaskToReturnTo = ACTIVITY_TYPE_STANDARD;
/** If original intent did not allow relinquishing task identity, save that information */
private boolean mNeverRelinquishIdentity = true;
@@ -316,7 +319,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
private TaskWindowContainerController mWindowContainerController;
TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
- IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor, int type) {
+ IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor) {
mService = service;
mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
TaskPersister.IMAGE_EXTENSION;
@@ -329,7 +332,6 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
mActivities = new ArrayList<>();
mCallingUid = info.applicationInfo.uid;
mCallingPackage = info.packageName;
- taskType = type;
setIntent(_intent, info);
setMinDimensions(info);
touchActiveTime();
@@ -358,8 +360,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
maxRecents = Math.min(Math.max(info.maxRecents, 1),
ActivityManager.getMaxAppRecentsLimitStatic());
- taskType = APPLICATION_ACTIVITY_TYPE;
- mTaskToReturnTo = HOME_ACTIVITY_TYPE;
+ mTaskToReturnTo = ACTIVITY_TYPE_HOME;
lastTaskDescription = _taskDescription;
touchActiveTime();
mService.mTaskChangeNotificationController.notifyTaskCreated(_taskId, realActivity);
@@ -368,7 +369,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
private TaskRecord(ActivityManagerService service, int _taskId, Intent _intent,
Intent _affinityIntent, String _affinity, String _rootAffinity,
ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset,
- boolean _autoRemoveRecents, boolean _askedCompatMode, int _taskType, int _userId,
+ boolean _autoRemoveRecents, boolean _askedCompatMode, int _userId,
int _effectiveUid, String _lastDescription, ArrayList<ActivityRecord> activities,
long _firstActiveTime, long _lastActiveTime, long lastTimeMoved,
boolean neverRelinquishIdentity, TaskDescription _lastTaskDescription,
@@ -393,8 +394,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
isAvailable = true;
autoRemoveRecents = _autoRemoveRecents;
askedCompatMode = _askedCompatMode;
- taskType = _taskType;
- mTaskToReturnTo = HOME_ACTIVITY_TYPE;
+ mTaskToReturnTo = ACTIVITY_TYPE_HOME;
userId = _userId;
mUserSetupComplete = userSetupComplete;
effectiveUid = _effectiveUid;
@@ -433,8 +433,8 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
final Configuration overrideConfig = getOverrideConfiguration();
setWindowContainerController(new TaskWindowContainerController(taskId, this,
getStack().getWindowContainerController(), userId, bounds, overrideConfig,
- mResizeMode, mSupportsPictureInPicture, isHomeTask(), onTop, showForAllUsers,
- lastTaskDescription));
+ mResizeMode, mSupportsPictureInPicture, onTop,
+ showForAllUsers, lastTaskDescription));
}
/**
@@ -887,16 +887,16 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
return this.intent.filterEquals(intent);
}
- void setTaskToReturnTo(int taskToReturnTo) {
- mTaskToReturnTo = (taskToReturnTo == RECENTS_ACTIVITY_TYPE)
- ? HOME_ACTIVITY_TYPE : taskToReturnTo;
+ void setTaskToReturnTo(/*@WindowConfiguration.ActivityType*/ int taskToReturnTo) {
+ mTaskToReturnTo = taskToReturnTo == ACTIVITY_TYPE_RECENTS
+ ? ACTIVITY_TYPE_HOME : taskToReturnTo;
}
void setTaskToReturnTo(ActivityRecord source) {
- if (source.isRecentsActivity()) {
- setTaskToReturnTo(RECENTS_ACTIVITY_TYPE);
- } else if (source.isAssistantActivity()) {
- setTaskToReturnTo(ASSISTANT_ACTIVITY_TYPE);
+ if (source.isActivityTypeRecents()) {
+ setTaskToReturnTo(ACTIVITY_TYPE_RECENTS);
+ } else if (source.isActivityTypeAssistant()) {
+ setTaskToReturnTo(ACTIVITY_TYPE_ASSISTANT);
}
}
@@ -904,6 +904,14 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
return mTaskToReturnTo;
}
+ boolean returnsToHomeTask() {
+ return mTaskToReturnTo == ACTIVITY_TYPE_HOME;
+ }
+
+ boolean returnsToStandardTask() {
+ return mTaskToReturnTo == ACTIVITY_TYPE_STANDARD;
+ }
+
void setPrevAffiliate(TaskRecord prevAffiliate) {
mPrevAffiliate = prevAffiliate;
mPrevAffiliateTaskId = prevAffiliate == null ? INVALID_TASK_ID : prevAffiliate.taskId;
@@ -1133,6 +1141,16 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
addActivityAtIndex(mActivities.size(), r);
}
+ @Override
+ /*@WindowConfiguration.ActivityType*/
+ public int getActivityType() {
+ final int applicationType = super.getActivityType();
+ if (applicationType != ACTIVITY_TYPE_UNDEFINED || mActivities.isEmpty()) {
+ return applicationType;
+ }
+ return mActivities.get(0).getActivityType();
+ }
+
/**
* Adds an activity {@param r} at the given {@param index}. The activity {@param r} must either
* be in the current task or unparented to any task.
@@ -1153,7 +1171,17 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
}
// Only set this based on the first activity
if (mActivities.isEmpty()) {
- taskType = r.mActivityType;
+ // TODO: propagating this change to the WM side...Should probably be done by having
+ // ConfigurationContainer change listener that the WindowContainerController registers
+ // for.
+ if (r.getActivityType() == ACTIVITY_TYPE_UNDEFINED) {
+ // Normally non-standard activity type for the activity record will be set when the
+ // object is created, however we delay setting the standard application type until
+ // this point so that the task can set the type for additional activities added in
+ // the else condition below.
+ r.setActivityType(ACTIVITY_TYPE_STANDARD);
+ }
+ setActivityType(r.getActivityType());
isPersistable = r.isPersistable();
mCallingUid = r.launchedFromUid;
mCallingPackage = r.launchedFromPackage;
@@ -1162,7 +1190,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
ActivityManager.getMaxAppRecentsLimitStatic());
} else {
// Otherwise make all added activities match this one.
- r.mActivityType = taskType;
+ r.setActivityType(getActivityType());
}
final int size = mActivities.size();
@@ -1427,28 +1455,12 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
return false;
}
- boolean isHomeTask() {
- return taskType == HOME_ACTIVITY_TYPE;
- }
-
- boolean isRecentsTask() {
- return taskType == RECENTS_ACTIVITY_TYPE;
- }
-
- boolean isAssistantTask() {
- return taskType == ASSISTANT_ACTIVITY_TYPE;
- }
-
- boolean isApplicationTask() {
- return taskType == APPLICATION_ACTIVITY_TYPE;
- }
-
boolean isOverHomeStack() {
- return mTaskToReturnTo == HOME_ACTIVITY_TYPE;
+ return mTaskToReturnTo == ACTIVITY_TYPE_HOME;
}
boolean isOverAssistantStack() {
- return mTaskToReturnTo == ASSISTANT_ACTIVITY_TYPE;
+ return mTaskToReturnTo == ACTIVITY_TYPE_ASSISTANT;
}
private boolean isResizeable(boolean checkSupportsPip) {
@@ -1642,7 +1654,6 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
out.attribute(null, ATTR_USERID, String.valueOf(userId));
out.attribute(null, ATTR_USER_SETUP_COMPLETE, String.valueOf(mUserSetupComplete));
out.attribute(null, ATTR_EFFECTIVE_UID, String.valueOf(effectiveUid));
- out.attribute(null, ATTR_TASKTYPE, String.valueOf(taskType));
out.attribute(null, ATTR_FIRSTACTIVETIME, String.valueOf(firstActiveTime));
out.attribute(null, ATTR_LASTACTIVETIME, String.valueOf(lastActiveTime));
out.attribute(null, ATTR_LASTTIMEMOVED, String.valueOf(mLastTimeMoved));
@@ -1712,7 +1723,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
boolean rootHasReset = false;
boolean autoRemoveRecents = false;
boolean askedCompatMode = false;
- int taskType = ActivityRecord.APPLICATION_ACTIVITY_TYPE;
+ int taskType = 0;
int userId = 0;
boolean userSetupComplete = true;
int effectiveUid = -1;
@@ -1867,7 +1878,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
// they are marked as RESIZE_MODE_RESIZEABLE to RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION
// since we didn't have that differentiation before version 1 and the system didn't
// resize home activities before then.
- if (taskType == HOME_ACTIVITY_TYPE && resizeMode == RESIZE_MODE_RESIZEABLE) {
+ if (taskType == 1 /* old home type */ && resizeMode == RESIZE_MODE_RESIZEABLE) {
resizeMode = RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
}
} else {
@@ -1883,7 +1894,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
final TaskRecord task = new TaskRecord(stackSupervisor.mService, taskId, intent,
affinityIntent, affinity, rootAffinity, realActivity, origActivity, rootHasReset,
- autoRemoveRecents, askedCompatMode, taskType, userId, effectiveUid, lastDescription,
+ autoRemoveRecents, askedCompatMode, userId, effectiveUid, lastDescription,
activities, firstActiveTime, lastActiveTime, lastTimeOnTop, neverRelinquishIdentity,
taskDescription, taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor,
callingUid, callingPackage, resizeMode, supportsPictureInPicture, privileged,
@@ -2105,13 +2116,13 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
* The task will be moved (and stack focus changed) later if necessary.
*/
int getLaunchStackId() {
- if (isRecentsTask()) {
+ if (isActivityTypeRecents()) {
return RECENTS_STACK_ID;
}
- if (isHomeTask()) {
+ if (isActivityTypeHome()) {
return HOME_STACK_ID;
}
- if (isAssistantTask()) {
+ if (isActivityTypeAssistant()) {
return ASSISTANT_STACK_ID;
}
if (mBounds != null) {
@@ -2190,12 +2201,12 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
pw.print(prefix); pw.print("realActivity=");
pw.println(realActivity.flattenToShortString());
}
- if (autoRemoveRecents || isPersistable || taskType != 0 || mTaskToReturnTo != 0
- || numFullscreen != 0) {
+ if (autoRemoveRecents || isPersistable || !isActivityTypeStandard()
+ || mTaskToReturnTo != ACTIVITY_TYPE_STANDARD || numFullscreen != 0) {
pw.print(prefix); pw.print("autoRemoveRecents="); pw.print(autoRemoveRecents);
pw.print(" isPersistable="); pw.print(isPersistable);
pw.print(" numFullscreen="); pw.print(numFullscreen);
- pw.print(" taskType="); pw.print(taskType);
+ pw.print(" activityType="); pw.print(getActivityType());
pw.print(" mTaskToReturnTo="); pw.println(mTaskToReturnTo);
}
if (rootWasReset || mNeverRelinquishIdentity || mReuseTask
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index faf4729ead35..91b15912fcb5 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -801,12 +801,23 @@ public class AudioService extends IAudioService.Stub
public void systemReady() {
sendMsg(mAudioHandler, MSG_SYSTEM_READY, SENDMSG_QUEUE,
0, 0, null, 0);
- try {
- ActivityManager.getService().registerUidObserver(mUidObserver,
- ActivityManager.UID_OBSERVER_CACHED | ActivityManager.UID_OBSERVER_GONE,
- ActivityManager.PROCESS_STATE_UNKNOWN, null);
- } catch (RemoteException e) {
- // ignored; both services live in system_server
+ if (false) {
+ // This is turned off for now, because it is racy and thus causes apps to break.
+ // Currently banning a uid means that if an app tries to start playing an audio
+ // stream, that will be preventing, and unbanning it will not allow that stream
+ // to resume. However these changes in uid state are racy with what the app is doing,
+ // so that after taking a process out of the cached state we can't guarantee that
+ // we will unban the uid before the app actually tries to start playing audio.
+ // (To do that, the activity manager would need to wait until it knows for sure
+ // that the ban has been removed, before telling the app to do whatever it is
+ // supposed to do that caused it to go out of the cached state.)
+ try {
+ ActivityManager.getService().registerUidObserver(mUidObserver,
+ ActivityManager.UID_OBSERVER_CACHED | ActivityManager.UID_OBSERVER_GONE,
+ ActivityManager.PROCESS_STATE_UNKNOWN, null);
+ } catch (RemoteException e) {
+ // ignored; both services live in system_server
+ }
}
}
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 79bed73a0ca0..c6998d6a108c 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -520,11 +520,12 @@ public final class JobSchedulerService extends com.android.server.SystemService
if (DEBUG) {
Slog.d(TAG, "Receieved: " + action);
}
+ final String pkgName = getPackageName(intent);
+ final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+
if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
// Purge the app's jobs if the whole package was just disabled. When this is
// the case the component name will be a bare package name.
- final String pkgName = getPackageName(intent);
- final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
if (pkgName != null && pkgUid != -1) {
final String[] changedComponents = intent.getStringArrayExtra(
Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
@@ -544,7 +545,8 @@ public final class JobSchedulerService extends com.android.server.SystemService
Slog.d(TAG, "Removing jobs for package " + pkgName
+ " in user " + userId);
}
- cancelJobsForUid(pkgUid, "app package state changed");
+ cancelJobsForPackageAndUid(pkgName, pkgUid,
+ "app disabled");
}
} catch (RemoteException|IllegalArgumentException e) {
/*
@@ -573,7 +575,7 @@ public final class JobSchedulerService extends com.android.server.SystemService
if (DEBUG) {
Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
}
- cancelJobsForUid(uidRemoved, "app uninstalled");
+ cancelJobsForPackageAndUid(pkgName, uidRemoved, "app uninstalled");
}
} else if (Intent.ACTION_USER_REMOVED.equals(action)) {
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
@@ -584,8 +586,6 @@ public final class JobSchedulerService extends com.android.server.SystemService
} else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
// Has this package scheduled any jobs, such that we will take action
// if it were to be force-stopped?
- final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
- final String pkgName = intent.getData().getSchemeSpecificPart();
if (pkgUid != -1) {
List<JobStatus> jobsForUid;
synchronized (mLock) {
@@ -604,13 +604,11 @@ public final class JobSchedulerService extends com.android.server.SystemService
}
} else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
// possible force-stop
- final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
- final String pkgName = intent.getData().getSchemeSpecificPart();
if (pkgUid != -1) {
if (DEBUG) {
Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid);
}
- cancelJobsForPackageAndUid(pkgName, pkgUid);
+ cancelJobsForPackageAndUid(pkgName, pkgUid, "app force stopped");
}
}
}
@@ -790,13 +788,17 @@ public final class JobSchedulerService extends com.android.server.SystemService
}
}
- void cancelJobsForPackageAndUid(String pkgName, int uid) {
+ void cancelJobsForPackageAndUid(String pkgName, int uid, String reason) {
+ if ("android".equals(pkgName)) {
+ Slog.wtfStack(TAG, "Can't cancel all jobs for system package");
+ return;
+ }
synchronized (mLock) {
final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
for (int i = jobsForUid.size() - 1; i >= 0; i--) {
final JobStatus job = jobsForUid.get(i);
if (job.getSourcePackageName().equals(pkgName)) {
- cancelJobImplLocked(job, null, "app force stopped");
+ cancelJobImplLocked(job, null, reason);
}
}
}
@@ -811,8 +813,7 @@ public final class JobSchedulerService extends com.android.server.SystemService
*/
public void cancelJobsForUid(int uid, String reason) {
if (uid == Process.SYSTEM_UID) {
- // This really shouldn't happen.
- Slog.wtfStack(TAG, "cancelJobsForUid() called for system uid");
+ Slog.wtfStack(TAG, "Can't cancel all jobs for system uid");
return;
}
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index 031bdd0ee39c..d3fd3a992a31 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -261,6 +261,13 @@ public final class JobServiceContext implements ServiceConnection {
return mRunningJob;
}
+ /**
+ * Used only for debugging. Will return <code>"&lt;null&gt;"</code> if there is no job running.
+ */
+ private String getRunningJobNameLocked() {
+ return mRunningJob != null ? mRunningJob.toShortString() : "<null>";
+ }
+
/** Called externally when a job that was scheduled for execution should be cancelled. */
void cancelExecutingJobLocked(int reason, String debugReason) {
doCancelLocked(reason, debugReason);
@@ -522,7 +529,7 @@ public final class JobServiceContext implements ServiceConnection {
/** Start the job on the service. */
private void handleServiceBoundLocked() {
if (DEBUG) {
- Slog.d(TAG, "handleServiceBound for " + mRunningJob.toShortString());
+ Slog.d(TAG, "handleServiceBound for " + getRunningJobNameLocked());
}
if (mVerb != VERB_BINDING) {
Slog.e(TAG, "Sending onStartJob for a job that isn't pending. "
@@ -639,36 +646,34 @@ public final class JobServiceContext implements ServiceConnection {
private void handleOpTimeoutLocked() {
switch (mVerb) {
case VERB_BINDING:
- Slog.w(TAG, "Time-out while trying to bind " + mRunningJob.toShortString() +
- ", dropping.");
+ Slog.w(TAG, "Time-out while trying to bind " + getRunningJobNameLocked()
+ + ", dropping.");
closeAndCleanupJobLocked(false /* needsReschedule */, "timed out while binding");
break;
case VERB_STARTING:
// Client unresponsive - wedged or failed to respond in time. We don't really
// know what happened so let's log it and notify the JobScheduler
// FINISHED/NO-RETRY.
- Slog.w(TAG, "No response from client for onStartJob " +
- mRunningJob != null ? mRunningJob.toShortString() : "<null>");
+ Slog.w(TAG, "No response from client for onStartJob "
+ + getRunningJobNameLocked());
closeAndCleanupJobLocked(false /* needsReschedule */, "timed out while starting");
break;
case VERB_STOPPING:
// At least we got somewhere, so fail but ask the JobScheduler to reschedule.
- Slog.w(TAG, "No response from client for onStopJob " +
- mRunningJob != null ? mRunningJob.toShortString() : "<null>");
+ Slog.w(TAG, "No response from client for onStopJob "
+ + getRunningJobNameLocked());
closeAndCleanupJobLocked(true /* needsReschedule */, "timed out while stopping");
break;
case VERB_EXECUTING:
// Not an error - client ran out of time.
Slog.i(TAG, "Client timed out while executing (no jobFinished received), " +
- "sending onStop: " +
- mRunningJob != null ? mRunningJob.toShortString() : "<null>");
+ "sending onStop: " + getRunningJobNameLocked());
mParams.setStopReason(JobParameters.REASON_TIMEOUT);
sendStopMessageLocked("timeout while executing");
break;
default:
- Slog.e(TAG, "Handling timeout for an invalid job state: " +
- mRunningJob != null ? mRunningJob.toShortString() : "<null>"
- + ", dropping.");
+ Slog.e(TAG, "Handling timeout for an invalid job state: "
+ + getRunningJobNameLocked() + ", dropping.");
closeAndCleanupJobLocked(false /* needsReschedule */, "invalid timeout");
}
}
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 922df1e3dba8..3795b7f3091c 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -18,9 +18,7 @@ package com.android.server.media;
import com.android.internal.util.DumpUtils;
import com.android.server.Watchdog;
-import com.android.server.media.AudioPlaybackMonitor.OnAudioPlayerActiveStateChangedListener;
-import android.Manifest;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -96,9 +94,10 @@ public final class MediaRouterService extends IMediaRouterService.Stub
private final ArrayMap<IBinder, ClientRecord> mAllClientRecords =
new ArrayMap<IBinder, ClientRecord>();
private int mCurrentUserId = -1;
- private boolean mHasBluetoothRoute = false;
+ private boolean mGlobalBluetoothA2dpOn = false;
private final IAudioService mAudioService;
private final AudioPlaybackMonitor mAudioPlaybackMonitor;
+ private final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo();
public MediaRouterService(Context context) {
mContext = context;
@@ -137,13 +136,39 @@ public final class MediaRouterService extends IMediaRouterService.Stub
audioRoutes = mAudioService.startWatchingRoutes(new IAudioRoutesObserver.Stub() {
@Override
public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) {
- mHasBluetoothRoute = newRoutes.bluetoothName != null;
+ synchronized (mLock) {
+ if (newRoutes.mainType != mCurAudioRoutesInfo.mainType) {
+ if ((newRoutes.mainType & (AudioRoutesInfo.MAIN_HEADSET
+ | AudioRoutesInfo.MAIN_HEADPHONES
+ | AudioRoutesInfo.MAIN_USB)) == 0) {
+ // headset was plugged out.
+ mGlobalBluetoothA2dpOn = newRoutes.bluetoothName != null;
+ } else {
+ // headset was plugged in.
+ mGlobalBluetoothA2dpOn = false;
+ }
+ mCurAudioRoutesInfo.mainType = newRoutes.mainType;
+ }
+ if (!TextUtils.equals(
+ newRoutes.bluetoothName, mCurAudioRoutesInfo.bluetoothName)) {
+ if (newRoutes.bluetoothName == null) {
+ // BT was disconnected.
+ mGlobalBluetoothA2dpOn = false;
+ } else {
+ // BT was connected or changed.
+ mGlobalBluetoothA2dpOn = true;
+ }
+ mCurAudioRoutesInfo.bluetoothName = newRoutes.bluetoothName;
+ }
+ }
}
});
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in the audio service.");
}
- mHasBluetoothRoute = (audioRoutes != null && audioRoutes.bluetoothName != null);
+ synchronized (mLock) {
+ mGlobalBluetoothA2dpOn = (audioRoutes != null && audioRoutes.bluetoothName != null);
+ }
}
public void systemRunning() {
@@ -246,6 +271,14 @@ public final class MediaRouterService extends IMediaRouterService.Stub
// Binder call
@Override
+ public boolean isGlobalBluetoothA2doOn() {
+ synchronized (mLock) {
+ return mGlobalBluetoothA2dpOn;
+ }
+ }
+
+ // Binder call
+ @Override
public void setDiscoveryRequest(IMediaRouterClient client,
int routeTypes, boolean activeScan) {
if (client == null) {
@@ -346,7 +379,12 @@ public final class MediaRouterService extends IMediaRouterService.Stub
void restoreBluetoothA2dp() {
try {
- mAudioService.setBluetoothA2dpOn(mHasBluetoothRoute);
+ boolean a2dpOn = false;
+ synchronized (mLock) {
+ a2dpOn = mGlobalBluetoothA2dpOn;
+ }
+ Slog.v(TAG, "restoreBluetoothA2dp( " + a2dpOn + ")");
+ mAudioService.setBluetoothA2dpOn(a2dpOn);
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException while calling setBluetoothA2dpOn.");
}
@@ -354,12 +392,14 @@ public final class MediaRouterService extends IMediaRouterService.Stub
void restoreRoute(int uid) {
ClientRecord clientRecord = null;
- UserRecord userRecord = mUserRecords.get(UserHandle.getUserId(uid));
- if (userRecord != null && userRecord.mClientRecords != null) {
- for (ClientRecord cr : userRecord.mClientRecords) {
- if (validatePackageName(uid, cr.mPackageName)) {
- clientRecord = cr;
- break;
+ synchronized (mLock) {
+ UserRecord userRecord = mUserRecords.get(UserHandle.getUserId(uid));
+ if (userRecord != null && userRecord.mClientRecords != null) {
+ for (ClientRecord cr : userRecord.mClientRecords) {
+ if (validatePackageName(uid, cr.mPackageName)) {
+ clientRecord = cr;
+ break;
+ }
}
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 9a76294cdd9e..0c9f65aeff98 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1476,6 +1476,19 @@ public class NotificationManagerService extends SystemService {
savePolicyFile();
}
+ private void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group,
+ boolean fromApp, boolean fromListener) {
+ Preconditions.checkNotNull(group);
+ Preconditions.checkNotNull(pkg);
+ mRankingHelper.createNotificationChannelGroup(pkg, uid, group,
+ fromApp);
+ if (!fromListener) {
+ mListeners.notifyNotificationChannelGroupChanged(pkg,
+ UserHandle.of(UserHandle.getCallingUserId()), group,
+ NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
+ }
+ }
+
private ArrayList<ComponentName> getSuppressors() {
ArrayList<ComponentName> names = new ArrayList<ComponentName>();
for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
@@ -1763,6 +1776,14 @@ public class NotificationManagerService extends SystemService {
}
@Override
+ public void updateNotificationChannelGroupForPackage(String pkg, int uid,
+ NotificationChannelGroup group) throws RemoteException {
+ enforceSystemOrSystemUI("Caller not system or systemui");
+ createNotificationChannelGroup(pkg, uid, group, false, false);
+ savePolicyFile();
+ }
+
+ @Override
public void createNotificationChannelGroups(String pkg,
ParceledListSlice channelGroupList) throws RemoteException {
checkCallerIsSystemOrSameApp(pkg);
@@ -1770,12 +1791,7 @@ public class NotificationManagerService extends SystemService {
final int groupSize = groups.size();
for (int i = 0; i < groupSize; i++) {
final NotificationChannelGroup group = groups.get(i);
- Preconditions.checkNotNull(group, "group in list is null");
- mRankingHelper.createNotificationChannelGroup(pkg, Binder.getCallingUid(), group,
- true /* fromTargetApp */);
- mListeners.notifyNotificationChannelGroupChanged(pkg,
- UserHandle.of(UserHandle.getCallingUserId()), group,
- NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
+ createNotificationChannelGroup(pkg, Binder.getCallingUid(), group, true, false);
}
savePolicyFile();
}
@@ -1921,6 +1937,14 @@ public class NotificationManagerService extends SystemService {
}
@Override
+ public NotificationChannelGroup getPopulatedNotificationChannelGroupForPackage(
+ String pkg, int uid, String groupId, boolean includeDeleted) {
+ enforceSystemOrSystemUI("getPopulatedNotificationChannelGroupForPackage");
+ return mRankingHelper.getNotificationChannelGroupWithChannels(
+ pkg, uid, groupId, includeDeleted);
+ }
+
+ @Override
public NotificationChannelGroup getNotificationChannelGroupForPackage(
String groupId, String pkg, int uid) {
enforceSystemOrSystemUI("getNotificationChannelGroupForPackage");
@@ -2923,6 +2947,17 @@ public class NotificationManagerService extends SystemService {
}
@Override
+ public void updateNotificationChannelGroupFromPrivilegedListener(
+ INotificationListener token, String pkg, UserHandle user,
+ NotificationChannelGroup group) throws RemoteException {
+ Preconditions.checkNotNull(user);
+ verifyPrivilegedListener(token, user);
+ createNotificationChannelGroup(
+ pkg, getUidForPackageAndUser(pkg, user), group, false, true);
+ savePolicyFile();
+ }
+
+ @Override
public void updateNotificationChannelFromPrivilegedListener(INotificationListener token,
String pkg, UserHandle user, NotificationChannel channel) throws RemoteException {
Preconditions.checkNotNull(channel);
@@ -3618,9 +3653,10 @@ public class NotificationManagerService extends SystemService {
usageStats.registerSuspendedByAdmin(r);
return isPackageSuspended;
}
-
final boolean isBlocked =
- mRankingHelper.getImportance(pkg, callingUid) == NotificationManager.IMPORTANCE_NONE
+ mRankingHelper.isGroupBlocked(pkg, callingUid, r.getChannel().getGroup())
+ || mRankingHelper.getImportance(pkg, callingUid)
+ == NotificationManager.IMPORTANCE_NONE
|| r.getChannel().getImportance() == NotificationManager.IMPORTANCE_NONE;
if (isBlocked) {
Slog.e(TAG, "Suppressing notification from package by user request.");
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index 36da04dfc3c6..b5ef1c60f607 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -29,6 +29,7 @@ public interface RankingConfig {
void setShowBadge(String packageName, int uid, boolean showBadge);
boolean canShowBadge(String packageName, int uid);
boolean badgingEnabled(UserHandle userHandle);
+ boolean isGroupBlocked(String packageName, int uid, String groupId);
Collection<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
int uid);
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 3386fe832e0f..1736a74e1270 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -215,6 +215,7 @@ public class RankingHelper implements RankingConfig {
if (!TextUtils.isEmpty(id)) {
NotificationChannelGroup group
= new NotificationChannelGroup(id, groupName);
+ group.populateFromXml(parser);
r.groups.put(id, group);
}
}
@@ -493,6 +494,19 @@ public class RankingHelper implements RankingConfig {
updateConfig();
}
+ @Override
+ public boolean isGroupBlocked(String packageName, int uid, String groupId) {
+ if (groupId == null) {
+ return false;
+ }
+ Record r = getOrCreateRecord(packageName, uid);
+ NotificationChannelGroup group = r.groups.get(groupId);
+ if (group == null) {
+ return false;
+ }
+ return group.isBlocked();
+ }
+
int getPackagePriority(String pkg, int uid) {
return getOrCreateRecord(pkg, uid).priority;
}
@@ -514,9 +528,16 @@ public class RankingHelper implements RankingConfig {
}
final NotificationChannelGroup oldGroup = r.groups.get(group.getId());
if (!group.equals(oldGroup)) {
- // will log for new entries as well as name changes
+ // will log for new entries as well as name/description changes
MetricsLogger.action(getChannelGroupLog(group.getId(), pkg));
}
+ if (oldGroup != null) {
+ group.setChannels(oldGroup.getChannels());
+
+ if (fromTargetApp) {
+ group.setBlocked(oldGroup.isBlocked());
+ }
+ }
r.groups.put(group.getId(), group);
}
@@ -552,6 +573,9 @@ public class RankingHelper implements RankingConfig {
existing.setName(channel.getName().toString());
existing.setDescription(channel.getDescription());
existing.setBlockableSystem(channel.isBlockableSystem());
+ if (existing.getGroup() == null) {
+ existing.setGroup(channel.getGroup());
+ }
// Apps are allowed to downgrade channel importance if the user has not changed any
// fields on this channel yet.
@@ -684,6 +708,27 @@ public class RankingHelper implements RankingConfig {
}
}
+ public NotificationChannelGroup getNotificationChannelGroupWithChannels(String pkg,
+ int uid, String groupId, boolean includeDeleted) {
+ Preconditions.checkNotNull(pkg);
+ Record r = getRecord(pkg, uid);
+ if (r == null || groupId == null || !r.groups.containsKey(groupId)) {
+ return null;
+ }
+ NotificationChannelGroup group = r.groups.get(groupId).clone();
+ group.setChannels(new ArrayList<>());
+ int N = r.channels.size();
+ for (int i = 0; i < N; i++) {
+ final NotificationChannel nc = r.channels.valueAt(i);
+ if (includeDeleted || !nc.isDeleted()) {
+ if (groupId.equals(nc.getGroup())) {
+ group.addChannel(nc);
+ }
+ }
+ }
+ return group;
+ }
+
public NotificationChannelGroup getNotificationChannelGroup(String groupId, String pkg,
int uid) {
Preconditions.checkNotNull(pkg);
@@ -710,6 +755,7 @@ public class RankingHelper implements RankingConfig {
NotificationChannelGroup ncg = groups.get(nc.getGroup());
if (ncg == null) {
ncg = r.groups.get(nc.getGroup()).clone();
+ ncg.setChannels(new ArrayList<>());
groups.put(nc.getGroup(), ncg);
}
diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java
index 04d91f882d04..807c343d0d10 100644
--- a/services/core/java/com/android/server/om/IdmapManager.java
+++ b/services/core/java/com/android/server/om/IdmapManager.java
@@ -92,26 +92,10 @@ class IdmapManager {
return new File(getIdmapPath(overlayPackage.applicationInfo.getBaseCodePath())).isFile();
}
- boolean isDangerous(@NonNull final PackageInfo overlayPackage, final int userId) {
- // unused userId: see comment in OverlayManagerServiceImpl.removeIdmapIfPossible
- return isDangerous(getIdmapPath(overlayPackage.applicationInfo.getBaseCodePath()));
- }
-
private String getIdmapPath(@NonNull final String baseCodePath) {
final StringBuilder sb = new StringBuilder("/data/resource-cache/");
sb.append(baseCodePath.substring(1).replace('/', '@'));
sb.append("@idmap");
return sb.toString();
}
-
- private boolean isDangerous(@NonNull final String idmapPath) {
- try (DataInputStream dis = new DataInputStream(new FileInputStream(idmapPath))) {
- final int magic = dis.readInt();
- final int version = dis.readInt();
- final int dangerous = dis.readInt();
- return dangerous != 0;
- } catch (IOException e) {
- return true;
- }
- }
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index db6e9749535b..c3957f432f4c 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -169,8 +169,9 @@ final class OverlayManagerServiceImpl {
}
final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId);
- updateAllOverlaysForTarget(packageName, userId, targetPackage);
- mListener.onOverlaysChanged(packageName, userId);
+ if (updateAllOverlaysForTarget(packageName, userId, targetPackage)) {
+ mListener.onOverlaysChanged(packageName, userId);
+ }
}
void onTargetPackageChanged(@NonNull final String packageName, final int userId) {
@@ -210,7 +211,9 @@ final class OverlayManagerServiceImpl {
Slog.d(TAG, "onTargetPackageRemoved packageName=" + packageName + " userId=" + userId);
}
- updateAllOverlaysForTarget(packageName, userId, null);
+ if (updateAllOverlaysForTarget(packageName, userId, null)) {
+ mListener.onOverlaysChanged(packageName, userId);
+ }
}
/**
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index d21100c0cf72..68913c3a887a 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -7820,13 +7820,13 @@ public class PhoneWindowManager implements WindowManagerPolicy {
case HapticFeedbackConstants.VIRTUAL_KEY:
return VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE:
- return VibrationEffect.get(VibrationEffect.EFFECT_TICK);
+ return VibrationEffect.get(VibrationEffect.EFFECT_TICK, false);
case HapticFeedbackConstants.KEYBOARD_PRESS: // == HapticFeedbackConstants.KEYBOARD_TAP
return VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
case HapticFeedbackConstants.KEYBOARD_RELEASE:
- return VibrationEffect.get(VibrationEffect.EFFECT_TICK);
+ return VibrationEffect.get(VibrationEffect.EFFECT_TICK, false);
case HapticFeedbackConstants.TEXT_HANDLE_MOVE:
- return VibrationEffect.get(VibrationEffect.EFFECT_TICK);
+ return VibrationEffect.get(VibrationEffect.EFFECT_TICK, false);
default:
return null;
}
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index c2edc04db2e1..c76b90593ab3 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -450,7 +450,7 @@ public class AppWindowAnimator {
return isAnimating;
}
- void dump(PrintWriter pw, String prefix, boolean dumpAll) {
+ void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("mAppToken="); pw.println(mAppToken);
pw.print(prefix); pw.print("mAnimator="); pw.println(mAnimator);
pw.print(prefix); pw.print("freezingScreen="); pw.print(freezingScreen);
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index f628d5e6de27..66e0a152e09b 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -365,7 +365,6 @@ public class AppWindowContainerController
// Now that the app is going invisible, we can remove it. It will be restarted
// if made visible again.
wtoken.removeDeadWindows();
- mService.mUnknownAppVisibilityController.appRemovedOrHidden(wtoken);
} else {
if (!mService.mAppTransition.isTransitionSet()
&& mService.mAppTransition.isReady()) {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index d625003305b6..f70035ca9c92 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -362,10 +362,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
boolean runningAppAnimation = false;
+ if (mAppAnimator.animation == AppWindowAnimator.sDummyAnimation) {
+ mAppAnimator.setNullAnimation();
+ }
if (transit != AppTransition.TRANSIT_UNSET) {
- if (mAppAnimator.animation == AppWindowAnimator.sDummyAnimation) {
- mAppAnimator.setNullAnimation();
- }
if (mService.applyAnimationLocked(this, lp, transit, visible, isVoiceInteraction)) {
delayed = runningAppAnimation = true;
}
@@ -1618,6 +1618,9 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
if (mRemovingFromDisplay) {
pw.println(prefix + "mRemovingFromDisplay=" + mRemovingFromDisplay);
}
+ if (mAppAnimator.isAnimating()) {
+ mAppAnimator.dump(pw, prefix + " ");
+ }
}
@Override
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index cff2fadd7649..7953ee430934 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -21,7 +21,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.animation.AnimationHandler;
-import android.animation.AnimationHandler.AnimationFrameCallbackProvider;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
@@ -32,13 +31,11 @@ import android.os.IBinder;
import android.os.Debug;
import android.util.ArrayMap;
import android.util.Slog;
-import android.view.Choreographer;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.view.WindowManagerInternal;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -142,9 +139,6 @@ public class BoundsAnimationController {
// True if this this animation was canceled and will be replaced the another animation from
// the same {@link #BoundsAnimationTarget} target.
private boolean mSkipFinalResize;
- // True if this animation replaced a previous animation of the same
- // {@link #BoundsAnimationTarget} target.
- private final boolean mSkipAnimationStart;
// True if this animation was canceled by the user, not as a part of a replacing animation
private boolean mSkipAnimationEnd;
@@ -159,6 +153,7 @@ public class BoundsAnimationController {
// Whether to schedule PiP mode changes on animation start/end
private @SchedulePipModeChangedState int mSchedulePipModeChangedState;
+ private @SchedulePipModeChangedState int mPrevSchedulePipModeChangedState;
// Depending on whether we are animating from
// a smaller to a larger size
@@ -171,14 +166,14 @@ public class BoundsAnimationController {
BoundsAnimator(BoundsAnimationTarget target, Rect from, Rect to,
@SchedulePipModeChangedState int schedulePipModeChangedState,
- boolean moveFromFullscreen, boolean moveToFullscreen,
- boolean replacingExistingAnimation) {
+ @SchedulePipModeChangedState int prevShedulePipModeChangedState,
+ boolean moveFromFullscreen, boolean moveToFullscreen) {
super();
mTarget = target;
mFrom.set(from);
mTo.set(to);
- mSkipAnimationStart = replacingExistingAnimation;
mSchedulePipModeChangedState = schedulePipModeChangedState;
+ mPrevSchedulePipModeChangedState = prevShedulePipModeChangedState;
mMoveFromFullscreen = moveFromFullscreen;
mMoveToFullscreen = moveToFullscreen;
addUpdateListener(this);
@@ -200,7 +195,7 @@ public class BoundsAnimationController {
@Override
public void onAnimationStart(Animator animation) {
if (DEBUG) Slog.d(TAG, "onAnimationStart: mTarget=" + mTarget
- + " mSkipAnimationStart=" + mSkipAnimationStart
+ + " mPrevSchedulePipModeChangedState=" + mPrevSchedulePipModeChangedState
+ " mSchedulePipModeChangedState=" + mSchedulePipModeChangedState);
mFinishAnimationAfterTransition = false;
mTmpRect.set(mFrom.left, mFrom.top, mFrom.left + mFrozenTaskWidth,
@@ -210,18 +205,26 @@ public class BoundsAnimationController {
// running
updateBooster();
- // Ensure that we have prepared the target for animation before
- // we trigger any size changes, so it can swap surfaces
- // in to appropriate modes, or do as it wishes otherwise.
- if (!mSkipAnimationStart) {
+ // Ensure that we have prepared the target for animation before we trigger any size
+ // changes, so it can swap surfaces in to appropriate modes, or do as it wishes
+ // otherwise.
+ if (mPrevSchedulePipModeChangedState == NO_PIP_MODE_CHANGED_CALLBACKS) {
mTarget.onAnimationStart(mSchedulePipModeChangedState ==
- SCHEDULE_PIP_MODE_CHANGED_ON_START);
+ SCHEDULE_PIP_MODE_CHANGED_ON_START, false /* forceUpdate */);
// When starting an animation from fullscreen, pause here and wait for the
// windows-drawn signal before we start the rest of the transition down into PiP.
if (mMoveFromFullscreen) {
pause();
}
+ } else if (mPrevSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_END &&
+ mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
+ // We are replacing a running animation into PiP, but since it hasn't completed, the
+ // client will not currently receive any picture-in-picture mode change callbacks.
+ // However, we still need to report to them that they are leaving PiP, so this will
+ // force an update via a mode changed callback.
+ mTarget.onAnimationStart(true /* schedulePipModeChangedCallback */,
+ true /* forceUpdate */);
}
// Immediately update the task bounds if they have to become larger, but preserve
@@ -388,6 +391,8 @@ public class BoundsAnimationController {
boolean moveFromFullscreen, boolean moveToFullscreen) {
final BoundsAnimator existing = mRunningAnimations.get(target);
final boolean replacing = existing != null;
+ @SchedulePipModeChangedState int prevSchedulePipModeChangedState =
+ NO_PIP_MODE_CHANGED_CALLBACKS;
if (DEBUG) Slog.d(TAG, "animateBounds: target=" + target + " from=" + from + " to=" + to
+ " schedulePipModeChangedState=" + schedulePipModeChangedState
@@ -403,6 +408,9 @@ public class BoundsAnimationController {
return existing;
}
+ // Save the previous state
+ prevSchedulePipModeChangedState = existing.mSchedulePipModeChangedState;
+
// Update the PiP callback states if we are replacing the animation
if (existing.mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
if (schedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
@@ -428,7 +436,8 @@ public class BoundsAnimationController {
existing.cancel();
}
final BoundsAnimator animator = new BoundsAnimator(target, from, to,
- schedulePipModeChangedState, moveFromFullscreen, moveToFullscreen, replacing);
+ schedulePipModeChangedState, prevSchedulePipModeChangedState,
+ moveFromFullscreen, moveToFullscreen);
mRunningAnimations.put(target, animator);
animator.setFloatValues(0f, 1f);
animator.setDuration((animationDuration != -1 ? animationDuration
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
index 8b1bf7bf77dc..647a2d6deac6 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
@@ -31,7 +31,7 @@ interface BoundsAnimationTarget {
* @param schedulePipModeChangedCallback whether or not to schedule the PiP mode changed
* callbacks
*/
- void onAnimationStart(boolean schedulePipModeChangedCallback);
+ void onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate);
/**
* Sets the size of the target (without any intermediate steps, like scheduling animation)
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index ace40c1f1e73..21b67f1f9821 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -16,6 +16,13 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.activityTypeToString;
+
import android.app.WindowConfiguration;
import android.content.res.Configuration;
@@ -41,6 +48,9 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
*/
private Configuration mMergedOverrideConfiguration = new Configuration();
+ // TODO: Can't have ag/2592611 soon enough!
+ private final Configuration mTmpConfig = new Configuration();
+
/**
* Returns full configuration applied to this configuration container.
* This method should be used for getting settings applied in each particular level of the
@@ -120,10 +130,56 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
}
/** Sets the windowing mode for the configuration container. */
- public void setWindowingMode(/* @WindowConfiguration.WindowingMode...triggers Jack compiler bug...*/
- int windowingMode) {
- mOverrideConfiguration.windowConfiguration.setWindowingMode(windowingMode);
- onOverrideConfigurationChanged(mOverrideConfiguration);
+ public void setWindowingMode(/*@WindowConfiguration.WindowingMode*/ int windowingMode) {
+ mTmpConfig.setTo(getOverrideConfiguration());
+ mTmpConfig.windowConfiguration.setWindowingMode(windowingMode);
+ onOverrideConfigurationChanged(mTmpConfig);
+ }
+
+ /** Returns the activity type associated with the the configuration container. */
+ /*@WindowConfiguration.ActivityType*/
+ public int getActivityType() {
+ return mFullConfiguration.windowConfiguration.getActivityType();
+ }
+
+ /** Sets the activity type to associate with the configuration container. */
+ public void setActivityType(/*@WindowConfiguration.ActivityType*/ int activityType) {
+ int currentActivityType = getActivityType();
+ if (currentActivityType == activityType) {
+ return;
+ }
+ if (currentActivityType != ACTIVITY_TYPE_UNDEFINED) {
+ throw new IllegalStateException("Can't change activity type once set: " + this
+ + " activityType=" + activityTypeToString(activityType));
+ }
+ mTmpConfig.setTo(getOverrideConfiguration());
+ mTmpConfig.windowConfiguration.setActivityType(activityType);
+ onOverrideConfigurationChanged(mTmpConfig);
+ }
+
+ public boolean isActivityTypeHome() {
+ return getActivityType() == ACTIVITY_TYPE_HOME;
+ }
+
+ public boolean isActivityTypeRecents() {
+ return getActivityType() == ACTIVITY_TYPE_RECENTS;
+ }
+
+ public boolean isActivityTypeAssistant() {
+ return getActivityType() == ACTIVITY_TYPE_ASSISTANT;
+ }
+
+ public boolean isActivityTypeStandard() {
+ return getActivityType() == ACTIVITY_TYPE_STANDARD;
+ }
+
+ public boolean hasCompatibleActivityType(ConfigurationContainer other) {
+ /*@WindowConfiguration.ActivityType*/ int thisType = getActivityType();
+ /*@WindowConfiguration.ActivityType*/ int otherType = other.getActivityType();
+
+ return thisType == otherType
+ || thisType == ACTIVITY_TYPE_UNDEFINED
+ || otherType == ACTIVITY_TYPE_UNDEFINED;
}
/**
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index ce21991e1b3e..030b986ef0e9 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -608,8 +608,7 @@ public class DockedStackDividerController implements DimLayerUser {
final TaskStack fullscreenStack =
mDisplayContent.getStackById(FULLSCREEN_WORKSPACE_STACK_ID);
final boolean homeVisible = homeTask.getTopVisibleAppToken() != null;
- final boolean homeBehind = (fullscreenStack != null && fullscreenStack.isVisible())
- || (homeStack.hasMultipleTaskWithHomeTaskNotTop());
+ final boolean homeBehind = fullscreenStack != null && fullscreenStack.isVisible();
setMinimizedDockedStack(homeVisible && !homeBehind, animate);
}
diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowController.java b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
index 135f4000dd81..b5c9b99b35d6 100644
--- a/services/core/java/com/android/server/wm/PinnedStackWindowController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
@@ -204,10 +204,12 @@ public class PinnedStackWindowController extends StackWindowController {
*/
/** Calls directly into activity manager so window manager lock shouldn't held. */
- public void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds) {
+ public void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds,
+ boolean forceUpdate) {
if (mListener != null) {
PinnedStackWindowListener listener = (PinnedStackWindowListener) mListener;
- listener.updatePictureInPictureModeForPinnedStackAnimation(targetStackBounds);
+ listener.updatePictureInPictureModeForPinnedStackAnimation(targetStackBounds,
+ forceUpdate);
}
}
}
diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowListener.java b/services/core/java/com/android/server/wm/PinnedStackWindowListener.java
index 12b9c1f0c552..33e8a60329bf 100644
--- a/services/core/java/com/android/server/wm/PinnedStackWindowListener.java
+++ b/services/core/java/com/android/server/wm/PinnedStackWindowListener.java
@@ -28,5 +28,6 @@ public interface PinnedStackWindowListener extends StackWindowListener {
* Called when the stack container pinned stack animation will change the picture-in-picture
* mode. This is a direct call into ActivityManager.
*/
- default void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds) {}
+ default void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds,
+ boolean forceUpdate) {}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 60384f27d594..f57238e34694 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -97,27 +97,23 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU
private boolean mDragResizing;
private int mDragResizeMode;
- private boolean mHomeTask;
-
private TaskDescription mTaskDescription;
// If set to true, the task will report that it is not in the floating
- // state regardless of it's stack affilation. As the floating state drives
+ // state regardless of it's stack affiliation. As the floating state drives
// production of content insets this can be used to preserve them across
// stack moves and we in fact do so when moving from full screen to pinned.
private boolean mPreserveNonFloatingState = false;
Task(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds,
Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture,
- boolean homeTask, TaskDescription taskDescription,
- TaskWindowContainerController controller) {
+ TaskDescription taskDescription, TaskWindowContainerController controller) {
mTaskId = taskId;
mStack = stack;
mUserId = userId;
mService = service;
mResizeMode = resizeMode;
mSupportsPictureInPicture = supportsPictureInPicture;
- mHomeTask = homeTask;
setController(controller);
setBounds(bounds, overrideConfig);
mTaskDescription = taskDescription;
@@ -370,10 +366,6 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU
return isResizeable();
}
- boolean isHomeTask() {
- return mHomeTask;
- }
-
boolean resizeLocked(Rect bounds, Configuration overrideConfig, boolean forced) {
int boundsChanged = setBounds(bounds, overrideConfig);
if (forced) {
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 1a6e2c87e695..6ec7565ab156 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -18,18 +18,9 @@ package com.android.server.wm;
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
-import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID;
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.HOME_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static android.app.ActivityManager.StackId.RECENTS_STACK_ID;
-import static android.app.WindowConfiguration.WINDOWING_MODE_DOCKED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.res.Configuration.DENSITY_DPI_UNDEFINED;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -52,7 +43,6 @@ import static com.android.server.wm.proto.StackProto.ID;
import static com.android.server.wm.proto.StackProto.TASKS;
import android.app.ActivityManager.StackId;
-import android.app.WindowConfiguration;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.graphics.Region;
@@ -170,20 +160,10 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
}
Task findHomeTask() {
- if (mStackId != HOME_STACK_ID) {
+ if (!isActivityTypeHome() || mChildren.isEmpty()) {
return null;
}
-
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- if (mChildren.get(i).isHomeTask()) {
- return mChildren.get(i);
- }
- }
- return null;
- }
-
- boolean hasMultipleTaskWithHomeTaskNotTop() {
- return mChildren.size() > 1 && !mChildren.get(mChildren.size() - 1).isHomeTask();
+ return mChildren.get(mChildren.size() - 1);
}
/**
@@ -1470,7 +1450,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
* small portion which the home stack currently is resized to.
*/
- if (task.isHomeTask() && isMinimizedDockAndHomeStackResizable()) {
+ if (task.isActivityTypeHome() && isMinimizedDockAndHomeStackResizable()) {
mDisplayContent.getLogicalDisplayRect(mTmpRect);
} else {
task.getDimBounds(mTmpRect);
@@ -1525,7 +1505,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
}
@Override // AnimatesBounds
- public void onAnimationStart(boolean schedulePipModeChangedCallback) {
+ public void onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate) {
// Hold the lock since this is called from the BoundsAnimator running on the UiThread
synchronized (mService.mWindowMap) {
mBoundsAnimatingRequested = false;
@@ -1550,9 +1530,11 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
final PinnedStackWindowController controller =
(PinnedStackWindowController) getController();
if (schedulePipModeChangedCallback && controller != null) {
- // We need to schedule the PiP mode change after the animation down, so use the
- // final bounds
- controller.updatePictureInPictureModeForPinnedStackAnimation(null);
+ // We need to schedule the PiP mode change before the animation up. It is possible
+ // in this case for the animation down to not have been completed, so always
+ // force-schedule and update to the client to ensure that it is notified that it
+ // is no longer in picture-in-picture mode
+ controller.updatePictureInPictureModeForPinnedStackAnimation(null, forceUpdate);
}
}
}
@@ -1580,7 +1562,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
// We need to schedule the PiP mode change after the animation down, so use the
// final bounds
controller.updatePictureInPictureModeForPinnedStackAnimation(
- mBoundsAnimationTarget);
+ mBoundsAnimationTarget, false /* forceUpdate */);
}
if (finalStackSize != null) {
diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerController.java b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
index 54a6cc01c831..d8929c9d80c2 100644
--- a/services/core/java/com/android/server/wm/TaskWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
@@ -51,18 +51,17 @@ public class TaskWindowContainerController
public TaskWindowContainerController(int taskId, TaskWindowContainerListener listener,
StackWindowController stackController, int userId, Rect bounds,
Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture,
- boolean homeTask, boolean toTop, boolean showForAllUsers,
- TaskDescription taskDescription) {
+ boolean toTop, boolean showForAllUsers, TaskDescription taskDescription) {
this(taskId, listener, stackController, userId, bounds, overrideConfig, resizeMode,
- supportsPictureInPicture, homeTask, toTop, showForAllUsers, taskDescription,
+ supportsPictureInPicture, toTop, showForAllUsers, taskDescription,
WindowManagerService.getInstance());
}
public TaskWindowContainerController(int taskId, TaskWindowContainerListener listener,
StackWindowController stackController, int userId, Rect bounds,
Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture,
- boolean homeTask, boolean toTop, boolean showForAllUsers,
- TaskDescription taskDescription, WindowManagerService service) {
+ boolean toTop, boolean showForAllUsers, TaskDescription taskDescription,
+ WindowManagerService service) {
super(listener, service);
mTaskId = taskId;
mHandler = new H(new WeakReference<>(this), service.mH.getLooper());
@@ -78,7 +77,7 @@ public class TaskWindowContainerController
}
EventLog.writeEvent(WM_TASK_CREATED, taskId, stack.mStackId);
final Task task = createTask(taskId, stack, userId, bounds, overrideConfig, resizeMode,
- supportsPictureInPicture, homeTask, taskDescription);
+ supportsPictureInPicture, taskDescription);
final int position = toTop ? POSITION_TOP : POSITION_BOTTOM;
// We only want to move the parents to the parents if we are creating this task at the
// top of its stack.
@@ -89,9 +88,9 @@ public class TaskWindowContainerController
@VisibleForTesting
Task createTask(int taskId, TaskStack stack, int userId, Rect bounds,
Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture,
- boolean homeTask, TaskDescription taskDescription) {
+ TaskDescription taskDescription) {
return new Task(taskId, stack, userId, mService, bounds, overrideConfig, resizeMode,
- supportsPictureInPicture, homeTask, taskDescription, this);
+ supportsPictureInPicture, taskDescription, this);
}
@Override
diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp
index 0370490668ec..d2f374dd9e08 100644
--- a/services/core/jni/com_android_server_VibratorService.cpp
+++ b/services/core/jni/com_android_server_VibratorService.cpp
@@ -153,9 +153,9 @@ static jlong vibratorPerformEffect(JNIEnv*, jobject, jlong effect, jint strength
if (status == Status::OK) {
return lengthMs;
} else if (status != Status::UNSUPPORTED_OPERATION) {
- // Don't warn on UNSUPPORTED_OPERATION, that's a normal even and just means the motor
- // doesn't have a pre-defined waveform to perform for it, so we should just fall back
- // to the framework waveforms.
+ // Don't warn on UNSUPPORTED_OPERATION, that's a normal event and just means the motor
+ // doesn't have a pre-defined waveform to perform for it, so we should just give the
+ // opportunity to fall back to the framework waveforms.
ALOGE("Failed to perform haptic effect: effect=%" PRId64 ", strength=%" PRId32
", error=%" PRIu32 ").", static_cast<int64_t>(effect),
static_cast<int32_t>(strength), static_cast<uint32_t>(status));
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c7db7947d293..d6b55678040d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -43,6 +43,7 @@ import static android.app.admin.DevicePolicyManager.DELEGATION_PACKAGE_ACCESS;
import static android.app.admin.DevicePolicyManager.DELEGATION_PERMISSION_GRANT;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
import static android.app.admin.DevicePolicyManager.PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
+import static android.app.admin.DevicePolicyManager.START_USER_IN_BACKGROUND;
import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
@@ -614,11 +615,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
} else if (Intent.ACTION_USER_STARTED.equals(action)) {
synchronized (DevicePolicyManagerService.this) {
+ maybeSendAdminEnabledBroadcastLocked(userHandle);
// Reset the policy data
mUserData.remove(userHandle);
- sendAdminEnabledBroadcastLocked(userHandle);
}
handlePackagesChanged(null /* check all admins */, userHandle);
+ } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
+ synchronized (DevicePolicyManagerService.this) {
+ maybeSendAdminEnabledBroadcastLocked(userHandle);
+ }
} else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
handlePackagesChanged(null /* check all admins */, userHandle);
} else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
@@ -1854,6 +1859,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
filter.addAction(Intent.ACTION_USER_ADDED);
filter.addAction(Intent.ACTION_USER_REMOVED);
filter.addAction(Intent.ACTION_USER_STARTED);
+ filter.addAction(Intent.ACTION_USER_UNLOCKED);
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
filter = new IntentFilter();
@@ -2372,11 +2378,18 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
sendAdminCommandLocked(admin, action, null, result);
}
+ void sendAdminCommandLocked(ActiveAdmin admin, String action, Bundle adminExtras,
+ BroadcastReceiver result) {
+ sendAdminCommandLocked(admin, action, adminExtras, result, false);
+ }
+
/**
* Send an update to one specific admin, get notified when that admin returns a result.
+ *
+ * @return whether the broadcast was successfully sent
*/
- void sendAdminCommandLocked(ActiveAdmin admin, String action, Bundle adminExtras,
- BroadcastReceiver result) {
+ boolean sendAdminCommandLocked(ActiveAdmin admin, String action, Bundle adminExtras,
+ BroadcastReceiver result, boolean inForeground) {
Intent intent = new Intent(action);
intent.setComponent(admin.info.getComponent());
if (UserManager.isDeviceInDemoMode(mContext)) {
@@ -2385,15 +2398,25 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (action.equals(DeviceAdminReceiver.ACTION_PASSWORD_EXPIRING)) {
intent.putExtra("expiration", admin.passwordExpirationDate);
}
+ if (inForeground) {
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ }
if (adminExtras != null) {
intent.putExtras(adminExtras);
}
+ if (mInjector.getPackageManager().queryBroadcastReceiversAsUser(
+ intent,
+ PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
+ admin.getUserHandle()).isEmpty()) {
+ return false;
+ }
if (result != null) {
mContext.sendOrderedBroadcastAsUser(intent, admin.getUserHandle(),
null, result, mHandler, Activity.RESULT_OK, null, null);
} else {
mContext.sendBroadcastAsUser(intent, admin.getUserHandle());
}
+ return true;
}
/**
@@ -8099,20 +8122,27 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
-
- private void sendAdminEnabledBroadcastLocked(int userHandle) {
+ private void maybeSendAdminEnabledBroadcastLocked(int userHandle) {
DevicePolicyData policyData = getUserData(userHandle);
if (policyData.mAdminBroadcastPending) {
// Send the initialization data to profile owner and delete the data
ActiveAdmin admin = getProfileOwnerAdminLocked(userHandle);
+ boolean clearInitBundle = true;
if (admin != null) {
PersistableBundle initBundle = policyData.mInitBundle;
- sendAdminCommandLocked(admin, DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED,
- initBundle == null ? null : new Bundle(initBundle), null);
+ clearInitBundle = sendAdminCommandLocked(admin,
+ DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED,
+ initBundle == null ? null : new Bundle(initBundle),
+ null /* result receiver */,
+ true /* send in foreground */);
+ }
+ if (clearInitBundle) {
+ // If there's no admin or we've successfully called the admin, clear the init bundle
+ // otherwise, keep it around
+ policyData.mInitBundle = null;
+ policyData.mAdminBroadcastPending = false;
+ saveSettingsLocked(userHandle);
}
- policyData.mInitBundle = null;
- policyData.mAdminBroadcastPending = false;
- saveSettingsLocked(userHandle);
}
}
@@ -8158,7 +8188,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (user == null) {
return null;
}
- // Set admin.
final long id = mInjector.binderClearCallingIdentity();
try {
final String adminPkg = admin.getPackageName();
@@ -8171,30 +8200,38 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
0 /*installFlags*/, PackageManager.INSTALL_REASON_POLICY);
}
} catch (RemoteException e) {
- Slog.e(LOG_TAG, "Failed to make remote calls for createAndManageUser, "
- + "removing created user", e);
- mUserManager.removeUser(user.getIdentifier());
- return null;
+ // Does not happen, same process
}
+ // Set admin.
setActiveAdmin(profileOwner, true, userHandle);
- // User is not started yet, the broadcast by setActiveAdmin will not be received.
- // So we store adminExtras for broadcasting when the user starts for first time.
- synchronized(this) {
+ final String ownerName = getProfileOwnerName(Process.myUserHandle().getIdentifier());
+ setProfileOwner(profileOwner, ownerName, userHandle);
+
+ synchronized (this) {
DevicePolicyData policyData = getUserData(userHandle);
policyData.mInitBundle = adminExtras;
policyData.mAdminBroadcastPending = true;
saveSettingsLocked(userHandle);
}
- final String ownerName = getProfileOwnerName(Process.myUserHandle().getIdentifier());
- setProfileOwner(profileOwner, ownerName, userHandle);
if ((flags & DevicePolicyManager.SKIP_SETUP_WIZARD) != 0) {
Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.USER_SETUP_COMPLETE, 1, userHandle);
}
+ if ((flags & START_USER_IN_BACKGROUND) != 0) {
+ try {
+ mInjector.getIActivityManager().startUserInBackground(user.getIdentifier());
+ } catch (RemoteException re) {
+ // Does not happen, same process
+ }
+ }
+
return user;
+ } catch (Throwable re) {
+ mUserManager.removeUser(user.getIdentifier());
+ return null;
} finally {
mInjector.binderRestoreCallingIdentity(id);
}
@@ -9077,6 +9114,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return false;
}
mLockPatternUtils.setLockScreenDisabled(disabled, userId);
+ mInjector.getIWindowManager().dismissKeyguard(null);
+ } catch (RemoteException e) {
+ // Same process, does not happen.
} finally {
mInjector.binderRestoreCallingIdentity(ident);
}
diff --git a/services/robotests/Android.mk b/services/robotests/Android.mk
new file mode 100644
index 000000000000..717176247d36
--- /dev/null
+++ b/services/robotests/Android.mk
@@ -0,0 +1,78 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+############################################################
+# FrameworksServicesLib app just for Robolectric test target. #
+############################################################
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := FrameworksServicesLib
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_PRIVILEGED_MODULE := true
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ frameworks-base-testutils \
+ services.backup \
+ services.core \
+ android-support-test \
+ mockito-target-minus-junit4 \
+ platform-test-annotations \
+ truth-prebuilt
+
+include $(BUILD_PACKAGE)
+
+#############################################
+# FrameworksServices Robolectric test target. #
+#############################################
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+# Include the testing libraries (JUnit4 + Robolectric libs).
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ platform-system-robolectric \
+ truth-prebuilt
+
+LOCAL_JAVA_LIBRARIES := \
+ junit \
+ platform-robolectric-prebuilt
+
+LOCAL_INSTRUMENTATION_FOR := FrameworksServicesLib
+LOCAL_MODULE := FrameworksServicesRoboTests
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+#############################################################
+# FrameworksServices runner target to run the previous target. #
+#############################################################
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := RunFrameworksServicesRoboTests
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ FrameworksServicesRoboTests
+
+LOCAL_TEST_PACKAGE := FrameworksServicesLib
+
+LOCAL_INSTRUMENT_SOURCE_DIRS := $(dir $(LOCAL_PATH))backup/java
+
+include prebuilts/misc/common/robolectric/run_robotests.mk
diff --git a/services/robotests/src/com/android/server/backup/TransportManagerTest.java b/services/robotests/src/com/android/server/backup/TransportManagerTest.java
new file mode 100644
index 000000000000..f345da2b548c
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/TransportManagerTest.java
@@ -0,0 +1,513 @@
+/*
+ * 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.server.backup;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.IBinder;
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.backup.testing.BackupTransportStub;
+import com.android.server.backup.testing.DefaultPackageManagerWithQueryIntentServicesAsUser;
+import com.android.server.backup.testing.ShadowBackupTransportStub;
+import com.android.server.backup.testing.ShadowContextImplForBackup;
+import com.android.server.backup.testing.TransportBoundListenerStub;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.res.builder.RobolectricPackageManager;
+import org.robolectric.shadows.ShadowLog;
+import org.robolectric.shadows.ShadowLooper;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(
+ manifest = Config.NONE,
+ sdk = 23,
+ shadows = {
+ ShadowContextImplForBackup.class,
+ ShadowBackupTransportStub.class
+ }
+)
+@Presubmit
+public class TransportManagerTest {
+ private static final String PACKAGE_NAME = "some.package.name";
+ private static final String ANOTHER_PACKAGE_NAME = "another.package.name";
+
+ private TransportInfo mTransport1;
+ private TransportInfo mTransport2;
+
+ private RobolectricPackageManager mPackageManager;
+
+ private final TransportBoundListenerStub mTransportBoundListenerStub =
+ new TransportBoundListenerStub(true);
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ ShadowLog.stream = System.out;
+ mPackageManager = new DefaultPackageManagerWithQueryIntentServicesAsUser(
+ RuntimeEnvironment.getAppResourceLoader());
+ RuntimeEnvironment.setRobolectricPackageManager(mPackageManager);
+
+ mTransport1 = new TransportInfo(PACKAGE_NAME, "transport1.name");
+ mTransport2 = new TransportInfo(PACKAGE_NAME, "transport2.name");
+
+ ShadowContextImplForBackup.sComponentBinderMap.put(mTransport1.componentName,
+ mTransport1.binder);
+ ShadowContextImplForBackup.sComponentBinderMap.put(mTransport2.componentName,
+ mTransport2.binder);
+ ShadowBackupTransportStub.sBinderTransportMap.put(mTransport1.binder, mTransport1.stub);
+ ShadowBackupTransportStub.sBinderTransportMap.put(mTransport2.binder, mTransport2.stub);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ ShadowContextImplForBackup.resetBackupShadowState();
+ }
+
+ @Test
+ public void onPackageAdded_bindsToAllTransports() throws Exception {
+ setUpPackageWithTransports(PACKAGE_NAME, Arrays.asList(mTransport1, mTransport2),
+ ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+
+ TransportManager transportManager = new TransportManager(
+ RuntimeEnvironment.application.getApplicationContext(),
+ new HashSet<>(Arrays.asList(
+ mTransport1.componentName, mTransport2.componentName)),
+ null /* defaultTransport */,
+ mTransportBoundListenerStub,
+ ShadowLooper.getMainLooper());
+ transportManager.onPackageAdded(PACKAGE_NAME);
+
+ assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn(
+ Arrays.asList(mTransport1.componentName, mTransport2.componentName));
+ assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
+ Arrays.asList(mTransport1.name, mTransport2.name));
+ assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isTrue();
+ assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isTrue();
+ }
+
+ @Test
+ public void onPackageAdded_oneTransportUnavailable_bindsToOnlyOneTransport() throws Exception {
+ setUpPackageWithTransports(PACKAGE_NAME, Arrays.asList(mTransport1, mTransport2),
+ ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+
+ ShadowContextImplForBackup.sUnbindableComponents.add(mTransport1.componentName);
+
+ TransportManager transportManager = new TransportManager(
+ RuntimeEnvironment.application.getApplicationContext(),
+ new HashSet<>(Arrays.asList(
+ mTransport1.componentName, mTransport2.componentName)),
+ null /* defaultTransport */,
+ mTransportBoundListenerStub,
+ ShadowLooper.getMainLooper());
+ transportManager.onPackageAdded(PACKAGE_NAME);
+
+ assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn(
+ Collections.singleton(mTransport2.componentName));
+ assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
+ Collections.singleton(mTransport2.name));
+ assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isFalse();
+ assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isTrue();
+ }
+
+ @Test
+ public void onPackageAdded_whitelistIsNull_doesNotBindToTransports() throws Exception {
+ setUpPackageWithTransports(PACKAGE_NAME, Arrays.asList(mTransport1, mTransport2),
+ ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+
+ TransportManager transportManager = new TransportManager(
+ RuntimeEnvironment.application.getApplicationContext(),
+ null /* whitelist */,
+ null /* defaultTransport */,
+ mTransportBoundListenerStub,
+ ShadowLooper.getMainLooper());
+ transportManager.onPackageAdded(PACKAGE_NAME);
+
+ assertThat(transportManager.getAllTransportComponents()).isEmpty();
+ assertThat(transportManager.getBoundTransportNames()).isEmpty();
+ assertThat(mTransportBoundListenerStub.isCalled()).isFalse();
+ }
+
+ @Test
+ public void onPackageAdded_onlyOneTransportWhitelisted_onlyConnectsToWhitelistedTransport()
+ throws Exception {
+ setUpPackageWithTransports(PACKAGE_NAME, Arrays.asList(mTransport1, mTransport2),
+ ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+
+ TransportManager transportManager = new TransportManager(
+ RuntimeEnvironment.application.getApplicationContext(),
+ new HashSet<>(Collections.singleton(mTransport2.componentName)),
+ null /* defaultTransport */,
+ mTransportBoundListenerStub,
+ ShadowLooper.getMainLooper());
+ transportManager.onPackageAdded(PACKAGE_NAME);
+
+ assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn(
+ Collections.singleton(mTransport2.componentName));
+ assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
+ Collections.singleton(mTransport2.name));
+ assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isFalse();
+ assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isTrue();
+ }
+
+ @Test
+ public void onPackageAdded_appIsNotPrivileged_doesNotBindToTransports() throws Exception {
+ setUpPackageWithTransports(PACKAGE_NAME, Arrays.asList(mTransport1, mTransport2), 0);
+
+ TransportManager transportManager = new TransportManager(
+ RuntimeEnvironment.application.getApplicationContext(),
+ new HashSet<>(Arrays.asList(
+ mTransport1.componentName, mTransport2.componentName)),
+ null /* defaultTransport */,
+ mTransportBoundListenerStub,
+ ShadowLooper.getMainLooper());
+ transportManager.onPackageAdded(PACKAGE_NAME);
+
+ assertThat(transportManager.getAllTransportComponents()).isEmpty();
+ assertThat(transportManager.getBoundTransportNames()).isEmpty();
+ assertThat(mTransportBoundListenerStub.isCalled()).isFalse();
+ }
+
+ @Test
+ public void onPackageRemoved_transportsUnbound() throws Exception {
+ TransportManager transportManager = createTransportManagerAndSetUpTransports(
+ Arrays.asList(mTransport1, mTransport2), mTransport1.name);
+
+ transportManager.onPackageRemoved(PACKAGE_NAME);
+
+ assertThat(transportManager.getAllTransportComponents()).isEmpty();
+ assertThat(transportManager.getBoundTransportNames()).isEmpty();
+ }
+
+ @Test
+ public void onPackageRemoved_incorrectPackageName_nothingHappens() throws Exception {
+ TransportManager transportManager = createTransportManagerAndSetUpTransports(
+ Arrays.asList(mTransport1, mTransport2), mTransport1.name);
+
+ transportManager.onPackageRemoved(ANOTHER_PACKAGE_NAME);
+
+ assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn(
+ Arrays.asList(mTransport1.componentName, mTransport2.componentName));
+ assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
+ Arrays.asList(mTransport1.name, mTransport2.name));
+ }
+
+ @Test
+ public void onPackageChanged_oneComponentChanged_onlyOneTransportRebound() throws Exception {
+ TransportManager transportManager = createTransportManagerAndSetUpTransports(
+ Arrays.asList(mTransport1, mTransport2), mTransport1.name);
+
+ transportManager.onPackageChanged(PACKAGE_NAME, new String[]{mTransport2.name});
+
+ assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn(
+ Arrays.asList(mTransport1.componentName, mTransport2.componentName));
+ assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
+ Arrays.asList(mTransport1.name, mTransport2.name));
+ assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isFalse();
+ assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isTrue();
+ }
+
+ @Test
+ public void onPackageChanged_nothingChanged_noTransportsRebound() throws Exception {
+ TransportManager transportManager = createTransportManagerAndSetUpTransports(
+ Arrays.asList(mTransport1, mTransport2), mTransport1.name);
+
+ transportManager.onPackageChanged(PACKAGE_NAME, new String[0]);
+
+ assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn(
+ Arrays.asList(mTransport1.componentName, mTransport2.componentName));
+ assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
+ Arrays.asList(mTransport1.name, mTransport2.name));
+ assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isFalse();
+ assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isFalse();
+ }
+
+ @Test
+ public void onPackageChanged_unexpectedComponentChanged_noTransportsRebound() throws Exception {
+ TransportManager transportManager = createTransportManagerAndSetUpTransports(
+ Arrays.asList(mTransport1, mTransport2), mTransport1.name);
+
+ transportManager.onPackageChanged(PACKAGE_NAME, new String[]{"unexpected.component"});
+
+ assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn(
+ Arrays.asList(mTransport1.componentName, mTransport2.componentName));
+ assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
+ Arrays.asList(mTransport1.name, mTransport2.name));
+ assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isFalse();
+ assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isFalse();
+ }
+
+ @Test
+ public void onPackageChanged_transportsRebound() throws Exception {
+ TransportManager transportManager = createTransportManagerAndSetUpTransports(
+ Arrays.asList(mTransport1, mTransport2), mTransport1.name);
+
+ transportManager.onPackageChanged(PACKAGE_NAME, new String[]{mTransport2.name});
+
+ assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn(
+ Arrays.asList(mTransport1.componentName, mTransport2.componentName));
+ assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
+ Arrays.asList(mTransport1.name, mTransport2.name));
+ assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isFalse();
+ assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isTrue();
+ }
+
+ @Test
+ public void getTransportBinder_returnsCorrectBinder() throws Exception {
+ TransportManager transportManager = createTransportManagerAndSetUpTransports(
+ Arrays.asList(mTransport1, mTransport2), mTransport1.name);
+
+ assertThat(transportManager.getTransportBinder(mTransport1.name)).isEqualTo(
+ mTransport1.stub);
+ assertThat(transportManager.getTransportBinder(mTransport2.name)).isEqualTo(
+ mTransport2.stub);
+ }
+
+ @Test
+ public void getTransportBinder_incorrectTransportName_returnsNull() throws Exception {
+ TransportManager transportManager = createTransportManagerAndSetUpTransports(
+ Arrays.asList(mTransport1, mTransport2), mTransport1.name);
+
+ assertThat(transportManager.getTransportBinder("incorrect.transport")).isNull();
+ }
+
+ @Test
+ public void getTransportBinder_oneTransportUnavailable_returnsCorrectBinder() throws Exception {
+ TransportManager transportManager =
+ createTransportManagerAndSetUpTransports(Collections.singletonList(mTransport2),
+ Collections.singletonList(mTransport1), mTransport1.name);
+
+ assertThat(transportManager.getTransportBinder(mTransport1.name)).isNull();
+ assertThat(transportManager.getTransportBinder(mTransport2.name)).isEqualTo(
+ mTransport2.stub);
+ }
+
+ @Test
+ public void getCurrentTransport_selectTransportNotCalled_returnsDefaultTransport()
+ throws Exception {
+ TransportManager transportManager = createTransportManagerAndSetUpTransports(
+ Arrays.asList(mTransport1, mTransport2), mTransport1.name);
+
+ assertThat(transportManager.getCurrentTransportName()).isEqualTo(mTransport1.name);
+ }
+
+ @Test
+ public void getCurrentTransport_selectTransportCalled_returnsCorrectTransport()
+ throws Exception {
+ TransportManager transportManager = createTransportManagerAndSetUpTransports(
+ Arrays.asList(mTransport1, mTransport2), mTransport1.name);
+
+ assertThat(transportManager.getCurrentTransportName()).isEqualTo(mTransport1.name);
+
+ transportManager.selectTransport(mTransport2.name);
+
+ assertThat(transportManager.getCurrentTransportName()).isEqualTo(mTransport2.name);
+ }
+
+ @Test
+ public void getCurrentTransportBinder_returnsCorrectBinder() throws Exception {
+ TransportManager transportManager = createTransportManagerAndSetUpTransports(
+ Arrays.asList(mTransport1, mTransport2), mTransport1.name);
+
+ assertThat(transportManager.getCurrentTransportBinder()).isEqualTo(mTransport1.stub);
+ }
+
+ @Test
+ public void getCurrentTransportBinder_transportNotBound_returnsNull() throws Exception {
+ TransportManager transportManager =
+ createTransportManagerAndSetUpTransports(Collections.singletonList(mTransport2),
+ Collections.singletonList(mTransport1), mTransport2.name);
+
+ transportManager.selectTransport(mTransport1.name);
+
+ assertThat(transportManager.getCurrentTransportBinder()).isNull();
+ }
+
+ @Test
+ public void getTransportName_returnsCorrectTransportName() throws Exception {
+ TransportManager transportManager = createTransportManagerAndSetUpTransports(
+ Arrays.asList(mTransport1, mTransport2), mTransport1.name);
+
+ assertThat(transportManager.getTransportName(mTransport1.stub)).isEqualTo(mTransport1.name);
+ assertThat(transportManager.getTransportName(mTransport2.stub)).isEqualTo(mTransport2.name);
+ }
+
+ @Test
+ public void getTransportName_transportNotBound_returnsNull() throws Exception {
+ TransportManager transportManager =
+ createTransportManagerAndSetUpTransports(Collections.singletonList(mTransport2),
+ Collections.singletonList(mTransport1), mTransport1.name);
+
+ assertThat(transportManager.getTransportName(mTransport1.stub)).isNull();
+ assertThat(transportManager.getTransportName(mTransport2.stub)).isEqualTo(mTransport2.name);
+ }
+
+ @Test
+ public void getTransportWhitelist_returnsCorrectWhiteList() throws Exception {
+ TransportManager transportManager = new TransportManager(
+ RuntimeEnvironment.application.getApplicationContext(),
+ new HashSet<>(Arrays.asList(mTransport1.componentName, mTransport2.componentName)),
+ mTransport1.name,
+ mTransportBoundListenerStub,
+ ShadowLooper.getMainLooper());
+
+ assertThat(transportManager.getTransportWhitelist()).containsExactlyElementsIn(
+ Arrays.asList(mTransport1.componentName, mTransport2.componentName));
+ }
+
+ @Test
+ public void getTransportWhitelist_whiteListIsNull_returnsEmptyArray() throws Exception {
+ TransportManager transportManager = new TransportManager(
+ RuntimeEnvironment.application.getApplicationContext(),
+ null /* whitelist */,
+ mTransport1.name,
+ mTransportBoundListenerStub,
+ ShadowLooper.getMainLooper());
+
+ assertThat(transportManager.getTransportWhitelist()).isEmpty();
+ }
+
+ @Test
+ public void selectTransport_setsTransportCorrectlyAndReturnsPreviousTransport()
+ throws Exception {
+ TransportManager transportManager = new TransportManager(
+ RuntimeEnvironment.application.getApplicationContext(),
+ null /* whitelist */,
+ mTransport1.name,
+ mTransportBoundListenerStub,
+ ShadowLooper.getMainLooper());
+
+ assertThat(transportManager.selectTransport(mTransport2.name)).isEqualTo(mTransport1.name);
+ assertThat(transportManager.selectTransport(mTransport1.name)).isEqualTo(mTransport2.name);
+ }
+
+ private void setUpPackageWithTransports(String packageName, List<TransportInfo> transports,
+ int flags) throws Exception {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.packageName = packageName;
+ packageInfo.applicationInfo = new ApplicationInfo();
+ packageInfo.applicationInfo.privateFlags = flags;
+
+ mPackageManager.addPackage(packageInfo);
+
+ List<ResolveInfo> transportsInfo = new ArrayList<>();
+ for (TransportInfo transport : transports) {
+ ResolveInfo info = new ResolveInfo();
+ info.serviceInfo = new ServiceInfo();
+ info.serviceInfo.packageName = packageName;
+ info.serviceInfo.name = transport.name;
+ transportsInfo.add(info);
+ }
+
+ Intent intent = new Intent(TransportManager.SERVICE_ACTION_TRANSPORT_HOST);
+ intent.setPackage(packageName);
+
+ mPackageManager.addResolveInfoForIntent(intent, transportsInfo);
+ }
+
+ private TransportManager createTransportManagerAndSetUpTransports(
+ List<TransportInfo> availableTransports, String defaultTransportName) throws Exception {
+ return createTransportManagerAndSetUpTransports(availableTransports,
+ Collections.<TransportInfo>emptyList(), defaultTransportName);
+ }
+
+ private TransportManager createTransportManagerAndSetUpTransports(
+ List<TransportInfo> availableTransports, List<TransportInfo> unavailableTransports,
+ String defaultTransportName)
+ throws Exception {
+ List<String> availableTransportsNames = new ArrayList<>();
+ List<ComponentName> availableTransportsComponentNames = new ArrayList<>();
+ for (TransportInfo transport : availableTransports) {
+ availableTransportsNames.add(transport.name);
+ availableTransportsComponentNames.add(transport.componentName);
+ }
+
+ List<ComponentName> allTransportsComponentNames = new ArrayList<>();
+ allTransportsComponentNames.addAll(availableTransportsComponentNames);
+ for (TransportInfo transport : unavailableTransports) {
+ allTransportsComponentNames.add(transport.componentName);
+ }
+
+ for (TransportInfo transport : unavailableTransports) {
+ ShadowContextImplForBackup.sUnbindableComponents.add(transport.componentName);
+ }
+
+ setUpPackageWithTransports(PACKAGE_NAME, Arrays.asList(mTransport1, mTransport2),
+ ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+
+ TransportManager transportManager = new TransportManager(
+ RuntimeEnvironment.application.getApplicationContext(),
+ new HashSet<>(allTransportsComponentNames),
+ defaultTransportName,
+ mTransportBoundListenerStub,
+ ShadowLooper.getMainLooper());
+ transportManager.onPackageAdded(PACKAGE_NAME);
+
+ assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn(
+ availableTransportsComponentNames);
+ assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
+ availableTransportsNames);
+ for (TransportInfo transport : availableTransports) {
+ assertThat(mTransportBoundListenerStub.isCalledForTransport(transport.stub)).isTrue();
+ }
+ for (TransportInfo transport : unavailableTransports) {
+ assertThat(mTransportBoundListenerStub.isCalledForTransport(transport.stub)).isFalse();
+ }
+
+ mTransportBoundListenerStub.resetState();
+
+ return transportManager;
+ }
+
+ private static class TransportInfo {
+ public final String packageName;
+ public final String name;
+ public final ComponentName componentName;
+ public final BackupTransportStub stub;
+ public final IBinder binder;
+
+ TransportInfo(String packageName, String name) {
+ this.packageName = packageName;
+ this.name = name;
+ this.componentName = new ComponentName(packageName, name);
+ this.stub = new BackupTransportStub(name);
+ this.binder = mock(IBinder.class);
+ }
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/testing/BackupTransportStub.java b/services/robotests/src/com/android/server/backup/testing/BackupTransportStub.java
new file mode 100644
index 000000000000..ec09f908c90d
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/testing/BackupTransportStub.java
@@ -0,0 +1,179 @@
+/*
+ * 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.server.backup.testing;
+
+import android.app.backup.RestoreDescription;
+import android.app.backup.RestoreSet;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+
+import com.android.internal.backup.IBackupTransport;
+
+/**
+ * Stub backup transport, doing nothing and returning default values.
+ */
+public class BackupTransportStub implements IBackupTransport {
+
+ private final String mName;
+
+ public BackupTransportStub(String name) {
+ mName = name;
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return null;
+ }
+
+ @Override
+ public String name() throws RemoteException {
+ return mName;
+ }
+
+ @Override
+ public Intent configurationIntent() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public String currentDestinationString() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public Intent dataManagementIntent() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public String dataManagementLabel() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public String transportDirName() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public long requestBackupTime() throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public int initializeDevice() throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd, int flags)
+ throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public int clearBackupData(PackageInfo packageInfo) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public int finishBackup() throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public RestoreSet[] getAvailableRestoreSets() throws RemoteException {
+ return new RestoreSet[0];
+ }
+
+ @Override
+ public long getCurrentRestoreSet() throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public int startRestore(long token, PackageInfo[] packages) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public RestoreDescription nextRestorePackage() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public int getRestoreData(ParcelFileDescriptor outFd) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public void finishRestore() throws RemoteException {
+
+ }
+
+ @Override
+ public long requestFullBackupTime() throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public int performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor socket,
+ int flags)
+ throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public int checkFullBackupSize(long size) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public int sendBackupData(int numBytes) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public void cancelFullBackup() throws RemoteException {
+
+ }
+
+ @Override
+ public boolean isAppEligibleForBackup(PackageInfo targetPackage, boolean isFullBackup)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public long getBackupQuota(String packageName, boolean isFullBackup)
+ throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public int abortFullRestore() throws RemoteException {
+ return 0;
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/testing/DefaultPackageManagerWithQueryIntentServicesAsUser.java b/services/robotests/src/com/android/server/backup/testing/DefaultPackageManagerWithQueryIntentServicesAsUser.java
new file mode 100644
index 000000000000..5a0967ba2420
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/testing/DefaultPackageManagerWithQueryIntentServicesAsUser.java
@@ -0,0 +1,43 @@
+/*
+ * 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.server.backup.testing;
+
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+
+import org.robolectric.res.ResourceLoader;
+import org.robolectric.res.builder.DefaultPackageManager;
+
+import java.util.List;
+
+/**
+ * Implementation of PackageManager for Robolectric which handles queryIntentServicesAsUser().
+ */
+public class DefaultPackageManagerWithQueryIntentServicesAsUser extends
+ DefaultPackageManager {
+
+ /* package */
+ public DefaultPackageManagerWithQueryIntentServicesAsUser(
+ ResourceLoader appResourceLoader) {
+ super(appResourceLoader);
+ }
+
+ @Override
+ public List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int flags, int userId) {
+ return super.queryIntentServices(intent, flags);
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/testing/ShadowBackupTransportStub.java b/services/robotests/src/com/android/server/backup/testing/ShadowBackupTransportStub.java
new file mode 100644
index 000000000000..48a12f0f5435
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/testing/ShadowBackupTransportStub.java
@@ -0,0 +1,40 @@
+/*
+ * 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.server.backup.testing;
+
+import android.os.IBinder;
+
+import com.android.internal.backup.IBackupTransport;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Shadow IBackupTransport.Stub, returns a transport corresponding to the binder.
+ */
+@Implements(IBackupTransport.Stub.class)
+public class ShadowBackupTransportStub {
+ public static Map<IBinder, IBackupTransport> sBinderTransportMap = new HashMap<>();
+
+ @Implementation
+ public static IBackupTransport asInterface(IBinder obj) {
+ return sBinderTransportMap.get(obj);
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/testing/ShadowContextImplForBackup.java b/services/robotests/src/com/android/server/backup/testing/ShadowContextImplForBackup.java
new file mode 100644
index 000000000000..c3975db3057f
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/testing/ShadowContextImplForBackup.java
@@ -0,0 +1,64 @@
+/*
+ * 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.server.backup.testing;
+
+import android.annotation.RequiresPermission;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.UserHandle;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.shadows.ShadowContextImpl;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Implementation of ContextImpl shadow, handling bindServiceAsUser().
+ */
+@Implements(className = ShadowContextImpl.CLASS_NAME)
+public class ShadowContextImplForBackup extends ShadowContextImpl {
+ public static Map<ComponentName, IBinder> sComponentBinderMap = new HashMap<>();
+ public static Set<ComponentName> sUnbindableComponents = new HashSet<>();
+
+ @Implementation
+ public boolean bindServiceAsUser(@RequiresPermission Intent service, ServiceConnection conn,
+ int flags, UserHandle user) {
+ if (sUnbindableComponents.contains(service.getComponent())) {
+ return false;
+ }
+
+ ShadowApplication.getInstance().setComponentNameAndServiceForBindService(
+ service.getComponent(), sComponentBinderMap.get(service.getComponent()));
+ return bindService(service, conn, flags);
+ }
+
+
+ /**
+ * Resets backup-related shadow state.
+ */
+ public static void resetBackupShadowState() {
+ sComponentBinderMap.clear();
+ sUnbindableComponents.clear();
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/testing/TransportBoundListenerStub.java b/services/robotests/src/com/android/server/backup/testing/TransportBoundListenerStub.java
new file mode 100644
index 000000000000..84ac2c212854
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/testing/TransportBoundListenerStub.java
@@ -0,0 +1,64 @@
+/*
+ * 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.server.backup.testing;
+
+import com.android.internal.backup.IBackupTransport;
+import com.android.server.backup.TransportManager;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Stub implementation of TransportBoundListener, which returns given result and can tell whether
+ * it was called for given transport.
+ */
+public class TransportBoundListenerStub implements
+ TransportManager.TransportBoundListener {
+ private boolean mAlwaysReturnSuccess;
+ private Set<IBackupTransport> mTransportsCalledFor = new HashSet<>();
+
+ public TransportBoundListenerStub(boolean alwaysReturnSuccess) {
+ this.mAlwaysReturnSuccess = alwaysReturnSuccess;
+ }
+
+ @Override
+ public boolean onTransportBound(IBackupTransport binder) {
+ mTransportsCalledFor.add(binder);
+ return mAlwaysReturnSuccess;
+ }
+
+ /**
+ * Returns whether the listener was called for the specified transport at least once.
+ */
+ public boolean isCalledForTransport(IBackupTransport binder) {
+ return mTransportsCalledFor.contains(binder);
+ }
+
+ /**
+ * Returns whether the listener was called at least once.
+ */
+ public boolean isCalled() {
+ return !mTransportsCalledFor.isEmpty();
+ }
+
+ /**
+ * Resets listener calls.
+ */
+ public void resetState() {
+ mTransportsCalledFor.clear();
+ }
+}
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
index 04b42f1ee312..ddd21df78e85 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -429,6 +429,20 @@ public class NotificationManagerServiceTest extends NotificationTestCase {
}
@Test
+ public void testBlockedNotifications_blockedChannelGroup() throws Exception {
+ when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
+ mNotificationManagerService.setRankingHelper(mRankingHelper);
+ when(mRankingHelper.isGroupBlocked(anyString(), anyInt(), anyString())).thenReturn(true);
+
+ NotificationChannel channel = new NotificationChannel("id", "name",
+ NotificationManager.IMPORTANCE_HIGH);
+ channel.setGroup("something");
+ NotificationRecord r = generateNotificationRecord(channel);
+ assertTrue(mNotificationManagerService.isBlocked(r, mUsageStats));
+ verify(mUsageStats, times(1)).registerBlocked(eq(r));
+ }
+
+ @Test
public void testEnqueuedBlockedNotifications_blockedApp() throws Exception {
when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
diff --git a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
index 65bf33084bb4..306dd98acaad 100644
--- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
@@ -240,12 +240,23 @@ public class RankingHelperTest extends NotificationTestCase {
private void compareGroups(NotificationChannelGroup expected, NotificationChannelGroup actual) {
assertEquals(expected.getId(), actual.getId());
assertEquals(expected.getName(), actual.getName());
+ assertEquals(expected.getDescription(), actual.getDescription());
+ assertEquals(expected.isBlocked(), actual.isBlocked());
}
private NotificationChannel getChannel() {
return new NotificationChannel("id", "name", IMPORTANCE_LOW);
}
+ private NotificationChannel findChannel(List<NotificationChannel> channels, String id) {
+ for (NotificationChannel channel : channels) {
+ if (channel.getId().equals(id)) {
+ return channel;
+ }
+ }
+ return null;
+ }
+
@Test
public void testFindAfterRankingWithASplitGroup() throws Exception {
ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(3);
@@ -299,6 +310,8 @@ public class RankingHelperTest extends NotificationTestCase {
@Test
public void testChannelXml() throws Exception {
NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye");
+ ncg.setBlocked(true);
+ ncg.setDescription("group desc");
NotificationChannelGroup ncg2 = new NotificationChannelGroup("2", "hello");
NotificationChannel channel1 =
new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
@@ -1294,6 +1307,26 @@ public class RankingHelperTest extends NotificationTestCase {
}
@Test
+ public void testCreateChannel_addToGroup() throws Exception {
+ NotificationChannelGroup group = new NotificationChannelGroup("group", "");
+ mHelper.createNotificationChannelGroup(PKG, UID, group, true);
+ NotificationChannel nc = new NotificationChannel("id", "hello", IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(PKG, UID, nc, true);
+ NotificationChannel actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
+ assertNull(actual.getGroup());
+
+ nc = new NotificationChannel("id", "hello", IMPORTANCE_HIGH);
+ nc.setGroup(group.getId());
+ mHelper.createNotificationChannel(PKG, UID, nc, true);
+
+ actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
+ assertNotNull(actual.getGroup());
+ assertEquals(IMPORTANCE_DEFAULT, actual.getImportance());
+
+ verify(mHandler, times(1)).requestSort();
+ }
+
+ @Test
public void testDumpChannelsJson() throws Exception {
final ApplicationInfo upgrade = new ApplicationInfo();
upgrade.targetSdkVersion = Build.VERSION_CODES.O;
@@ -1388,4 +1421,77 @@ public class RankingHelperTest extends NotificationTestCase {
assertEquals(newLabel, mHelper.getNotificationChannel(PKG, UID,
NotificationChannel.DEFAULT_CHANNEL_ID, false).getName());
}
+
+ @Test
+ public void testIsGroupBlocked_noGroup() throws Exception {
+ assertFalse(mHelper.isGroupBlocked(PKG, UID, null));
+
+ assertFalse(mHelper.isGroupBlocked(PKG, UID, "non existent group"));
+ }
+
+ @Test
+ public void testIsGroupBlocked_notBlocked() throws Exception {
+ NotificationChannelGroup group = new NotificationChannelGroup("id", "name");
+ mHelper.createNotificationChannelGroup(PKG, UID, group, true);
+
+ assertFalse(mHelper.isGroupBlocked(PKG, UID, group.getId()));
+ }
+
+ @Test
+ public void testIsGroupBlocked_blocked() throws Exception {
+ NotificationChannelGroup group = new NotificationChannelGroup("id", "name");
+ mHelper.createNotificationChannelGroup(PKG, UID, group, true);
+ group.setBlocked(true);
+ mHelper.createNotificationChannelGroup(PKG, UID, group, false);
+
+ assertTrue(mHelper.isGroupBlocked(PKG, UID, group.getId()));
+ }
+
+ @Test
+ public void testIsGroup_appCannotResetBlock() throws Exception {
+ NotificationChannelGroup group = new NotificationChannelGroup("id", "name");
+ mHelper.createNotificationChannelGroup(PKG, UID, group, true);
+ NotificationChannelGroup group2 = group.clone();
+ group2.setBlocked(true);
+ mHelper.createNotificationChannelGroup(PKG, UID, group2, false);
+ assertTrue(mHelper.isGroupBlocked(PKG, UID, group.getId()));
+
+ NotificationChannelGroup group3 = group.clone();
+ group3.setBlocked(false);
+ mHelper.createNotificationChannelGroup(PKG, UID, group3, true);
+ assertTrue(mHelper.isGroupBlocked(PKG, UID, group.getId()));
+ }
+
+ @Test
+ public void testGetNotificationChannelGroupWithChannels() throws Exception {
+ NotificationChannelGroup group = new NotificationChannelGroup("group", "");
+ NotificationChannelGroup other = new NotificationChannelGroup("something else", "");
+ mHelper.createNotificationChannelGroup(PKG, UID, group, true);
+ mHelper.createNotificationChannelGroup(PKG, UID, other, true);
+
+ NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
+ a.setGroup(group.getId());
+ NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_DEFAULT);
+ b.setGroup(other.getId());
+ NotificationChannel c = new NotificationChannel("c", "c", IMPORTANCE_DEFAULT);
+ c.setGroup(group.getId());
+ NotificationChannel d = new NotificationChannel("d", "d", IMPORTANCE_DEFAULT);
+
+ mHelper.createNotificationChannel(PKG, UID, a, true);
+ mHelper.createNotificationChannel(PKG, UID, b, true);
+ mHelper.createNotificationChannel(PKG, UID, c, true);
+ mHelper.createNotificationChannel(PKG, UID, d, true);
+ mHelper.deleteNotificationChannel(PKG, UID, c.getId());
+
+ NotificationChannelGroup retrieved = mHelper.getNotificationChannelGroupWithChannels(
+ PKG, UID, group.getId(), true);
+ assertEquals(2, retrieved.getChannels().size());
+ compareChannels(a, findChannel(retrieved.getChannels(), a.getId()));
+ compareChannels(c, findChannel(retrieved.getChannels(), c.getId()));
+
+ retrieved = mHelper.getNotificationChannelGroupWithChannels(
+ PKG, UID, group.getId(), false);
+ assertEquals(1, retrieved.getChannels().size());
+ compareChannels(a, findChannel(retrieved.getChannels(), a.getId()));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
new file mode 100644
index 000000000000..50824e32e50d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
@@ -0,0 +1,535 @@
+/*
+ * 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.server.accessibility;
+
+import static android.util.ExceptionUtils.propagate;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_POINTER_DOWN;
+import static android.view.MotionEvent.ACTION_POINTER_UP;
+
+import static com.android.server.testutils.TestUtils.strictMock;
+
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.DebugUtils;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+
+import com.android.server.testutils.OffsettableClock;
+import com.android.server.testutils.TestHandler;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.function.IntConsumer;
+
+
+@RunWith(AndroidJUnit4.class)
+public class MagnificationGestureHandlerTest {
+
+ public static final int STATE_IDLE = 1;
+ public static final int STATE_ZOOMED = 2;
+ public static final int STATE_2TAPS = 3;
+ public static final int STATE_ZOOMED_2TAPS = 4;
+ public static final int STATE_SHORTCUT_TRIGGERED = 5;
+ public static final int STATE_DRAGGING_TMP = 6;
+ public static final int STATE_DRAGGING = 7;
+ public static final int STATE_PANNING = 8;
+ public static final int STATE_SCALING_AND_PANNING = 9;
+
+
+ public static final int FIRST_STATE = STATE_IDLE;
+ public static final int LAST_STATE = STATE_SCALING_AND_PANNING;
+
+ // Co-prime x and y, to potentially catch x-y-swapped errors
+ public static final float DEFAULT_X = 301;
+ public static final float DEFAULT_Y = 299;
+
+ private Context mContext;
+ private AccessibilityManagerService mAms;
+ private MagnificationController mMagnificationController;
+ private OffsettableClock mClock;
+ private MagnificationGestureHandler mMgh;
+ private TestHandler mHandler;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getContext();
+ mAms = new AccessibilityManagerService(mContext);
+ mMagnificationController = new MagnificationController(
+ mContext, mAms, /* lock */ new Object()) {
+ @Override
+ public boolean magnificationRegionContains(float x, float y) {
+ return true;
+ }
+
+ @Override
+ void setForceShowMagnifiableBounds(boolean show) {}
+ };
+ mMagnificationController.mRegistered = true;
+ mClock = new OffsettableClock.Stopped();
+
+ boolean detectTripleTap = true;
+ boolean detectShortcutTrigger = true;
+ mMgh = newInstance(detectTripleTap, detectShortcutTrigger);
+ }
+
+ @NonNull
+ public MagnificationGestureHandler newInstance(boolean detectTripleTap,
+ boolean detectShortcutTrigger) {
+ MagnificationGestureHandler h = new MagnificationGestureHandler(
+ mContext, mMagnificationController,
+ detectTripleTap, detectShortcutTrigger);
+ mHandler = new TestHandler(h.mDetectingStateHandler, mClock);
+ h.mDetectingStateHandler.mHandler = mHandler;
+ h.setNext(strictMock(EventStreamTransformation.class));
+ return h;
+ }
+
+ @Test
+ public void testInitialState_isIdle() {
+ assertIn(STATE_IDLE);
+ }
+
+ /**
+ * Covers paths to get to and back between each state and {@link #STATE_IDLE}
+ * This navigates between states using "canonical" paths, specified in
+ * {@link #goFromStateIdleTo} (for traversing away from {@link #STATE_IDLE}) and
+ * {@link #returnToNormalFrom} (for navigating back to {@link #STATE_IDLE})
+ */
+ @Test
+ public void testEachState_isReachableAndRecoverable() {
+ forEachState(state -> {
+ goFromStateIdleTo(state);
+ assertIn(state);
+
+ returnToNormalFrom(state);
+ try {
+ assertIn(STATE_IDLE);
+ } catch (AssertionError e) {
+ throw new AssertionError("Failed while testing state " + stateToString(state), e);
+ }
+ });
+ }
+
+ @Test
+ public void testStates_areMutuallyExclusive() {
+ forEachState(state1 -> {
+ forEachState(state2 -> {
+ if (state1 < state2) {
+ goFromStateIdleTo(state1);
+ try {
+ assertIn(state2);
+ fail("State " + stateToString(state1) + " also implies state "
+ + stateToString(state2) + stateDump());
+ } catch (AssertionError e) {
+ // expected
+ returnToNormalFrom(state1);
+ }
+ }
+ });
+ });
+ }
+
+ /**
+ * Covers edges of the graph not covered by "canonical" transitions specified in
+ * {@link #goFromStateIdleTo} and {@link #returnToNormalFrom}
+ */
+ @SuppressWarnings("Convert2MethodRef")
+ @Test
+ public void testAlternativeTransitions_areWorking() {
+ // A11y button followed by a tap&hold turns temporary "viewport dragging" zoom on
+ assertTransition(STATE_SHORTCUT_TRIGGERED, () -> {
+ send(downEvent());
+ fastForward1sec();
+ }, STATE_DRAGGING_TMP);
+
+ // A11y button followed by a tap turns zoom on
+ assertTransition(STATE_SHORTCUT_TRIGGERED, () -> tap(), STATE_ZOOMED);
+
+ // A11y button pressed second time negates the 1st press
+ assertTransition(STATE_SHORTCUT_TRIGGERED, () -> triggerShortcut(), STATE_IDLE);
+
+ // A11y button turns zoom off
+ assertTransition(STATE_ZOOMED, () -> triggerShortcut(), STATE_IDLE);
+
+
+ // Double tap times out while zoomed
+ assertTransition(STATE_ZOOMED_2TAPS, () -> {
+ allowEventDelegation();
+ fastForward1sec();
+ }, STATE_ZOOMED);
+
+ // tap+tap+swipe gets delegated
+ assertTransition(STATE_2TAPS, () -> {
+ allowEventDelegation();
+ swipe();
+ }, STATE_IDLE);
+ }
+
+ @Test
+ public void testNonTransitions_dontChangeState() {
+ // ACTION_POINTER_DOWN triggers event delegation if not magnifying
+ assertStaysIn(STATE_IDLE, () -> {
+ allowEventDelegation();
+ send(downEvent());
+ send(pointerEvent(ACTION_POINTER_DOWN, DEFAULT_X * 2, DEFAULT_Y));
+ });
+
+ // Long tap breaks the triple-tap detection sequence
+ Runnable tapAndLongTap = () -> {
+ allowEventDelegation();
+ tap();
+ longTap();
+ };
+ assertStaysIn(STATE_IDLE, tapAndLongTap);
+ assertStaysIn(STATE_ZOOMED, tapAndLongTap);
+
+ // Triple tap with delays in between doesn't count
+ Runnable slow3tap = () -> {
+ tap();
+ fastForward1sec();
+ tap();
+ fastForward1sec();
+ tap();
+ };
+ assertStaysIn(STATE_IDLE, slow3tap);
+ assertStaysIn(STATE_ZOOMED, slow3tap);
+ }
+
+ @Test
+ public void testDisablingTripleTap_removesInputLag() {
+ mMgh = newInstance(/* detect3tap */ false, /* detectShortcut */ true);
+ goFromStateIdleTo(STATE_IDLE);
+ allowEventDelegation();
+ tap();
+ // no fast forward
+ verify(mMgh.mNext, times(2)).onMotionEvent(any(), any(), anyInt());
+ }
+
+ private void assertTransition(int fromState, Runnable transitionAction, int toState) {
+ goFromStateIdleTo(fromState);
+ transitionAction.run();
+ assertIn(toState);
+ returnToNormalFrom(toState);
+ }
+
+ private void assertStaysIn(int state, Runnable action) {
+ assertTransition(state, action, state);
+ }
+
+ private void forEachState(IntConsumer action) {
+ for (int state = FIRST_STATE; state <= LAST_STATE; state++) {
+ action.accept(state);
+ }
+ }
+
+ private void allowEventDelegation() {
+ doNothing().when(mMgh.mNext).onMotionEvent(any(), any(), anyInt());
+ }
+
+ private void fastForward1sec() {
+ fastForward(1000);
+ }
+
+ private void fastForward(int ms) {
+ mClock.fastForward(ms);
+ mHandler.timeAdvance();
+ }
+
+ /**
+ * Asserts that {@link #mMgh the handler} is in the given {@code state}
+ */
+ private void assertIn(int state) {
+ switch (state) {
+
+ // Asserts on separate lines for accurate stack traces
+
+ case STATE_IDLE: {
+ check(tapCount() < 2, state);
+ check(!mMgh.mShortcutTriggered, state);
+ check(!isZoomed(), state);
+ } break;
+ case STATE_ZOOMED: {
+ check(isZoomed(), state);
+ check(tapCount() < 2, state);
+ } break;
+ case STATE_2TAPS: {
+ check(!isZoomed(), state);
+ check(tapCount() == 2, state);
+ } break;
+ case STATE_ZOOMED_2TAPS: {
+ check(isZoomed(), state);
+ check(tapCount() == 2, state);
+ } break;
+ case STATE_DRAGGING: {
+ check(mMgh.mCurrentState == MagnificationGestureHandler.STATE_VIEWPORT_DRAGGING,
+ state);
+ check(mMgh.mViewportDraggingStateHandler.mZoomedInBeforeDrag, state);
+ } break;
+ case STATE_DRAGGING_TMP: {
+ check(mMgh.mCurrentState == MagnificationGestureHandler.STATE_VIEWPORT_DRAGGING,
+ state);
+ check(!mMgh.mViewportDraggingStateHandler.mZoomedInBeforeDrag, state);
+ } break;
+ case STATE_SHORTCUT_TRIGGERED: {
+ check(mMgh.mShortcutTriggered, state);
+ check(!isZoomed(), state);
+ } break;
+ case STATE_PANNING: {
+ check(mMgh.mCurrentState == MagnificationGestureHandler.STATE_PANNING_SCALING,
+ state);
+ check(!mMgh.mPanningScalingStateHandler.mScaling, state);
+ } break;
+ case STATE_SCALING_AND_PANNING: {
+ check(mMgh.mCurrentState == MagnificationGestureHandler.STATE_PANNING_SCALING,
+ state);
+ check(mMgh.mPanningScalingStateHandler.mScaling, state);
+ } break;
+ default: throw new IllegalArgumentException("Illegal state: " + state);
+ }
+ }
+
+ /**
+ * Defines a "canonical" path from {@link #STATE_IDLE} to {@code state}
+ */
+ private void goFromStateIdleTo(int state) {
+ try {
+ switch (state) {
+ case STATE_IDLE: {
+ mMgh.clearAndTransitionToStateDetecting();
+ } break;
+ case STATE_2TAPS: {
+ goFromStateIdleTo(STATE_IDLE);
+ tap();
+ tap();
+ } break;
+ case STATE_ZOOMED: {
+ if (mMgh.mDetectTripleTap) {
+ goFromStateIdleTo(STATE_2TAPS);
+ tap();
+ } else {
+ goFromStateIdleTo(STATE_SHORTCUT_TRIGGERED);
+ tap();
+ }
+ } break;
+ case STATE_ZOOMED_2TAPS: {
+ goFromStateIdleTo(STATE_ZOOMED);
+ tap();
+ tap();
+ } break;
+ case STATE_DRAGGING: {
+ goFromStateIdleTo(STATE_ZOOMED_2TAPS);
+ send(downEvent());
+ fastForward1sec();
+ } break;
+ case STATE_DRAGGING_TMP: {
+ goFromStateIdleTo(STATE_2TAPS);
+ send(downEvent());
+ fastForward1sec();
+ } break;
+ case STATE_SHORTCUT_TRIGGERED: {
+ goFromStateIdleTo(STATE_IDLE);
+ triggerShortcut();
+ } break;
+ case STATE_PANNING: {
+ goFromStateIdleTo(STATE_ZOOMED);
+ send(downEvent());
+ send(pointerEvent(ACTION_POINTER_DOWN, DEFAULT_X * 2, DEFAULT_Y));
+ } break;
+ case STATE_SCALING_AND_PANNING: {
+ goFromStateIdleTo(STATE_PANNING);
+ send(pointerEvent(ACTION_MOVE, DEFAULT_X * 2, DEFAULT_Y * 3));
+ send(pointerEvent(ACTION_MOVE, DEFAULT_X * 2, DEFAULT_Y * 4));
+ } break;
+ default:
+ throw new IllegalArgumentException("Illegal state: " + state);
+ }
+ } catch (Throwable t) {
+ throw new RuntimeException("Failed to go to state " + stateToString(state), t);
+ }
+ }
+
+ /**
+ * Defines a "canonical" path from {@code state} to {@link #STATE_IDLE}
+ */
+ private void returnToNormalFrom(int state) {
+ switch (state) {
+ case STATE_IDLE: {
+ // no op
+ } break;
+ case STATE_2TAPS: {
+ allowEventDelegation();
+ fastForward1sec();
+ } break;
+ case STATE_ZOOMED: {
+ if (mMgh.mDetectTripleTap) {
+ tap();
+ tap();
+ returnToNormalFrom(STATE_ZOOMED_2TAPS);
+ } else {
+ triggerShortcut();
+ }
+ } break;
+ case STATE_ZOOMED_2TAPS: {
+ tap();
+ } break;
+ case STATE_DRAGGING: {
+ send(upEvent());
+ returnToNormalFrom(STATE_ZOOMED);
+ } break;
+ case STATE_DRAGGING_TMP: {
+ send(upEvent());
+ } break;
+ case STATE_SHORTCUT_TRIGGERED: {
+ triggerShortcut();
+ } break;
+ case STATE_PANNING: {
+ send(pointerEvent(ACTION_POINTER_UP, DEFAULT_X * 2, DEFAULT_Y));
+ send(upEvent());
+ returnToNormalFrom(STATE_ZOOMED);
+ } break;
+ case STATE_SCALING_AND_PANNING: {
+ returnToNormalFrom(STATE_PANNING);
+ } break;
+ default: throw new IllegalArgumentException("Illegal state: " + state);
+ }
+ }
+
+ private void check(boolean condition, int expectedState) {
+ if (!condition) {
+ fail("Expected to be in state " + stateToString(expectedState) + stateDump());
+ }
+ }
+
+ private boolean isZoomed() {
+ return mMgh.mMagnificationController.isMagnifying();
+ }
+
+ private int tapCount() {
+ return mMgh.mDetectingStateHandler.tapCount();
+ }
+
+ private static String stateToString(int state) {
+ return DebugUtils.valueToString(MagnificationGestureHandlerTest.class, "STATE_", state);
+ }
+
+ private void tap() {
+ MotionEvent downEvent = downEvent();
+ send(downEvent);
+ send(upEvent(downEvent.getDownTime()));
+ }
+
+ private void swipe() {
+ MotionEvent downEvent = downEvent();
+ send(downEvent);
+ send(moveEvent(DEFAULT_X * 2, DEFAULT_Y * 2));
+ send(upEvent(downEvent.getDownTime()));
+ }
+
+ private void longTap() {
+ MotionEvent downEvent = downEvent();
+ send(downEvent);
+ fastForward(2000);
+ send(upEvent(downEvent.getDownTime()));
+ }
+
+ private void triggerShortcut() {
+ mMgh.notifyShortcutTriggered();
+ }
+
+ private void send(MotionEvent event) {
+ event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+ try {
+ mMgh.onMotionEvent(event, event, /* policyFlags */ 0);
+ } catch (Throwable t) {
+ throw new RuntimeException("Exception while handling " + event, t);
+ }
+ fastForward(1);
+ }
+
+ private MotionEvent moveEvent(float x, float y) {
+ return MotionEvent.obtain(defaultDownTime(), mClock.now(), ACTION_MOVE, x, y, 0);
+ }
+
+ private MotionEvent downEvent() {
+ return MotionEvent.obtain(mClock.now(), mClock.now(),
+ ACTION_DOWN, DEFAULT_X, DEFAULT_Y, 0);
+ }
+
+ private MotionEvent upEvent() {
+ return upEvent(defaultDownTime());
+ }
+
+ private MotionEvent upEvent(long downTime) {
+ return MotionEvent.obtain(downTime, mClock.now(),
+ MotionEvent.ACTION_UP, DEFAULT_X, DEFAULT_Y, 0);
+ }
+
+ private long defaultDownTime() {
+ MotionEvent lastDown = mMgh.mDetectingStateHandler.mLastDown;
+ return lastDown == null ? mClock.now() - 1 : lastDown.getDownTime();
+ }
+
+ private MotionEvent pointerEvent(int action, float x, float y) {
+ MotionEvent.PointerProperties defPointerProperties = new MotionEvent.PointerProperties();
+ defPointerProperties.id = 0;
+ defPointerProperties.toolType = MotionEvent.TOOL_TYPE_FINGER;
+ MotionEvent.PointerProperties pointerProperties = new MotionEvent.PointerProperties();
+ pointerProperties.id = 1;
+ pointerProperties.toolType = MotionEvent.TOOL_TYPE_FINGER;
+
+ MotionEvent.PointerCoords defPointerCoords = new MotionEvent.PointerCoords();
+ defPointerCoords.x = DEFAULT_X;
+ defPointerCoords.y = DEFAULT_Y;
+ MotionEvent.PointerCoords pointerCoords = new MotionEvent.PointerCoords();
+ pointerCoords.x = x;
+ pointerCoords.y = y;
+
+ return MotionEvent.obtain(
+ /* downTime */ mClock.now(),
+ /* eventTime */ mClock.now(),
+ /* action */ action,
+ /* pointerCount */ 2,
+ /* pointerProperties */ new MotionEvent.PointerProperties[] {
+ defPointerProperties, pointerProperties },
+ /* pointerCoords */ new MotionEvent.PointerCoords[] { defPointerCoords, pointerCoords },
+ /* metaState */ 0,
+ /* buttonState */ 0,
+ /* xPrecision */ 1.0f,
+ /* yPrecision */ 1.0f,
+ /* deviceId */ 0,
+ /* edgeFlags */ 0,
+ /* source */ InputDevice.SOURCE_TOUCHSCREEN,
+ /* flags */ 0);
+ }
+
+ private String stateDump() {
+ return "\nCurrent state dump:\n" + mMgh + "\n" + mHandler.getPendingMessages();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/backup/BackupPasswordManagerTest.java b/services/tests/servicestests/src/com/android/server/backup/BackupPasswordManagerTest.java
index 04c02510cb3d..bc162977de2b 100644
--- a/services/tests/servicestests/src/com/android/server/backup/BackupPasswordManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/BackupPasswordManagerTest.java
@@ -16,7 +16,7 @@
package com.android.server.backup;
-import static com.android.server.testutis.TestUtils.assertExpectException;
+import static com.android.server.testutils.TestUtils.assertExpectException;
import static com.google.common.truth.Truth.assertThat;
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index e3faa5280859..e8a1811e1a63 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -22,7 +22,7 @@ import static android.os.UserManagerInternal.CAMERA_DISABLED_GLOBALLY;
import static android.os.UserManagerInternal.CAMERA_DISABLED_LOCALLY;
import static android.os.UserManagerInternal.CAMERA_NOT_DISABLED;
-import static com.android.server.testutis.TestUtils.assertExpectException;
+import static com.android.server.testutils.TestUtils.assertExpectException;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
@@ -54,11 +54,11 @@ import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.PasswordMetrics;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.content.pm.StringParceledListSlice;
import android.content.pm.UserInfo;
import android.graphics.Color;
@@ -167,6 +167,11 @@ public class DevicePolicyManagerTest extends DpmTestBase {
mServiceContext.binder.callingUid = DpmMockContext.CALLER_UID;
when(getServices().packageManager.hasSystemFeature(eq(PackageManager.FEATURE_DEVICE_ADMIN)))
.thenReturn(true);
+ doReturn(Collections.singletonList(new ResolveInfo()))
+ .when(getServices().packageManager).queryBroadcastReceiversAsUser(
+ any(Intent.class),
+ anyInt(),
+ any(UserHandle.class));
// By default, pretend all users are running and unlocked.
when(getServices().userManager.isUserUnlocked(anyInt())).thenReturn(true);
diff --git a/services/tests/servicestests/src/com/android/server/testutils/OffsettableClock.java b/services/tests/servicestests/src/com/android/server/testutils/OffsettableClock.java
new file mode 100644
index 000000000000..8dabbc4d4356
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/testutils/OffsettableClock.java
@@ -0,0 +1,79 @@
+/*
+ * 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.server.testutils;
+
+import android.os.SystemClock;
+
+import java.util.function.LongSupplier;
+
+/**
+ * A time supplier (in the format of a {@code long} as the amount of milliseconds) similar
+ * to {@link SystemClock#uptimeMillis()}, but with the ability to {@link #fastForward}
+ * and {@link #rewind}
+ *
+ * Implements {@link LongSupplier} to be interchangeable with {@code SystemClock::uptimeMillis}
+ *
+ * Can be provided to {@link TestHandler} to "mock time" for the delayed execution testing
+ *
+ * @see OffsettableClock.Stopped for a version of this clock that does not advance on its own
+ */
+public class OffsettableClock implements LongSupplier {
+ private long mOffset = 0L;
+
+ /**
+ * @return Current time in milliseconds, according to this clock
+ */
+ public long now() {
+ return realNow() + mOffset;
+ }
+
+ /**
+ * Can be overriden with a constant for a clock that stands still, and is only ever moved
+ * manually
+ */
+ public long realNow() {
+ return SystemClock.uptimeMillis();
+ }
+
+ public void fastForward(long timeMs) {
+ mOffset += timeMs;
+ }
+ public void rewind(long timeMs) {
+ fastForward(-timeMs);
+ }
+ public void reset() {
+ mOffset = 0;
+ }
+
+ /** @deprecated Only present for {@link LongSupplier} contract */
+ @Override
+ @Deprecated
+ public long getAsLong() {
+ return now();
+ }
+
+ /**
+ * An {@link OffsettableClock} that does not advance with real time, and can only be
+ * advanced manually via {@link #fastForward}
+ */
+ public static class Stopped extends OffsettableClock {
+ @Override
+ public long realNow() {
+ return 0L;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/testutils/TestHandler.java b/services/tests/servicestests/src/com/android/server/testutils/TestHandler.java
new file mode 100644
index 000000000000..2d4bc0f8b7d0
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/testutils/TestHandler.java
@@ -0,0 +1,156 @@
+/*
+ * 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.server.testutils;
+
+
+import static android.util.ExceptionUtils.getRootCause;
+import static android.util.ExceptionUtils.propagate;
+
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.util.ArrayMap;
+
+import java.util.Map;
+import java.util.PriorityQueue;
+import java.util.function.LongSupplier;
+
+/**
+ * A test {@link Handler} that stores incoming {@link Message}s and {@link Runnable callbacks}
+ * in a {@link PriorityQueue} based on time, to be manually processed later in a correct order
+ * either all together with {@link #flush}, or only those due at the current time with
+ * {@link #timeAdvance}.
+ *
+ * For the latter use case this also supports providing a custom clock (in a format of a
+ * milliseconds-returning {@link LongSupplier}), that will be used for storing the messages'
+ * timestamps to be posted at, and checked against during {@link #timeAdvance}.
+ *
+ * This allows to test code that uses {@link Handler}'s delayed invocation capabilities, such as
+ * {@link Handler#sendMessageDelayed} or {@link Handler#postDelayed} without resorting to
+ * synchronously {@link Thread#sleep}ing in your test.
+ *
+ * @see OffsettableClock for a useful custom clock implementation to use with this handler
+ */
+public class TestHandler extends Handler {
+ private static final LongSupplier DEFAULT_CLOCK = SystemClock::uptimeMillis;
+
+ private final PriorityQueue<MsgInfo> mMessages = new PriorityQueue<>();
+ /**
+ * Map of: {@code message id -> count of such messages currently pending }
+ */
+ // Boxing is ok here - both msg ids and their pending counts tend to be well below 128
+ private final Map<Integer, Integer> mPendingMsgTypeCounts = new ArrayMap<>();
+ private final LongSupplier mClock;
+
+ public TestHandler(Callback callback) {
+ this(callback, DEFAULT_CLOCK);
+ }
+
+ public TestHandler(Callback callback, LongSupplier clock) {
+ super(callback);
+ mClock = clock;
+ }
+
+ @Override
+ public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+ mPendingMsgTypeCounts.put(msg.what,
+ mPendingMsgTypeCounts.getOrDefault(msg.what, 0) + 1);
+
+ // uptimeMillis is an absolute time obtained as SystemClock.uptimeMillis() + offsetMillis
+ // if custom clock is given, recalculate the time with regards to it
+ if (mClock != DEFAULT_CLOCK) {
+ uptimeMillis = uptimeMillis - SystemClock.uptimeMillis() + mClock.getAsLong();
+ }
+
+ // post a dummy queue entry to keep track of message removal
+ return super.sendMessageAtTime(msg, Long.MAX_VALUE)
+ && mMessages.add(new MsgInfo(Message.obtain(msg), uptimeMillis));
+ }
+
+ /** @see TestHandler */
+ public void timeAdvance() {
+ long now = mClock.getAsLong();
+ while (!mMessages.isEmpty() && mMessages.peek().sendTime <= now) {
+ dispatch(mMessages.poll());
+ }
+ }
+
+ /**
+ * Dispatch all messages in order
+ *
+ * @see TestHandler
+ */
+ public void flush() {
+ MsgInfo msg;
+ while ((msg = mMessages.poll()) != null) {
+ dispatch(msg);
+ }
+ }
+
+ public PriorityQueue<MsgInfo> getPendingMessages() {
+ return new PriorityQueue<>(mMessages);
+ }
+
+ private void dispatch(MsgInfo msg) {
+ int msgId = msg.message.what;
+
+ if (!hasMessages(msgId)) {
+ // Handler.removeMessages(msgId) must have been called
+ return;
+ }
+
+ try {
+ Integer pendingMsgCount = mPendingMsgTypeCounts.getOrDefault(msgId, 0);
+ if (pendingMsgCount <= 1) {
+ removeMessages(msgId);
+ }
+ mPendingMsgTypeCounts.put(msgId, pendingMsgCount - 1);
+
+ dispatchMessage(msg.message);
+ } catch (Throwable t) {
+ // Append stack trace of this message being posted as a cause for a helpful
+ // test error message
+ throw propagate(getRootCause(t).initCause(msg.postPoint));
+ } finally {
+ msg.message.recycle();
+ }
+ }
+
+ private class MsgInfo implements Comparable<MsgInfo> {
+ public final Message message;
+ public final long sendTime;
+ public final RuntimeException postPoint;
+
+ private MsgInfo(Message message, long sendTime) {
+ this.message = message;
+ this.sendTime = sendTime;
+ this.postPoint = new RuntimeException("Message originated from here:");
+ }
+
+ @Override
+ public int compareTo(MsgInfo o) {
+ return (int) (sendTime - o.sendTime);
+ }
+
+ @Override
+ public String toString() {
+ return "MsgInfo{" +
+ "message=" + message +
+ ", sendTime=" + sendTime +
+ '}';
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/testutis/TestUtils.java b/services/tests/servicestests/src/com/android/server/testutils/TestUtils.java
index 88289888b0dd..b200293ee916 100644
--- a/services/tests/servicestests/src/com/android/server/testutis/TestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/testutils/TestUtils.java
@@ -13,12 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.server.testutis;
+package com.android.server.testutils;
import android.test.MoreAsserts;
import junit.framework.Assert;
+import org.mockito.Mockito;
+import org.mockito.stubbing.Answer;
+
public class TestUtils {
private TestUtils() {
}
@@ -44,4 +47,17 @@ public class TestUtils {
Assert.fail("Expected exception type " + expectedExceptionType.getName()
+ " was not thrown");
}
+
+ /**
+ * EasyMock-style "strict" mock that throws immediately on any interaction that was not
+ * explicitly allowed.
+ *
+ * You can allow certain method calls on a whitelist basis by stubbing them e.g. with
+ * {@link Mockito#doAnswer}, {@link Mockito#doNothing}, etc.
+ */
+ public static <T> T strictMock(Class<T> c) {
+ return Mockito.mock(c, (Answer) invocation -> {
+ throw new AssertionError("Unexpected invocation: " + invocation);
+ });
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
index 9d32496c7817..0081214a24da 100644
--- a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
@@ -126,6 +126,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase {
boolean mMovedToFullscreen;
boolean mAnimationStarted;
boolean mSchedulePipModeChangedOnStart;
+ boolean mForcePipModeChangedCallback;
boolean mAnimationEnded;
Rect mAnimationEndFinalStackBounds;
boolean mSchedulePipModeChangedOnEnd;
@@ -140,6 +141,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase {
mAnimationStarted = false;
mAnimationEnded = false;
mAnimationEndFinalStackBounds = null;
+ mForcePipModeChangedCallback = false;
mSchedulePipModeChangedOnStart = false;
mSchedulePipModeChangedOnEnd = false;
mStackBounds = from;
@@ -148,10 +150,11 @@ public class BoundsAnimationControllerTests extends WindowTestsBase {
}
@Override
- public void onAnimationStart(boolean schedulePipModeChangedCallback) {
+ public void onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate) {
mAwaitingAnimationStart = false;
mAnimationStarted = true;
mSchedulePipModeChangedOnStart = schedulePipModeChangedCallback;
+ mForcePipModeChangedCallback = forceUpdate;
}
@Override
@@ -232,7 +235,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase {
return this;
}
- BoundsAnimationDriver restart(Rect to) {
+ BoundsAnimationDriver restart(Rect to, boolean expectStartedAndPipModeChangedCallback) {
if (mAnimator == null) {
throw new IllegalArgumentException("Call start() to start a new animation");
}
@@ -251,8 +254,15 @@ public class BoundsAnimationControllerTests extends WindowTestsBase {
assertSame(oldAnimator, mAnimator);
}
- // No animation start for replacing animation
- assertTrue(!mTarget.mAnimationStarted);
+ if (expectStartedAndPipModeChangedCallback) {
+ // Replacing animation with pending pip mode changed callback, ensure we update
+ assertTrue(mTarget.mAnimationStarted);
+ assertTrue(mTarget.mSchedulePipModeChangedOnStart);
+ assertTrue(mTarget.mForcePipModeChangedCallback);
+ } else {
+ // No animation start for replacing animation
+ assertTrue(!mTarget.mAnimationStarted);
+ }
mTarget.mAnimationStarted = true;
return this;
}
@@ -467,7 +477,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase {
mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING)
.expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
.update(0.25f)
- .restart(BOUNDS_FLOATING)
+ .restart(BOUNDS_FLOATING, false /* expectStartedAndPipModeChangedCallback */)
.end()
.expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
}
@@ -478,7 +488,8 @@ public class BoundsAnimationControllerTests extends WindowTestsBase {
mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING)
.expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
.update(0.25f)
- .restart(BOUNDS_SMALLER_FLOATING)
+ .restart(BOUNDS_SMALLER_FLOATING,
+ false /* expectStartedAndPipModeChangedCallback */)
.end()
.expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
}
@@ -486,10 +497,12 @@ public class BoundsAnimationControllerTests extends WindowTestsBase {
@UiThreadTest
@Test
public void testFullscreenToFloatingCancelFromAnimationToFullscreenBounds() throws Exception {
+ // When animating from fullscreen and the animation is interruped, we expect the animation
+ // start callback to be made, with a forced pip mode change callback
mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING)
.expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
.update(0.25f)
- .restart(BOUNDS_FULL)
+ .restart(BOUNDS_FULL, true /* expectStartedAndPipModeChangedCallback */)
.end()
.expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN);
}
@@ -512,7 +525,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase {
mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL)
.expectStarted(SCHEDULE_PIP_MODE_CHANGED)
.update(0.25f)
- .restart(BOUNDS_FULL)
+ .restart(BOUNDS_FULL, false /* expectStartedAndPipModeChangedCallback */)
.end()
.expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN);
}
@@ -523,7 +536,8 @@ public class BoundsAnimationControllerTests extends WindowTestsBase {
mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL)
.expectStarted(SCHEDULE_PIP_MODE_CHANGED)
.update(0.25f)
- .restart(BOUNDS_SMALLER_FLOATING)
+ .restart(BOUNDS_SMALLER_FLOATING,
+ false /* expectStartedAndPipModeChangedCallback */)
.end()
.expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/ConfigurationContainerTests.java b/services/tests/servicestests/src/com/android/server/wm/ConfigurationContainerTests.java
index 0f9b7217bd24..d441df024599 100644
--- a/services/tests/servicestests/src/com/android/server/wm/ConfigurationContainerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/ConfigurationContainerTests.java
@@ -16,24 +16,30 @@
package com.android.server.wm;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import android.content.res.Configuration;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
-import static org.junit.Assert.assertEquals;
-
/**
* Test class for {@link ConfigurationContainer}.
*
@@ -201,6 +207,62 @@ public class ConfigurationContainerTests {
assertEquals(mergedConfig2, child2.getConfiguration());
}
+ @Test
+ public void testSetWindowingMode() throws Exception {
+ final TestConfigurationContainer root = new TestConfigurationContainer();
+ root.setWindowingMode(WINDOWING_MODE_UNDEFINED);
+ final TestConfigurationContainer child = root.addChild();
+ child.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ assertEquals(WINDOWING_MODE_UNDEFINED, root.getWindowingMode());
+ assertEquals(WINDOWING_MODE_FREEFORM, child.getWindowingMode());
+
+ root.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ assertEquals(WINDOWING_MODE_FULLSCREEN, root.getWindowingMode());
+ assertEquals(WINDOWING_MODE_FREEFORM, child.getWindowingMode());
+ }
+
+ @Test
+ public void testSetActivityType() throws Exception {
+ final TestConfigurationContainer root = new TestConfigurationContainer();
+ root.setActivityType(ACTIVITY_TYPE_UNDEFINED);
+ final TestConfigurationContainer child = root.addChild();
+ child.setActivityType(ACTIVITY_TYPE_STANDARD);
+ assertEquals(ACTIVITY_TYPE_UNDEFINED, root.getActivityType());
+ assertEquals(ACTIVITY_TYPE_STANDARD, child.getActivityType());
+
+ boolean gotException = false;
+ try {
+ // Can't change activity type once set.
+ child.setActivityType(ACTIVITY_TYPE_HOME);
+ } catch (IllegalStateException e) {
+ gotException = true;
+ }
+ assertTrue("Can't change activity type once set.", gotException);
+
+ gotException = false;
+ try {
+ // Parent can't change child's activity type once set.
+ root.setActivityType(ACTIVITY_TYPE_HOME);
+ } catch (IllegalStateException e) {
+ gotException = true;
+ }
+ assertTrue("Parent can't change activity type once set.", gotException);
+ assertEquals(ACTIVITY_TYPE_HOME, root.getActivityType());
+
+ final TestConfigurationContainer child2 = new TestConfigurationContainer();
+ child2.setActivityType(ACTIVITY_TYPE_RECENTS);
+
+ gotException = false;
+ try {
+ // Can't re-parent to a different activity type.
+ root.addChild(child2);
+ } catch (IllegalStateException e) {
+ gotException = true;
+ }
+ assertTrue("Can't re-parent to a different activity type.", gotException);
+
+ }
+
/**
* Contains minimal implementation of {@link ConfigurationContainer}'s abstract behavior needed
* for testing.
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
index a1ff2d7f9267..3f75b412d100 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
@@ -27,8 +27,9 @@ import android.support.test.runner.AndroidJUnit4;
import android.view.DisplayInfo;
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS;
import static android.app.WindowConfiguration.WINDOW_CONFIG_WINDOWING_MODE;
import static android.content.pm.ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
@@ -118,6 +119,22 @@ public class WindowConfigurationTests extends WindowTestsBase {
assertEquals(blankWinConfig.compareTo(winConfig1), 1);
}
+ @Test
+ public void testSetActivityType() throws Exception {
+ final WindowConfiguration config = new WindowConfiguration();
+ config.setActivityType(ACTIVITY_TYPE_HOME);
+ assertEquals(ACTIVITY_TYPE_HOME, config.getActivityType());
+
+ boolean gotException = false;
+ try {
+ // Can't change activity type once set.
+ config.setActivityType(ACTIVITY_TYPE_STANDARD);
+ } catch (IllegalStateException e) {
+ gotException = true;
+ }
+ assertTrue("Can't change activity type once set.", gotException);
+ }
+
/** Ensures the configuration app bounds at the root level match the app dimensions. */
@Test
public void testAppBounds_RootConfigurationBounds() throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
index 6e253e743ddb..df3f7553e83c 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
@@ -75,7 +75,7 @@ public class WindowFrameTests extends WindowTestsBase {
final Rect mInsetBounds = new Rect();
boolean mFullscreenForTest = true;
TaskWithBounds(Rect bounds) {
- super(0, mStubStack, 0, sWm, null, null, 0, false, false, new TaskDescription(), null);
+ super(0, mStubStack, 0, sWm, null, null, 0, false, new TaskDescription(), null);
mBounds = bounds;
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
index 40c79bbb183d..ebe00ce8d51d 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
@@ -67,7 +67,7 @@ public class WindowTestUtils {
public static Task createTaskInStack(WindowManagerService service, TaskStack stack,
int userId) {
final Task newTask = new Task(sNextTaskId++, stack, userId, service, null, EMPTY, 0, false,
- false, new ActivityManager.TaskDescription(), null);
+ new ActivityManager.TaskDescription(), null);
stack.addTask(newTask, POSITION_TOP);
return newTask;
}
@@ -175,10 +175,9 @@ public class WindowTestUtils {
TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds,
Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture,
- boolean homeTask, TaskWindowContainerController controller) {
+ TaskWindowContainerController controller) {
super(taskId, stack, userId, service, bounds, overrideConfig, resizeMode,
- supportsPictureInPicture, homeTask, new ActivityManager.TaskDescription(),
- controller);
+ supportsPictureInPicture, new ActivityManager.TaskDescription(), controller);
}
boolean shouldDeferRemoval() {
@@ -229,7 +228,7 @@ public class WindowTestUtils {
}
}, stackController, 0 /* userId */, null /* bounds */,
EMPTY /* overrideConfig*/, RESIZE_MODE_UNRESIZEABLE,
- false /* supportsPictureInPicture */, false /* homeTask*/, true /* toTop*/,
+ false /* supportsPictureInPicture */, true /* toTop*/,
true /* showForAllUsers */, new ActivityManager.TaskDescription(),
stackController.mService);
}
@@ -237,9 +236,9 @@ public class WindowTestUtils {
@Override
TestTask createTask(int taskId, TaskStack stack, int userId, Rect bounds,
Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture,
- boolean homeTask, ActivityManager.TaskDescription taskDescription) {
+ ActivityManager.TaskDescription taskDescription) {
return new TestTask(taskId, stack, userId, mService, bounds, overrideConfig, resizeMode,
- supportsPictureInPicture, homeTask, this);
+ supportsPictureInPicture, this);
}
}
diff --git a/tools/aapt/SdkConstants.h b/tools/aapt/SdkConstants.h
index bf56ec024699..b982d0d2ea63 100644
--- a/tools/aapt/SdkConstants.h
+++ b/tools/aapt/SdkConstants.h
@@ -43,6 +43,7 @@ enum {
SDK_NOUGAT_MR1 = 25,
SDK_O = 26,
SDK_O_MR1 = 27,
+ SDK_P = 10000, // STOPSHIP Replace with the real version.
};
#endif // H_AAPT_SDK_CONSTANTS
diff --git a/tools/aapt2/SdkConstants.h b/tools/aapt2/SdkConstants.h
index 5c32ed4fd849..13584c0bf840 100644
--- a/tools/aapt2/SdkConstants.h
+++ b/tools/aapt2/SdkConstants.h
@@ -53,6 +53,7 @@ enum : ApiVersion {
SDK_NOUGAT_MR1 = 25,
SDK_O = 26,
SDK_O_MR1 = 27,
+ SDK_P = 10000, // STOPSHIP Replace with the real version.
};
ApiVersion FindAttributeSdkLevel(const ResourceId& id);
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index a0ffefad1e1c..6fb1793d90ed 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -293,6 +293,7 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor,
manifest_action["instrumentation"]["meta-data"] = meta_data_action;
manifest_action["original-package"];
+ manifest_action["overlay"];
manifest_action["protected-broadcast"];
manifest_action["uses-permission"];
manifest_action["uses-permission-sdk-23"];