summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp10
-rw-r--r--apex/Android.bp18
-rw-r--r--apex/jobscheduler/service/java/com/android/server/TEST_MAPPING1
-rw-r--r--apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING1
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING3
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING2
-rw-r--r--apex/media/framework/Android.bp6
-rw-r--r--apex/media/framework/java/android/media/MediaParser.java77
-rw-r--r--apex/sdkextensions/framework/Android.bp6
-rw-r--r--api/current.txt3
-rwxr-xr-xapi/system-current.txt4
-rw-r--r--api/test-current.txt3
-rw-r--r--core/java/android/app/ActivityThread.java27
-rw-r--r--core/java/android/app/INotificationManager.aidl8
-rw-r--r--core/java/android/app/LoadedApk.java5
-rw-r--r--core/java/android/app/NotificationManager.java4
-rw-r--r--core/java/android/app/WindowConfiguration.java14
-rw-r--r--core/java/android/bluetooth/BluetoothHearingAid.java10
-rw-r--r--core/java/android/content/Intent.java19
-rw-r--r--core/java/android/inputmethodservice/InlineSuggestionSession.java9
-rw-r--r--core/java/android/net/InvalidPacketException.java2
-rw-r--r--core/java/android/net/NetworkCapabilities.java31
-rw-r--r--core/java/android/os/incremental/V4Signature.java14
-rw-r--r--core/java/android/os/storage/IStorageManager.aidl1
-rw-r--r--core/java/android/os/storage/StorageManager.java30
-rw-r--r--core/java/android/service/voice/AlwaysOnHotwordDetector.java15
-rw-r--r--core/java/android/service/voice/VoiceInteractionService.java43
-rw-r--r--core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java15
-rw-r--r--core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java18
-rw-r--r--core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java8
-rw-r--r--core/java/android/util/apk/ApkSignatureVerifier.java40
-rw-r--r--core/java/android/util/apk/ApkSigningBlockUtils.java19
-rw-r--r--core/java/android/view/InsetsController.java30
-rw-r--r--core/java/android/view/InsetsSourceConsumer.java36
-rw-r--r--core/java/android/view/SurfaceView.java14
-rw-r--r--core/java/android/widget/Editor.java57
-rw-r--r--core/java/android/widget/Toast.java7
-rw-r--r--core/java/com/android/internal/widget/ResolverDrawerLayout.java2
-rw-r--r--core/proto/android/app/settings_enums.proto4
-rw-r--r--core/res/res/values/themes.xml2
-rw-r--r--core/tests/coretests/src/android/view/InsetsControllerTest.java27
-rw-r--r--core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java23
-rw-r--r--core/tests/coretests/src/android/widget/EditorCursorDragTest.java36
-rw-r--r--data/etc/hiddenapi-package-whitelist.xml3
-rw-r--r--media/java/android/media/AudioManager.java1
-rw-r--r--media/java/android/media/MediaCodec.java38
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java8
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java7
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java33
-rw-r--r--packages/SystemUI/res/layout/bubble_overflow_activity.xml1
-rw-r--r--packages/SystemUI/res/layout/bubble_overflow_view.xml41
-rw-r--r--packages/SystemUI/res/layout/keyguard_media_header.xml4
-rw-r--r--packages/SystemUI/res/layout/qs_media_panel.xml2
-rw-r--r--packages/SystemUI/res/values/dimens.xml4
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java13
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardMediaPlayer.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java72
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/CurrentUserObservable.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt117
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinator.java69
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java450
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java312
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java47
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinatorTest.java106
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java35
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java11
-rw-r--r--packages/Tethering/common/TetheringLib/Android.bp1
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerService.java249
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java137
-rw-r--r--services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java5
-rw-r--r--services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java26
-rw-r--r--services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java30
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java23
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java20
-rw-r--r--services/core/java/com/android/server/SystemServerInitThreadPool.java11
-rw-r--r--services/core/java/com/android/server/am/BugReportHandlerUtil.java32
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java1
-rw-r--r--services/core/java/com/android/server/location/GnssLocationProvider.java6
-rw-r--r--services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java124
-rw-r--r--services/core/java/com/android/server/notification/BubbleExtractor.java17
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java74
-rw-r--r--services/core/java/com/android/server/notification/ShortcutHelper.java14
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java134
-rw-r--r--services/core/java/com/android/server/policy/PermissionPolicyService.java7
-rw-r--r--services/core/java/com/android/server/power/TEST_MAPPING3
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java13
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java5
-rw-r--r--services/core/java/com/android/server/wm/ActivityStack.java140
-rw-r--r--services/core/java/com/android/server/wm/ActivityStackSupervisor.java20
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java20
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java5
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java73
-rw-r--r--services/core/java/com/android/server/wm/ConfigurationContainer.java5
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java323
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java42
-rw-r--r--services/core/java/com/android/server/wm/InsetsPolicy.java8
-rw-r--r--services/core/java/com/android/server/wm/KeyguardController.java7
-rw-r--r--services/core/java/com/android/server/wm/RecentTasks.java2
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimation.java5
-rw-r--r--services/core/java/com/android/server/wm/ResetTargetTaskHelper.java57
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java38
-rw-r--r--services/core/java/com/android/server/wm/Task.java137
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java86
-rw-r--r--services/core/java/com/android/server/wm/TaskTile.java227
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java82
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java4
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java38
-rw-r--r--services/tests/servicestests/AndroidManifest.xml241
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java6
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/BubbleCheckerTest.java8
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java28
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java15
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java71
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java19
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java8
-rw-r--r--startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java39
-rw-r--r--tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java52
-rw-r--r--tests/RollbackTest/RollbackTest.xml4
-rw-r--r--tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java14
-rw-r--r--tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java91
-rw-r--r--tests/net/common/java/android/net/NetworkCapabilitiesTest.java12
-rw-r--r--wifi/Android.bp9
151 files changed, 3222 insertions, 1991 deletions
diff --git a/Android.bp b/Android.bp
index 3f06ffd7ecd8..b0e0b35a1f76 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1133,16 +1133,6 @@ aidl_mapping {
output: "framework-aidl-mappings.txt",
}
-genrule {
- name: "framework-annotation-proc-index",
- srcs: [":framework-annotation-proc"],
- cmd: "unzip -qp $(in) unsupportedappusage/unsupportedappusage_index.csv > $(out)",
- out: ["unsupportedappusage_index.csv"],
- dist: {
- targets: ["droidcore"],
- },
-}
-
// Avoid including Parcelable classes as we don't want to have two copies of
// Parcelable cross the libraries. This is used by telephony-common (frameworks/opt/telephony)
// and TeleService app (packages/services/Telephony).
diff --git a/apex/Android.bp b/apex/Android.bp
index cd34f98690d5..e8f6e6bf2c46 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -84,6 +84,24 @@ stubs_defaults {
},
}
+java_defaults {
+ name: "framework-module-stubs-lib-defaults-publicapi",
+ installable: false,
+ sdk_version: "module_current",
+}
+
+java_defaults {
+ name: "framework-module-stubs-lib-defaults-systemapi",
+ installable: false,
+ sdk_version: "module_current",
+}
+
+java_defaults {
+ name: "framework-module-stubs-lib-defaults-module_libs_api",
+ installable: false,
+ sdk_version: "module_current",
+}
+
// The defaults for module_libs comes in two parts - defaults for API checks
// and defaults for stub generation. This is because we want the API txt
// files to *only* include the module_libs_api, but the stubs to include
diff --git a/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING
index 8fbfb1daaf6f..d99830dc47c9 100644
--- a/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING
@@ -7,6 +7,7 @@
],
"options": [
{"include-filter": "com.android.server.DeviceIdleControllerTest"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
}
diff --git a/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING
index bc7a7d3bef7d..b76c582cf287 100644
--- a/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING
@@ -4,6 +4,7 @@
"name": "FrameworksMockingServicesTests",
"options": [
{"include-filter": "com.android.server.DeviceIdleControllerTest"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
index e2e118074aac..484fec31e594 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
@@ -3,6 +3,7 @@
{
"name": "CtsJobSchedulerTestCases",
"options": [
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.LargeTest"}
]
@@ -11,6 +12,7 @@
"name": "FrameworksMockingServicesTests",
"options": [
{"include-filter": "com.android.server.job"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
},
@@ -18,6 +20,7 @@
"name": "FrameworksServicesTests",
"options": [
{"include-filter": "com.android.server.job"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
}
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
index ba7572a5fb44..c5dc51cc9c24 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
@@ -4,6 +4,7 @@
"name": "CtsUsageStatsTestCases",
"options": [
{"include-filter": "android.app.usage.cts.UsageStatsTest"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
},
@@ -11,6 +12,7 @@
"name": "FrameworksServicesTests",
"options": [
{"include-filter": "com.android.server.usage"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
}
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index 821dd9e46c19..99e82e7a3367 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -132,19 +132,19 @@ droidstubs {
java_library {
name: "framework-media-stubs-publicapi",
srcs: [":framework-media-stubs-srcs-publicapi"],
- sdk_version: "current",
+ defaults: ["framework-module-stubs-lib-defaults-publicapi"],
}
java_library {
name: "framework-media-stubs-systemapi",
srcs: [":framework-media-stubs-srcs-systemapi"],
- sdk_version: "system_current",
+ defaults: ["framework-module-stubs-lib-defaults-systemapi"],
}
java_library {
name: "framework-media-stubs-module_libs_api",
srcs: [":framework-media-stubs-srcs-module_libs_api"],
- sdk_version: "system_current",
+ defaults: ["framework-module-stubs-lib-defaults-module_libs_api"],
}
java_library {
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
index 02c55b7c8df9..5f86ed621084 100644
--- a/apex/media/framework/java/android/media/MediaParser.java
+++ b/apex/media/framework/java/android/media/MediaParser.java
@@ -52,6 +52,7 @@ import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.util.ParsableByteArray;
+import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.video.ColorInfo;
import java.io.EOFException;
@@ -60,6 +61,7 @@ import java.io.InterruptedIOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.ByteBuffer;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
@@ -688,12 +690,83 @@ public final class MediaParser {
* Returns an immutable list with the names of the parsers that are suitable for container
* formats with the given {@link MediaFormat}.
*
- * <p>TODO: List which properties are taken into account. E.g. MimeType.
+ * <p>A parser supports a {@link MediaFormat} if the mime type associated with {@link
+ * MediaFormat#KEY_MIME} corresponds to the supported container format.
+ *
+ * @param mediaFormat The {@link MediaFormat} to check support for.
+ * @return The parser names that support the given {@code mediaFormat}, or the list of all
+ * parsers available if no container specific format information is provided.
*/
@NonNull
@ParserName
public static List<String> getParserNames(@NonNull MediaFormat mediaFormat) {
- throw new UnsupportedOperationException();
+ String mimeType = mediaFormat.getString(MediaFormat.KEY_MIME);
+ mimeType = mimeType == null ? null : Util.toLowerInvariant(mimeType.trim());
+ if (TextUtils.isEmpty(mimeType)) {
+ // No MIME type provided. Return all.
+ return Collections.unmodifiableList(
+ new ArrayList<>(EXTRACTOR_FACTORIES_BY_NAME.keySet()));
+ }
+ ArrayList<String> result = new ArrayList<>();
+ switch (mimeType) {
+ case "video/x-matroska":
+ case "audio/x-matroska":
+ case "video/x-webm":
+ case "audio/x-webm":
+ result.add(PARSER_NAME_MATROSKA);
+ break;
+ case "video/mp4":
+ case "audio/mp4":
+ case "application/mp4":
+ result.add(PARSER_NAME_MP4);
+ result.add(PARSER_NAME_FMP4);
+ break;
+ case "audio/mpeg":
+ result.add(PARSER_NAME_MP3);
+ break;
+ case "audio/aac":
+ result.add(PARSER_NAME_ADTS);
+ break;
+ case "audio/ac3":
+ result.add(PARSER_NAME_AC3);
+ break;
+ case "video/mp2t":
+ case "audio/mp2t":
+ result.add(PARSER_NAME_TS);
+ break;
+ case "video/x-flv":
+ result.add(PARSER_NAME_FLV);
+ break;
+ case "video/ogg":
+ case "audio/ogg":
+ case "application/ogg":
+ result.add(PARSER_NAME_OGG);
+ break;
+ case "video/mp2p":
+ case "video/mp1s":
+ result.add(PARSER_NAME_PS);
+ break;
+ case "audio/vnd.wave":
+ case "audio/wav":
+ case "audio/wave":
+ case "audio/x-wav":
+ result.add(PARSER_NAME_WAV);
+ break;
+ case "audio/amr":
+ result.add(PARSER_NAME_AMR);
+ break;
+ case "audio/ac4":
+ result.add(PARSER_NAME_AC4);
+ break;
+ case "audio/flac":
+ case "audio/x-flac":
+ result.add(PARSER_NAME_FLAC);
+ break;
+ default:
+ // No parsers support the given mime type. Do nothing.
+ break;
+ }
+ return Collections.unmodifiableList(result);
}
// Private fields.
diff --git a/apex/sdkextensions/framework/Android.bp b/apex/sdkextensions/framework/Android.bp
index 86f4ab7c1128..707113b9672c 100644
--- a/apex/sdkextensions/framework/Android.bp
+++ b/apex/sdkextensions/framework/Android.bp
@@ -86,7 +86,7 @@ droidstubs {
java_library {
name: "framework-sdkextensions-stubs-publicapi",
srcs: [":framework-sdkextensions-stubs-srcs-publicapi"],
- sdk_version: "current",
+ defaults: ["framework-module-stubs-lib-defaults-publicapi"],
visibility: [
"//frameworks/base", // Framework
"//frameworks/base/apex/sdkextensions", // sdkextensions SDK
@@ -96,7 +96,7 @@ java_library {
java_library {
name: "framework-sdkextensions-stubs-systemapi",
srcs: [":framework-sdkextensions-stubs-srcs-systemapi"],
- sdk_version: "system_current",
+ defaults: ["framework-module-stubs-lib-defaults-systemapi"],
visibility: [
"//frameworks/base", // Framework
"//frameworks/base/apex/sdkextensions", // sdkextensions SDK
@@ -106,7 +106,7 @@ java_library {
java_library {
name: "framework-sdkextensions-stubs-module_libs_api",
srcs: [":framework-sdkextensions-stubs-srcs-module_libs_api"],
- sdk_version: "system_current",
+ defaults: ["framework-module-stubs-lib-defaults-module_libs_api"],
visibility: [
"//frameworks/base", // Framework
"//frameworks/base/apex/sdkextensions", // sdkextensions SDK
diff --git a/api/current.txt b/api/current.txt
index 680b2248249a..c7e2e6f9c399 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -25341,11 +25341,12 @@ package android.media {
public final class MediaCodec.QueueRequest {
method public void queue();
method @NonNull public android.media.MediaCodec.QueueRequest setByteBufferParameter(@NonNull String, @NonNull java.nio.ByteBuffer);
+ method @NonNull public android.media.MediaCodec.QueueRequest setEncryptedLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, int, @NonNull android.media.MediaCodec.CryptoInfo);
method @NonNull public android.media.MediaCodec.QueueRequest setFlags(int);
method @NonNull public android.media.MediaCodec.QueueRequest setFloatParameter(@NonNull String, float);
method @NonNull public android.media.MediaCodec.QueueRequest setHardwareBuffer(@NonNull android.hardware.HardwareBuffer);
method @NonNull public android.media.MediaCodec.QueueRequest setIntegerParameter(@NonNull String, int);
- method @NonNull public android.media.MediaCodec.QueueRequest setLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, int, @Nullable android.media.MediaCodec.CryptoInfo);
+ method @NonNull public android.media.MediaCodec.QueueRequest setLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, int);
method @NonNull public android.media.MediaCodec.QueueRequest setLongParameter(@NonNull String, long);
method @NonNull public android.media.MediaCodec.QueueRequest setPresentationTimeUs(long);
method @NonNull public android.media.MediaCodec.QueueRequest setStringParameter(@NonNull String, @NonNull String);
diff --git a/api/system-current.txt b/api/system-current.txt
index a1fd0dbd8df0..7e25382f06d9 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6061,7 +6061,7 @@ package android.net {
method public void release();
}
- public class InvalidPacketException extends java.lang.Exception {
+ public final class InvalidPacketException extends java.lang.Exception {
ctor public InvalidPacketException(int);
method public int getError();
field public static final int ERROR_INVALID_IP_ADDRESS = -21; // 0xffffffeb
@@ -6255,7 +6255,7 @@ package android.net {
field public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24; // 0x18
}
- public static class NetworkCapabilities.Builder {
+ public static final class NetworkCapabilities.Builder {
ctor public NetworkCapabilities.Builder();
ctor public NetworkCapabilities.Builder(@NonNull android.net.NetworkCapabilities);
method @NonNull public android.net.NetworkCapabilities.Builder addCapability(int);
diff --git a/api/test-current.txt b/api/test-current.txt
index 0bd8a195edff..ba6feedc65ba 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -535,6 +535,7 @@ package android.app {
method public void setWindowingMode(int);
method public void writeToParcel(android.os.Parcel, int);
field public static final int ACTIVITY_TYPE_ASSISTANT = 4; // 0x4
+ field public static final int ACTIVITY_TYPE_DREAM = 5; // 0x5
field public static final int ACTIVITY_TYPE_HOME = 2; // 0x2
field public static final int ACTIVITY_TYPE_RECENTS = 3; // 0x3
field public static final int ACTIVITY_TYPE_STANDARD = 1; // 0x1
@@ -1833,7 +1834,7 @@ package android.net {
field public static final int TRANSPORT_TEST = 7; // 0x7
}
- public static class NetworkCapabilities.Builder {
+ public static final class NetworkCapabilities.Builder {
ctor public NetworkCapabilities.Builder();
ctor public NetworkCapabilities.Builder(@NonNull android.net.NetworkCapabilities);
method @NonNull public android.net.NetworkCapabilities.Builder addCapability(int);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index bd3fee2d1fc7..21b56d3e337f 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4974,10 +4974,8 @@ public final class ActivityThread extends ClientTransactionHandler {
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason) {
ActivityClientRecord r = mActivities.get(token);
- Class<? extends Activity> activityClass = null;
if (localLOGV) Slog.v(TAG, "Performing finish of " + r);
if (r != null) {
- activityClass = r.activity.getClass();
r.activity.mConfigChangeFlags |= configChanges;
if (finishing) {
r.activity.mFinished = true;
@@ -5030,7 +5028,6 @@ public final class ActivityThread extends ClientTransactionHandler {
synchronized (mResourcesManager) {
mActivities.remove(token);
}
- StrictMode.decrementExpectedActivityCount(activityClass);
return r;
}
@@ -5050,6 +5047,7 @@ public final class ActivityThread extends ClientTransactionHandler {
ActivityClientRecord r = performDestroyActivity(token, finishing,
configChanges, getNonConfigInstance, reason);
if (r != null) {
+ Class<? extends Activity> activityClass = r.activity.getClass();
cleanUpPendingRemoveWindows(r, finishing);
WindowManager wm = r.activity.getWindowManager();
View v = r.activity.mDecor;
@@ -5074,14 +5072,14 @@ public final class ActivityThread extends ClientTransactionHandler {
}
if (wtoken != null && r.mPendingRemoveWindow == null) {
WindowManagerGlobal.getInstance().closeAll(wtoken,
- r.activity.getClass().getName(), "Activity");
+ activityClass.getName(), "Activity");
} else if (r.mPendingRemoveWindow != null) {
// We're preserving only one window, others should be closed so app views
// will be detached before the final tear down. It should be done now because
// some components (e.g. WebView) rely on detach callbacks to perform receiver
// unregister and other cleanup.
WindowManagerGlobal.getInstance().closeAllExceptView(token, v,
- r.activity.getClass().getName(), "Activity");
+ activityClass.getName(), "Activity");
}
r.activity.mDecor = null;
}
@@ -5093,18 +5091,23 @@ public final class ActivityThread extends ClientTransactionHandler {
// about leaking windows, because that is a bug, so if they are
// using this recreate facility then they get to live with leaks.
WindowManagerGlobal.getInstance().closeAll(token,
- r.activity.getClass().getName(), "Activity");
+ activityClass.getName(), "Activity");
}
// Mocked out contexts won't be participating in the normal
// process lifecycle, but if we're running with a proper
// ApplicationContext we need to have it tear down things
// cleanly.
- Context c = r.activity.getBaseContext();
- if (c instanceof ContextImpl) {
- ((ContextImpl) c).scheduleFinalCleanup(
- r.activity.getClass().getName(), "Activity");
+ final ContextImpl impl = ContextImpl.getImpl(r.activity);
+ if (impl != null) {
+ impl.scheduleFinalCleanup(activityClass.getName(), "Activity");
}
+
+ r.activity = null;
+ r.window = null;
+ r.hideForNow = false;
+ r.nextIdle = null;
+ StrictMode.decrementExpectedActivityCount(activityClass);
}
if (finishing) {
try {
@@ -5334,10 +5337,6 @@ public final class ActivityThread extends ClientTransactionHandler {
handleDestroyActivity(r.token, false, configChanges, true, reason);
- r.activity = null;
- r.window = null;
- r.hideForNow = false;
- r.nextIdle = null;
// Merge any pending results and pending intents; don't just replace them
if (pendingResults != null) {
if (r.pendingResults == null) {
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 948546b18473..78d3581a7012 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -48,8 +48,6 @@ interface INotificationManager
void clearData(String pkg, int uid, boolean fromApp);
void enqueueTextToast(String pkg, IBinder token, CharSequence text, int duration, int displayId, @nullable ITransientNotificationCallback callback);
void enqueueToast(String pkg, IBinder token, ITransientNotification callback, int duration, int displayId);
- // TODO(b/144152069): Remove this after assessing impact on dogfood.
- void enqueueTextOrCustomToast(String pkg, IBinder token, ITransientNotification callback, int duration, int displayId, boolean isCustom);
void cancelToast(String pkg, IBinder token);
void finishToken(String pkg, IBinder token);
@@ -123,10 +121,14 @@ interface INotificationManager
// INotificationListener method.
@UnsupportedAppUsage
StatusBarNotification[] getActiveNotifications(String callingPkg);
+ StatusBarNotification[] getActiveNotificationsWithAttribution(String callingPkg,
+ String callingAttributionTag);
@UnsupportedAppUsage
StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count, boolean includeSnoozed);
+ StatusBarNotification[] getHistoricalNotificationsWithAttribution(String callingPkg,
+ String callingAttributionTag, int count, boolean includeSnoozed);
- NotificationHistory getNotificationHistory(String callingPkg);
+ NotificationHistory getNotificationHistory(String callingPkg, String callingAttributionTag);
void registerListener(in INotificationListener listener, in ComponentName component, int userid);
void unregisterListener(in INotificationListener listener, int userid);
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index d650801f39ea..10f7835b3d69 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -801,6 +801,11 @@ public final class LoadedApk {
makePaths(mActivityThread, isBundledApp, mApplicationInfo, zipPaths, libPaths);
String libraryPermittedPath = mDataDir;
+ if (mActivityThread == null) {
+ // In a zygote context where mActivityThread is null we can't access the app data dir
+ // and including this in libraryPermittedPath would cause SELinux denials.
+ libraryPermittedPath = "";
+ }
if (isBundledApp) {
// For bundled apps, add the base directory of the app (e.g.,
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 811b9c082be2..0e97e3fe06ce 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -29,6 +29,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ParceledListSlice;
+import android.content.pm.ShortcutInfo;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Build;
@@ -841,7 +842,8 @@ public class NotificationManager {
}
/**
- * Returns the notification channel settings for a given channel and conversation id.
+ * Returns the notification channel settings for a given channel and
+ * {@link ShortcutInfo#getId() conversation id}.
*
* <p>The channel must belong to your package, or to a package you are an approved notification
* delegate for (see {@link #canNotifyAsPackage(String)}), or it will not be returned. To query
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 6b40890e17c7..37e07de9809a 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -141,6 +141,8 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
public static final int ACTIVITY_TYPE_RECENTS = 3;
/** Assistant activity type. */
public static final int ACTIVITY_TYPE_ASSISTANT = 4;
+ /** Dream activity type. */
+ public static final int ACTIVITY_TYPE_DREAM = 5;
/** @hide */
@IntDef(prefix = { "ACTIVITY_TYPE_" }, value = {
@@ -149,6 +151,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
ACTIVITY_TYPE_HOME,
ACTIVITY_TYPE_RECENTS,
ACTIVITY_TYPE_ASSISTANT,
+ ACTIVITY_TYPE_DREAM,
})
public @interface ActivityType {}
@@ -746,9 +749,11 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
* @hide
*/
public boolean isAlwaysOnTop() {
- return mWindowingMode == WINDOWING_MODE_PINNED || (mAlwaysOnTop == ALWAYS_ON_TOP_ON
- && (mWindowingMode == WINDOWING_MODE_FREEFORM
- || mWindowingMode == WINDOWING_MODE_MULTI_WINDOW));
+ if (mWindowingMode == WINDOWING_MODE_PINNED) return true;
+ if (mActivityType == ACTIVITY_TYPE_DREAM) return true;
+ if (mAlwaysOnTop != ALWAYS_ON_TOP_ON) return false;
+ return mWindowingMode == WINDOWING_MODE_FREEFORM
+ || mWindowingMode == WINDOWING_MODE_MULTI_WINDOW;
}
/**
@@ -798,7 +803,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
/** @hide */
public static boolean supportSplitScreenWindowingMode(int activityType) {
- return activityType != ACTIVITY_TYPE_ASSISTANT;
+ return activityType != ACTIVITY_TYPE_ASSISTANT && activityType != ACTIVITY_TYPE_DREAM;
}
/** @hide */
@@ -823,6 +828,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
case ACTIVITY_TYPE_HOME: return "home";
case ACTIVITY_TYPE_RECENTS: return "recents";
case ACTIVITY_TYPE_ASSISTANT: return "assistant";
+ case ACTIVITY_TYPE_DREAM: return "dream";
}
return String.valueOf(applicationType);
}
diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java
index e0674d75974a..fa62a02499e0 100644
--- a/core/java/android/bluetooth/BluetoothHearingAid.java
+++ b/core/java/android/bluetooth/BluetoothHearingAid.java
@@ -379,6 +379,7 @@ public final class BluetoothHearingAid implements BluetoothProfile {
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
+ verifyDeviceNotNull(device, "setConnectionPolicy");
final IBluetoothHearingAid service = getService();
try {
if (service != null && isEnabled()
@@ -428,6 +429,7 @@ public final class BluetoothHearingAid implements BluetoothProfile {
@RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
+ verifyDeviceNotNull(device, "getConnectionPolicy");
final IBluetoothHearingAid service = getService();
try {
if (service != null && isEnabled()
@@ -504,6 +506,7 @@ public final class BluetoothHearingAid implements BluetoothProfile {
if (VDBG) {
log("getHiSyncId(" + device + ")");
}
+ verifyDeviceNotNull(device, "getConnectionPolicy");
final IBluetoothHearingAid service = getService();
try {
if (service == null) {
@@ -577,6 +580,13 @@ public final class BluetoothHearingAid implements BluetoothProfile {
return false;
}
+ private void verifyDeviceNotNull(BluetoothDevice device, String methodName) {
+ if (device == null) {
+ Log.e(TAG, methodName + ": device param is null");
+ throw new IllegalArgumentException("Device cannot be null");
+ }
+ }
+
private boolean isValidDevice(BluetoothDevice device) {
if (device == null) return false;
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 95385ee9b79c..b1d6c830d3b7 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -11215,6 +11215,17 @@ public class Intent implements Parcelable, Cloneable {
&& hasWebURI();
}
+ private boolean isImageCaptureIntent() {
+ return (MediaStore.ACTION_IMAGE_CAPTURE.equals(mAction)
+ || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(mAction)
+ || MediaStore.ACTION_VIDEO_CAPTURE.equals(mAction));
+ }
+
+ /** @hide */
+ public boolean isImplicitImageCaptureIntent() {
+ return mPackage == null && mComponent == null && isImageCaptureIntent();
+ }
+
/**
* @hide
*/
@@ -11241,9 +11252,7 @@ public class Intent implements Parcelable, Cloneable {
}
putParcelableArrayListExtra(EXTRA_STREAM, newStreams);
}
- } else if (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
- || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(action)
- || MediaStore.ACTION_VIDEO_CAPTURE.equals(action)) {
+ } else if (isImageCaptureIntent()) {
final Uri output = getParcelableExtra(MediaStore.EXTRA_OUTPUT);
if (output != null) {
putExtra(MediaStore.EXTRA_OUTPUT, maybeAddUserId(output, contentUserHint));
@@ -11349,9 +11358,7 @@ public class Intent implements Parcelable, Cloneable {
}
} catch (ClassCastException e) {
}
- } else if (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
- || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(action)
- || MediaStore.ACTION_VIDEO_CAPTURE.equals(action)) {
+ } else if (isImageCaptureIntent()) {
final Uri output;
try {
output = getParcelableExtra(MediaStore.EXTRA_OUTPUT);
diff --git a/core/java/android/inputmethodservice/InlineSuggestionSession.java b/core/java/android/inputmethodservice/InlineSuggestionSession.java
index c31cb4e61b6e..9b3e8c9c137d 100644
--- a/core/java/android/inputmethodservice/InlineSuggestionSession.java
+++ b/core/java/android/inputmethodservice/InlineSuggestionSession.java
@@ -166,9 +166,14 @@ class InlineSuggestionSession {
}
return;
}
-
+ // The IME doesn't have information about the virtual view id for the child views in the
+ // web view, so we are only comparing the parent view id here. This means that for cases
+ // where there are two input fields in the web view, they will have the same view id
+ // (although different virtual child id), and we will not be able to distinguish them.
+ final AutofillId imeClientFieldId = mClientAutofillIdSupplier.get();
if (!mComponentName.getPackageName().equals(mClientPackageNameSupplier.get())
- || !fieldId.equalsIgnoreSession(mClientAutofillIdSupplier.get())) {
+ || imeClientFieldId == null
+ || fieldId.getViewId() != imeClientFieldId.getViewId()) {
if (DEBUG) {
Log.d(TAG,
"handleOnInlineSuggestionsResponse() called on the wrong package/field "
diff --git a/core/java/android/net/InvalidPacketException.java b/core/java/android/net/InvalidPacketException.java
index b3b0f11a776b..1873d778c0f2 100644
--- a/core/java/android/net/InvalidPacketException.java
+++ b/core/java/android/net/InvalidPacketException.java
@@ -27,7 +27,7 @@ import java.lang.annotation.RetentionPolicy;
* @hide
*/
@SystemApi
-public class InvalidPacketException extends Exception {
+public final class InvalidPacketException extends Exception {
private final int mError;
// Must match SocketKeepalive#ERROR_INVALID_IP_ADDRESS.
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index fcfcebdf0862..af9414c56654 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -415,6 +415,20 @@ public final class NetworkCapabilities implements Parcelable {
| (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY);
/**
+ * Capabilities that are allowed for test networks. This list must be set so that it is safe
+ * for an unprivileged user to create a network with these capabilities via shell. As such,
+ * it must never contain capabilities that are generally useful to the system, such as
+ * INTERNET, IMS, SUPL, etc.
+ */
+ private static final long TEST_NETWORKS_ALLOWED_CAPABILITIES =
+ (1 << NET_CAPABILITY_NOT_METERED)
+ | (1 << NET_CAPABILITY_NOT_RESTRICTED)
+ | (1 << NET_CAPABILITY_NOT_VPN)
+ | (1 << NET_CAPABILITY_NOT_ROAMING)
+ | (1 << NET_CAPABILITY_NOT_CONGESTED)
+ | (1 << NET_CAPABILITY_NOT_SUSPENDED);
+
+ /**
* Adds the given capability to this {@code NetworkCapability} instance.
* Note that when searching for a network to satisfy a request, all capabilities
* requested must be satisfied.
@@ -646,6 +660,21 @@ public final class NetworkCapabilities implements Parcelable {
}
/**
+ * Test networks have strong restrictions on what capabilities they can have. Enforce these
+ * restrictions.
+ * @hide
+ */
+ public void restrictCapabilitesForTestNetwork() {
+ final long originalCapabilities = mNetworkCapabilities;
+ final NetworkSpecifier originalSpecifier = mNetworkSpecifier;
+ clearAll();
+ // Reset the transports to only contain TRANSPORT_TEST.
+ mTransportTypes = (1 << TRANSPORT_TEST);
+ mNetworkCapabilities = originalCapabilities & TEST_NETWORKS_ALLOWED_CAPABILITIES;
+ mNetworkSpecifier = originalSpecifier;
+ }
+
+ /**
* Representing the transport type. Apps should generally not care about transport. A
* request for a fast internet connection could be satisfied by a number of different
* transports. If any are specified here it will be satisfied a Network that matches
@@ -2000,7 +2029,7 @@ public final class NetworkCapabilities implements Parcelable {
*/
@SystemApi
@TestApi
- public static class Builder {
+ public static final class Builder {
private final NetworkCapabilities mCaps;
/**
diff --git a/core/java/android/os/incremental/V4Signature.java b/core/java/android/os/incremental/V4Signature.java
index 71f931da1a92..5cc73caa4f60 100644
--- a/core/java/android/os/incremental/V4Signature.java
+++ b/core/java/android/os/incremental/V4Signature.java
@@ -72,16 +72,16 @@ public class V4Signature {
* V4 signature data.
*/
public static class SigningInfo {
- public final byte[] v3Digest; // used to match with the corresponding APK
+ public final byte[] apkDigest; // used to match with the corresponding APK
public final byte[] certificate; // ASN.1 DER form
public final byte[] additionalData; // a free-form binary data blob
public final byte[] publicKey; // ASN.1 DER, must match the certificate
public final int signatureAlgorithmId; // see the APK v2 doc for the list
public final byte[] signature;
- SigningInfo(byte[] v3Digest, byte[] certificate, byte[] additionalData,
+ SigningInfo(byte[] apkDigest, byte[] certificate, byte[] additionalData,
byte[] publicKey, int signatureAlgorithmId, byte[] signature) {
- this.v3Digest = v3Digest;
+ this.apkDigest = apkDigest;
this.certificate = certificate;
this.additionalData = additionalData;
this.publicKey = publicKey;
@@ -94,13 +94,13 @@ public class V4Signature {
*/
public static SigningInfo fromByteArray(byte[] bytes) throws IOException {
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
- byte[] v3Digest = readBytes(buffer);
+ byte[] apkDigest = readBytes(buffer);
byte[] certificate = readBytes(buffer);
byte[] additionalData = readBytes(buffer);
byte[] publicKey = readBytes(buffer);
int signatureAlgorithmId = buffer.getInt();
byte[] signature = readBytes(buffer);
- return new SigningInfo(v3Digest, certificate, additionalData, publicKey,
+ return new SigningInfo(apkDigest, certificate, additionalData, publicKey,
signatureAlgorithmId, signature);
}
}
@@ -150,7 +150,7 @@ public class V4Signature {
final int size =
4/*size*/ + 8/*fileSize*/ + 4/*hash_algorithm*/ + 1/*log2_blocksize*/ + bytesSize(
hashingInfo.salt) + bytesSize(hashingInfo.rawRootHash) + bytesSize(
- signingInfo.v3Digest) + bytesSize(signingInfo.certificate) + bytesSize(
+ signingInfo.apkDigest) + bytesSize(signingInfo.certificate) + bytesSize(
signingInfo.additionalData);
ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN);
buffer.putInt(size);
@@ -159,7 +159,7 @@ public class V4Signature {
buffer.put(hashingInfo.log2BlockSize);
writeBytes(buffer, hashingInfo.salt);
writeBytes(buffer, hashingInfo.rawRootHash);
- writeBytes(buffer, signingInfo.v3Digest);
+ writeBytes(buffer, signingInfo.apkDigest);
writeBytes(buffer, signingInfo.certificate);
writeBytes(buffer, signingInfo.additionalData);
return buffer.array();
diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl
index bbc936d76e1c..99bdfd1fc103 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -194,4 +194,5 @@ interface IStorageManager {
boolean needsCheckpoint() = 86;
void abortChanges(in String message, boolean retry) = 87;
void clearUserKeyAuth(int userId, int serialNumber, in byte[] token, in byte[] secret) = 88;
+ void fixupAppDir(in String path) = 89;
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 1454aac66d21..a68cc3dfe0e4 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -2470,6 +2470,36 @@ public class StorageManager {
}
}
+ /**
+ * Asks StorageManager to fixup the permissions of an application-private directory.
+ *
+ * On devices without sdcardfs, filesystem permissions aren't magically fixed up. This
+ * is problematic mostly in application-private directories, which are owned by the
+ * application itself; if another process with elevated permissions creates a file
+ * in these directories, the UID will be wrong, and the owning package won't be able
+ * to access the files.
+ *
+ * This API can be used to recursively fix up the permissions on the passed in path.
+ * The default platform user of this API is the DownloadProvider, which can download
+ * things in application-private directories on their behalf.
+ *
+ * This API doesn't require any special permissions, because it merely changes the
+ * permissions of a directory to what they should anyway be.
+ *
+ * @param path the path for which we should fix up the permissions
+ *
+ * @hide
+ */
+ public void fixupAppDir(@NonNull File path) {
+ try {
+ mStorageManager.fixupAppDir(path.getCanonicalPath());
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to get canonical path for " + path.getPath(), e);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/** {@hide} */
private static void setCacheBehavior(File path, String name, boolean enabled)
throws IOException {
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 7d070b1d9df6..f8265d6366ab 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -780,15 +780,16 @@ public class AlwaysOnHotwordDetector {
audioCapabilities |= AUDIO_CAPABILITY_NOISE_SUPPRESSION;
}
- int code = STATUS_ERROR;
+ int code;
try {
code = mModelManagementService.startRecognition(
mKeyphraseMetadata.id, mLocale.toLanguageTag(), mInternalCallback,
new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
recognitionExtra, null /* additional data */, audioCapabilities));
} catch (RemoteException e) {
- Slog.w(TAG, "RemoteException in startRecognition!", e);
+ throw e.rethrowFromSystemServer();
}
+
if (code != STATUS_OK) {
Slog.w(TAG, "startRecognition() failed with error code " + code);
}
@@ -796,12 +797,12 @@ public class AlwaysOnHotwordDetector {
}
private int stopRecognitionLocked() {
- int code = STATUS_ERROR;
+ int code;
try {
code = mModelManagementService.stopRecognition(mKeyphraseMetadata.id,
mInternalCallback);
} catch (RemoteException e) {
- Slog.w(TAG, "RemoteException in stopRecognition!", e);
+ throw e.rethrowFromSystemServer();
}
if (code != STATUS_OK) {
@@ -968,12 +969,12 @@ public class AlwaysOnHotwordDetector {
}
}
- ModuleProperties dspModuleProperties = null;
+ ModuleProperties dspModuleProperties;
try {
dspModuleProperties =
mModelManagementService.getDspModuleProperties();
} catch (RemoteException e) {
- Slog.w(TAG, "RemoteException in getDspProperties!", e);
+ throw e.rethrowFromSystemServer();
}
// No DSP available
@@ -989,7 +990,7 @@ public class AlwaysOnHotwordDetector {
mKeyphraseMetadata = mModelManagementService.getEnrolledKeyphraseMetadata(
mText, mLocale.toLanguageTag());
} catch (RemoteException e) {
- Slog.w(TAG, "RemoteException in internalUpdateEnrolledKeyphraseMetadata", e);
+ throw e.rethrowFromSystemServer();
}
}
}
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index b54e4d9b876d..45d3465fdae8 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -35,6 +35,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
import android.util.ArraySet;
+import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IVoiceActionCheckCallback;
@@ -47,6 +48,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
+import java.util.Objects;
import java.util.Set;
/**
@@ -63,6 +65,8 @@ import java.util.Set;
* separate process from this one.
*/
public class VoiceInteractionService extends Service {
+ static final String TAG = VoiceInteractionService.class.getSimpleName();
+
/**
* The {@link Intent} that must be declared as handled by the service.
* To be supported, the service must also require the
@@ -240,9 +244,22 @@ public class VoiceInteractionService extends Service {
public void onReady() {
mSystemService = IVoiceInteractionManagerService.Stub.asInterface(
ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
+ Objects.requireNonNull(mSystemService);
+ try {
+ mSystemService.asBinder().linkToDeath(mDeathRecipient, 0);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "unable to link to death with system service");
+ }
mKeyphraseEnrollmentInfo = new KeyphraseEnrollmentInfo(getPackageManager());
}
+ private IBinder.DeathRecipient mDeathRecipient = () -> {
+ Log.e(TAG, "system service binder died shutting down");
+ Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
+ VoiceInteractionService::onShutdownInternal, VoiceInteractionService.this));
+ };
+
+
private void onShutdownInternal() {
onShutdown();
// Stop any active recognitions when shutting down.
@@ -349,16 +366,24 @@ public class VoiceInteractionService extends Service {
}
private void safelyShutdownHotwordDetector() {
- try {
- synchronized (mLock) {
- if (mHotwordDetector != null) {
- mHotwordDetector.stopRecognition();
- mHotwordDetector.invalidate();
- mHotwordDetector = null;
- }
+ synchronized (mLock) {
+ if (mHotwordDetector == null) {
+ return;
+ }
+
+ try {
+ mHotwordDetector.stopRecognition();
+ } catch (Exception ex) {
+ // Ignore.
}
- } catch (Exception ex) {
- // Ignore.
+
+ try {
+ mHotwordDetector.invalidate();
+ } catch (Exception ex) {
+ // Ignore.
+ }
+
+ mHotwordDetector = null;
}
}
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
index 04be71f2babc..346fe293d7ae 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -24,6 +24,7 @@ import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmContent
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm;
+import static android.util.apk.ApkSigningBlockUtils.pickBestDigestForV4;
import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray;
import android.util.ArrayMap;
@@ -117,7 +118,10 @@ public class ApkSignatureSchemeV2Verifier {
return vSigner.certs;
}
- private static VerifiedSigner verify(String apkFile, boolean verifyIntegrity)
+ /**
+ * Same as above returns the full signer object, containing additional info e.g. digest.
+ */
+ public static VerifiedSigner verify(String apkFile, boolean verifyIntegrity)
throws SignatureNotFoundException, SecurityException, IOException {
try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) {
return verify(apk, verifyIntegrity);
@@ -209,9 +213,11 @@ public class ApkSignatureSchemeV2Verifier {
verityDigest, apk.length(), signatureInfo);
}
+ byte[] digest = pickBestDigestForV4(contentDigests);
+
return new VerifiedSigner(
signerCerts.toArray(new X509Certificate[signerCerts.size()][]),
- verityRootHash);
+ verityRootHash, digest);
}
private static X509Certificate[] verifySigner(
@@ -426,11 +432,14 @@ public class ApkSignatureSchemeV2Verifier {
*/
public static class VerifiedSigner {
public final X509Certificate[][] certs;
+
public final byte[] verityRootHash;
+ public final byte[] digest;
- public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash) {
+ public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash, byte[] digest) {
this.certs = certs;
this.verityRootHash = verityRootHash;
+ this.digest = digest;
}
}
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
index 2437af26770b..4ab541b616ed 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
@@ -16,8 +16,6 @@
package android.util.apk;
-import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256;
-import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512;
import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
import static android.util.apk.ApkSigningBlockUtils.compareSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.getContentDigestAlgorithmJcaDigestAlgorithm;
@@ -26,6 +24,7 @@ import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmContent
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm;
+import static android.util.apk.ApkSigningBlockUtils.pickBestDigestForV4;
import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray;
import android.os.Build;
@@ -213,24 +212,11 @@ public class ApkSignatureSchemeV3Verifier {
verityDigest, apk.length(), signatureInfo);
}
- result.digest = pickBestV3DigestForV4(contentDigests);
+ result.digest = pickBestDigestForV4(contentDigests);
return result;
}
- // Keep in sync with pickBestV3DigestForV4 in apksigner.V3SchemeVerifier.
- private static byte[] pickBestV3DigestForV4(Map<Integer, byte[]> contentDigests) {
- final int[] orderedContentDigestTypes =
- {CONTENT_DIGEST_CHUNKED_SHA512, CONTENT_DIGEST_VERITY_CHUNKED_SHA256,
- CONTENT_DIGEST_CHUNKED_SHA256};
- for (int contentDigestType : orderedContentDigestTypes) {
- if (contentDigests.containsKey(contentDigestType)) {
- return contentDigests.get(contentDigestType);
- }
- }
- return null;
- }
-
private static VerifiedSigner verifySigner(
ByteBuffer signerBlock,
Map<Integer, byte[]> contentDigests,
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
index 8c240d99f590..d40efce0b3b3 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
@@ -145,7 +145,7 @@ public class ApkSignatureSchemeV4Verifier {
"Public key mismatch between certificate and signature record");
}
- return new VerifiedSigner(new Certificate[]{certificate}, signingInfo.v3Digest);
+ return new VerifiedSigner(new Certificate[]{certificate}, signingInfo.apkDigest);
}
/**
@@ -155,11 +155,11 @@ public class ApkSignatureSchemeV4Verifier {
*/
public static class VerifiedSigner {
public final Certificate[] certs;
- public byte[] v3Digest;
+ public byte[] apkDigest;
- public VerifiedSigner(Certificate[] certs, byte[] v3Digest) {
+ public VerifiedSigner(Certificate[] certs, byte[] apkDigest) {
this.certs = certs;
- this.v3Digest = v3Digest;
+ this.apkDigest = apkDigest;
}
}
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index c1cee48cc663..ab8f80d3d1a5 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -184,27 +184,45 @@ public class ApkSignatureVerifier {
Signature[] signerSigs = convertToSignatures(signerCerts);
if (verifyFull) {
- // v4 is an add-on and requires v3 signature to validate against its certificates
- ApkSignatureSchemeV3Verifier.VerifiedSigner nonstreaming =
- ApkSignatureSchemeV3Verifier.unsafeGetCertsWithoutVerification(apkPath);
- Certificate[][] nonstreamingCerts = new Certificate[][]{nonstreaming.certs};
- Signature[] nonstreamingSigs = convertToSignatures(nonstreamingCerts);
+ byte[] nonstreamingDigest = null;
+ Certificate[][] nonstreamingCerts = null;
+
+ try {
+ // v4 is an add-on and requires v2 or v3 signature to validate against its
+ // certificate and digest
+ ApkSignatureSchemeV3Verifier.VerifiedSigner v3Signer =
+ ApkSignatureSchemeV3Verifier.unsafeGetCertsWithoutVerification(apkPath);
+ nonstreamingDigest = v3Signer.digest;
+ nonstreamingCerts = new Certificate[][]{v3Signer.certs};
+ } catch (SignatureNotFoundException e) {
+ try {
+ ApkSignatureSchemeV2Verifier.VerifiedSigner v2Signer =
+ ApkSignatureSchemeV2Verifier.verify(apkPath, false);
+ nonstreamingDigest = v2Signer.digest;
+ nonstreamingCerts = v2Signer.certs;
+ } catch (SignatureNotFoundException ee) {
+ throw new SecurityException(
+ "V4 verification failed to collect V2/V3 certificates from : "
+ + apkPath, ee);
+ }
+ }
+ Signature[] nonstreamingSigs = convertToSignatures(nonstreamingCerts);
if (nonstreamingSigs.length != signerSigs.length) {
throw new SecurityException(
- "Invalid number of certificates: " + nonstreaming.certs.length);
+ "Invalid number of certificates: " + nonstreamingSigs.length);
}
for (int i = 0, size = signerSigs.length; i < size; ++i) {
if (!nonstreamingSigs[i].equals(signerSigs[i])) {
- throw new SecurityException("V4 signature certificate does not match V3");
+ throw new SecurityException(
+ "V4 signature certificate does not match V2/V3");
}
}
- // TODO(b/151240006): add support for v2 digest and make it mandatory.
- if (!ArrayUtils.isEmpty(vSigner.v3Digest) && !ArrayUtils.equals(vSigner.v3Digest,
- nonstreaming.digest, vSigner.v3Digest.length)) {
- throw new SecurityException("V3 digest in V4 signature does not match V3");
+ if (!ArrayUtils.equals(vSigner.apkDigest, nonstreamingDigest,
+ vSigner.apkDigest.length)) {
+ throw new SecurityException("APK digest in V4 signature does not match V2/V3");
}
}
diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java
index 4fe8515143a0..2a4b65d23e64 100644
--- a/core/java/android/util/apk/ApkSigningBlockUtils.java
+++ b/core/java/android/util/apk/ApkSigningBlockUtils.java
@@ -421,6 +421,10 @@ final class ApkSigningBlockUtils {
static final int CONTENT_DIGEST_CHUNKED_SHA512 = 2;
static final int CONTENT_DIGEST_VERITY_CHUNKED_SHA256 = 3;
+ private static final int[] V4_CONTENT_DIGEST_ALGORITHMS =
+ {CONTENT_DIGEST_CHUNKED_SHA512, CONTENT_DIGEST_VERITY_CHUNKED_SHA256,
+ CONTENT_DIGEST_CHUNKED_SHA256};
+
static int compareSignatureAlgorithm(int sigAlgorithm1, int sigAlgorithm2) {
int digestAlgorithm1 = getSignatureAlgorithmContentDigestAlgorithm(sigAlgorithm1);
int digestAlgorithm2 = getSignatureAlgorithmContentDigestAlgorithm(sigAlgorithm2);
@@ -572,6 +576,21 @@ final class ApkSigningBlockUtils {
}
/**
+ * Returns the best digest from the map of available digests.
+ * similarly to compareContentDigestAlgorithm.
+ *
+ * Keep in sync with pickBestDigestForV4 in apksigner's ApkSigningBlockUtils.
+ */
+ static byte[] pickBestDigestForV4(Map<Integer, byte[]> contentDigests) {
+ for (int algo : V4_CONTENT_DIGEST_ALGORITHMS) {
+ if (contentDigests.containsKey(algo)) {
+ return contentDigests.get(algo);
+ }
+ }
+ return null;
+ }
+
+ /**
* Returns new byte buffer whose content is a shared subsequence of this buffer's content
* between the specified start (inclusive) and end (exclusive) positions. As opposed to
* {@link ByteBuffer#slice()}, the returned buffer's byte order is the same as the source
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 43f80f1490ad..e6bd84391fef 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -17,6 +17,7 @@
package android.view;
import static android.view.InsetsState.ITYPE_IME;
+import static android.view.InsetsState.toInternalType;
import static android.view.InsetsState.toPublicType;
import static android.view.WindowInsets.Type.all;
import static android.view.WindowInsets.Type.ime;
@@ -26,8 +27,6 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_BEHAVIOR_CONT
import android.animation.AnimationHandler;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
import android.animation.TypeEvaluator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
@@ -38,11 +37,9 @@ import android.graphics.Rect;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.RemoteException;
-import android.renderscript.Sampler.Value;
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
-import android.util.Property;
import android.util.SparseArray;
import android.view.InsetsSourceConsumer.ShowResult;
import android.view.InsetsState.InternalInsetsType;
@@ -467,7 +464,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
if (!localStateChanged && mLastDispachedState.equals(state)) {
return false;
}
- mState.set(state);
+ updateState(state);
mLastDispachedState.set(state, true /* copySources */);
applyLocalVisibilityOverride();
if (localStateChanged) {
@@ -479,6 +476,20 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
return true;
}
+ private void updateState(InsetsState newState) {
+ mState.setDisplayFrame(newState.getDisplayFrame());
+ for (int i = newState.getSourcesCount() - 1; i >= 0; i--) {
+ InsetsSource source = newState.sourceAt(i);
+ getSourceConsumer(source.getType()).updateSource(source);
+ }
+ for (int i = mState.getSourcesCount() - 1; i >= 0; i--) {
+ InsetsSource source = mState.sourceAt(i);
+ if (newState.peekSource(source.getType()) == null) {
+ mState.removeSource(source.getType());
+ }
+ }
+ }
+
/**
* @see InsetsState#calculateInsets
*/
@@ -858,8 +869,15 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
control.cancel();
}
for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
- if (mRunningAnimations.get(i).runner == control) {
+ RunningAnimation runningAnimation = mRunningAnimations.get(i);
+ if (runningAnimation.runner == control) {
mRunningAnimations.remove(i);
+ ArraySet<Integer> types = toInternalType(control.getTypes());
+ for (int j = types.size() - 1; j >= 0; j--) {
+ if (getSourceConsumer(types.valueAt(j)).notifyAnimationFinished()) {
+ mViewRoot.notifyInsetsChanged();
+ }
+ }
break;
}
}
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 332573449e18..f36621c0a49e 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -16,11 +16,13 @@
package android.view;
+import static android.view.InsetsController.ANIMATION_TYPE_NONE;
import static android.view.InsetsController.AnimationType;
import static android.view.InsetsState.toPublicType;
import android.annotation.IntDef;
import android.annotation.Nullable;
+import android.graphics.Rect;
import android.view.InsetsState.InternalInsetsType;
import android.view.SurfaceControl.Transaction;
import android.view.WindowInsets.Type.InsetsType;
@@ -64,6 +66,8 @@ public class InsetsSourceConsumer {
private final Supplier<Transaction> mTransactionSupplier;
private @Nullable InsetsSourceControl mSourceControl;
private boolean mHasWindowFocus;
+ private Rect mPendingFrame;
+ private Rect mPendingVisibleFrame;
public InsetsSourceConsumer(@InternalInsetsType int type, InsetsState state,
Supplier<Transaction> transactionSupplier, InsetsController controller) {
@@ -215,6 +219,38 @@ public class InsetsSourceConsumer {
// no-op for types that always return ShowResult#SHOW_IMMEDIATELY.
}
+ void updateSource(InsetsSource newSource) {
+ InsetsSource source = mState.peekSource(mType);
+ if (source == null || mController.getAnimationType(mType) == ANIMATION_TYPE_NONE
+ || source.getFrame().equals(newSource.getFrame())) {
+ mState.addSource(newSource);
+ return;
+ }
+
+ // Frame is changing while animating. Keep note of the new frame but keep existing frame
+ // until animaition is finished.
+ newSource = new InsetsSource(newSource);
+ mPendingFrame = new Rect(newSource.getFrame());
+ mPendingVisibleFrame = newSource.getVisibleFrame() != null
+ ? new Rect(newSource.getVisibleFrame())
+ : null;
+ newSource.setFrame(source.getFrame());
+ newSource.setVisibleFrame(source.getVisibleFrame());
+ mState.addSource(newSource);
+ }
+
+ boolean notifyAnimationFinished() {
+ if (mPendingFrame != null) {
+ InsetsSource source = mState.getSource(mType);
+ source.setFrame(mPendingFrame);
+ source.setVisibleFrame(mPendingVisibleFrame);
+ mPendingFrame = null;
+ mPendingVisibleFrame = null;
+ return true;
+ }
+ return false;
+ }
+
/**
* Sets requested visibility from the client, regardless of whether we are able to control it at
* the moment.
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 59fc6e9b5ede..c89e0c9fc60e 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -176,6 +176,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
boolean mUseAlpha = false;
float mSurfaceAlpha = 1f;
boolean mClipSurfaceToBounds;
+ int mBackgroundColor = Color.BLACK;
@UnsupportedAppUsage
boolean mHaveFrame = false;
@@ -828,6 +829,12 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
}
+ private Transaction updateBackgroundColor(Transaction t) {
+ final float[] colorComponents = new float[] { Color.red(mBackgroundColor) / 255.f,
+ Color.green(mBackgroundColor) / 255.f, Color.blue(mBackgroundColor) / 255.f };
+ t.setColor(mBackgroundControl, colorComponents);
+ return t;
+ }
private void releaseSurfaces() {
mSurfaceAlpha = 1f;
@@ -1000,6 +1007,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
updateBackgroundVisibility(mTmpTransaction);
+ updateBackgroundColor(mTmpTransaction);
if (mUseAlpha) {
mTmpTransaction.setAlpha(mSurfaceControl, alpha);
mSurfaceAlpha = alpha;
@@ -1399,10 +1407,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
return;
}
- final float[] colorComponents = new float[] { Color.red(bgColor) / 255.f,
- Color.green(bgColor) / 255.f, Color.blue(bgColor) / 255.f };
-
- mTmpTransaction.setColor(mBackgroundControl, colorComponents).apply();
+ mBackgroundColor = bgColor;
+ updateBackgroundColor(mTmpTransaction).apply();
}
@UnsupportedAppUsage
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 4aeea10eee68..62dd192a6d67 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -405,6 +405,13 @@ public class Editor {
// The actual zoom value may changes based on this initial zoom value.
private float mInitialZoom = 1f;
+ // For calculating the line change slops while moving cursor/selection.
+ // The slop max/min value include line height and the slop on the upper/lower line.
+ private static final int LINE_CHANGE_SLOP_MAX_DP = 45;
+ private static final int LINE_CHANGE_SLOP_MIN_DP = 12;
+ private int mLineChangeSlopMax;
+ private int mLineChangeSlopMin;
+
Editor(TextView textView) {
mTextView = textView;
// Synchronize the filter list, which places the undo input filter at the end.
@@ -430,6 +437,14 @@ public class Editor {
logCursor("Editor", "New magnifier is %s.",
mNewMagnifierEnabled ? "enabled" : "disabled");
}
+
+ mLineChangeSlopMax = (int) TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP, LINE_CHANGE_SLOP_MAX_DP,
+ mTextView.getContext().getResources().getDisplayMetrics());
+ mLineChangeSlopMin = (int) TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP, LINE_CHANGE_SLOP_MIN_DP,
+ mTextView.getContext().getResources().getDisplayMetrics());
+
}
@VisibleForTesting
@@ -6018,7 +6033,14 @@ public class Editor {
}
}
- private int getCurrentLineAdjustedForSlop(Layout layout, int prevLine, float y) {
+ @VisibleForTesting
+ public void setLineChangeSlopMinMaxForTesting(final int min, final int max) {
+ mLineChangeSlopMin = min;
+ mLineChangeSlopMax = max;
+ }
+
+ @VisibleForTesting
+ public int getCurrentLineAdjustedForSlop(Layout layout, int prevLine, float y) {
final int trueLine = mTextView.getLineAtCoordinate(y);
if (layout == null || prevLine > layout.getLineCount()
|| layout.getLineCount() <= 0 || prevLine < 0) {
@@ -6031,28 +6053,21 @@ public class Editor {
return trueLine;
}
+ final int lineHeight = layout.getLineBottom(prevLine) - layout.getLineTop(prevLine);
+ int slop = (int)(LINE_SLOP_MULTIPLIER_FOR_HANDLEVIEWS
+ * (layout.getLineBottom(trueLine) - layout.getLineTop(trueLine)));
+ slop = Math.max(mLineChangeSlopMin,
+ Math.min(mLineChangeSlopMax, lineHeight + slop)) - lineHeight;
+ slop = Math.max(0, slop);
+
final float verticalOffset = mTextView.viewportToContentVerticalOffset();
- final int lineCount = layout.getLineCount();
- final float slop = mTextView.getLineHeight() * LINE_SLOP_MULTIPLIER_FOR_HANDLEVIEWS;
-
- final float firstLineTop = layout.getLineTop(0) + verticalOffset;
- final float prevLineTop = layout.getLineTop(prevLine) + verticalOffset;
- final float yTopBound = Math.max(prevLineTop - slop, firstLineTop + slop);
-
- final float lastLineBottom = layout.getLineBottom(lineCount - 1) + verticalOffset;
- final float prevLineBottom = layout.getLineBottom(prevLine) + verticalOffset;
- final float yBottomBound = Math.min(prevLineBottom + slop, lastLineBottom - slop);
-
- // Determine if we've moved lines based on y position and previous line.
- int currLine;
- if (y <= yTopBound) {
- currLine = Math.max(prevLine - 1, 0);
- } else if (y >= yBottomBound) {
- currLine = Math.min(prevLine + 1, lineCount - 1);
- } else {
- currLine = prevLine;
+ if (trueLine > prevLine && y >= layout.getLineBottom(prevLine) + slop + verticalOffset) {
+ return trueLine;
+ }
+ if (trueLine < prevLine && y <= layout.getLineTop(prevLine) - slop + verticalOffset) {
+ return trueLine;
}
- return currLine;
+ return prevLine;
}
/**
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 8943da4a2fc0..4f14539dd976 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -144,9 +144,6 @@ public class Toast {
@Nullable
private CharSequence mText;
- // TODO(b/144152069): Remove this after assessing impact on dogfood.
- private boolean mIsCustomToast;
-
/**
* Construct an empty Toast object. You must call {@link #setView} before you
* can call {@link #show}.
@@ -214,8 +211,7 @@ public class Toast {
service.enqueueTextToast(pkg, mToken, mText, mDuration, displayId, callback);
}
} else {
- service.enqueueTextOrCustomToast(pkg, mToken, tn, mDuration, displayId,
- mIsCustomToast);
+ service.enqueueToast(pkg, mToken, tn, mDuration, displayId);
}
} catch (RemoteException e) {
// Empty
@@ -253,7 +249,6 @@ public class Toast {
*/
@Deprecated
public void setView(View view) {
- mIsCustomToast = true;
mNextView = view;
}
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index c83cab77dd13..31527e8dbe5d 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -227,7 +227,7 @@ public class ResolverDrawerLayout extends ViewGroup {
}
final int oldCollapsibleHeight = mCollapsibleHeight;
- mCollapsibleHeight = Math.max(mCollapsibleHeight, getMaxCollapsedHeight());
+ mCollapsibleHeight = Math.min(mCollapsibleHeight, getMaxCollapsedHeight());
if (updateCollapseOffset(oldCollapsibleHeight, !isDragging())) {
return;
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index ed2c5b2b4930..ab57e3dcdc53 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2665,4 +2665,8 @@ enum PageId {
// Open: Settings > Sound > Do Not Disturb > People > Messages
// OS: R
DND_MESSAGES = 1839;
+
+ // Open: Settings > Sound > Do Not Disturb > Apps > <Choose App>
+ // OS: R
+ DND_APPS_BYPASSING = 1840;
}
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 2ef0c927cc61..88f9fc2199e5 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -702,7 +702,7 @@ please see themes_device_defaults.xml.
</style>
<style name="Theme.Dream">
- <item name="windowBackground">@null</item>
+ <item name="windowBackground">@color/black</item>
<item name="windowDisablePreview">true</item>
</style>
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 34a1016f0ae9..efdb51dcdfa9 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -31,6 +31,7 @@ import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -638,6 +639,32 @@ public class InsetsControllerTest {
});
}
+ @Test
+ public void testFrameUpdateDuringAnimation() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+
+ mController.onControlsChanged(createSingletonControl(ITYPE_IME));
+
+ // Pretend IME is calling
+ mController.show(ime(), true /* fromIme */);
+
+ InsetsState copy = new InsetsState(mController.getState(), true /* copySources */);
+ copy.getSource(ITYPE_IME).setFrame(0, 1, 2, 3);
+ copy.getSource(ITYPE_IME).setVisibleFrame(new Rect(4, 5, 6, 7));
+ mController.onStateChanged(copy);
+ assertNotEquals(new Rect(0, 1, 2, 3),
+ mController.getState().getSource(ITYPE_IME).getFrame());
+ assertNotEquals(new Rect(4, 5, 6, 7),
+ mController.getState().getSource(ITYPE_IME).getVisibleFrame());
+ mController.cancelExistingAnimation();
+ assertEquals(new Rect(0, 1, 2, 3),
+ mController.getState().getSource(ITYPE_IME).getFrame());
+ assertEquals(new Rect(4, 5, 6, 7),
+ mController.getState().getSource(ITYPE_IME).getVisibleFrame());
+ });
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+
private void waitUntilNextFrame() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
Choreographer.getMainThreadInstance().postCallback(Choreographer.CALLBACK_COMMIT,
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java
index ba27fac05a99..eae1bbc930d4 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java
@@ -15,11 +15,12 @@
*/
package android.view.contentcapture;
+import static org.mockito.Mockito.mock;
import static org.testng.Assert.assertThrows;
+import android.content.ContentCaptureOptions;
import android.content.Context;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -37,16 +38,20 @@ public class ContentCaptureManagerTest {
@Mock
private Context mMockContext;
- private ContentCaptureManager mManager;
-
- @Before
- public void before() {
- mManager = new ContentCaptureManager(mMockContext, /* service= */ null,
- /* options= */ null);
+ @Test
+ public void testConstructor_invalidParametersThrowsException() {
+ assertThrows(NullPointerException.class,
+ () -> new ContentCaptureManager(mMockContext, /* service= */ null, /* options= */
+ null));
}
@Test
- public void testRemoveData_invalid() {
- assertThrows(NullPointerException.class, () -> mManager.removeData(null));
+ public void testRemoveData_invalidParametersThrowsException() {
+ final IContentCaptureManager mockService = mock(IContentCaptureManager.class);
+ final ContentCaptureOptions options = new ContentCaptureOptions(null);
+ final ContentCaptureManager manager =
+ new ContentCaptureManager(mMockContext, mockService, options);
+
+ assertThrows(NullPointerException.class, () -> manager.removeData(null));
}
}
diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
index f81964c9cdf9..1b5ce8fd4ce5 100644
--- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
+++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
@@ -16,6 +16,7 @@
package android.widget;
+import static android.text.Spanned.SPAN_INCLUSIVE_EXCLUSIVE;
import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
import static android.widget.espresso.TextViewActions.dragOnText;
import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex;
@@ -37,6 +38,9 @@ import android.app.Activity;
import android.app.Instrumentation;
import android.graphics.Rect;
import android.text.Layout;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.style.AbsoluteSizeSpan;
import android.util.ArraySet;
import android.util.Log;
import android.view.InputDevice;
@@ -489,6 +493,38 @@ public class EditorCursorDragTest {
}
@Test
+ public void testLineChangeSlop() throws Throwable {
+ TextView tv = mActivity.findViewById(R.id.textview);
+ Spannable s = new SpannableString("a\nb\nc");
+ s.setSpan(new AbsoluteSizeSpan(10), 2, 4, SPAN_INCLUSIVE_EXCLUSIVE);
+ s.setSpan(new AbsoluteSizeSpan(32), 4, 5, SPAN_INCLUSIVE_EXCLUSIVE);
+ mInstrumentation.runOnMainSync(() -> tv.setText(s));
+
+ Layout layout = tv.getLayout();
+ Editor editor = tv.getEditorForTesting();
+ final float verticalOffset = tv.getExtendedPaddingTop();
+ editor.setLineChangeSlopMinMaxForTesting(30, 65);
+ // Hit top part of upper line, jump to upper line.
+ assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 5 + verticalOffset))
+ .isEqualTo(0);
+ // Hit bottom part of upper line, stay at current line.
+ assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 40 + verticalOffset))
+ .isEqualTo(1);
+ // Hit current line, stay at current line.
+ assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 70 + verticalOffset))
+ .isEqualTo(1);
+ // Hit top part of lower line, stay at current line.
+ assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 85 + verticalOffset))
+ .isEqualTo(1);
+ // Hit bottom part of lower line, jump to lower line.
+ assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 110 + verticalOffset))
+ .isEqualTo(2);
+ // Hit lower line of lower line, jump to target line.
+ assertThat(editor.getCurrentLineAdjustedForSlop(layout, 0, 110 + verticalOffset))
+ .isEqualTo(2);
+ }
+
+ @Test
public void testCursorDrag_snapDistance() throws Throwable {
String text = "line1: This is the 1st line: A\n"
+ "line2: This is the 2nd line: B\n"
diff --git a/data/etc/hiddenapi-package-whitelist.xml b/data/etc/hiddenapi-package-whitelist.xml
index 5c89da05a68c..98f5824011ae 100644
--- a/data/etc/hiddenapi-package-whitelist.xml
+++ b/data/etc/hiddenapi-package-whitelist.xml
@@ -61,7 +61,6 @@ Do NOT include any apps that are updatable via Play Store!
<hidden-api-whitelisted-app package="com.android.terminal" />
<hidden-api-whitelisted-app package="com.android.wallpaper" />
<hidden-api-whitelisted-app package="jp.co.omronsoft.openwnn" />
- <!-- STOPSHIP: Remove this when fixing all @hide usage for tethering.-->
- <hidden-api-whitelisted-app package="com.android.networkstack.tethering" />
+ <!-- TODO: Remove NetworkStack whitelisting -->
<hidden-api-whitelisted-app package="com.android.networkstack" />
</config>
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 383202b20550..fffdd683f22c 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -4579,6 +4579,7 @@ public class AudioManager {
* {@hide}
*/
@UnsupportedAppUsage
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public void setWiredDeviceConnectionState(int type, int state, String address, String name) {
final IAudioService service = getService();
try {
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index c0461bc598ed..1d70a0dbc57c 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -46,6 +46,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
@@ -3048,16 +3049,47 @@ final public class MediaCodec {
* @param block The linear block object
* @param offset The byte offset into the input buffer at which the data starts.
* @param size The number of bytes of valid input data.
- * @param cryptoInfo Metadata describing the structure of the encrypted input sample.
- * may be null for non-encrypted content.
* @return this object
* @throws IllegalStateException if a buffer is already set
*/
public @NonNull QueueRequest setLinearBlock(
@NonNull LinearBlock block,
int offset,
+ int size) {
+ if (!isAccessible()) {
+ throw new IllegalStateException("The request is stale");
+ }
+ if (mLinearBlock != null || mHardwareBuffer != null) {
+ throw new IllegalStateException("Cannot set block twice");
+ }
+ mLinearBlock = block;
+ mOffset = offset;
+ mSize = size;
+ mCryptoInfo = null;
+ return this;
+ }
+
+ /**
+ * Set an encrypted linear block to this queue request. Exactly one buffer must be
+ * set for a queue request before calling {@link #queue}. It is possible
+ * to use the same {@link LinearBlock} object for multiple queue
+ * requests. The behavior is undefined if the range of the buffer
+ * overlaps for multiple requests, or the application writes into the
+ * region being processed by the codec.
+ *
+ * @param block The linear block object
+ * @param offset The byte offset into the input buffer at which the data starts.
+ * @param size The number of bytes of valid input data.
+ * @param cryptoInfo Metadata describing the structure of the encrypted input sample.
+ * @return this object
+ * @throws IllegalStateException if a buffer is already set
+ */
+ public @NonNull QueueRequest setEncryptedLinearBlock(
+ @NonNull LinearBlock block,
+ int offset,
int size,
- @Nullable MediaCodec.CryptoInfo cryptoInfo) {
+ @NonNull MediaCodec.CryptoInfo cryptoInfo) {
+ Objects.requireNonNull(cryptoInfo);
if (!isAccessible()) {
throw new IllegalStateException("The request is stale");
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index d17f242d5d63..a1fba4a018e2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -174,7 +174,7 @@ public class HearingAidProfile implements LocalBluetoothProfile {
@Override
public boolean isEnabled(BluetoothDevice device) {
- if (mService == null) {
+ if (mService == null || device == null) {
return false;
}
return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
@@ -182,7 +182,7 @@ public class HearingAidProfile implements LocalBluetoothProfile {
@Override
public int getConnectionPolicy(BluetoothDevice device) {
- if (mService == null) {
+ if (mService == null || device == null) {
return CONNECTION_POLICY_FORBIDDEN;
}
return mService.getConnectionPolicy(device);
@@ -191,7 +191,7 @@ public class HearingAidProfile implements LocalBluetoothProfile {
@Override
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
boolean isEnabled = false;
- if (mService == null) {
+ if (mService == null || device == null) {
return false;
}
if (enabled) {
@@ -213,7 +213,7 @@ public class HearingAidProfile implements LocalBluetoothProfile {
}
public long getHiSyncId(BluetoothDevice device) {
- if (mService == null) {
+ if (mService == null || device == null) {
return BluetoothHearingAid.HI_SYNC_ID_INVALID;
}
return mService.getHiSyncId(device);
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index b1300a97a324..922caeb0a817 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -66,6 +66,7 @@ public class LocalMediaManager implements BluetoothCallback {
private LocalBluetoothManager mLocalBluetoothManager;
private InfoMediaManager mInfoMediaManager;
private String mPackageName;
+ private MediaDevice mOnTransferBluetoothDevice;
@VisibleForTesting
List<MediaDevice> mMediaDevices = new ArrayList<>();
@@ -143,7 +144,7 @@ public class LocalMediaManager implements BluetoothCallback {
final CachedBluetoothDevice cachedDevice =
((BluetoothMediaDevice) device).getCachedDevice();
if (!cachedDevice.isConnected() && !cachedDevice.isBusy()) {
- device.setState(MediaDeviceState.STATE_CONNECTING);
+ mOnTransferBluetoothDevice = connectDevice;
cachedDevice.connect();
return;
}
@@ -389,6 +390,10 @@ public class LocalMediaManager implements BluetoothCallback {
mCurrentConnectedDevice = infoMediaDevice != null
? infoMediaDevice : updateCurrentConnectedDevice();
dispatchDeviceListUpdate();
+ if (mOnTransferBluetoothDevice != null && mOnTransferBluetoothDevice.isConnected()) {
+ connectDevice(mOnTransferBluetoothDevice);
+ mOnTransferBluetoothDevice = null;
+ }
}
private List<MediaDevice> buildDisconnectedBluetoothDevice() {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index 6b3a97f8c8de..4c61ef504090 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -23,6 +23,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -107,8 +108,8 @@ public class LocalMediaManagerTest {
when(mLocalProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
when(mLocalProfileManager.getHearingAidProfile()).thenReturn(mHapProfile);
- mInfoMediaDevice1 = new InfoMediaDevice(mContext, mMediaRouter2Manager, mRouteInfo1,
- TEST_PACKAGE_NAME);
+ mInfoMediaDevice1 = spy(new InfoMediaDevice(mContext, mMediaRouter2Manager, mRouteInfo1,
+ TEST_PACKAGE_NAME));
mInfoMediaDevice2 = new InfoMediaDevice(mContext, mMediaRouter2Manager, mRouteInfo2,
TEST_PACKAGE_NAME);
mLocalMediaManager = new LocalMediaManager(mContext, mLocalBluetoothManager,
@@ -565,6 +566,34 @@ public class LocalMediaManagerTest {
}
@Test
+ public void onDeviceListAdded_transferToDisconnectedBluetooth_verifyConnectDevice() {
+ final List<MediaDevice> devices = new ArrayList<>();
+ final MediaDevice currentDevice = mock(MediaDevice.class);
+ final MediaDevice device = mock(BluetoothMediaDevice.class);
+ final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
+ mLocalMediaManager.mMediaDevices.add(device);
+ mLocalMediaManager.mMediaDevices.add(currentDevice);
+
+ when(device.getId()).thenReturn(TEST_DEVICE_ID_1);
+ when(currentDevice.getId()).thenReturn(TEST_CURRENT_DEVICE_ID);
+ when(((BluetoothMediaDevice) device).getCachedDevice()).thenReturn(cachedDevice);
+ when(cachedDevice.isConnected()).thenReturn(false);
+ when(cachedDevice.isBusy()).thenReturn(false);
+
+ mLocalMediaManager.registerCallback(mCallback);
+ mLocalMediaManager.connectDevice(device);
+
+ verify(cachedDevice).connect();
+ when(device.isConnected()).thenReturn(true);
+ mLocalMediaManager.mCurrentConnectedDevice = currentDevice;
+ devices.add(mInfoMediaDevice1);
+ devices.add(currentDevice);
+ mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
+
+ verify(mInfoMediaDevice1).connect();
+ }
+
+ @Test
public void onRequestFailed_shouldDispatchOnRequestFailed() {
mLocalMediaManager.registerCallback(mCallback);
diff --git a/packages/SystemUI/res/layout/bubble_overflow_activity.xml b/packages/SystemUI/res/layout/bubble_overflow_activity.xml
index a06f434d068c..65b04fd8fd99 100644
--- a/packages/SystemUI/res/layout/bubble_overflow_activity.xml
+++ b/packages/SystemUI/res/layout/bubble_overflow_activity.xml
@@ -19,6 +19,7 @@
android:id="@+id/bubble_overflow_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:paddingTop="@dimen/bubble_overflow_padding"
android:orientation="vertical"
android:layout_gravity="center_horizontal">
diff --git a/packages/SystemUI/res/layout/bubble_overflow_view.xml b/packages/SystemUI/res/layout/bubble_overflow_view.xml
new file mode 100644
index 000000000000..d67c81d67ada
--- /dev/null
+++ b/packages/SystemUI/res/layout/bubble_overflow_view.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/bubble_overflow_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <com.android.systemui.bubbles.BadgedImageView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/bubble_view"
+ android:layout_gravity="center"
+ android:layout_width="@dimen/individual_bubble_size"
+ android:layout_height="@dimen/individual_bubble_size"/>
+
+ <TextView
+ android:id="@+id/bubble_view_name"
+ android:fontFamily="@*android:string/config_bodyFontFamily"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2"
+ android:textColor="?android:attr/textColorSecondary"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:maxLines="1"
+ android:layout_gravity="center"
+ android:gravity="center"/>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/keyguard_media_header.xml b/packages/SystemUI/res/layout/keyguard_media_header.xml
index 9c2d244cb8ec..de9ef218083b 100644
--- a/packages/SystemUI/res/layout/keyguard_media_header.xml
+++ b/packages/SystemUI/res/layout/keyguard_media_header.xml
@@ -52,7 +52,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
- android:gravity="center_horizontal|fill_vertical"
+ android:gravity="center"
android:padding="16dp"
>
<ImageView
@@ -67,7 +67,7 @@
<LinearLayout
android:orientation="vertical"
android:layout_width="0dp"
- android:layout_height="@dimen/qs_media_album_size"
+ android:layout_height="wrap_content"
android:layout_weight="1"
>
<LinearLayout
diff --git a/packages/SystemUI/res/layout/qs_media_panel.xml b/packages/SystemUI/res/layout/qs_media_panel.xml
index 9ef8c1dbbb95..fe8557bfcce4 100644
--- a/packages/SystemUI/res/layout/qs_media_panel.xml
+++ b/packages/SystemUI/res/layout/qs_media_panel.xml
@@ -56,7 +56,7 @@
<LinearLayout
android:orientation="vertical"
android:layout_width="0dp"
- android:layout_height="@dimen/qs_media_album_size"
+ android:layout_height="wrap_content"
android:layout_weight="1"
>
<LinearLayout
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index e45cbecd3aa1..432cd749abbd 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1150,8 +1150,8 @@
<dimen name="bubble_overflow_height">380dp</dimen>
<!-- Bubble overflow padding when there are no bubbles -->
<dimen name="bubble_overflow_empty_state_padding">16dp</dimen>
- <!-- Margin of overflow bubbles -->
- <dimen name="bubble_overflow_margin">16dp</dimen>
+ <!-- Padding of container for overflow bubbles -->
+ <dimen name="bubble_overflow_padding">5dp</dimen>
<!-- Height of the triangle that points to the expanded bubble -->
<dimen name="bubble_pointer_height">4dp</dimen>
<!-- Width of the triangle that points to the expanded bubble -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 07bd3a0567e6..136935080824 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -73,14 +73,19 @@ public class QuickStepContract {
public static final int SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED = 1 << 9;
// The search feature is disabled (either by SUW/SysUI/device policy)
public static final int SYSUI_STATE_SEARCH_DISABLED = 1 << 10;
- // The notification panel is expanded and interactive (either locked or unlocked), and the
- // quick settings is not expanded
+ // The notification panel is expanded and interactive (either locked or unlocked), and quick
+ // settings is expanded.
public static final int SYSUI_STATE_QUICK_SETTINGS_EXPANDED = 1 << 11;
// Winscope tracing is enabled
public static final int SYSUI_STATE_TRACING_ENABLED = 1 << 12;
// The Assistant gesture should be constrained. It is up to the launcher implementation to
// decide how to constrain it
public static final int SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED = 1 << 13;
+ // The bubble stack is expanded. This means that the home gesture should be ignored, since a
+ // swipe up is an attempt to close the bubble stack, but that the back gesture should remain
+ // enabled (since it's used to navigate back within the bubbled app, or to collapse the bubble
+ // stack.
+ public static final int SYSUI_STATE_BUBBLES_EXPANDED = 1 << 14;
@Retention(RetentionPolicy.SOURCE)
@IntDef({SYSUI_STATE_SCREEN_PINNING,
@@ -96,7 +101,8 @@ public class QuickStepContract {
SYSUI_STATE_HOME_DISABLED,
SYSUI_STATE_SEARCH_DISABLED,
SYSUI_STATE_TRACING_ENABLED,
- SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED
+ SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED,
+ SYSUI_STATE_BUBBLES_EXPANDED
})
public @interface SystemUiStateFlags {}
@@ -118,6 +124,7 @@ public class QuickStepContract {
str.add((flags & SYSUI_STATE_TRACING_ENABLED) != 0 ? "tracing" : "");
str.add((flags & SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED) != 0
? "asst_gesture_constrain" : "");
+ str.add((flags & SYSUI_STATE_BUBBLES_EXPANDED) != 0 ? "bubbles_expanded" : "");
return str.toString();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMediaPlayer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMediaPlayer.java
index b0017269ff7e..4fcacc276ac1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMediaPlayer.java
@@ -114,7 +114,9 @@ public class KeyguardMediaPlayer {
throw new IllegalStateException("cannot update controls, views not bound");
}
if (mediaMetadata == null) {
- throw new IllegalArgumentException("media metadata was null");
+ mMediaNotifView.setVisibility(View.GONE);
+ Log.d(TAG, "media metadata was null");
+ return;
}
mMediaNotifView.setVisibility(View.VISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index c9ce8a10cca7..01c2faa62403 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -74,6 +74,7 @@ import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.bubbles.dagger.BubbleModule;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.PinnedStackListenerForwarder;
@@ -174,6 +175,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private IStatusBarService mBarService;
+ private SysUiState mSysUiState;
// Used for determining view rect for touch interaction
private Rect mTempRect = new Rect();
@@ -290,11 +292,12 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
NotifPipeline notifPipeline,
FeatureFlags featureFlags,
DumpManager dumpManager,
- FloatingContentCoordinator floatingContentCoordinator) {
+ FloatingContentCoordinator floatingContentCoordinator,
+ SysUiState sysUiState) {
this(context, notificationShadeWindowController, statusBarStateController, shadeController,
data, null /* synchronizer */, configurationController, interruptionStateProvider,
zenModeController, notifUserManager, groupManager, entryManager,
- notifPipeline, featureFlags, dumpManager, floatingContentCoordinator);
+ notifPipeline, featureFlags, dumpManager, floatingContentCoordinator, sysUiState);
}
/**
@@ -315,7 +318,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
NotifPipeline notifPipeline,
FeatureFlags featureFlags,
DumpManager dumpManager,
- FloatingContentCoordinator floatingContentCoordinator) {
+ FloatingContentCoordinator floatingContentCoordinator,
+ SysUiState sysUiState) {
dumpManager.registerDumpable(TAG, this);
mContext = context;
mShadeController = shadeController;
@@ -340,6 +344,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
});
configurationController.addCallback(this /* configurationListener */);
+ mSysUiState = sysUiState;
mBubbleData = data;
mBubbleData.setListener(mBubbleDataListener);
@@ -593,7 +598,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
private void ensureStackViewCreated() {
if (mStackView == null) {
mStackView = new BubbleStackView(
- mContext, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator);
+ mContext, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator,
+ mSysUiState);
ViewGroup nsv = mNotificationShadeWindowController.getNotificationShadeView();
int bubbleScrimIndex = nsv.indexOfChild(nsv.findViewById(R.id.scrim_for_bubble));
int stackIndex = bubbleScrimIndex + 1; // Show stack above bubble scrim.
@@ -957,9 +963,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
String key = orderedKeys[i];
NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(key);
rankingMap.getRanking(key, mTmpRanking);
- if (mBubbleData.hasBubbleWithKey(key) && !mTmpRanking.canBubble()) {
+ boolean isActiveBubble = mBubbleData.hasBubbleWithKey(key);
+ if (isActiveBubble && !mTmpRanking.canBubble()) {
mBubbleData.notificationEntryRemoved(entry, BubbleController.DISMISS_BLOCKED);
- } else if (entry != null && mTmpRanking.isBubble()) {
+ } else if (entry != null && mTmpRanking.isBubble() && !isActiveBubble) {
entry.setFlagBubble(true);
onEntryUpdated(entry);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index 077ffd3729e5..8bc35f4058e9 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -233,6 +233,7 @@ public class BubbleData {
Bubble b = mOverflowBubbles.get(i);
if (b.getKey().equals(entry.getKey())) {
moveOverflowBubbleToPending(b);
+ b.setEntry(entry);
return b;
}
}
@@ -240,6 +241,7 @@ public class BubbleData {
for (int i = 0; i < mPendingBubbles.size(); i++) {
Bubble b = mPendingBubbles.get(i);
if (b.getKey().equals(entry.getKey())) {
+ b.setEntry(entry);
return b;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
index 7636c6712e41..b65198595095 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
@@ -21,14 +21,20 @@ import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
import android.app.Activity;
+import android.app.Notification;
+import android.app.Person;
+import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.DisplayMetrics;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
+import android.widget.TextView;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -67,13 +73,22 @@ public class BubbleOverflowActivity extends Activity {
mEmptyState = findViewById(R.id.bubble_overflow_empty_state);
mRecyclerView = findViewById(R.id.bubble_overflow_recycler);
+
+ Resources res = getResources();
+ final int columns = res.getInteger(R.integer.bubbles_overflow_columns);
mRecyclerView.setLayoutManager(
- new GridLayoutManager(getApplicationContext(),
- getResources().getInteger(R.integer.bubbles_overflow_columns)));
+ new GridLayoutManager(getApplicationContext(), columns));
+
+ DisplayMetrics displayMetrics = new DisplayMetrics();
+ getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
+ final int viewWidth = displayMetrics.widthPixels / columns;
+
+ final int maxOverflowBubbles = res.getInteger(R.integer.bubbles_max_overflow);
+ final int rows = (int) Math.ceil((double) maxOverflowBubbles / columns);
+ final int viewHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height) / rows;
- int bubbleMargin = getResources().getDimensionPixelSize(R.dimen.bubble_overflow_margin);
mAdapter = new BubbleOverflowAdapter(mOverflowBubbles,
- mBubbleController::promoteBubbleFromOverflow, bubbleMargin);
+ mBubbleController::promoteBubbleFromOverflow, viewWidth, viewHeight);
mRecyclerView.setAdapter(mAdapter);
onDataChanged(mBubbleController.getOverflowBubbles());
mBubbleController.setOverflowCallback(() -> {
@@ -139,39 +154,48 @@ public class BubbleOverflowActivity extends Activity {
class BubbleOverflowAdapter extends RecyclerView.Adapter<BubbleOverflowAdapter.ViewHolder> {
private Consumer<Bubble> mPromoteBubbleFromOverflow;
private List<Bubble> mBubbles;
- private int mBubbleMargin;
+ private int mWidth;
+ private int mHeight;
- public BubbleOverflowAdapter(List<Bubble> list, Consumer<Bubble> promoteBubble,
- int bubbleMargin) {
+ public BubbleOverflowAdapter(List<Bubble> list, Consumer<Bubble> promoteBubble, int width,
+ int height) {
mBubbles = list;
mPromoteBubbleFromOverflow = promoteBubble;
- mBubbleMargin = bubbleMargin;
+ mWidth = width;
+ mHeight = height;
}
@Override
public BubbleOverflowAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
- BadgedImageView view = (BadgedImageView) LayoutInflater.from(parent.getContext())
- .inflate(R.layout.bubble_view, parent, false);
+ LinearLayout overflowView = (LinearLayout) LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.bubble_overflow_view, parent, false);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
- LinearLayout.LayoutParams.WRAP_CONTENT
- );
- params.setMargins(mBubbleMargin, mBubbleMargin, mBubbleMargin, mBubbleMargin);
- view.setLayoutParams(params);
- return new ViewHolder(view);
+ LinearLayout.LayoutParams.WRAP_CONTENT);
+ params.width = mWidth;
+ params.height = mHeight;
+ overflowView.setLayoutParams(params);
+ return new ViewHolder(overflowView);
}
@Override
public void onBindViewHolder(ViewHolder vh, int index) {
- Bubble bubble = mBubbles.get(index);
+ Bubble b = mBubbles.get(index);
- vh.mBadgedImageView.update(bubble);
- vh.mBadgedImageView.setOnClickListener(view -> {
- mBubbles.remove(bubble);
+ vh.iconView.update(b);
+ vh.iconView.setOnClickListener(view -> {
+ mBubbles.remove(b);
notifyDataSetChanged();
- mPromoteBubbleFromOverflow.accept(bubble);
+ mPromoteBubbleFromOverflow.accept(b);
});
+
+ Bubble.FlyoutMessage message = b.getFlyoutMessage();
+ if (message != null && message.senderName != null) {
+ vh.textView.setText(message.senderName);
+ } else {
+ vh.textView.setText(b.getAppName());
+ }
}
@Override
@@ -180,11 +204,13 @@ class BubbleOverflowAdapter extends RecyclerView.Adapter<BubbleOverflowAdapter.V
}
public static class ViewHolder extends RecyclerView.ViewHolder {
- public BadgedImageView mBadgedImageView;
+ public BadgedImageView iconView;
+ public TextView textView;
- public ViewHolder(BadgedImageView v) {
+ public ViewHolder(LinearLayout v) {
super(v);
- mBadgedImageView = v;
+ iconView = v.findViewById(R.id.bubble_view);
+ textView = v.findViewById(R.id.bubble_view_name);
}
}
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 541c8cf19943..7191a203ea8c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -80,6 +80,8 @@ import com.android.systemui.R;
import com.android.systemui.bubbles.animation.ExpandedAnimationController;
import com.android.systemui.bubbles.animation.PhysicsAnimationLayout;
import com.android.systemui.bubbles.animation.StackAnimationController;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.util.DismissCircleView;
import com.android.systemui.util.FloatingContentCoordinator;
@@ -241,6 +243,7 @@ public class BubbleStackView extends FrameLayout {
private BubbleTouchHandler mTouchHandler;
private BubbleController.BubbleExpandListener mExpandListener;
+ private SysUiState mSysUiState;
private boolean mViewUpdatedRequested = false;
private boolean mIsExpansionAnimating = false;
@@ -437,7 +440,8 @@ public class BubbleStackView extends FrameLayout {
public BubbleStackView(Context context, BubbleData data,
@Nullable SurfaceSynchronizer synchronizer,
- FloatingContentCoordinator floatingContentCoordinator) {
+ FloatingContentCoordinator floatingContentCoordinator,
+ SysUiState sysUiState) {
super(context);
mBubbleData = data;
@@ -445,6 +449,8 @@ public class BubbleStackView extends FrameLayout {
mTouchHandler = new BubbleTouchHandler(this, data, context);
setOnTouchListener(mTouchHandler);
+ mSysUiState = sysUiState;
+
Resources res = getResources();
mMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered);
mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
@@ -1055,6 +1061,11 @@ public class BubbleStackView extends FrameLayout {
if (shouldExpand == mIsExpanded) {
return;
}
+
+ mSysUiState
+ .setFlag(QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED, shouldExpand)
+ .commitUpdate(mContext.getDisplayId());
+
if (mIsExpanded) {
animateCollapse();
logBubbleEvent(mExpandedBubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
index 27c9e9895324..e84e932c9e61 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
@@ -21,6 +21,7 @@ import android.content.Context;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.bubbles.BubbleData;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -62,7 +63,8 @@ public interface BubbleModule {
NotifPipeline notifPipeline,
FeatureFlags featureFlags,
DumpManager dumpManager,
- FloatingContentCoordinator floatingContentCoordinator) {
+ FloatingContentCoordinator floatingContentCoordinator,
+ SysUiState sysUiState) {
return new BubbleController(
context,
notificationShadeWindowController,
@@ -79,6 +81,7 @@ public interface BubbleModule {
notifPipeline,
featureFlags,
dumpManager,
- floatingContentCoordinator);
+ floatingContentCoordinator,
+ sysUiState);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 09d7d26e4dfe..b3299916356c 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -490,6 +490,9 @@ public class DozeTriggers implements DozeMachine.Part {
public void onPowerSaveChanged(boolean active) {
if (mDozeHost.isPowerSaveActive()) {
mMachine.requestState(DozeMachine.State.DOZE);
+ } else if (mMachine.getState() == DozeMachine.State.DOZE
+ && mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT)) {
+ mMachine.requestState(DozeMachine.State.DOZE_AOD);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 73539f9380e6..6514ca44cf98 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -1849,7 +1849,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
.alpha(1)
.translationX(0)
.translationY(0)
- .setDuration(300)
+ .setDuration(450)
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.setUpdateListener(animation -> {
float animatedValue = animation.getAnimatedFraction();
@@ -1878,7 +1878,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
.alpha(0)
.translationX(mGlobalActionsLayout.getAnimationOffsetX())
.translationY(mGlobalActionsLayout.getAnimationOffsetY())
- .setDuration(300)
+ .setDuration(550)
.withEndAction(this::completeDismiss)
.setInterpolator(new LogAccelerateInterpolator())
.setUpdateListener(animation -> {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index 902b5785c30a..6ce5e7cf506c 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -346,6 +346,7 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
mSurfaceTransactionHelper
.crop(tx, mLeash, destinationBounds)
+ .resetScale(tx, mLeash, destinationBounds)
.round(tx, mLeash, mInPip);
scheduleFinishResizePip(tx, destinationBounds, TRANSITION_DIRECTION_NONE, null);
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
index 014f3b51b4dc..1a01cfedb82e 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
@@ -208,7 +208,8 @@ public class PipResizeGestureHandler {
final Rect currentPipBounds = mMotionHelper.getBounds();
mLastResizeBounds.set(TaskResizingAlgorithm.resizeDrag(ev.getX(), ev.getY(),
mDownPoint.x, mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x,
- mMinSize.y, mMaxSize, true, true));
+ mMinSize.y, mMaxSize, true,
+ mLastDownBounds.width() > mLastDownBounds.height()));
mPipBoundsHandler.transformBoundsToAspectRatio(mLastResizeBounds);
mPipTaskOrganizer.scheduleUserResizePip(mLastDownBounds, mLastResizeBounds,
null);
diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserObservable.java b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserObservable.java
index 3cdc01d95f58..dea8c32dc88d 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserObservable.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserObservable.java
@@ -38,7 +38,7 @@ public class CurrentUserObservable {
@Override
protected void onInactive() {
super.onInactive();
- mTracker.startTracking();
+ mTracker.stopTracking();
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 3879c164a84c..1aa78311a989 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -25,6 +25,7 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
+import android.app.ActivityTaskManager;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -129,6 +130,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
private int mDividerInsets;
private final Display mDefaultDisplay;
+ private boolean mSupportSplitScreenMultiWindow;
private int mDividerSize;
private int mTouchElevation;
@@ -282,6 +284,8 @@ public class DividerView extends FrameLayout implements OnTouchListener,
final DisplayManager displayManager =
(DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
mDefaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
+ mSupportSplitScreenMultiWindow =
+ ActivityTaskManager.supportsSplitScreenMultiWindow(mContext);
}
@Override
@@ -354,6 +358,11 @@ public class DividerView extends FrameLayout implements OnTouchListener,
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ if (!mSupportSplitScreenMultiWindow) {
+ super.onLayout(changed, left, top, right, bottom);
+ return;
+ }
+
if (mFirstLayout) {
// Wait for first layout so that the ViewRootImpl surface has been created.
initializeSurfaceState();
@@ -1085,6 +1094,13 @@ public class DividerView extends FrameLayout implements OnTouchListener,
crop.offsetTo(-(otherTaskRect.left - otherRect.left),
-(otherTaskRect.top - otherRect.top));
t.setWindowCrop(mTiles.mSecondarySurface, crop);
+ // Reposition home and recents surfaces or they would be positioned relatively to its
+ // parent (split-screen secondary task) position.
+ for (int i = mTiles.mHomeAndRecentsSurfaces.size() - 1; i >= 0; --i) {
+ t.setPosition(mTiles.mHomeAndRecentsSurfaces.get(i),
+ mTiles.mHomeBounds.left - otherTaskRect.left,
+ mTiles.mHomeBounds.top - otherTaskRect.top);
+ }
final SurfaceControl dividerCtrl = getWindowSurfaceControl();
if (dividerCtrl != null) {
if (isHorizontalDivision()) {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
index c4089e5dd070..6cb7f4ff7204 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
@@ -26,12 +26,15 @@ import static android.window.WindowOrganizer.TaskOrganizer;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.WindowConfiguration;
+import android.graphics.Rect;
import android.os.RemoteException;
import android.util.Log;
import android.view.Display;
-import android.window.ITaskOrganizer;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
+import android.window.ITaskOrganizer;
+
+import java.util.ArrayList;
class SplitScreenTaskOrganizer extends ITaskOrganizer.Stub {
private static final String TAG = "SplitScreenTaskOrganizer";
@@ -43,6 +46,8 @@ class SplitScreenTaskOrganizer extends ITaskOrganizer.Stub {
SurfaceControl mSecondarySurface;
SurfaceControl mPrimaryDim;
SurfaceControl mSecondaryDim;
+ ArrayList<SurfaceControl> mHomeAndRecentsSurfaces = new ArrayList<>();
+ Rect mHomeBounds = new Rect();
final Divider mDivider;
SplitScreenTaskOrganizer(Divider divider) {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
index 8724e490a558..6ed7afe152df 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -29,9 +29,9 @@ import android.graphics.Rect;
import android.os.RemoteException;
import android.util.Log;
import android.view.Display;
+import android.view.WindowManagerGlobal;
import android.window.IWindowContainer;
import android.window.WindowContainerTransaction;
-import android.view.WindowManagerGlobal;
import android.window.WindowOrganizer;
import com.android.internal.annotations.GuardedBy;
@@ -157,6 +157,7 @@ public class WindowManagerProxy {
for (int i = homeStacks.size() - 1; i >= 0; --i) {
wct.setBounds(homeStacks.get(i), homeBounds);
}
+ layout.mTiles.mHomeBounds.set(homeBounds);
return isHomeResizable;
}
@@ -180,13 +181,17 @@ public class WindowManagerProxy {
if (rootTasks.isEmpty()) {
return false;
}
+ tiles.mHomeAndRecentsSurfaces.clear();
for (int i = rootTasks.size() - 1; i >= 0; --i) {
- if (rootTasks.get(i).configuration.windowConfiguration.getWindowingMode()
+ final ActivityManager.RunningTaskInfo rootTask = rootTasks.get(i);
+ if (isHomeOrRecentTask(rootTask)) {
+ tiles.mHomeAndRecentsSurfaces.add(rootTask.token.getLeash());
+ }
+ if (rootTask.configuration.windowConfiguration.getWindowingMode()
!= WINDOWING_MODE_FULLSCREEN) {
continue;
}
- wct.reparent(rootTasks.get(i).token, tiles.mSecondary.token,
- true /* onTop */);
+ wct.reparent(rootTask.token, tiles.mSecondary.token, true /* onTop */);
}
boolean isHomeResizable = applyHomeTasksMinimized(layout, null /* parent */, wct);
WindowOrganizer.applyTransaction(wct);
@@ -213,6 +218,7 @@ public class WindowManagerProxy {
// Set launch root first so that any task created after getChildContainers and
// before reparent (pretty unlikely) are put into fullscreen.
TaskOrganizer.setLaunchRoot(Display.DEFAULT_DISPLAY, null);
+ tiles.mHomeAndRecentsSurfaces.clear();
// TODO(task-org): Once task-org is more complete, consider using Appeared/Vanished
// plus specific APIs to clean this up.
List<ActivityManager.RunningTaskInfo> primaryChildren =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 8945f360f7b8..a3faa80485d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -67,32 +67,9 @@ class NotificationShadeDepthController @Inject constructor(
private var updateScheduled: Boolean = false
private var shadeExpansion = 0f
@VisibleForTesting
- var shadeSpring = SpringAnimation(this, object :
- FloatPropertyCompat<NotificationShadeDepthController>("shadeBlurRadius") {
- override fun setValue(rect: NotificationShadeDepthController?, value: Float) {
- shadeBlurRadius = value.toInt()
- }
-
- override fun getValue(rect: NotificationShadeDepthController?): Float {
- return shadeBlurRadius.toFloat()
- }
- })
- private val zoomInterpolator = Interpolators.ACCELERATE_DECELERATE
-
- /**
- * Radius that we're animating to.
- */
- private var pendingShadeBlurRadius = -1
-
- /**
- * Shade blur radius on the current frame.
- */
- private var shadeBlurRadius = 0
- set(value) {
- if (field == value) return
- field = value
- scheduleUpdate()
- }
+ var shadeSpring = DepthAnimation()
+ @VisibleForTesting
+ var globalActionsSpring = DepthAnimation()
/**
* Blur radius of the wake-up animation on this frame.
@@ -103,7 +80,6 @@ class NotificationShadeDepthController @Inject constructor(
field = value
scheduleUpdate()
}
- private var globalDialogVisibility = 0f
/**
* Callback that updates the window blur value and is called only once per frame.
@@ -111,12 +87,9 @@ class NotificationShadeDepthController @Inject constructor(
private val updateBlurCallback = Choreographer.FrameCallback {
updateScheduled = false
- val blur = max(shadeBlurRadius,
- max(wakeAndUnlockBlurRadius, blurUtils.blurRadiusOfRatio(globalDialogVisibility)))
+ val blur = max(max(shadeSpring.radius, wakeAndUnlockBlurRadius), globalActionsSpring.radius)
blurUtils.applyBlur(blurRoot?.viewRootImpl ?: root.viewRootImpl, blur)
- val rawZoom = max(blurUtils.ratioOfBlurRadius(blur), globalDialogVisibility)
- wallpaperManager.setWallpaperZoomOut(root.windowToken,
- zoomInterpolator.getInterpolation(rawZoom))
+ wallpaperManager.setWallpaperZoomOut(root.windowToken, blurUtils.ratioOfBlurRadius(blur))
notificationShadeWindowController.setBackgroundBlurRadius(blur)
}
@@ -163,8 +136,9 @@ class NotificationShadeDepthController @Inject constructor(
}
override fun onDozingChanged(isDozing: Boolean) {
- if (isDozing && shadeSpring.isRunning) {
- shadeSpring.skipToEnd()
+ if (isDozing) {
+ shadeSpring.finishIfRunning()
+ globalActionsSpring.finishIfRunning()
}
}
}
@@ -174,10 +148,6 @@ class NotificationShadeDepthController @Inject constructor(
if (WAKE_UP_ANIMATION_ENABLED) {
keyguardStateController.addCallback(keyguardStateCallback)
}
- shadeSpring.spring = SpringForce(0.0f)
- shadeSpring.spring.dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
- shadeSpring.spring.stiffness = SpringForce.STIFFNESS_LOW
- shadeSpring.addEndListener { _, _, _, _ -> pendingShadeBlurRadius = -1 }
statusBarStateController.addCallback(statusBarStateCallback)
}
@@ -198,11 +168,7 @@ class NotificationShadeDepthController @Inject constructor(
newBlur = blurUtils.blurRadiusOfRatio(shadeExpansion)
}
- if (pendingShadeBlurRadius == newBlur) {
- return
- }
- pendingShadeBlurRadius = newBlur
- shadeSpring.animateToFinalPosition(newBlur.toFloat())
+ shadeSpring.animateTo(newBlur)
}
private fun scheduleUpdate(viewToBlur: View? = null) {
@@ -215,19 +181,72 @@ class NotificationShadeDepthController @Inject constructor(
}
fun updateGlobalDialogVisibility(visibility: Float, dialogView: View) {
- if (visibility == globalDialogVisibility) {
- return
- }
- globalDialogVisibility = visibility
- scheduleUpdate(dialogView)
+ globalActionsSpring.animateTo(blurUtils.blurRadiusOfRatio(visibility), dialogView)
}
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
IndentingPrintWriter(pw, " ").let {
it.println("StatusBarWindowBlurController:")
it.increaseIndent()
- it.println("shadeBlurRadius: $shadeBlurRadius")
+ it.println("shadeRadius: ${shadeSpring.radius}")
+ it.println("globalActionsRadius: ${globalActionsSpring.radius}")
it.println("wakeAndUnlockBlur: $wakeAndUnlockBlurRadius")
}
}
+
+ /**
+ * Animation helper that smoothly animates the depth using a spring and deals with frame
+ * invalidation.
+ */
+ inner class DepthAnimation() {
+ /**
+ * Blur radius visible on the UI, in pixels.
+ */
+ var radius = 0
+ private set
+
+ /**
+ * Radius that we're animating to.
+ */
+ private var pendingRadius = -1
+
+ /**
+ * View on {@link Surface} that wants depth.
+ */
+ private var view: View? = null
+
+ private var springAnimation = SpringAnimation(this, object :
+ FloatPropertyCompat<DepthAnimation>("blurRadius") {
+ override fun setValue(rect: DepthAnimation?, value: Float) {
+ radius = value.toInt()
+ scheduleUpdate(view)
+ }
+
+ override fun getValue(rect: DepthAnimation?): Float {
+ return radius.toFloat()
+ }
+ })
+
+ init {
+ springAnimation.spring = SpringForce(0.0f)
+ springAnimation.spring.dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
+ springAnimation.spring.stiffness = SpringForce.STIFFNESS_MEDIUM
+ springAnimation.addEndListener { _, _, _, _ -> pendingRadius = -1 }
+ }
+
+ fun animateTo(newRadius: Int, viewToBlur: View? = null) {
+ if (pendingRadius == newRadius && view == viewToBlur) {
+ return
+ }
+ view = viewToBlur
+ pendingRadius = newRadius
+ springAnimation.animateToFinalPosition(newRadius.toFloat())
+ }
+
+ fun finishIfRunning() {
+ if (springAnimation.isRunning) {
+ springAnimation.skipToEnd()
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index c6d84ff79bde..d2f781d2e19c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -651,7 +651,7 @@ public class NotificationEntryManager implements
*/
public void updateNotifications(String reason) {
reapplyFilterAndSort(reason);
- if (mPresenter != null) {
+ if (mPresenter != null && !mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
mPresenter.updateNotificationViews();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt
index 0437877d8330..cf670bd5a424 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt
@@ -142,9 +142,11 @@ class NotifViewManager @Inject constructor(
// To attach rows we can use _this one weird trick_: if the intended view to add does not
// have a parent, then simply add it (and its children).
entries.forEach { entry ->
- val listItem = rowRegistry.requireView(entry)
+ // TODO: We should eventually map GroupEntry's themselves to views so that we don't
+ // depend on representativeEntry here which may actually be null in the future
+ val listItem = rowRegistry.requireView(entry.representativeEntry!!)
- if (listItem.view.parent != null) {
+ if (listItem.view.parent == null) {
listContainer.addListItem(listItem)
stabilityManager.notifyViewAddition(listItem.view)
}
@@ -153,7 +155,8 @@ class NotifViewManager @Inject constructor(
for ((idx, childEntry) in entry.children.withIndex()) {
val childListItem = rowRegistry.requireView(childEntry)
// Child hasn't been added yet. add it!
- if (!listItem.notificationChildren.contains(childListItem)) {
+ if (listItem.notificationChildren == null ||
+ !listItem.notificationChildren.contains(childListItem)) {
// TODO: old code here just Log.wtf()'d here. This might wreak havoc
if (childListItem.view.parent != null) {
throw IllegalStateException("trying to add a notification child that " +
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 7019b5b42cf4..808e1b38eb43 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -108,6 +108,12 @@ public final class NotificationEntry extends ListEntry {
/** If this notification was filtered out, then the filter that did the filtering. */
@Nullable NotifFilter mExcludingFilter;
+ /**
+ * The NotifFilter, if any, that was active on this notification during the previous run of
+ * the list builder.
+ */
+ @Nullable NotifFilter mPreviousExcludingFilter;
+
/** If this was a group child that was promoted to the top level, then who did the promoting. */
@Nullable NotifPromoter mNotifPromoter;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index f7d6cef92b8f..19f7cefe76a0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -316,6 +316,7 @@ public class ShadeListBuilder implements Dumpable {
// Step 7: Lock in our group structure and log anything that's changed since the last run
mPipelineState.incrementTo(STATE_FINALIZING);
+ logFilterChanges();
logParentingChanges();
freeEmptyGroups();
@@ -363,6 +364,9 @@ public class ShadeListBuilder implements Dumpable {
entry.setPreviousParent(entry.getParent());
entry.setParent(null);
+ entry.mPreviousExcludingFilter = entry.mExcludingFilter;
+ entry.mExcludingFilter = null;
+
if (entry.mFirstAddedIteration == -1) {
entry.mFirstAddedIteration = mIterationCount;
}
@@ -371,8 +375,10 @@ public class ShadeListBuilder implements Dumpable {
mNotifList.clear();
}
- private void filterNotifs(Collection<? extends ListEntry> entries,
- List<ListEntry> out, List<NotifFilter> filters) {
+ private void filterNotifs(
+ Collection<? extends ListEntry> entries,
+ List<ListEntry> out,
+ List<NotifFilter> filters) {
final long now = mSystemClock.uptimeMillis();
for (ListEntry entry : entries) {
if (entry instanceof GroupEntry) {
@@ -585,8 +591,9 @@ public class ShadeListBuilder implements Dumpable {
* filtered out during any of the filtering steps.
*/
private void annulAddition(ListEntry entry) {
- entry.setSection(-1);
- entry.mNotifSection = null;
+ // TODO: We should null out the entry's section and promoter here. However, if we do that,
+ // future runs will think that the section changed. We need a mPreviousNotifSection,
+ // similar to what we do for parents.
entry.setParent(null);
if (entry.mFirstAddedIteration == mIterationCount) {
entry.mFirstAddedIteration = -1;
@@ -615,6 +622,17 @@ public class ShadeListBuilder implements Dumpable {
mGroups.values().removeIf(ge -> ge.getSummary() == null && ge.getChildren().isEmpty());
}
+ private void logFilterChanges() {
+ for (NotificationEntry entry : mAllEntries) {
+ if (entry.mExcludingFilter != entry.mPreviousExcludingFilter) {
+ mLogger.logFilterChanged(
+ entry.getKey(),
+ entry.mPreviousExcludingFilter,
+ entry.mExcludingFilter);
+ }
+ }
+ }
+
private void logParentingChanges() {
for (NotificationEntry entry : mAllEntries) {
if (entry.getParent() != entry.getPreviousParent()) {
@@ -680,21 +698,8 @@ public class ShadeListBuilder implements Dumpable {
};
private boolean applyFilters(NotificationEntry entry, long now, List<NotifFilter> filters) {
- NotifFilter filter = findRejectingFilter(entry, now, filters);
-
- if (filter != entry.mExcludingFilter) {
- mLogger.logFilterChanged(
- entry.getKey(),
- entry.mExcludingFilter != null ? entry.mExcludingFilter.getName() : null,
- filter != null ? filter.getName() : null);
-
- // Note that groups and summaries can also be filtered out later if they're part of a
- // malformed group. We currently don't have a great way to track that beyond parenting
- // change logs. Consider adding something similar to mExcludingFilter for them.
- entry.mExcludingFilter = filter;
- }
-
- return filter != null;
+ entry.mExcludingFilter = findRejectingFilter(entry, now, filters);
+ return entry.mExcludingFilter != null;
}
@Nullable private static NotifFilter findRejectingFilter(NotificationEntry entry, long now,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinator.java
new file mode 100644
index 000000000000..261ae079a2c8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinator.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator;
+
+import android.content.pm.UserInfo;
+import android.util.SparseArray;
+
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+
+import javax.inject.Inject;
+
+/**
+ * A coordinator that filters out notifications for other users
+ *
+ * The NotifCollection contains the notifs for ALL users, so we need to remove any notifications
+ * that have been posted specifically to other users. Note that some system notifications are not
+ * posted to any particular user, and so must be shown to everyone.
+ *
+ * TODO: The NotificationLockscreenUserManager currently maintains the list of active user profiles.
+ * We should spin that off into a standalone section at some point.
+ */
+public class HideNotifsForOtherUsersCoordinator implements Coordinator {
+ private final NotificationLockscreenUserManager mLockscreenUserManager;
+
+ @Inject
+ public HideNotifsForOtherUsersCoordinator(
+ NotificationLockscreenUserManager lockscreenUserManager) {
+ mLockscreenUserManager = lockscreenUserManager;
+ }
+
+ @Override
+ public void attach(NotifPipeline pipeline) {
+ pipeline.addPreGroupFilter(mFilter);
+ mLockscreenUserManager.addUserChangedListener(mUserChangedListener);
+ }
+
+ private final NotifFilter mFilter = new NotifFilter("NotCurrentUserFilter") {
+ @Override
+ public boolean shouldFilterOut(NotificationEntry entry, long now) {
+ return !mLockscreenUserManager
+ .isCurrentProfile(entry.getSbn().getUser().getIdentifier());
+ }
+ };
+
+ private final UserChangedListener mUserChangedListener = new UserChangedListener() {
+ @Override
+ public void onCurrentProfilesChanged(SparseArray<UserInfo> currentProfiles) {
+ mFilter.invalidateList();
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
index aaf71f58cb5c..b7738569ad3c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
@@ -95,11 +95,6 @@ public class KeyguardCoordinator implements Coordinator {
public boolean shouldFilterOut(NotificationEntry entry, long now) {
final StatusBarNotification sbn = entry.getSbn();
- // FILTER OUT the notification when the notification isn't for the current profile
- if (!mLockscreenUserManager.isCurrentProfile(sbn.getUserId())) {
- return true;
- }
-
// FILTER OUT the notification when the keyguard is showing and...
if (mKeyguardStateController.isShowing()) {
// ... user settings or the device policy manager doesn't allow lockscreen
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
index 98104f84f30e..03c0ae6fde50 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
@@ -49,15 +49,17 @@ public class NotifCoordinators implements Dumpable {
public NotifCoordinators(
DumpManager dumpManager,
FeatureFlags featureFlags,
- HeadsUpCoordinator headsUpCoordinator,
+ HideNotifsForOtherUsersCoordinator hideNotifsForOtherUsersCoordinator,
KeyguardCoordinator keyguardCoordinator,
RankingCoordinator rankingCoordinator,
ForegroundCoordinator foregroundCoordinator,
DeviceProvisionedCoordinator deviceProvisionedCoordinator,
BubbleCoordinator bubbleCoordinator,
+ HeadsUpCoordinator headsUpCoordinator,
PreparationCoordinator preparationCoordinator) {
dumpManager.registerDumpable(TAG, this);
mCoordinators.add(new HideLocallyDismissedNotifsCoordinator());
+ mCoordinators.add(hideNotifsForOtherUsersCoordinator);
mCoordinators.add(keyguardCoordinator);
mCoordinators.add(rankingCoordinator);
mCoordinators.add(foregroundCoordinator);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
index 7e9e76096873..e9cbf32ee052 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
@@ -45,7 +45,8 @@ public class RankingCoordinator implements Coordinator {
public void attach(NotifPipeline pipeline) {
mStatusBarStateController.addCallback(mStatusBarStateCallback);
- pipeline.addPreGroupFilter(mNotifFilter);
+ pipeline.addPreGroupFilter(mSuspendedFilter);
+ pipeline.addPreGroupFilter(mDozingFilter);
}
/**
@@ -53,33 +54,30 @@ public class RankingCoordinator implements Coordinator {
* NotifListBuilder invalidates the notification list each time the ranking is updated,
* so we don't need to explicitly invalidate this filter on ranking update.
*/
- private final NotifFilter mNotifFilter = new NotifFilter(TAG) {
+ private final NotifFilter mSuspendedFilter = new NotifFilter("IsSuspendedFilter") {
@Override
public boolean shouldFilterOut(NotificationEntry entry, long now) {
- // App suspended from Ranking
- if (entry.getRanking().isSuspended()) {
- return true;
- }
+ return entry.getRanking().isSuspended();
+ }
+ };
+ private final NotifFilter mDozingFilter = new NotifFilter("IsDozingFilter") {
+ @Override
+ public boolean shouldFilterOut(NotificationEntry entry, long now) {
// Dozing + DND Settings from Ranking object
if (mStatusBarStateController.isDozing() && entry.shouldSuppressAmbient()) {
return true;
}
- if (!mStatusBarStateController.isDozing() && entry.shouldSuppressNotificationList()) {
- return true;
- }
-
- return false;
+ return !mStatusBarStateController.isDozing() && entry.shouldSuppressNotificationList();
}
};
-
private final StatusBarStateController.StateListener mStatusBarStateCallback =
new StatusBarStateController.StateListener() {
@Override
public void onDozingChanged(boolean isDozing) {
- mNotifFilter.invalidateList();
+ mDozingFilter.invalidateList();
}
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
index 763547ce1638..e946cf16b3f6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
@@ -23,6 +23,7 @@ import com.android.systemui.log.LogLevel.WARNING
import com.android.systemui.log.dagger.NotificationLog
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
import javax.inject.Inject
class ShadeListBuilderLogger @Inject constructor(
@@ -126,13 +127,13 @@ class ShadeListBuilderLogger @Inject constructor(
fun logFilterChanged(
key: String,
- prevFilter: String?,
- newFilter: String?
+ prevFilter: NotifFilter?,
+ newFilter: NotifFilter?
) {
buffer.log(TAG, INFO, {
str1 = key
- str2 = prevFilter
- str3 = newFilter
+ str2 = prevFilter?.name
+ str3 = newFilter?.name
}, {
"Filter changed for $str1: $str2 -> $str3"
})
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index be8af82f8baf..823b18660bed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -6478,7 +6478,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
private boolean hasActiveNotifications() {
if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
- return mNotifPipeline.getShadeList().isEmpty();
+ return !mNotifPipeline.getShadeList().isEmpty();
} else {
return mEntryManager.hasActiveNotifications();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index cf9d43eeff73..d70484e9cf41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
-
import android.annotation.IntDef;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -30,241 +28,98 @@ import android.os.Trace;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.AttributeSet;
-import android.view.ViewTreeObserver;
-import android.view.accessibility.AccessibilityNodeInfo;
+import android.util.SparseArray;
+import android.view.ViewTreeObserver.OnPreDrawListener;
import com.android.internal.graphics.ColorUtils;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.statusbar.KeyguardAffordanceView;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
-import com.android.systemui.statusbar.phone.ScrimController.ScrimVisibility;
-import com.android.systemui.statusbar.policy.AccessibilityController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import javax.inject.Inject;
-import javax.inject.Named;
-
/**
* Manages the different states and animations of the unlock icon.
*/
-public class LockIcon extends KeyguardAffordanceView implements
- ViewTreeObserver.OnPreDrawListener {
-
- private static final int STATE_LOCKED = 0;
- private static final int STATE_LOCK_OPEN = 1;
- private static final int STATE_SCANNING_FACE = 2;
- private static final int STATE_BIOMETRICS_ERROR = 3;
- private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- private final AccessibilityController mAccessibilityController;
- private final KeyguardStateController mKeyguardStateController;
- private final KeyguardBypassController mBypassController;
- private final NotificationWakeUpCoordinator mWakeUpCoordinator;
- private final HeadsUpManagerPhone mHeadsUpManager;
-
- private int mLastState = 0;
- private boolean mForceUpdate;
- private boolean mTransientBiometricsError;
- private boolean mIsFaceUnlockState;
- private boolean mSimLocked;
- private int mDensity;
+public class LockIcon extends KeyguardAffordanceView {
+
+ static final int STATE_LOCKED = 0;
+ static final int STATE_LOCK_OPEN = 1;
+ static final int STATE_SCANNING_FACE = 2;
+ static final int STATE_BIOMETRICS_ERROR = 3;
+ private float mDozeAmount;
+ private int mIconColor;
+ private StateProvider mStateProvider;
+ private int mOldState;
private boolean mPulsing;
private boolean mDozing;
- private boolean mDocked;
- private boolean mBlockUpdates;
- private int mIconColor;
- private float mDozeAmount;
- private boolean mBouncerShowingScrimmed;
- private boolean mWakeAndUnlockRunning;
- private boolean mKeyguardShowing;
- private boolean mShowingLaunchAffordance;
private boolean mKeyguardJustShown;
- private boolean mUpdatePending;
- private boolean mBouncerPreHideAnimation;
- private int mStatusBarState = StatusBarState.SHADE;
-
- private final KeyguardStateController.Callback mKeyguardMonitorCallback =
- new KeyguardStateController.Callback() {
- @Override
- public void onKeyguardShowingChanged() {
- boolean force = false;
- boolean wasShowing = mKeyguardShowing;
- mKeyguardShowing = mKeyguardStateController.isShowing();
- if (!wasShowing && mKeyguardShowing && mBlockUpdates) {
- mBlockUpdates = false;
- force = true;
- }
- if (!wasShowing && mKeyguardShowing) {
- mKeyguardJustShown = true;
- }
- update(force);
- }
-
- @Override
- public void onKeyguardFadingAwayChanged() {
- if (!mKeyguardStateController.isKeyguardFadingAway()) {
- mBouncerPreHideAnimation = false;
- if (mBlockUpdates) {
- mBlockUpdates = false;
- update(true /* force */);
- }
- }
- }
-
- @Override
- public void onUnlockedChanged() {
- update();
- }
- };
-
- @Inject
- public LockIcon(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
- AccessibilityController accessibilityController,
- KeyguardBypassController bypassController,
- NotificationWakeUpCoordinator wakeUpCoordinator,
- KeyguardStateController keyguardStateController,
- HeadsUpManagerPhone headsUpManager) {
- super(context, attrs);
- mContext = context;
- mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
- mAccessibilityController = accessibilityController;
- mBypassController = bypassController;
- mWakeUpCoordinator = wakeUpCoordinator;
- mKeyguardStateController = keyguardStateController;
- mHeadsUpManager = headsUpManager;
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- mKeyguardStateController.addCallback(mKeyguardMonitorCallback);
- mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure();
- update();
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- mKeyguardStateController.removeCallback(mKeyguardMonitorCallback);
- }
+ private final SparseArray<Drawable> mDrawableCache = new SparseArray<>();
- /**
- * If we're currently presenting an authentication error message.
- */
- public void setTransientBiometricsError(boolean transientBiometricsError) {
- mTransientBiometricsError = transientBiometricsError;
- update();
- }
-
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- final int density = newConfig.densityDpi;
- if (density != mDensity) {
- mDensity = density;
- update();
- }
- }
-
- public void update() {
- update(false /* force */);
- }
-
- public void update(boolean force) {
- if (force) {
- mForceUpdate = true;
- }
- if (!mUpdatePending) {
- mUpdatePending = true;
- getViewTreeObserver().addOnPreDrawListener(this);
- }
- }
+ private final OnPreDrawListener mOnPreDrawListener = new OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ getViewTreeObserver().removeOnPreDrawListener(this);
- @Override
- public boolean onPreDraw() {
- mUpdatePending = false;
- getViewTreeObserver().removeOnPreDrawListener(this);
-
- int state = getState();
- int lastState = mLastState;
- boolean keyguardJustShown = mKeyguardJustShown;
- mIsFaceUnlockState = state == STATE_SCANNING_FACE;
- mLastState = state;
- mKeyguardJustShown = false;
-
- boolean shouldUpdate = lastState != state || mForceUpdate;
- if (mBlockUpdates && canBlockUpdates()) {
- shouldUpdate = false;
- }
- if (shouldUpdate) {
- mForceUpdate = false;
- @LockAnimIndex final int lockAnimIndex = getAnimationIndexForTransition(lastState,
- state, mPulsing, mDozing, keyguardJustShown);
- boolean isAnim = lockAnimIndex != -1;
- int iconRes = isAnim ? getThemedAnimationResId(lockAnimIndex) : getIconForState(state);
-
- Drawable icon = mContext.getDrawable(iconRes);
- final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable
- ? (AnimatedVectorDrawable) icon
- : null;
+ int newState = mStateProvider.getState();
+ Drawable icon = getIcon(newState);
setImageDrawable(icon, false);
- if (mIsFaceUnlockState) {
- announceForAccessibility(getContext().getString(
+
+ if (newState == STATE_SCANNING_FACE) {
+ announceForAccessibility(getResources().getString(
R.string.accessibility_scanning_face));
}
- if (animation != null && isAnim) {
+ if (icon instanceof AnimatedVectorDrawable) {
+ final AnimatedVectorDrawable animation = (AnimatedVectorDrawable) icon;
animation.forceAnimationOnUI();
animation.clearAnimationCallbacks();
- animation.registerAnimationCallback(new Animatable2.AnimationCallback() {
- @Override
- public void onAnimationEnd(Drawable drawable) {
- if (getDrawable() == animation && state == getState()
- && doesAnimationLoop(lockAnimIndex)) {
- animation.start();
- } else {
- Trace.endAsyncSection("LockIcon#Animation", state);
- }
- }
- });
- Trace.beginAsyncSection("LockIcon#Animation", state);
+ animation.registerAnimationCallback(
+ new Animatable2.AnimationCallback() {
+ @Override
+ public void onAnimationEnd(Drawable drawable) {
+ if (getDrawable() == animation
+ && newState == mStateProvider.getState()
+ && newState == STATE_SCANNING_FACE) {
+ animation.start();
+ } else {
+ Trace.endAsyncSection("LockIcon#Animation", newState);
+ }
+ }
+ });
+ Trace.beginAsyncSection("LockIcon#Animation", newState);
animation.start();
}
+
+ return true;
}
- updateDarkTint();
+ };
- updateIconVisibility();
- updateClickability();
+ public LockIcon(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
- return true;
+ void setStateProvider(StateProvider stateProvider) {
+ mStateProvider = stateProvider;
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ mDrawableCache.clear();
}
/**
* Update the icon visibility
* @return true if the visibility changed
*/
- boolean updateIconVisibility() {
- boolean onAodNotPulsingOrDocked = mDozing && (!mPulsing || mDocked);
- boolean invisible = onAodNotPulsingOrDocked || mWakeAndUnlockRunning
- || mShowingLaunchAffordance;
- if (mBypassController.getBypassEnabled() && !mBouncerShowingScrimmed) {
- if ((mHeadsUpManager.isHeadsUpGoingAway() || mHeadsUpManager.hasPinnedHeadsUp()
- || mStatusBarState == StatusBarState.KEYGUARD)
- && !mWakeUpCoordinator.getNotificationsFullyHidden()) {
- invisible = true;
- }
- }
- boolean wasInvisible = getVisibility() == INVISIBLE;
- if (invisible != wasInvisible) {
- setVisibility(invisible ? INVISIBLE : VISIBLE);
+ boolean updateIconVisibility(boolean visible) {
+ boolean wasVisible = getVisibility() == VISIBLE;
+ if (visible != wasVisible) {
+ setVisibility(visible ? VISIBLE : INVISIBLE);
animate().cancel();
- if (!invisible) {
+ if (visible) {
setScaleX(0);
setScaleY(0);
animate()
@@ -280,49 +135,47 @@ public class LockIcon extends KeyguardAffordanceView implements
return false;
}
- private boolean canBlockUpdates() {
- return mKeyguardShowing || mKeyguardStateController.isKeyguardFadingAway();
+ void update(int oldState, boolean pulsing, boolean dozing, boolean keyguardJustShown) {
+ mOldState = oldState;
+ mPulsing = pulsing;
+ mDozing = dozing;
+ mKeyguardJustShown = keyguardJustShown;
+
+ getViewTreeObserver().addOnPreDrawListener(mOnPreDrawListener);
}
- private void updateClickability() {
- if (mAccessibilityController == null) {
- return;
- }
- boolean canLock = mKeyguardStateController.isMethodSecure()
- && mKeyguardStateController.canDismissLockScreen();
- boolean clickToUnlock = mAccessibilityController.isAccessibilityEnabled();
- setClickable(clickToUnlock);
- setLongClickable(canLock && !clickToUnlock);
- setFocusable(mAccessibilityController.isAccessibilityEnabled());
+ void setDozeAmount(float dozeAmount) {
+ mDozeAmount = dozeAmount;
+ updateDarkTint();
}
- @Override
- public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfo(info);
- boolean fingerprintRunning = mKeyguardUpdateMonitor.isFingerprintDetectionRunning();
- // Only checking if unlocking with Biometric is allowed (no matter strong or non-strong
- // as long as primary auth, i.e. PIN/pattern/password, is not required), so it's ok to
- // pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the
- // check of whether non-strong biometric is allowed
- boolean unlockingAllowed = mKeyguardUpdateMonitor
- .isUnlockingWithBiometricAllowed(true /* isStrongBiometric */);
- if (fingerprintRunning && unlockingAllowed) {
- AccessibilityNodeInfo.AccessibilityAction unlock
- = new AccessibilityNodeInfo.AccessibilityAction(
- AccessibilityNodeInfo.ACTION_CLICK,
- getContext().getString(R.string.accessibility_unlock_without_fingerprint));
- info.addAction(unlock);
- info.setHintText(getContext().getString(
- R.string.accessibility_waiting_for_fingerprint));
- } else if (mIsFaceUnlockState) {
- //Avoid 'button' to be spoken for scanning face
- info.setClassName(LockIcon.class.getName());
- info.setContentDescription(getContext().getString(
- R.string.accessibility_scanning_face));
+ void onThemeChange(int iconColor) {
+ mDrawableCache.clear();
+ mIconColor = iconColor;
+ updateDarkTint();
+ }
+
+ private void updateDarkTint() {
+ int color = ColorUtils.blendARGB(mIconColor, Color.WHITE, mDozeAmount);
+ setImageTintList(ColorStateList.valueOf(color));
+ }
+
+ private Drawable getIcon(int newState) {
+ @LockAnimIndex final int lockAnimIndex =
+ getAnimationIndexForTransition(mOldState, newState, mPulsing, mDozing,
+ mKeyguardJustShown);
+
+ boolean isAnim = lockAnimIndex != -1;
+ int iconRes = isAnim ? getThemedAnimationResId(lockAnimIndex) : getIconForState(newState);
+
+ if (!mDrawableCache.contains(iconRes)) {
+ mDrawableCache.put(iconRes, getResources().getDrawable(iconRes));
}
+
+ return mDrawableCache.get(iconRes);
}
- private int getIconForState(int state) {
+ static int getIconForState(int state) {
int iconRes;
switch (state) {
case STATE_LOCKED:
@@ -343,11 +196,7 @@ public class LockIcon extends KeyguardAffordanceView implements
return iconRes;
}
- private boolean doesAnimationLoop(@LockAnimIndex int lockAnimIndex) {
- return lockAnimIndex == SCANNING;
- }
-
- private static int getAnimationIndexForTransition(int oldState, int newState, boolean pulsing,
+ static int getAnimationIndexForTransition(int oldState, int newState, boolean pulsing,
boolean dozing, boolean keyguardJustShown) {
// Never animate when screen is off
@@ -367,42 +216,10 @@ public class LockIcon extends KeyguardAffordanceView implements
return -1;
}
- public void setBouncerShowingScrimmed(boolean bouncerShowing) {
- mBouncerShowingScrimmed = bouncerShowing;
- if (mBypassController.getBypassEnabled()) {
- update();
- }
- }
-
- /**
- * Animate padlock opening when bouncer challenge is solved.
- */
- public void onBouncerPreHideAnimation() {
- mBouncerPreHideAnimation = true;
- update();
- }
-
- void setIconColor(int iconColor) {
- mIconColor = iconColor;
- updateDarkTint();
- }
-
- void setSimLocked(boolean simLocked) {
- mSimLocked = simLocked;
- }
-
- /** Set if the device is docked. */
- public void setDocked(boolean docked) {
- if (mDocked != docked) {
- mDocked = docked;
- update();
- }
- }
-
@Retention(RetentionPolicy.SOURCE)
@IntDef({ERROR, UNLOCK, LOCK, SCANNING})
@interface LockAnimIndex {}
- private static final int ERROR = 0, UNLOCK = 1, LOCK = 2, SCANNING = 3;
+ static final int ERROR = 0, UNLOCK = 1, LOCK = 2, SCANNING = 3;
private static final int[][] LOCK_ANIM_RES_IDS = new int[][] {
{
R.anim.lock_to_error,
@@ -433,7 +250,7 @@ public class LockIcon extends KeyguardAffordanceView implements
private int getThemedAnimationResId(@LockAnimIndex int lockAnimIndex) {
final String setting = TextUtils.emptyIfNull(
Settings.Secure.getString(getContext().getContentResolver(),
- Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES));
+ Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES));
if (setting.contains("com.android.theme.icon_pack.circular.android")) {
return LOCK_ANIM_RES_IDS[1][lockAnimIndex];
} else if (setting.contains("com.android.theme.icon_pack.filled.android")) {
@@ -444,83 +261,8 @@ public class LockIcon extends KeyguardAffordanceView implements
return LOCK_ANIM_RES_IDS[0][lockAnimIndex];
}
- private int getState() {
- KeyguardUpdateMonitor updateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
- if ((mKeyguardStateController.canDismissLockScreen() || !mKeyguardShowing
- || mKeyguardStateController.isKeyguardGoingAway()) && !mSimLocked) {
- return STATE_LOCK_OPEN;
- } else if (mTransientBiometricsError) {
- return STATE_BIOMETRICS_ERROR;
- } else if (updateMonitor.isFaceDetectionRunning() && !mPulsing) {
- return STATE_SCANNING_FACE;
- } else {
- return STATE_LOCKED;
- }
- }
-
- /**
- * When keyguard is in pulsing (AOD2) state.
- * @param pulsing {@code true} when pulsing.
- */
- public void setPulsing(boolean pulsing) {
- mPulsing = pulsing;
- update();
+ interface StateProvider {
+ int getState();
}
- private void updateDarkTint() {
- int color = ColorUtils.blendARGB(mIconColor, Color.WHITE, mDozeAmount);
- setImageTintList(ColorStateList.valueOf(color));
- }
-
- /**
- * We need to hide the lock whenever there's a fingerprint unlock, otherwise you'll see the
- * icon on top of the black front scrim.
- * @param wakeAndUnlock are we wake and unlocking
- * @param isUnlock are we currently unlocking
- */
- public void onBiometricAuthModeChanged(boolean wakeAndUnlock, boolean isUnlock) {
- if (wakeAndUnlock) {
- mWakeAndUnlockRunning = true;
- }
- if (isUnlock && mBypassController.getBypassEnabled() && canBlockUpdates()) {
- // We don't want the icon to change while we are unlocking
- mBlockUpdates = true;
- }
- update();
- }
-
- /**
- * When we're launching an affordance, like double pressing power to open camera.
- */
- public void onShowingLaunchAffordanceChanged(boolean showing) {
- mShowingLaunchAffordance = showing;
- update();
- }
-
- /**
- * Called whenever the scrims become opaque, transparent or semi-transparent.
- */
- public void onScrimVisibilityChanged(@ScrimVisibility int scrimsVisible) {
- if (mWakeAndUnlockRunning
- && scrimsVisible == ScrimController.TRANSPARENT) {
- mWakeAndUnlockRunning = false;
- update();
- }
- }
-
- void setDozing(boolean dozing) {
- mDozing = dozing;
- update();
- }
-
- void setDozeAmount(float dozeAmount) {
- mDozeAmount = dozeAmount;
- updateDarkTint();
- }
-
- /** Set the StatusBarState. */
- public void setStatusBarState(int statusBarState) {
- mStatusBarState = statusBarState;
- updateIconVisibility();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
index 2b1a8a472d9e..f7c861b84a68 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
@@ -16,11 +16,19 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.statusbar.phone.LockIcon.STATE_BIOMETRICS_ERROR;
+import static com.android.systemui.statusbar.phone.LockIcon.STATE_LOCKED;
+import static com.android.systemui.statusbar.phone.LockIcon.STATE_LOCK_OPEN;
+import static com.android.systemui.statusbar.phone.LockIcon.STATE_SCANNING_FACE;
+
+import android.content.res.Configuration;
+import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.hardware.biometrics.BiometricSourceType;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityNodeInfo;
import androidx.annotation.Nullable;
@@ -29,15 +37,18 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dock.DockManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator.WakeUpListener;
import com.android.systemui.statusbar.policy.AccessibilityController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.util.Optional;
@@ -59,6 +70,21 @@ public class LockscreenLockIconController {
private final NotificationWakeUpCoordinator mNotificationWakeUpCoordinator;
private final KeyguardBypassController mKeyguardBypassController;
private final Optional<DockManager> mDockManager;
+ private final KeyguardStateController mKeyguardStateController;
+ private final Resources mResources;
+ private final HeadsUpManagerPhone mHeadsUpManagerPhone;
+ private boolean mKeyguardShowing;
+ private boolean mKeyguardJustShown;
+ private boolean mBlockUpdates;
+ private boolean mPulsing;
+ private boolean mDozing;
+ private boolean mSimLocked;
+ private boolean mTransientBiometricsError;
+ private boolean mDocked;
+ private boolean mWakeAndUnlockRunning;
+ private boolean mShowingLaunchAffordance;
+ private boolean mBouncerShowingScrimmed;
+ private int mStatusBarState = StatusBarState.SHADE;
private LockIcon mLockIcon;
private View.OnAttachStateChangeListener mOnAttachStateChangeListener =
@@ -69,10 +95,13 @@ public class LockscreenLockIconController {
mConfigurationController.addCallback(mConfigurationListener);
mNotificationWakeUpCoordinator.addListener(mWakeUpListener);
mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
+ mKeyguardStateController.addCallback(mKeyguardMonitorCallback);
mDockManager.ifPresent(dockManager -> dockManager.addListener(mDockEventListener));
+ mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure();
mConfigurationListener.onThemeChanged();
+ update();
}
@Override
@@ -81,7 +110,7 @@ public class LockscreenLockIconController {
mConfigurationController.removeCallback(mConfigurationListener);
mNotificationWakeUpCoordinator.removeListener(mWakeUpListener);
mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback);
-
+ mKeyguardStateController.removeCallback(mKeyguardMonitorCallback);
mDockManager.ifPresent(dockManager -> dockManager.removeListener(mDockEventListener));
}
@@ -91,32 +120,44 @@ public class LockscreenLockIconController {
new StatusBarStateController.StateListener() {
@Override
public void onDozingChanged(boolean isDozing) {
- mLockIcon.setDozing(isDozing);
+ setDozing(isDozing);
}
@Override
public void onDozeAmountChanged(float linear, float eased) {
- mLockIcon.setDozeAmount(eased);
+ if (mLockIcon != null) {
+ mLockIcon.setDozeAmount(eased);
+ }
}
@Override
public void onStateChanged(int newState) {
- mLockIcon.setStatusBarState(newState);
+ setStatusBarState(newState);
}
};
private final ConfigurationListener mConfigurationListener = new ConfigurationListener() {
+ private int mDensity;
+
@Override
public void onThemeChanged() {
+ if (mLockIcon == null) {
+ return;
+ }
+
TypedArray typedArray = mLockIcon.getContext().getTheme().obtainStyledAttributes(
null, new int[]{ R.attr.wallpaperTextColor }, 0, 0);
int iconColor = typedArray.getColor(0, Color.WHITE);
typedArray.recycle();
- mLockIcon.setIconColor(iconColor);
+ mLockIcon.onThemeChange(iconColor);
}
@Override
public void onDensityOrFontScaleChanged() {
+ if (mLockIcon == null) {
+ return;
+ }
+
ViewGroup.LayoutParams lp = mLockIcon.getLayoutParams();
if (lp == null) {
return;
@@ -125,24 +166,41 @@ public class LockscreenLockIconController {
lp.height = mLockIcon.getResources().getDimensionPixelSize(
R.dimen.keyguard_lock_height);
mLockIcon.setLayoutParams(lp);
- mLockIcon.update(true /* force */);
+ update(true /* force */);
}
@Override
public void onLocaleListChanged() {
+ if (mLockIcon == null) {
+ return;
+ }
+
mLockIcon.setContentDescription(
mLockIcon.getResources().getText(R.string.accessibility_unlock_button));
- mLockIcon.update(true /* force */);
+ update(true /* force */);
+ }
+
+ @Override
+ public void onConfigChanged(Configuration newConfig) {
+ final int density = newConfig.densityDpi;
+ if (density != mDensity) {
+ mDensity = density;
+ update();
+ }
}
};
private final WakeUpListener mWakeUpListener = new WakeUpListener() {
@Override
+ public void onPulseExpansionChanged(boolean expandingChanged) {
+ }
+
+ @Override
public void onFullyHiddenChanged(boolean isFullyHidden) {
if (mKeyguardBypassController.getBypassEnabled()) {
- boolean changed = mLockIcon.updateIconVisibility();
+ boolean changed = updateIconVisibility();
if (changed) {
- mLockIcon.update();
+ update();
}
}
}
@@ -152,30 +210,103 @@ public class LockscreenLockIconController {
new KeyguardUpdateMonitorCallback() {
@Override
public void onSimStateChanged(int subId, int slotId, int simState) {
- mLockIcon.setSimLocked(mKeyguardUpdateMonitor.isSimPinSecure());
- mLockIcon.update();
+ mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure();
+ update();
}
@Override
public void onKeyguardVisibilityChanged(boolean showing) {
- mLockIcon.update();
+ update();
}
@Override
public void onBiometricRunningStateChanged(boolean running,
BiometricSourceType biometricSourceType) {
- mLockIcon.update();
+ update();
}
@Override
public void onStrongAuthStateChanged(int userId) {
- mLockIcon.update();
+ update();
}
};
private final DockManager.DockEventListener mDockEventListener =
- event -> mLockIcon.setDocked(event == DockManager.STATE_DOCKED
- || event == DockManager.STATE_DOCKED_HIDE);
+ event -> {
+ boolean docked =
+ event == DockManager.STATE_DOCKED || event == DockManager.STATE_DOCKED_HIDE;
+ if (docked != mDocked) {
+ mDocked = docked;
+ update();
+ }
+ };
+
+ private final KeyguardStateController.Callback mKeyguardMonitorCallback =
+ new KeyguardStateController.Callback() {
+ @Override
+ public void onKeyguardShowingChanged() {
+ boolean force = false;
+ boolean wasShowing = mKeyguardShowing;
+ mKeyguardShowing = mKeyguardStateController.isShowing();
+ if (!wasShowing && mKeyguardShowing && mBlockUpdates) {
+ mBlockUpdates = false;
+ force = true;
+ }
+ if (!wasShowing && mKeyguardShowing) {
+ mKeyguardJustShown = true;
+ }
+ update(force);
+ }
+
+ @Override
+ public void onKeyguardFadingAwayChanged() {
+ if (!mKeyguardStateController.isKeyguardFadingAway()) {
+ if (mBlockUpdates) {
+ mBlockUpdates = false;
+ update(true /* force */);
+ }
+ }
+ }
+
+ @Override
+ public void onUnlockedChanged() {
+ update();
+ }
+ };
+
+ private final View.AccessibilityDelegate mAccessibilityDelegate =
+ new View.AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host,
+ AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ boolean fingerprintRunning =
+ mKeyguardUpdateMonitor.isFingerprintDetectionRunning();
+ // Only checking if unlocking with Biometric is allowed (no matter strong or
+ // non-strong as long as primary auth, i.e. PIN/pattern/password, is not
+ // required), so it's ok to pass true for isStrongBiometric to
+ // isUnlockingWithBiometricAllowed() to bypass the check of whether non-strong
+ // biometric is allowed
+ boolean unlockingAllowed = mKeyguardUpdateMonitor
+ .isUnlockingWithBiometricAllowed(true /* isStrongBiometric */);
+ if (fingerprintRunning && unlockingAllowed) {
+ AccessibilityNodeInfo.AccessibilityAction unlock =
+ new AccessibilityNodeInfo.AccessibilityAction(
+ AccessibilityNodeInfo.ACTION_CLICK,
+ mResources.getString(
+ R.string.accessibility_unlock_without_fingerprint));
+ info.addAction(unlock);
+ info.setHintText(mResources.getString(
+ R.string.accessibility_waiting_for_fingerprint));
+ } else if (getState() == STATE_SCANNING_FACE) {
+ //Avoid 'button' to be spoken for scanning face
+ info.setClassName(LockIcon.class.getName());
+ info.setContentDescription(mResources.getString(
+ R.string.accessibility_scanning_face));
+ }
+ }
+ };
+ private int mLastState;
@Inject
public LockscreenLockIconController(LockscreenGestureLogger lockscreenGestureLogger,
@@ -188,7 +319,10 @@ public class LockscreenLockIconController {
ConfigurationController configurationController,
NotificationWakeUpCoordinator notificationWakeUpCoordinator,
KeyguardBypassController keyguardBypassController,
- @Nullable DockManager dockManager) {
+ @Nullable DockManager dockManager,
+ KeyguardStateController keyguardStateController,
+ @Main Resources resources,
+ HeadsUpManagerPhone headsUpManagerPhone) {
mLockscreenGestureLogger = lockscreenGestureLogger;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
@@ -200,24 +334,31 @@ public class LockscreenLockIconController {
mNotificationWakeUpCoordinator = notificationWakeUpCoordinator;
mKeyguardBypassController = keyguardBypassController;
mDockManager = dockManager == null ? Optional.empty() : Optional.of(dockManager);
+ mKeyguardStateController = keyguardStateController;
+ mResources = resources;
+ mHeadsUpManagerPhone = headsUpManagerPhone;
mKeyguardIndicationController.setLockIconController(this);
}
/**
* Associate the controller with a {@link LockIcon}
+ *
+ * TODO: change to an init method and inject the view.
*/
public void attach(LockIcon lockIcon) {
mLockIcon = lockIcon;
mLockIcon.setOnClickListener(this::handleClick);
mLockIcon.setOnLongClickListener(this::handleLongClick);
+ mLockIcon.setAccessibilityDelegate(mAccessibilityDelegate);
+ mLockIcon.setStateProvider(this::getState);
if (mLockIcon.isAttachedToWindow()) {
mOnAttachStateChangeListener.onViewAttachedToWindow(mLockIcon);
}
mLockIcon.addOnAttachStateChangeListener(mOnAttachStateChangeListener);
- mLockIcon.setStatusBarState(mStatusBarStateController.getState());
+ setStatusBarState(mStatusBarStateController.getState());
}
public LockIcon getView() {
@@ -228,8 +369,10 @@ public class LockscreenLockIconController {
* Called whenever the scrims become opaque, transparent or semi-transparent.
*/
public void onScrimVisibilityChanged(Integer scrimsVisible) {
- if (mLockIcon != null) {
- mLockIcon.onScrimVisibilityChanged(scrimsVisible);
+ if (mWakeAndUnlockRunning
+ && scrimsVisible == ScrimController.TRANSPARENT) {
+ mWakeAndUnlockRunning = false;
+ update();
}
}
@@ -237,55 +380,56 @@ public class LockscreenLockIconController {
* Propagate {@link StatusBar} pulsing state.
*/
public void setPulsing(boolean pulsing) {
- if (mLockIcon != null) {
- mLockIcon.setPulsing(pulsing);
- }
+ mPulsing = pulsing;
+ update();
}
/**
- * Called when the biometric authentication mode changes.
- *
- * @param wakeAndUnlock If the type is {@link BiometricUnlockController#isWakeAndUnlock()}
- * @param isUnlock If the type is {@link BiometricUnlockController#isBiometricUnlock()} ()
+ * We need to hide the lock whenever there's a fingerprint unlock, otherwise you'll see the
+ * icon on top of the black front scrim.
+ * @param wakeAndUnlock are we wake and unlocking
+ * @param isUnlock are we currently unlocking
*/
public void onBiometricAuthModeChanged(boolean wakeAndUnlock, boolean isUnlock) {
- if (mLockIcon != null) {
- mLockIcon.onBiometricAuthModeChanged(wakeAndUnlock, isUnlock);
+ if (wakeAndUnlock) {
+ mWakeAndUnlockRunning = true;
+ }
+ if (isUnlock && mKeyguardBypassController.getBypassEnabled() && canBlockUpdates()) {
+ // We don't want the icon to change while we are unlocking
+ mBlockUpdates = true;
}
+ update();
}
/**
* When we're launching an affordance, like double pressing power to open camera.
*/
public void onShowingLaunchAffordanceChanged(Boolean showing) {
- if (mLockIcon != null) {
- mLockIcon.onShowingLaunchAffordanceChanged(showing);
- }
+ mShowingLaunchAffordance = showing;
+ update();
}
/** Sets whether the bouncer is showing. */
public void setBouncerShowingScrimmed(boolean bouncerShowing) {
- if (mLockIcon != null) {
- mLockIcon.setBouncerShowingScrimmed(bouncerShowing);
+ mBouncerShowingScrimmed = bouncerShowing;
+ if (mKeyguardBypassController.getBypassEnabled()) {
+ update();
}
}
/**
- * When {@link KeyguardBouncer} starts to be dismissed and starts to play its animation.
+ * Animate padlock opening when bouncer challenge is solved.
*/
public void onBouncerPreHideAnimation() {
- if (mLockIcon != null) {
- mLockIcon.onBouncerPreHideAnimation();
- }
+ update();
}
/**
* If we're currently presenting an authentication error message.
*/
public void setTransientBiometricsError(boolean transientBiometricsError) {
- if (mLockIcon != null) {
- mLockIcon.setTransientBiometricsError(transientBiometricsError);
- }
+ mTransientBiometricsError = transientBiometricsError;
+ update();
}
private boolean handleLongClick(View view) {
@@ -306,4 +450,90 @@ public class LockscreenLockIconController {
}
mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
}
+
+ private void update() {
+ update(false /* force */);
+ }
+
+ private void update(boolean force) {
+ int state = getState();
+ boolean shouldUpdate = mLastState != state || force;
+ if (mBlockUpdates && canBlockUpdates()) {
+ shouldUpdate = false;
+ }
+ if (shouldUpdate && mLockIcon != null) {
+ mLockIcon.update(mLastState, mPulsing, mDozing, mKeyguardJustShown);
+ }
+ mLastState = state;
+ mKeyguardJustShown = false;
+ updateIconVisibility();
+ updateClickability();
+ }
+
+ private int getState() {
+ if ((mKeyguardStateController.canDismissLockScreen() || !mKeyguardShowing
+ || mKeyguardStateController.isKeyguardGoingAway()) && !mSimLocked) {
+ return STATE_LOCK_OPEN;
+ } else if (mTransientBiometricsError) {
+ return STATE_BIOMETRICS_ERROR;
+ } else if (mKeyguardUpdateMonitor.isFaceDetectionRunning() && !mPulsing) {
+ return STATE_SCANNING_FACE;
+ } else {
+ return STATE_LOCKED;
+ }
+ }
+
+ private boolean canBlockUpdates() {
+ return mKeyguardShowing || mKeyguardStateController.isKeyguardFadingAway();
+ }
+
+ private void setDozing(boolean isDozing) {
+ mDozing = isDozing;
+ update();
+ }
+
+ /** Set the StatusBarState. */
+ private void setStatusBarState(int statusBarState) {
+ mStatusBarState = statusBarState;
+ updateIconVisibility();
+ }
+
+ /**
+ * Update the icon visibility
+ * @return true if the visibility changed
+ */
+ private boolean updateIconVisibility() {
+ boolean onAodNotPulsingOrDocked = mDozing && (!mPulsing || mDocked);
+ boolean invisible = onAodNotPulsingOrDocked || mWakeAndUnlockRunning
+ || mShowingLaunchAffordance;
+ if (mKeyguardBypassController.getBypassEnabled() && !mBouncerShowingScrimmed) {
+ if ((mHeadsUpManagerPhone.isHeadsUpGoingAway()
+ || mHeadsUpManagerPhone.hasPinnedHeadsUp()
+ || mStatusBarState == StatusBarState.KEYGUARD)
+ && !mNotificationWakeUpCoordinator.getNotificationsFullyHidden()) {
+ invisible = true;
+ }
+ }
+
+ if (mLockIcon == null) {
+ return false;
+ }
+
+ return mLockIcon.updateIconVisibility(!invisible);
+ }
+
+ private void updateClickability() {
+ if (mAccessibilityController == null) {
+ return;
+ }
+ boolean canLock = mKeyguardStateController.isMethodSecure()
+ && mKeyguardStateController.canDismissLockScreen();
+ boolean clickToUnlock = mAccessibilityController.isAccessibilityEnabled();
+ if (mLockIcon != null) {
+ mLockIcon.setClickable(clickToUnlock);
+ mLockIcon.setLongClickable(canLock && !clickToUnlock);
+ mLockIcon.setFocusable(mAccessibilityController.isAccessibilityEnabled());
+ }
+ }
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 1c1e7c4eaa4a..977a307a3d95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -66,7 +66,7 @@ public class PhoneStatusBarView extends PanelBar {
}
};
private DarkReceiver mBattery;
- private int mRotationOrientation;
+ private int mRotationOrientation = -1;
@Nullable
private View mCenterIconSpace;
@Nullable
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 760a6d6a71c3..c6f79831e022 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -99,11 +99,12 @@ public class StatusBarWindowView extends FrameLayout {
}
// padding needed for corner cutout.
- int leftCornerCutoutPadding = 0;
- int rightCornerCutoutPadding = 0;
+ int leftCornerCutoutPadding = cutout.getSafeInsetLeft();
+ int rightCornerCutoutPadding = cutout.getSafeInsetRight();
if (cornerCutoutPadding != null) {
- leftCornerCutoutPadding = cornerCutoutPadding.first;
- rightCornerCutoutPadding = cornerCutoutPadding.second;
+ leftCornerCutoutPadding = Math.max(leftCornerCutoutPadding, cornerCutoutPadding.first);
+ rightCornerCutoutPadding = Math.max(rightCornerCutoutPadding,
+ cornerCutoutPadding.second);
}
return new Pair<>(
diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
index 56aae17f451e..c63712389a80 100644
--- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
+++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
@@ -34,7 +34,6 @@ import com.android.systemui.qs.QuickStatusBarHeader;
import com.android.systemui.qs.customize.QSCustomizer;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.phone.LockIcon;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -148,11 +147,6 @@ public class InjectionInflationController {
KeyguardMessageArea createKeyguardMessageArea();
/**
- * Creates the keyguard LockIcon.
- */
- LockIcon createLockIcon();
-
- /**
* Creates the QSPanel.
*/
QSPanel createQSPanel();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 6e612d7124ed..4f16031741bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -62,7 +62,9 @@ import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationRemoveInterceptor;
@@ -136,6 +138,9 @@ public class BubbleControllerTest extends SysuiTestCase {
@Mock
private FloatingContentCoordinator mFloatingContentCoordinator;
+ private SysUiState mSysUiState;
+ private boolean mSysUiStateBubblesExpanded;
+
@Captor
private ArgumentCaptor<NotificationEntryListener> mEntryListenerCaptor;
@Captor
@@ -229,6 +234,11 @@ public class BubbleControllerTest extends SysuiTestCase {
mZenModeConfig.suppressedVisualEffects = 0;
when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
+ mSysUiState = new SysUiState();
+ mSysUiState.addCallback(sysUiFlags ->
+ mSysUiStateBubblesExpanded =
+ (sysUiFlags & QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED) != 0);
+
TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
mock(PowerManager.class),
@@ -257,7 +267,8 @@ public class BubbleControllerTest extends SysuiTestCase {
mNotifPipeline,
mFeatureFlagsOldPipeline,
mDumpManager,
- mFloatingContentCoordinator);
+ mFloatingContentCoordinator,
+ mSysUiState);
mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener);
mBubbleController.setExpandListener(mBubbleExpandListener);
@@ -277,6 +288,7 @@ public class BubbleControllerTest extends SysuiTestCase {
assertTrue(mBubbleController.hasBubbles());
verify(mBubbleStateChangeListener).onHasBubblesChanged(true);
+ assertFalse(mSysUiStateBubblesExpanded);
}
@Test
@@ -284,6 +296,7 @@ public class BubbleControllerTest extends SysuiTestCase {
assertFalse(mBubbleController.hasBubbles());
mBubbleController.updateBubble(mRow.getEntry());
assertTrue(mBubbleController.hasBubbles());
+ assertFalse(mSysUiStateBubblesExpanded);
}
@Test
@@ -300,6 +313,8 @@ public class BubbleControllerTest extends SysuiTestCase {
assertNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
verify(mNotificationEntryManager, times(2)).updateNotifications(anyString());
verify(mBubbleStateChangeListener).onHasBubblesChanged(false);
+
+ assertFalse(mSysUiStateBubblesExpanded);
}
@Test
@@ -323,6 +338,8 @@ public class BubbleControllerTest extends SysuiTestCase {
verify(mNotificationEntryManager, times(1)).performRemoveNotification(
eq(mRow.getEntry().getSbn()), anyInt());
assertFalse(mBubbleController.hasBubbles());
+
+ assertFalse(mSysUiStateBubblesExpanded);
}
@Test
@@ -340,6 +357,8 @@ public class BubbleControllerTest extends SysuiTestCase {
verify(mNotificationEntryManager, times(3)).updateNotifications(any());
assertNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
assertNull(mBubbleData.getBubbleWithKey(mRow2.getEntry().getKey()));
+
+ assertFalse(mSysUiStateBubblesExpanded);
}
@Test
@@ -363,6 +382,8 @@ public class BubbleControllerTest extends SysuiTestCase {
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
assertTrue(mNotificationShadeWindowController.getBubbleExpanded());
+ assertTrue(mSysUiStateBubblesExpanded);
+
// Make sure the notif is suppressed
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
@@ -372,6 +393,8 @@ public class BubbleControllerTest extends SysuiTestCase {
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().getKey());
assertFalse(mBubbleController.isStackExpanded());
assertFalse(mNotificationShadeWindowController.getBubbleExpanded());
+
+ assertFalse(mSysUiStateBubblesExpanded);
}
@Test
@@ -395,6 +418,8 @@ public class BubbleControllerTest extends SysuiTestCase {
assertTrue(mBubbleController.isStackExpanded());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey());
+ assertTrue(mSysUiStateBubblesExpanded);
+
// Last added is the one that is expanded
assertEquals(mRow2.getEntry(), mBubbleData.getSelectedBubble().getEntry());
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
@@ -416,6 +441,8 @@ public class BubbleControllerTest extends SysuiTestCase {
// Collapse
mBubbleController.collapseStack();
assertFalse(mBubbleController.isStackExpanded());
+
+ assertFalse(mSysUiStateBubblesExpanded);
}
@Test
@@ -437,6 +464,8 @@ public class BubbleControllerTest extends SysuiTestCase {
assertTrue(mBubbleController.isStackExpanded());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
+ assertTrue(mSysUiStateBubblesExpanded);
+
// Notif is suppressed after expansion
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
@@ -463,6 +492,8 @@ public class BubbleControllerTest extends SysuiTestCase {
assertTrue(mBubbleController.isStackExpanded());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
+ assertTrue(mSysUiStateBubblesExpanded);
+
// Notif is suppressed after expansion
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
@@ -493,6 +524,8 @@ public class BubbleControllerTest extends SysuiTestCase {
BubbleStackView stackView = mBubbleController.getStackView();
mBubbleController.expandStack();
+ assertTrue(mSysUiStateBubblesExpanded);
+
assertTrue(mBubbleController.isStackExpanded());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey());
@@ -522,6 +555,8 @@ public class BubbleControllerTest extends SysuiTestCase {
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().getKey());
verify(mBubbleStateChangeListener).onHasBubblesChanged(false);
assertFalse(mBubbleController.hasBubbles());
+
+ assertFalse(mSysUiStateBubblesExpanded);
}
@Test
@@ -541,6 +576,8 @@ public class BubbleControllerTest extends SysuiTestCase {
// # of bubbles should change
verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
+
+ assertFalse(mSysUiStateBubblesExpanded);
}
@Test
@@ -559,6 +596,8 @@ public class BubbleControllerTest extends SysuiTestCase {
// # of bubbles should change
verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
+
+ assertTrue(mSysUiStateBubblesExpanded);
}
@Test
@@ -579,6 +618,8 @@ public class BubbleControllerTest extends SysuiTestCase {
// # of bubbles should change
verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
+
+ assertFalse(mSysUiStateBubblesExpanded);
}
@Test
@@ -605,6 +646,8 @@ public class BubbleControllerTest extends SysuiTestCase {
// # of bubbles should change
verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
+
+ assertFalse(mSysUiStateBubblesExpanded);
}
@Test
@@ -619,6 +662,8 @@ public class BubbleControllerTest extends SysuiTestCase {
mRow.getEntry().getKey(), mRow.getEntry(), REASON_APP_CANCEL);
mBubbleController.expandStackAndSelectBubble(key);
+
+ assertTrue(mSysUiStateBubblesExpanded);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
index 624464401a4a..a31e3f8d7cc9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -58,6 +58,7 @@ import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -132,6 +133,9 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
private KeyguardBypassController mKeyguardBypassController;
@Mock
private FloatingContentCoordinator mFloatingContentCoordinator;
+
+ private SysUiState mSysUiState = new SysUiState();
+
@Captor
private ArgumentCaptor<NotifCollectionListener> mNotifListenerCaptor;
private TestableBubbleController mBubbleController;
@@ -242,7 +246,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
mNotifPipeline,
mFeatureFlagsNewPipeline,
mDumpManager,
- mFloatingContentCoordinator);
+ mFloatingContentCoordinator,
+ mSysUiState);
mBubbleController.addNotifCallback(mNotifCallback);
mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener);
mBubbleController.setExpandListener(mBubbleExpandListener);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
index d3d90c408468..f4861028e81a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
@@ -19,6 +19,7 @@ package com.android.systemui.bubbles;
import android.content.Context;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -52,12 +53,13 @@ public class TestableBubbleController extends BubbleController {
NotifPipeline notifPipeline,
FeatureFlags featureFlags,
DumpManager dumpManager,
- FloatingContentCoordinator floatingContentCoordinator) {
+ FloatingContentCoordinator floatingContentCoordinator,
+ SysUiState sysUiState) {
super(context,
notificationShadeWindowController, statusBarStateController, shadeController,
data, Runnable::run, configurationController, interruptionStateProvider,
zenModeController, lockscreenUserManager, groupManager, entryManager,
- notifPipeline, featureFlags, dumpManager, floatingContentCoordinator);
+ notifPipeline, featureFlags, dumpManager, floatingContentCoordinator, sysUiState);
setInflateSynchronously(true);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index f061f34072d0..f4583f99f2d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -22,7 +22,6 @@ import android.testing.TestableLooper.RunWithLooper
import android.view.Choreographer
import android.view.View
import android.view.ViewRootImpl
-import androidx.dynamicanimation.animation.SpringAnimation
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
@@ -35,10 +34,14 @@ import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
-import org.mockito.Mockito.*
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyFloat
+import org.mockito.Mockito.anyString
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
@RunWith(AndroidTestingRunner::class)
@@ -56,7 +59,8 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
@Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var root: View
@Mock private lateinit var viewRootImpl: ViewRootImpl
- @Mock private lateinit var shadeSpring: SpringAnimation
+ @Mock private lateinit var shadeSpring: NotificationShadeDepthController.DepthAnimation
+ @Mock private lateinit var globalActionsSpring: NotificationShadeDepthController.DepthAnimation
@JvmField @Rule val mockitoRule = MockitoJUnit.rule()
private lateinit var statusBarStateListener: StatusBarStateController.StateListener
@@ -76,6 +80,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
keyguardStateController, choreographer, wallpaperManager,
notificationShadeWindowController, dumpManager)
notificationShadeDepthController.shadeSpring = shadeSpring
+ notificationShadeDepthController.globalActionsSpring = globalActionsSpring
notificationShadeDepthController.root = root
val captor = ArgumentCaptor.forClass(StatusBarStateController.StateListener::class.java)
@@ -92,7 +97,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
fun onPanelExpansionChanged_apliesBlur_ifShade() {
notificationShadeDepthController.onPanelExpansionChanged(1f /* expansion */,
false /* tracking */)
- verify(shadeSpring).animateToFinalPosition(eq(maxBlur.toFloat()))
+ verify(shadeSpring).animateTo(eq(maxBlur), any())
}
@Test
@@ -102,13 +107,13 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
statusBarState = StatusBarState.KEYGUARD
statusBarStateListener.onStateChanged(statusBarState)
- verify(shadeSpring).animateToFinalPosition(eq(0f))
+ verify(shadeSpring).animateTo(eq(0), any())
}
@Test
- fun updateGlobalDialogVisibility_schedulesUpdate() {
+ fun updateGlobalDialogVisibility_appliesBlur() {
notificationShadeDepthController.updateGlobalDialogVisibility(0.5f, root)
- verify(choreographer).postFrameCallback(any())
+ verify(globalActionsSpring).animateTo(eq(maxBlur / 2), safeEq(root))
}
private fun <T : Any> safeEq(value: T): T {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
index 261dc829c7e2..f4fbd7b7d8a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
@@ -48,7 +48,7 @@ public class NotificationEntryBuilder {
/* ListEntry properties */
private GroupEntry mParent;
- private int mSection;
+ private int mSection = -1;
public NotificationEntry build() {
StatusBarNotification sbn = mSbn != null ? mSbn : mSbnBuilder.build();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinatorTest.java
new file mode 100644
index 000000000000..87fc02062ad4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinatorTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.util.SparseArray;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable.PluggableListener;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class HideNotifsForOtherUsersCoordinatorTest extends SysuiTestCase {
+
+ @Mock private NotificationLockscreenUserManager mLockscreenUserManager;
+ @Mock private NotifPipeline mNotifPipeline;
+ @Mock private PluggableListener<NotifFilter> mInvalidationListener;
+
+ @Captor private ArgumentCaptor<UserChangedListener> mUserChangedListenerCaptor;
+ @Captor private ArgumentCaptor<NotifFilter> mNotifFilterCaptor;
+
+ private UserChangedListener mCapturedUserChangeListener;
+ private NotifFilter mCapturedNotifFilter;
+
+ private NotificationEntry mEntry = new NotificationEntryBuilder().build();
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ HideNotifsForOtherUsersCoordinator coordinator =
+ new HideNotifsForOtherUsersCoordinator(mLockscreenUserManager);
+ coordinator.attach(mNotifPipeline);
+
+ verify(mLockscreenUserManager).addUserChangedListener(mUserChangedListenerCaptor.capture());
+ verify(mNotifPipeline).addPreGroupFilter(mNotifFilterCaptor.capture());
+
+ mCapturedUserChangeListener = mUserChangedListenerCaptor.getValue();
+ mCapturedNotifFilter = mNotifFilterCaptor.getValue();
+
+ mCapturedNotifFilter.setInvalidationListener(mInvalidationListener);
+ }
+
+ @Test
+ public void testFilterOutNotifsFromOtherProfiles() {
+ // GIVEN that all notifs are NOT for the current user
+ when(mLockscreenUserManager.isCurrentProfile(anyInt())).thenReturn(false);
+
+ // THEN they should all be filtered out
+ assertTrue(mCapturedNotifFilter.shouldFilterOut(mEntry, 0));
+ }
+
+ @Test
+ public void testPreserveNotifsFromThisProfile() {
+ // GIVEN that all notifs ARE for the current user
+ when(mLockscreenUserManager.isCurrentProfile(anyInt())).thenReturn(true);
+
+ // THEN none should be filtered out
+ assertFalse(mCapturedNotifFilter.shouldFilterOut(mEntry, 0));
+ }
+
+ @Test
+ public void testFilterIsInvalidatedWhenProfilesChange() {
+ // WHEN the current user profiles change
+ mCapturedUserChangeListener.onCurrentProfilesChanged(new SparseArray<>());
+
+ // THEN the filter is invalidated
+ verify(mInvalidationListener).onPluggableInvalidated(mCapturedNotifFilter);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
index c4f3a1611afc..4f481081855f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
@@ -102,16 +102,6 @@ public class KeyguardCoordinatorTest extends SysuiTestCase {
}
@Test
- public void notificationNotForCurrentProfile() {
- // GIVEN the notification isn't for the given user
- setupUnfilteredState(mEntry);
- when(mLockscreenUserManager.isCurrentProfile(NOTIF_USER_ID)).thenReturn(false);
-
- // THEN filter out the entry
- assertTrue(mKeyguardFilter.shouldFilterOut(mEntry, 0));
- }
-
- @Test
public void keyguardNotShowing() {
// GIVEN the lockscreen isn't showing
setupUnfilteredState(mEntry);
@@ -229,9 +219,6 @@ public class KeyguardCoordinatorTest extends SysuiTestCase {
* KeyguardNotificationCoordinator when the keyguard is showing.
*/
private void setupUnfilteredState(NotificationEntry entry) {
- // notification is for current profile
- when(mLockscreenUserManager.isCurrentProfile(NOTIF_USER_ID)).thenReturn(true);
-
// keyguard is showing
when(mKeyguardStateController.isShowing()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
index e84f9cf352ed..85acbe6d440b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
@@ -19,9 +19,8 @@ package com.android.systemui.statusbar.notification.collection.coordinator;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -42,6 +41,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -51,20 +51,23 @@ public class RankingCoordinatorTest extends SysuiTestCase {
@Mock private StatusBarStateController mStatusBarStateController;
@Mock private NotifPipeline mNotifPipeline;
+
+ @Captor private ArgumentCaptor<NotifFilter> mNotifFilterCaptor;
+
private NotificationEntry mEntry;
- private RankingCoordinator mRankingCoordinator;
- private NotifFilter mRankingFilter;
+ private NotifFilter mCapturedSuspendedFilter;
+ private NotifFilter mCapturedDozingFilter;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mRankingCoordinator = new RankingCoordinator(mStatusBarStateController);
+ RankingCoordinator rankingCoordinator = new RankingCoordinator(mStatusBarStateController);
mEntry = new NotificationEntryBuilder().build();
- ArgumentCaptor<NotifFilter> filterCaptor = ArgumentCaptor.forClass(NotifFilter.class);
- mRankingCoordinator.attach(mNotifPipeline);
- verify(mNotifPipeline, times(1)).addPreGroupFilter(filterCaptor.capture());
- mRankingFilter = filterCaptor.getValue();
+ rankingCoordinator.attach(mNotifPipeline);
+ verify(mNotifPipeline, times(2)).addPreGroupFilter(mNotifFilterCaptor.capture());
+ mCapturedSuspendedFilter = mNotifFilterCaptor.getAllValues().get(0);
+ mCapturedDozingFilter = mNotifFilterCaptor.getAllValues().get(1);
}
@Test
@@ -73,7 +76,7 @@ public class RankingCoordinatorTest extends SysuiTestCase {
mEntry.setRanking(getRankingForUnfilteredNotif().build());
// THEN don't filter out the notification
- assertFalse(mRankingFilter.shouldFilterOut(mEntry, 0));
+ assertFalse(mCapturedSuspendedFilter.shouldFilterOut(mEntry, 0));
}
@Test
@@ -84,7 +87,7 @@ public class RankingCoordinatorTest extends SysuiTestCase {
.build());
// THEN filter out the notification
- assertTrue(mRankingFilter.shouldFilterOut(mEntry, 0));
+ assertTrue(mCapturedSuspendedFilter.shouldFilterOut(mEntry, 0));
}
@Test
@@ -98,13 +101,13 @@ public class RankingCoordinatorTest extends SysuiTestCase {
when(mStatusBarStateController.isDozing()).thenReturn(true);
// THEN filter out the notification
- assertTrue(mRankingFilter.shouldFilterOut(mEntry, 0));
+ assertTrue(mCapturedDozingFilter.shouldFilterOut(mEntry, 0));
// WHEN it's not dozing (showing the notification list)
when(mStatusBarStateController.isDozing()).thenReturn(false);
// THEN don't filter out the notification
- assertFalse(mRankingFilter.shouldFilterOut(mEntry, 0));
+ assertFalse(mCapturedDozingFilter.shouldFilterOut(mEntry, 0));
}
@Test
@@ -118,13 +121,13 @@ public class RankingCoordinatorTest extends SysuiTestCase {
when(mStatusBarStateController.isDozing()).thenReturn(true);
// THEN don't filter out the notification
- assertFalse(mRankingFilter.shouldFilterOut(mEntry, 0));
+ assertFalse(mCapturedDozingFilter.shouldFilterOut(mEntry, 0));
// WHEN it's not dozing (showing the notification list)
when(mStatusBarStateController.isDozing()).thenReturn(false);
// THEN filter out the notification
- assertTrue(mRankingFilter.shouldFilterOut(mEntry, 0));
+ assertTrue(mCapturedDozingFilter.shouldFilterOut(mEntry, 0));
}
private RankingBuilder getRankingForUnfilteredNotif() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java
index 487885ac244e..85b5d70c883c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java
@@ -21,6 +21,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.res.Resources;
import android.view.View;
import androidx.test.filters.SmallTest;
@@ -35,6 +36,7 @@ import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.policy.AccessibilityController;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
import org.junit.Test;
@@ -71,6 +73,12 @@ public class LockscreenIconControllerTest extends SysuiTestCase {
private KeyguardBypassController mKeyguardBypassController;
@Mock
private DockManager mDockManager;
+ @Mock
+ private KeyguardStateController mKeyguardStateController;
+ @Mock
+ private Resources mResources;
+ @Mock
+ private HeadsUpManagerPhone mHeadsUpManagerPhone;
@Before
@@ -81,7 +89,8 @@ public class LockscreenIconControllerTest extends SysuiTestCase {
mLockscreenGestureLogger, mKeyguardUpdateMonitor, mLockPatternUtils,
mShadeController, mAccessibilityController, mKeyguardIndicationController,
mStatusBarStateController, mConfigurationController, mNotificationWakeUpCoordinator,
- mKeyguardBypassController, mDockManager);
+ mKeyguardBypassController, mDockManager, mKeyguardStateController, mResources,
+ mHeadsUpManagerPhone);
mLockIconController.attach(mLockIcon);
}
diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp
index 2fbba68f1e03..00d0d9c428ff 100644
--- a/packages/Tethering/common/TetheringLib/Android.bp
+++ b/packages/Tethering/common/TetheringLib/Android.bp
@@ -60,6 +60,7 @@ java_library {
hostdex: true, // for hiddenapi check
visibility: ["//frameworks/base/packages/Tethering:__subpackages__"],
apex_available: ["com.android.tethering"],
+ permitted_packages: ["android.net"],
}
stubs_defaults {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index e49c1ed47c93..c6a54fc3d206 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -50,6 +50,7 @@ import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.DeviceConfig;
@@ -63,6 +64,7 @@ import android.util.LocalLog;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import android.util.TimeUtils;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillManager.SmartSuggestionMode;
@@ -151,6 +153,7 @@ public final class AutofillManagerService
private final LocalLog mWtfHistory = new LocalLog(50);
private final AutofillCompatState mAutofillCompatState = new AutofillCompatState();
+ private final DisabledInfoCache mDisabledInfoCache = new DisabledInfoCache();
private final LocalService mLocalService = new LocalService();
private final ActivityManagerInternal mAm;
@@ -302,14 +305,15 @@ public final class AutofillManagerService
@Override // from AbstractMasterSystemService
protected AutofillManagerServiceImpl newServiceLocked(@UserIdInt int resolvedUserId,
boolean disabled) {
- return new AutofillManagerServiceImpl(this, mLock, mUiLatencyHistory,
- mWtfHistory, resolvedUserId, mUi, mAutofillCompatState, disabled);
+ return new AutofillManagerServiceImpl(this, mLock, mUiLatencyHistory, mWtfHistory,
+ resolvedUserId, mUi, mAutofillCompatState, disabled, mDisabledInfoCache);
}
@Override // AbstractMasterSystemService
protected void onServiceRemoved(@NonNull AutofillManagerServiceImpl service,
@UserIdInt int userId) {
service.destroyLocked();
+ mDisabledInfoCache.remove(userId);
mAutofillCompatState.removeCompatibilityModeRequests(userId);
}
@@ -835,15 +839,10 @@ public final class AutofillManagerService
private void injectDisableAppInfo(@NonNull AutofillOptions options, int userId,
String packageName) {
- synchronized (mLock) {
- final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
- if (service != null) {
- options.appDisabledExpiration = service.getAppDisabledExpirationLocked(
- packageName);
- options.disabledActivities = service.getAppDisabledActivitiesLocked(
- packageName);
- }
- }
+ options.appDisabledExpiration =
+ mDisabledInfoCache.getAppDisabledExpiration(userId, packageName);
+ options.disabledActivities =
+ mDisabledInfoCache.getAppDisabledActivities(userId, packageName);
}
}
@@ -867,6 +866,234 @@ public final class AutofillManagerService
}
/**
+ * Stores autofill disable information, i.e. {@link AutofillDisabledInfo}, keyed by user id.
+ * The information is cleaned up when the service is removed.
+ */
+ static final class DisabledInfoCache {
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private final SparseArray<AutofillDisabledInfo> mCache = new SparseArray<>();
+
+ void remove(@UserIdInt int userId) {
+ synchronized (mLock) {
+ mCache.remove(userId);
+ }
+ }
+
+ void addDisabledAppLocked(@UserIdInt int userId, @NonNull String packageName,
+ long expiration) {
+ Preconditions.checkNotNull(packageName);
+ synchronized (mLock) {
+ AutofillDisabledInfo info =
+ getOrCreateAutofillDisabledInfoByUserIdLocked(userId);
+ info.putDisableAppsLocked(packageName, expiration);
+ }
+ }
+
+ void addDisabledActivityLocked(@UserIdInt int userId, @NonNull ComponentName componentName,
+ long expiration) {
+ Preconditions.checkNotNull(componentName);
+ synchronized (mLock) {
+ AutofillDisabledInfo info =
+ getOrCreateAutofillDisabledInfoByUserIdLocked(userId);
+ info.putDisableActivityLocked(componentName, expiration);
+ }
+ }
+
+ boolean isAutofillDisabledLocked(@UserIdInt int userId,
+ @NonNull ComponentName componentName) {
+ Preconditions.checkNotNull(componentName);
+ final boolean disabled;
+ synchronized (mLock) {
+ final AutofillDisabledInfo info = mCache.get(userId);
+ disabled = info != null ? info.isAutofillDisabledLocked(componentName) : false;
+ }
+ return disabled;
+ }
+
+ long getAppDisabledExpiration(@UserIdInt int userId, @NonNull String packageName) {
+ Preconditions.checkNotNull(packageName);
+ final Long expiration;
+ synchronized (mLock) {
+ final AutofillDisabledInfo info = mCache.get(userId);
+ expiration = info != null ? info.getAppDisabledExpirationLocked(packageName) : 0;
+ }
+ return expiration;
+ }
+
+ @Nullable
+ ArrayMap<String, Long> getAppDisabledActivities(@UserIdInt int userId,
+ @NonNull String packageName) {
+ Preconditions.checkNotNull(packageName);
+ final ArrayMap<String, Long> disabledList;
+ synchronized (mLock) {
+ final AutofillDisabledInfo info = mCache.get(userId);
+ disabledList =
+ info != null ? info.getAppDisabledActivitiesLocked(packageName) : null;
+ }
+ return disabledList;
+ }
+
+ void dump(@UserIdInt int userId, String prefix, PrintWriter pw) {
+ synchronized (mLock) {
+ final AutofillDisabledInfo info = mCache.get(userId);
+ if (info != null) {
+ info.dumpLocked(prefix, pw);
+ }
+ }
+ }
+
+ @NonNull
+ private AutofillDisabledInfo getOrCreateAutofillDisabledInfoByUserIdLocked(
+ @UserIdInt int userId) {
+ AutofillDisabledInfo info = mCache.get(userId);
+ if (info == null) {
+ info = new AutofillDisabledInfo();
+ mCache.put(userId, info);
+ }
+ return info;
+ }
+ }
+
+ /**
+ * The autofill disable information.
+ * <p>
+ * This contains disable information set by the AutofillService, e.g. disabled application
+ * expiration, disable activity expiration.
+ */
+ private static final class AutofillDisabledInfo {
+ /**
+ * Apps disabled by the service; key is package name, value is when they will be enabled
+ * again.
+ */
+ private ArrayMap<String, Long> mDisabledApps;
+ /**
+ * Activities disabled by the service; key is component name, value is when they will be
+ * enabled again.
+ */
+ private ArrayMap<ComponentName, Long> mDisabledActivities;
+
+ void putDisableAppsLocked(@NonNull String packageName, long expiration) {
+ if (mDisabledApps == null) {
+ mDisabledApps = new ArrayMap<>(1);
+ }
+ mDisabledApps.put(packageName, expiration);
+ }
+
+ void putDisableActivityLocked(@NonNull ComponentName componentName, long expiration) {
+ if (mDisabledActivities == null) {
+ mDisabledActivities = new ArrayMap<>(1);
+ }
+ mDisabledActivities.put(componentName, expiration);
+ }
+
+ long getAppDisabledExpirationLocked(@NonNull String packageName) {
+ if (mDisabledApps == null) {
+ return 0;
+ }
+ final Long expiration = mDisabledApps.get(packageName);
+ return expiration != null ? expiration : 0;
+ }
+
+ ArrayMap<String, Long> getAppDisabledActivitiesLocked(@NonNull String packageName) {
+ if (mDisabledActivities != null) {
+ final int size = mDisabledActivities.size();
+ ArrayMap<String, Long> disabledList = null;
+ for (int i = 0; i < size; i++) {
+ final ComponentName component = mDisabledActivities.keyAt(i);
+ if (packageName.equals(component.getPackageName())) {
+ if (disabledList == null) {
+ disabledList = new ArrayMap<>();
+ }
+ final long expiration = mDisabledActivities.valueAt(i);
+ disabledList.put(component.flattenToShortString(), expiration);
+ }
+ }
+ return disabledList;
+ }
+ return null;
+ }
+
+ boolean isAutofillDisabledLocked(@NonNull ComponentName componentName) {
+ // Check activities first.
+ long elapsedTime = 0;
+ if (mDisabledActivities != null) {
+ elapsedTime = SystemClock.elapsedRealtime();
+ final Long expiration = mDisabledActivities.get(componentName);
+ if (expiration != null) {
+ if (expiration >= elapsedTime) return true;
+ // Restriction expired - clean it up.
+ if (sVerbose) {
+ Slog.v(TAG, "Removing " + componentName.toShortString()
+ + " from disabled list");
+ }
+ mDisabledActivities.remove(componentName);
+ }
+ }
+
+ // Then check apps.
+ final String packageName = componentName.getPackageName();
+ if (mDisabledApps == null) return false;
+
+ final Long expiration = mDisabledApps.get(packageName);
+ if (expiration == null) return false;
+
+ if (elapsedTime == 0) {
+ elapsedTime = SystemClock.elapsedRealtime();
+ }
+
+ if (expiration >= elapsedTime) return true;
+
+ // Restriction expired - clean it up.
+ if (sVerbose) Slog.v(TAG, "Removing " + packageName + " from disabled list");
+ mDisabledApps.remove(packageName);
+ return false;
+ }
+
+ void dumpLocked(String prefix, PrintWriter pw) {
+ pw.print(prefix); pw.print("Disabled apps: ");
+ if (mDisabledApps == null) {
+ pw.println("N/A");
+ } else {
+ final int size = mDisabledApps.size();
+ pw.println(size);
+ final StringBuilder builder = new StringBuilder();
+ final long now = SystemClock.elapsedRealtime();
+ for (int i = 0; i < size; i++) {
+ final String packageName = mDisabledApps.keyAt(i);
+ final long expiration = mDisabledApps.valueAt(i);
+ builder.append(prefix).append(prefix)
+ .append(i).append(". ").append(packageName).append(": ");
+ TimeUtils.formatDuration((expiration - now), builder);
+ builder.append('\n');
+ }
+ pw.println(builder);
+ }
+
+ pw.print(prefix); pw.print("Disabled activities: ");
+ if (mDisabledActivities == null) {
+ pw.println("N/A");
+ } else {
+ final int size = mDisabledActivities.size();
+ pw.println(size);
+ final StringBuilder builder = new StringBuilder();
+ final long now = SystemClock.elapsedRealtime();
+ for (int i = 0; i < size; i++) {
+ final ComponentName component = mDisabledActivities.keyAt(i);
+ final long expiration = mDisabledActivities.valueAt(i);
+ builder.append(prefix).append(prefix)
+ .append(i).append(". ").append(component).append(": ");
+ TimeUtils.formatDuration((expiration - now), builder);
+ builder.append('\n');
+ }
+ pw.println(builder);
+ }
+ }
+ }
+
+ /**
* Compatibility mode metadata associated with all services.
*
* <p>This object is defined here instead of on each {@link AutofillManagerServiceImpl} because
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 6fbe1410bbad..d1805d96cad8 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -67,7 +67,6 @@ import android.util.LocalLog;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.TimeUtils;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillManager.SmartSuggestionMode;
@@ -80,6 +79,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.server.LocalServices;
import com.android.server.autofill.AutofillManagerService.AutofillCompatState;
+import com.android.server.autofill.AutofillManagerService.DisabledInfoCache;
import com.android.server.autofill.RemoteAugmentedAutofillService.RemoteAugmentedAutofillServiceCallbacks;
import com.android.server.autofill.ui.AutoFillUI;
import com.android.server.contentcapture.ContentCaptureManagerInternal;
@@ -90,7 +90,6 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
-
/**
* Bridge between the {@code system_server}'s {@link AutofillManagerService} and the
* app's {@link IAutoFillService} implementation.
@@ -125,19 +124,6 @@ final class AutofillManagerServiceImpl
private RemoteInlineSuggestionRenderService mRemoteInlineSuggestionRenderService;
/**
- * Apps disabled by the service; key is package name, value is when they will be enabled again.
- */
- @GuardedBy("mLock")
- private ArrayMap<String, Long> mDisabledApps;
-
- /**
- * Activities disabled by the service; key is component name, value is when they will be enabled
- * again.
- */
- @GuardedBy("mLock")
- private ArrayMap<ComponentName, Long> mDisabledActivities;
-
- /**
* Data used for field classification.
*/
@GuardedBy("mLock")
@@ -186,10 +172,12 @@ final class AutofillManagerServiceImpl
private final ContentCaptureManagerInternal mContentCaptureManagerInternal;
+ private final DisabledInfoCache mDisabledInfoCache;
+
AutofillManagerServiceImpl(AutofillManagerService master, Object lock,
LocalLog uiLatencyHistory, LocalLog wtfHistory, int userId, AutoFillUI ui,
AutofillCompatState autofillCompatState,
- boolean disabled) {
+ boolean disabled, DisabledInfoCache disableCache) {
super(master, lock, userId);
mUiLatencyHistory = uiLatencyHistory;
@@ -200,7 +188,7 @@ final class AutofillManagerServiceImpl
mInputMethodManagerInternal = LocalServices.getService(InputMethodManagerInternal.class);
mContentCaptureManagerInternal = LocalServices.getService(
ContentCaptureManagerInternal.class);
-
+ mDisabledInfoCache = disableCache;
updateLocked(disabled);
}
@@ -1045,45 +1033,7 @@ final class AutofillManagerServiceImpl
pw.println(isInlineSuggestionsEnabled());
pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune);
- pw.print(prefix); pw.print("Disabled apps: ");
-
- if (mDisabledApps == null) {
- pw.println("N/A");
- } else {
- final int size = mDisabledApps.size();
- pw.println(size);
- final StringBuilder builder = new StringBuilder();
- final long now = SystemClock.elapsedRealtime();
- for (int i = 0; i < size; i++) {
- final String packageName = mDisabledApps.keyAt(i);
- final long expiration = mDisabledApps.valueAt(i);
- builder.append(prefix).append(prefix)
- .append(i).append(". ").append(packageName).append(": ");
- TimeUtils.formatDuration((expiration - now), builder);
- builder.append('\n');
- }
- pw.println(builder);
- }
-
- pw.print(prefix); pw.print("Disabled activities: ");
-
- if (mDisabledActivities == null) {
- pw.println("N/A");
- } else {
- final int size = mDisabledActivities.size();
- pw.println(size);
- final StringBuilder builder = new StringBuilder();
- final long now = SystemClock.elapsedRealtime();
- for (int i = 0; i < size; i++) {
- final ComponentName component = mDisabledActivities.keyAt(i);
- final long expiration = mDisabledActivities.valueAt(i);
- builder.append(prefix).append(prefix)
- .append(i).append(". ").append(component).append(": ");
- TimeUtils.formatDuration((expiration - now), builder);
- builder.append('\n');
- }
- pw.println(builder);
- }
+ mDisabledInfoCache.dump(mUserId, prefix, pw);
final int size = mSessions.size();
if (size == 0) {
@@ -1480,15 +1430,13 @@ final class AutofillManagerServiceImpl
void disableAutofillForApp(@NonNull String packageName, long duration, int sessionId,
boolean compatMode) {
synchronized (mLock) {
- if (mDisabledApps == null) {
- mDisabledApps = new ArrayMap<>(1);
- }
long expiration = SystemClock.elapsedRealtime() + duration;
// Protect it against overflow
if (expiration < 0) {
expiration = Long.MAX_VALUE;
}
- mDisabledApps.put(packageName, expiration);
+ mDisabledInfoCache.addDisabledAppLocked(mUserId, packageName, expiration);
+
int intDuration = duration > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) duration;
mMetricsLogger.write(Helper.newLogMaker(MetricsEvent.AUTOFILL_SERVICE_DISABLED_APP,
packageName, getServicePackageName(), sessionId, compatMode)
@@ -1502,15 +1450,12 @@ final class AutofillManagerServiceImpl
void disableAutofillForActivity(@NonNull ComponentName componentName, long duration,
int sessionId, boolean compatMode) {
synchronized (mLock) {
- if (mDisabledActivities == null) {
- mDisabledActivities = new ArrayMap<>(1);
- }
long expiration = SystemClock.elapsedRealtime() + duration;
// Protect it against overflow
if (expiration < 0) {
expiration = Long.MAX_VALUE;
}
- mDisabledActivities.put(componentName, expiration);
+ mDisabledInfoCache.addDisabledActivityLocked(mUserId, componentName, expiration);
final int intDuration = duration > Integer.MAX_VALUE
? Integer.MAX_VALUE
: (int) duration;
@@ -1528,74 +1473,12 @@ final class AutofillManagerServiceImpl
}
}
- // Called by AutofillManagerService
- long getAppDisabledExpirationLocked(@NonNull String packageName) {
- if (mDisabledApps == null) {
- return 0;
- }
- final Long expiration = mDisabledApps.get(packageName);
- return expiration != null ? expiration : 0;
- }
-
- // Called by AutofillManagerService
- @Nullable
- ArrayMap<String, Long> getAppDisabledActivitiesLocked(@NonNull String packageName) {
- if (mDisabledActivities != null) {
- final int size = mDisabledActivities.size();
- ArrayMap<String, Long> disabledList = null;
- for (int i = 0; i < size; i++) {
- final ComponentName component = mDisabledActivities.keyAt(i);
- if (packageName.equals(component.getPackageName())) {
- if (disabledList == null) {
- disabledList = new ArrayMap<>();
- }
- final long expiration = mDisabledActivities.valueAt(i);
- disabledList.put(component.flattenToShortString(), expiration);
- }
- }
- return disabledList;
- }
- return null;
- }
-
/**
* Checks if autofill is disabled by service to the given activity.
*/
@GuardedBy("mLock")
private boolean isAutofillDisabledLocked(@NonNull ComponentName componentName) {
- // Check activities first.
- long elapsedTime = 0;
- if (mDisabledActivities != null) {
- elapsedTime = SystemClock.elapsedRealtime();
- final Long expiration = mDisabledActivities.get(componentName);
- if (expiration != null) {
- if (expiration >= elapsedTime) return true;
- // Restriction expired - clean it up.
- if (sVerbose) {
- Slog.v(TAG, "Removing " + componentName.toShortString()
- + " from disabled list");
- }
- mDisabledActivities.remove(componentName);
- }
- }
-
- // Then check apps.
- final String packageName = componentName.getPackageName();
- if (mDisabledApps == null) return false;
-
- final Long expiration = mDisabledApps.get(packageName);
- if (expiration == null) return false;
-
- if (elapsedTime == 0) {
- elapsedTime = SystemClock.elapsedRealtime();
- }
-
- if (expiration >= elapsedTime) return true;
-
- // Restriction expired - clean it up.
- if (sVerbose) Slog.v(TAG, "Removing " + packageName + " from disabled list");
- mDisabledApps.remove(packageName);
- return false;
+ return mDisabledInfoCache.isAutofillDisabledLocked(mUserId, componentName);
}
// Called by AutofillManager, checks UID.
diff --git a/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java b/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
index 5de817101177..4ba2c3d0a0d3 100644
--- a/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
+++ b/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
@@ -199,7 +199,10 @@ final class InlineSuggestionSession {
return false;
}
- if (!mImeInputViewStarted || !autofillId.equalsIgnoreSession(mImeFieldId)) {
+ // TODO(b/151846600): IME doesn't have access to the virtual id of the webview, so we
+ // only compare the view id for now.
+ if (!mImeInputViewStarted || mImeFieldId == null
+ || autofillId.getViewId() != mImeFieldId.getViewId()) {
if (sDebug) {
Log.d(TAG,
"onInlineSuggestionsResponseLocked not sent because input view is not "
diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java
index b54ec4ea9441..0fdabd09f9d4 100644
--- a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java
+++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java
@@ -21,13 +21,17 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityManager;
import android.app.contentsuggestions.ClassificationsRequest;
+import android.app.contentsuggestions.ContentSuggestionsManager;
import android.app.contentsuggestions.IClassificationsCallback;
import android.app.contentsuggestions.IContentSuggestionsManager;
import android.app.contentsuggestions.ISelectionsCallback;
import android.app.contentsuggestions.SelectionsRequest;
import android.content.Context;
import android.graphics.Bitmap;
+import android.graphics.ColorSpace;
+import android.graphics.GraphicBuffer;
import android.os.Binder;
import android.os.Bundle;
import android.os.RemoteException;
@@ -133,7 +137,7 @@ public class ContentSuggestionsManagerService extends
if (service != null) {
// TODO(b/147324195): Temporarily pass bitmap until we change the service API.
imageContextRequestExtras.putParcelable(EXTRA_BITMAP, bitmap);
- service.provideContextImageLocked(/* taskId = */ -1, imageContextRequestExtras);
+ service.provideContextImageFromBitmapLocked(imageContextRequestExtras);
} else {
if (VERBOSE) {
Slog.v(TAG, "provideContextImageLocked: no service for " + userId);
@@ -152,10 +156,28 @@ public class ContentSuggestionsManagerService extends
}
enforceCaller(UserHandle.getCallingUserId(), "provideContextImage");
+ GraphicBuffer snapshotBuffer = null;
+ int colorSpaceId = 0;
+
+ // Skip taking TaskSnapshot when bitmap is provided.
+ if (!imageContextRequestExtras.containsKey(ContentSuggestionsManager.EXTRA_BITMAP)) {
+ // Can block, so call before acquiring the lock.
+ ActivityManager.TaskSnapshot snapshot =
+ mActivityTaskManagerInternal.getTaskSnapshotBlocking(taskId, false);
+ if (snapshot != null) {
+ snapshotBuffer = snapshot.getSnapshot();
+ ColorSpace colorSpace = snapshot.getColorSpace();
+ if (colorSpace != null) {
+ colorSpaceId = colorSpace.getId();
+ }
+ }
+ }
+
synchronized (mLock) {
final ContentSuggestionsPerUserService service = getServiceForUserLocked(userId);
if (service != null) {
- service.provideContextImageLocked(taskId, imageContextRequestExtras);
+ service.provideContextImageLocked(taskId, snapshotBuffer, colorSpaceId,
+ imageContextRequestExtras);
} else {
if (VERBOSE) {
Slog.v(TAG, "provideContextImageLocked: no service for " + userId);
diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
index 7828050223f4..cf53b169515e 100644
--- a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
+++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
@@ -19,17 +19,14 @@ package com.android.server.contentsuggestions;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.contentsuggestions.ClassificationsRequest;
-import android.app.contentsuggestions.ContentSuggestionsManager;
import android.app.contentsuggestions.IClassificationsCallback;
import android.app.contentsuggestions.ISelectionsCallback;
import android.app.contentsuggestions.SelectionsRequest;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
-import android.graphics.ColorSpace;
import android.graphics.GraphicBuffer;
import android.os.Bundle;
import android.os.RemoteException;
@@ -95,26 +92,17 @@ public final class ContentSuggestionsPerUserService extends
}
@GuardedBy("mLock")
- void provideContextImageLocked(int taskId, @NonNull Bundle imageContextRequestExtras) {
+ void provideContextImageFromBitmapLocked(@NonNull Bundle bitmapContainingExtras) {
+ // No task or snapshot provided, the bitmap is contained in the extras
+ provideContextImageLocked(-1, null, 0, bitmapContainingExtras);
+ }
+
+ @GuardedBy("mLock")
+ void provideContextImageLocked(int taskId, @Nullable GraphicBuffer snapshot,
+ int colorSpaceIdForSnapshot, @NonNull Bundle imageContextRequestExtras) {
RemoteContentSuggestionsService service = ensureRemoteServiceLocked();
if (service != null) {
- GraphicBuffer snapshotBuffer = null;
- int colorSpaceId = 0;
-
- // Skip taking TaskSnapshot when bitmap is provided.
- if (!imageContextRequestExtras.containsKey(ContentSuggestionsManager.EXTRA_BITMAP)) {
- ActivityManager.TaskSnapshot snapshot =
- mActivityTaskManagerInternal.getTaskSnapshotNoRestore(taskId, false);
- if (snapshot != null) {
- snapshotBuffer = snapshot.getSnapshot();
- ColorSpace colorSpace = snapshot.getColorSpace();
- if (colorSpace != null) {
- colorSpaceId = colorSpace.getId();
- }
- }
- }
-
- service.provideContextImage(taskId, snapshotBuffer, colorSpaceId,
+ service.provideContextImage(taskId, snapshot, colorSpaceIdForSnapshot,
imageContextRequestExtras);
}
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index ec84ae73d655..76a8e1474a95 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -40,6 +40,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_TEST;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkPolicyManager.RULE_NONE;
import static android.net.NetworkPolicyManager.uidRulesToString;
@@ -50,6 +51,7 @@ import static android.system.OsConstants.IPPROTO_UDP;
import static java.util.Map.Entry;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
@@ -2702,10 +2704,18 @@ public class ConnectivityService extends IConnectivityManager.Stub
switch (msg.what) {
case NetworkAgent.EVENT_NETWORK_CAPABILITIES_CHANGED: {
- final NetworkCapabilities networkCapabilities = (NetworkCapabilities) msg.obj;
+ NetworkCapabilities networkCapabilities = (NetworkCapabilities) msg.obj;
if (networkCapabilities.hasConnectivityManagedCapability()) {
Slog.wtf(TAG, "BUG: " + nai + " has CS-managed capability.");
}
+ if (networkCapabilities.hasTransport(TRANSPORT_TEST)) {
+ // Make sure the original object is not mutated. NetworkAgent normally
+ // makes a copy of the capabilities when sending the message through
+ // the Messenger, but if this ever changes, not making a defensive copy
+ // here will give attack vectors to clients using this code path.
+ networkCapabilities = new NetworkCapabilities(networkCapabilities);
+ networkCapabilities.restrictCapabilitesForTestNetwork();
+ }
updateCapabilities(nai.getCurrentScore(), nai, networkCapabilities);
break;
}
@@ -5778,7 +5788,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
public Network registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
int currentScore, NetworkAgentConfig networkAgentConfig, int providerId) {
- enforceNetworkFactoryPermission();
+ if (networkCapabilities.hasTransport(TRANSPORT_TEST)) {
+ enforceAnyPermissionOf(Manifest.permission.MANAGE_TEST_NETWORKS);
+ // Strictly, sanitizing here is unnecessary as the capabilities will be sanitized in
+ // the call to mixInCapabilities below anyway, but sanitizing here means the NAI never
+ // sees capabilities that may be malicious, which might prevent mistakes in the future.
+ networkCapabilities = new NetworkCapabilities(networkCapabilities);
+ networkCapabilities.restrictCapabilitesForTestNetwork();
+ } else {
+ enforceNetworkFactoryPermission();
+ }
LinkProperties lp = new LinkProperties(linkProperties);
lp.ensureDirectlyConnectedRoutes();
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 0bf81e0678dc..bb9446078da6 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -155,6 +155,7 @@ import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.pm.Installer;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.storage.AppFuseBridge;
import com.android.server.storage.StorageSessionController;
import com.android.server.storage.StorageSessionController.ExternalStorageServiceException;
@@ -3268,6 +3269,25 @@ class StorageManagerService extends IStorageManager.Stub
}
}
+ @Override
+ public void fixupAppDir(String path) {
+ final Matcher matcher = KNOWN_APP_DIR_PATHS.matcher(path);
+ if (matcher.matches()) {
+ AndroidPackage pkg = mPmInternal.getPackage(matcher.group(3));
+ if (pkg != null) {
+ try {
+ mVold.fixupAppDir(path + "/", pkg.getUid());
+ } catch (RemoteException | ServiceSpecificException e) {
+ Log.e(TAG, "Failed to fixup app dir for " + pkg.getPackageName(), e);
+ }
+ } else {
+ Log.e(TAG, "Can't find package belonging to " + path);
+ }
+ } else {
+ Log.e(TAG, "Path " + path + " is not a valid application-specific directory");
+ }
+ }
+
/** Not thread safe */
class AppFuseMountScope extends AppFuseBridge.MountScope {
private boolean mMounted = false;
diff --git a/services/core/java/com/android/server/SystemServerInitThreadPool.java b/services/core/java/com/android/server/SystemServerInitThreadPool.java
index 8f914fe6f59f..94d6b135e554 100644
--- a/services/core/java/com/android/server/SystemServerInitThreadPool.java
+++ b/services/core/java/com/android/server/SystemServerInitThreadPool.java
@@ -25,6 +25,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.Preconditions;
import com.android.server.am.ActivityManagerService;
+import com.android.server.utils.TimingsTraceAndSlog;
import java.util.ArrayList;
import java.util.List;
@@ -93,6 +94,8 @@ public class SystemServerInitThreadPool {
mPendingTasks.add(description);
}
return mService.submit(() -> {
+ TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog();
+ traceLog.traceBegin("InitThreadPoolExec:" + description);
if (IS_DEBUGGABLE) {
Slog.d(TAG, "Started executing " + description);
}
@@ -100,6 +103,7 @@ public class SystemServerInitThreadPool {
runnable.run();
} catch (RuntimeException e) {
Slog.e(TAG, "Failure in " + description + ": " + e, e);
+ traceLog.traceEnd();
throw e;
}
synchronized (mPendingTasks) {
@@ -108,6 +112,7 @@ public class SystemServerInitThreadPool {
if (IS_DEBUGGABLE) {
Slog.d(TAG, "Finished executing " + description);
}
+ traceLog.traceEnd();
});
}
@@ -132,7 +137,10 @@ public class SystemServerInitThreadPool {
*/
static void shutdown() {
synchronized (LOCK) {
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+ t.traceBegin("WaitInitThreadPoolShutdown");
if (sInstance == null) {
+ t.traceEnd();
Slog.wtf(TAG, "Already shutdown", new Exception());
return;
}
@@ -147,6 +155,7 @@ public class SystemServerInitThreadPool {
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
dumpStackTraces();
+ t.traceEnd();
throw new IllegalStateException(TAG + " init interrupted");
}
if (!terminated) {
@@ -160,11 +169,13 @@ public class SystemServerInitThreadPool {
synchronized (sInstance.mPendingTasks) {
copy.addAll(sInstance.mPendingTasks);
}
+ t.traceEnd();
throw new IllegalStateException("Cannot shutdown. Unstarted tasks "
+ unstartedRunnables + " Unfinished tasks " + copy);
}
sInstance = null; // Make eligible for GC
Slog.d(TAG, "Shutdown successful");
+ t.traceEnd();
}
}
diff --git a/services/core/java/com/android/server/am/BugReportHandlerUtil.java b/services/core/java/com/android/server/am/BugReportHandlerUtil.java
index 03f4a54086a8..ba89fce0b3f8 100644
--- a/services/core/java/com/android/server/am/BugReportHandlerUtil.java
+++ b/services/core/java/com/android/server/am/BugReportHandlerUtil.java
@@ -16,20 +16,15 @@
package com.android.server.am;
-import static android.app.AppOpsManager.OP_NONE;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import android.app.Activity;
import android.app.BroadcastOptions;
-import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Binder;
-import android.os.BugreportManager;
-import android.os.BugreportParams;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
@@ -115,17 +110,9 @@ public final class BugReportHandlerUtil {
options.setBackgroundActivityStartsAllowed(true);
final long identity = Binder.clearCallingIdentity();
try {
- // Handler app's BroadcastReceiver should call setResultCode(Activity.RESULT_OK) to
- // let ResultBroadcastReceiver know the handler app is available.
- context.sendOrderedBroadcastAsUser(intent,
- UserHandle.of(handlerUser),
+ context.sendBroadcastAsUser(intent, UserHandle.of(handlerUser),
android.Manifest.permission.DUMP,
- OP_NONE, options.toBundle(),
- new ResultBroadcastReceiver(),
- /* scheduler= */ null,
- Activity.RESULT_CANCELED,
- /* initialData= */ null,
- /* initialExtras= */ null);
+ options.toBundle());
} catch (RuntimeException e) {
Slog.e(TAG, "Error while trying to launch bugreport handler app.", e);
return false;
@@ -189,19 +176,4 @@ public final class BugReportHandlerUtil {
Binder.restoreCallingIdentity(identity);
}
}
-
- private static class ResultBroadcastReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (getResultCode() == Activity.RESULT_OK) {
- return;
- }
-
- Slog.w(TAG, "Request bug report because handler app seems to be not available.");
- BugreportManager bugreportManager = context.getSystemService(BugreportManager.class);
- bugreportManager.requestBugreport(
- new BugreportParams(BugreportParams.BUGREPORT_MODE_INTERACTIVE),
- /* shareTitle= */null, /* shareDescription= */ null);
- }
- }
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 1f0146ad815d..ada3e4269f6a 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -4570,6 +4570,7 @@ public class AudioService extends IAudioService.Stub
public void setWiredDeviceConnectionState(int type,
@ConnectionState int state, String address, String name,
String caller) {
+ enforceModifyAudioRoutingPermission();
if (state != CONNECTION_STATE_CONNECTED
&& state != CONNECTION_STATE_DISCONNECTED) {
throw new IllegalArgumentException("Invalid state " + state);
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 58e332ab6d1d..c1fbcfba864a 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -653,9 +653,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
mWakeupIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_WAKEUP), 0);
mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_TIMEOUT), 0);
- mNetworkConnectivityHandler = new GnssNetworkConnectivityHandler(context,
- GnssLocationProvider.this::onNetworkAvailable, mLooper);
-
// App ops service to keep track of who is accessing the GPS
mAppOps = mContext.getSystemService(AppOpsManager.class);
@@ -677,6 +674,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
mNIHandler = new GpsNetInitiatedHandler(context,
mNetInitiatedListener,
mSuplEsEnabled);
+ mNetworkConnectivityHandler = new GnssNetworkConnectivityHandler(context,
+ GnssLocationProvider.this::onNetworkAvailable, mLooper, mNIHandler);
+
sendMessage(INITIALIZE_HANDLER, 0, null);
mGnssStatusListenerHelper = new GnssStatusListenerHelper(mContext, mHandler) {
diff --git a/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java b/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java
index 93227bd78a81..5d6474bdbccc 100644
--- a/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java
+++ b/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java
@@ -29,12 +29,22 @@ import android.os.PowerManager;
import android.provider.Telephony.Carriers;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.PreciseCallState;
+import android.telephony.PhoneStateListener;
import android.util.Log;
+import com.android.internal.location.GpsNetInitiatedHandler;
+
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
+import java.util.Map;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Iterator;
/**
* Handles network connection requests and network state change updates for AGPS data download.
@@ -86,6 +96,9 @@ class GnssNetworkConnectivityHandler {
private HashMap<Network, NetworkAttributes> mAvailableNetworkAttributes =
new HashMap<>(HASH_MAP_INITIAL_CAPACITY_TO_TRACK_CONNECTED_NETWORKS);
+ // Phone State Listeners to track all the active sub IDs
+ private HashMap<Integer, SubIdPhoneStateListener> mPhoneStateListeners;
+
private final ConnectivityManager mConnMgr;
private final Handler mHandler;
@@ -94,6 +107,9 @@ class GnssNetworkConnectivityHandler {
private int mAGpsDataConnectionState;
private InetAddress mAGpsDataConnectionIpAddr;
private int mAGpsType;
+ private int mActiveSubId = -1;
+ private final GpsNetInitiatedHandler mNiHandler;
+
private final Context mContext;
@@ -166,18 +182,109 @@ class GnssNetworkConnectivityHandler {
GnssNetworkConnectivityHandler(Context context,
GnssNetworkListener gnssNetworkListener,
- Looper looper) {
+ Looper looper,
+ GpsNetInitiatedHandler niHandler) {
mContext = context;
mGnssNetworkListener = gnssNetworkListener;
+ SubscriptionManager subManager = mContext.getSystemService(SubscriptionManager.class);
+ if (subManager != null) {
+ subManager.addOnSubscriptionsChangedListener(mOnSubscriptionsChangeListener);
+ }
+
PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
mHandler = new Handler(looper);
+ mNiHandler = niHandler;
mConnMgr = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
mSuplConnectivityCallback = createSuplConnectivityCallback();
}
+ /**
+ * SubId Phone State Listener is used cache the last active Sub ID when a call is made,
+ * which will be used during an emergency call to set the Network Specifier to the particular
+ * sub when an emergency supl connection is requested
+ */
+ private final class SubIdPhoneStateListener extends PhoneStateListener {
+ private Integer mSubId;
+ SubIdPhoneStateListener(Integer subId) {
+ mSubId = subId;
+ }
+ @Override
+ public void onPreciseCallStateChanged(PreciseCallState state) {
+ if (state.PRECISE_CALL_STATE_ACTIVE == state.getForegroundCallState()) {
+ mActiveSubId = mSubId;
+ if (DEBUG) Log.d(TAG, "mActiveSubId: " + mActiveSubId);
+ }
+ }
+ };
+
+ /**
+ * Subscription Changed Listener is used to get all active subscriptions and create a
+ * Phone State Listener for each Sub ID that we find in the active subscription list
+ */
+ private final SubscriptionManager.OnSubscriptionsChangedListener mOnSubscriptionsChangeListener
+ = new SubscriptionManager.OnSubscriptionsChangedListener() {
+ @Override
+ public void onSubscriptionsChanged() {
+ if (mPhoneStateListeners == null) {
+ // Capacity=2 Load-Factor=1.0, as typically no more than 2 SIMs
+ mPhoneStateListeners = new HashMap<Integer, SubIdPhoneStateListener>(2,1);
+ }
+ SubscriptionManager subManager = mContext.getSystemService(SubscriptionManager.class);
+ TelephonyManager telManager = mContext.getSystemService(TelephonyManager.class);
+ if (subManager != null && telManager != null) {
+ List<SubscriptionInfo> subscriptionInfoList =
+ subManager.getActiveSubscriptionInfoList();
+ HashSet<Integer> activeSubIds = new HashSet<Integer>();
+ if (subscriptionInfoList != null) {
+ if (DEBUG) Log.d(TAG, "Active Sub List size: " + subscriptionInfoList.size());
+ // populate phone state listeners with all new active subs
+ for (SubscriptionInfo subInfo : subscriptionInfoList) {
+ activeSubIds.add(subInfo.getSubscriptionId());
+ if (!mPhoneStateListeners.containsKey(subInfo.getSubscriptionId())) {
+ TelephonyManager subIdTelManager =
+ telManager.createForSubscriptionId(subInfo.getSubscriptionId());
+ if (subIdTelManager != null) {
+ if (DEBUG) Log.d(TAG, "Listener sub" + subInfo.getSubscriptionId());
+ SubIdPhoneStateListener subIdPhoneStateListener =
+ new SubIdPhoneStateListener(subInfo.getSubscriptionId());
+ mPhoneStateListeners.put(subInfo.getSubscriptionId(),
+ subIdPhoneStateListener);
+ subIdTelManager.listen(subIdPhoneStateListener,
+ PhoneStateListener.LISTEN_PRECISE_CALL_STATE);
+ }
+ }
+ }
+ }
+ // clean up phone state listeners than no longer have active subs
+ Iterator<Map.Entry<Integer, SubIdPhoneStateListener> > iterator =
+ mPhoneStateListeners.entrySet().iterator();
+ while (iterator.hasNext()) {
+ Map.Entry<Integer, SubIdPhoneStateListener> element = iterator.next();
+ if (!activeSubIds.contains(element.getKey())) {
+ TelephonyManager subIdTelManager =
+ telManager.createForSubscriptionId(element.getKey());
+ if (subIdTelManager != null) {
+ if (DEBUG) Log.d(TAG, "unregister listener sub " + element.getKey());
+ subIdTelManager.listen(element.getValue(),
+ PhoneStateListener.LISTEN_NONE);
+ // removes the element from mPhoneStateListeners
+ iterator.remove();
+ } else {
+ Log.e(TAG, "Telephony Manager for Sub " + element.getKey() + " null");
+ }
+ }
+ }
+ // clean up cached active phone call sub if it is no longer an active sub
+ if (!activeSubIds.contains(mActiveSubId)) {
+ mActiveSubId = -1;
+ }
+ }
+ }
+ };
+
void registerNetworkCallbacks() {
// register for connectivity change events.
NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder();
@@ -467,6 +574,12 @@ class GnssNetworkConnectivityHandler {
NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder();
networkRequestBuilder.addCapability(getNetworkCapability(mAGpsType));
networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+ // During an emergency call, and when we have cached the Active Sub Id, we set the
+ // Network Specifier so that the network request goes to the correct Sub Id
+ if (mNiHandler.getInEmergency() && mActiveSubId >= 0) {
+ if (DEBUG) Log.d(TAG, "Adding Network Specifier: " + Integer.toString(mActiveSubId));
+ networkRequestBuilder.setNetworkSpecifier(Integer.toString(mActiveSubId));
+ }
NetworkRequest networkRequest = networkRequestBuilder.build();
mConnMgr.requestNetwork(
networkRequest,
@@ -598,6 +711,15 @@ class GnssNetworkConnectivityHandler {
}
TelephonyManager phone = (TelephonyManager)
mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ // During an emergency call with an active sub id, get the Telephony Manager specific
+ // to the active sub to get the correct value from getServiceState and getNetworkType
+ if (mNiHandler.getInEmergency() && mActiveSubId >= 0) {
+ TelephonyManager subIdTelManager =
+ phone.createForSubscriptionId(mActiveSubId);
+ if (subIdTelManager != null) {
+ phone = subIdTelManager;
+ }
+ }
ServiceState serviceState = phone.getServiceState();
String projection = null;
String selection = null;
diff --git a/services/core/java/com/android/server/notification/BubbleExtractor.java b/services/core/java/com/android/server/notification/BubbleExtractor.java
index 77921236e0d7..f2eb176aa676 100644
--- a/services/core/java/com/android/server/notification/BubbleExtractor.java
+++ b/services/core/java/com/android/server/notification/BubbleExtractor.java
@@ -157,8 +157,8 @@ public class BubbleExtractor implements NotificationSignalExtractor {
}
/**
- * @return whether the user has enabled the provided notification to bubble, does not
- * account for policy.
+ * @return whether the user has enabled the provided notification to bubble, and if the
+ * developer has provided valid information for the notification to bubble.
*/
@VisibleForTesting
boolean canBubble(NotificationRecord r, String pkg, int userId) {
@@ -184,8 +184,17 @@ public class BubbleExtractor implements NotificationSignalExtractor {
}
String shortcutId = metadata.getShortcutId();
- boolean shortcutValid = shortcutId != null
- && mShortcutHelper.hasValidShortcutInfo(shortcutId, pkg, r.getUser());
+ String notificationShortcutId = r.getShortcutInfo() != null
+ ? r.getShortcutInfo().getId()
+ : null;
+ boolean shortcutValid = false;
+ if (notificationShortcutId != null && shortcutId != null) {
+ // NoMan already checks validity of shortcut, just check if they match.
+ shortcutValid = shortcutId.equals(notificationShortcutId);
+ } else if (shortcutId != null) {
+ shortcutValid =
+ mShortcutHelper.getValidShortcutInfo(shortcutId, pkg, r.getUser()) != null;
+ }
if (metadata.getIntent() == null && !shortcutValid) {
// Should have a shortcut if intent is null
logBubbleError(r.getKey(),
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 35dec5a59cec..7f805bef520d 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -139,6 +139,7 @@ import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
import android.companion.ICompanionDeviceManager;
import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentProvider;
@@ -387,10 +388,9 @@ public class NotificationManagerService extends SystemService {
* still post toasts created with
* {@link android.widget.Toast#makeText(Context, CharSequence, int)} and its variants while
* in the background.
- *
- * TODO(b/144152069): Add @EnabledAfter(Q) to target R+ after assessing impact on dogfood
*/
@ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
private static final long CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK = 128611929L;
private IActivityManager mAm;
@@ -2751,24 +2751,18 @@ public class NotificationManagerService extends SystemService {
@Override
public void enqueueTextToast(String pkg, IBinder token, CharSequence text, int duration,
int displayId, @Nullable ITransientNotificationCallback callback) {
- enqueueToast(pkg, token, text, null, duration, displayId, callback, false);
+ enqueueToast(pkg, token, text, null, duration, displayId, callback);
}
@Override
public void enqueueToast(String pkg, IBinder token, ITransientNotification callback,
int duration, int displayId) {
- enqueueToast(pkg, token, null, callback, duration, displayId, null, true);
- }
-
- @Override
- public void enqueueTextOrCustomToast(String pkg, IBinder token,
- ITransientNotification callback, int duration, int displayId, boolean isCustom) {
- enqueueToast(pkg, token, null, callback, duration, displayId, null, isCustom);
+ enqueueToast(pkg, token, null, callback, duration, displayId, null);
}
private void enqueueToast(String pkg, IBinder token, @Nullable CharSequence text,
@Nullable ITransientNotification callback, int duration, int displayId,
- @Nullable ITransientNotificationCallback textCallback, boolean isCustom) {
+ @Nullable ITransientNotificationCallback textCallback) {
if (DBG) {
Slog.i(TAG, "enqueueToast pkg=" + pkg + " token=" + token
+ " duration=" + duration + " displayId=" + displayId);
@@ -2807,11 +2801,15 @@ public class NotificationManagerService extends SystemService {
}
boolean isAppRenderedToast = (callback != null);
- if (isAppRenderedToast && isCustom && !isSystemToast
- && !isPackageInForegroundForToast(pkg, callingUid)) {
+ if (isAppRenderedToast && !isSystemToast && !isPackageInForegroundForToast(pkg,
+ callingUid)) {
boolean block;
long id = Binder.clearCallingIdentity();
try {
+ // CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK is gated on targetSdk, so block will be
+ // false for apps with targetSdk < R. For apps with targetSdk R+, text toasts
+ // are not app-rendered, so isAppRenderedToast == true means it's a custom
+ // toast.
block = mPlatformCompat.isChangeEnabledByPackageName(
CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK, pkg,
callingUser.getIdentifier());
@@ -2824,11 +2822,6 @@ public class NotificationManagerService extends SystemService {
Binder.restoreCallingIdentity(id);
}
if (block) {
- // TODO(b/144152069): Remove informative toast
- mUiHandler.post(() -> Toast.makeText(getContext(),
- "Background custom toast blocked for package " + pkg + ".\n"
- + "See g.co/dev/toast.",
- Toast.LENGTH_SHORT).show());
Slog.w(TAG, "Blocking custom toast from package " + pkg
+ " due to package not in the foreground");
return;
@@ -3453,7 +3446,7 @@ public class NotificationManagerService extends SystemService {
ArrayList<ConversationChannelWrapper> conversations =
mPreferencesHelper.getConversations(onlyImportant);
for (ConversationChannelWrapper conversation : conversations) {
- conversation.setShortcutInfo(mShortcutHelper.getShortcutInfo(
+ conversation.setShortcutInfo(mShortcutHelper.getValidShortcutInfo(
conversation.getNotificationChannel().getConversationId(),
conversation.getPkg(),
UserHandle.of(UserHandle.getUserId(conversation.getUid()))));
@@ -3476,7 +3469,7 @@ public class NotificationManagerService extends SystemService {
ArrayList<ConversationChannelWrapper> conversations =
mPreferencesHelper.getConversations(pkg, uid);
for (ConversationChannelWrapper conversation : conversations) {
- conversation.setShortcutInfo(mShortcutHelper.getShortcutInfo(
+ conversation.setShortcutInfo(mShortcutHelper.getValidShortcutInfo(
conversation.getNotificationChannel().getConversationId(),
pkg,
UserHandle.of(UserHandle.getUserId(uid))));
@@ -3638,13 +3631,23 @@ public class NotificationManagerService extends SystemService {
}
/**
+ * @deprecated Use {@link #getActiveNotificationsWithAttribution(String, String)} instead.
+ */
+ @Deprecated
+ @Override
+ public StatusBarNotification[] getActiveNotifications(String callingPkg) {
+ return getActiveNotificationsWithAttribution(callingPkg, null);
+ }
+
+ /**
* System-only API for getting a list of current (i.e. not cleared) notifications.
*
* Requires ACCESS_NOTIFICATIONS which is signature|system.
* @returns A list of all the notifications, in natural order.
*/
@Override
- public StatusBarNotification[] getActiveNotifications(String callingPkg) {
+ public StatusBarNotification[] getActiveNotificationsWithAttribution(String callingPkg,
+ String callingAttributionTag) {
// enforce() will ensure the calling uid has the correct permission
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NOTIFICATIONS,
@@ -3654,7 +3657,8 @@ public class NotificationManagerService extends SystemService {
int uid = Binder.getCallingUid();
// noteOp will check to make sure the callingPkg matches the uid
- if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
+ if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg,
+ callingAttributionTag, null)
== AppOpsManager.MODE_ALLOWED) {
synchronized (mNotificationLock) {
tmp = new StatusBarNotification[mNotificationList.size()];
@@ -3736,12 +3740,24 @@ public class NotificationManagerService extends SystemService {
}
/**
- * System-only API for getting a list of recent (cleared, no longer shown) notifications.
+ * @deprecated Use {@link #getHistoricalNotificationsWithAttribution} instead.
*/
+ @Deprecated
@Override
@RequiresPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS)
public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count,
boolean includeSnoozed) {
+ return getHistoricalNotificationsWithAttribution(callingPkg, null, count,
+ includeSnoozed);
+ }
+
+ /**
+ * System-only API for getting a list of recent (cleared, no longer shown) notifications.
+ */
+ @Override
+ @RequiresPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS)
+ public StatusBarNotification[] getHistoricalNotificationsWithAttribution(String callingPkg,
+ String callingAttributionTag, int count, boolean includeSnoozed) {
// enforce() will ensure the calling uid has the correct permission
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NOTIFICATIONS,
@@ -3751,7 +3767,8 @@ public class NotificationManagerService extends SystemService {
int uid = Binder.getCallingUid();
// noteOp will check to make sure the callingPkg matches the uid
- if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
+ if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg,
+ callingAttributionTag, null)
== AppOpsManager.MODE_ALLOWED) {
synchronized (mArchive) {
tmp = mArchive.getArray(count, includeSnoozed);
@@ -3767,7 +3784,8 @@ public class NotificationManagerService extends SystemService {
@Override
@WorkerThread
@RequiresPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS)
- public NotificationHistory getNotificationHistory(String callingPkg) {
+ public NotificationHistory getNotificationHistory(String callingPkg,
+ String callingAttributionTag) {
// enforce() will ensure the calling uid has the correct permission
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NOTIFICATIONS,
@@ -3775,7 +3793,8 @@ public class NotificationManagerService extends SystemService {
int uid = Binder.getCallingUid();
// noteOp will check to make sure the callingPkg matches the uid
- if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
+ if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg,
+ callingAttributionTag, null)
== AppOpsManager.MODE_ALLOWED) {
IntArray currentUserIds = mUserProfiles.getCurrentProfileIds();
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "notifHistoryReadHistory");
@@ -5647,7 +5666,8 @@ public class NotificationManagerService extends SystemService {
}
}
- r.setShortcutInfo(mShortcutHelper.getShortcutInfo(notification.getShortcutId(), pkg, user));
+ r.setShortcutInfo(mShortcutHelper.getValidShortcutInfo(
+ notification.getShortcutId(), pkg, user));
if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r,
r.getSbn().getOverrideGroupKey() != null)) {
diff --git a/services/core/java/com/android/server/notification/ShortcutHelper.java b/services/core/java/com/android/server/notification/ShortcutHelper.java
index 7bbb3b117517..f1ce3a7d9f48 100644
--- a/services/core/java/com/android/server/notification/ShortcutHelper.java
+++ b/services/core/java/com/android/server/notification/ShortcutHelper.java
@@ -121,7 +121,10 @@ class ShortcutHelper {
mLauncherAppsService = launcherApps;
}
- ShortcutInfo getShortcutInfo(String shortcutId, String packageName, UserHandle user) {
+ /**
+ * Only returns shortcut info if it's found and if it's {@link ShortcutInfo#isLongLived()}.
+ */
+ ShortcutInfo getValidShortcutInfo(String shortcutId, String packageName, UserHandle user) {
if (mLauncherAppsService == null) {
return null;
}
@@ -135,20 +138,15 @@ class ShortcutHelper {
query.setShortcutIds(Arrays.asList(shortcutId));
query.setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_CACHED);
List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(query, user);
- return shortcuts != null && shortcuts.size() > 0
+ ShortcutInfo info = shortcuts != null && shortcuts.size() > 0
? shortcuts.get(0)
: null;
+ return info != null && info.isLongLived() ? info : null;
} finally {
Binder.restoreCallingIdentity(token);
}
}
- boolean hasValidShortcutInfo(String shortcutId, String packageName,
- UserHandle user) {
- ShortcutInfo shortcutInfo = getShortcutInfo(shortcutId, packageName, user);
- return shortcutInfo != null && shortcutInfo.isLongLived();
- }
-
/**
* Shortcut based bubbles require some extra work to listen for shortcut changes.
*
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b79f75a2d335..e605eeba4d98 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -161,6 +161,7 @@ import android.content.pm.DataLoaderType;
import android.content.pm.FallbackCategoryProvider;
import android.content.pm.FeatureInfo;
import android.content.pm.IDexModuleRegisterCallback;
+import android.content.pm.IPackageChangeObserver;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageDeleteObserver2;
@@ -178,6 +179,7 @@ import android.content.pm.InstrumentationInfo;
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.KeySet;
import android.content.pm.ModuleInfo;
+import android.content.pm.PackageChangeEvent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageInstaller;
@@ -812,6 +814,10 @@ public class PackageManagerService extends IPackageManager.Stub
private final OverlayConfig mOverlayConfig;
+ @GuardedBy("itself")
+ final private ArrayList<IPackageChangeObserver> mPackageChangeObservers =
+ new ArrayList<>();
+
/**
* Unit tests will instantiate, extend and/or mock to mock dependencies / behaviors.
*
@@ -5064,15 +5070,16 @@ public class PackageManagerService extends IPackageManager.Stub
* action and a {@code android.intent.category.BROWSABLE} category</li>
* </ul>
*/
- int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps) {
+ int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps,
+ boolean matchSystemOnly) {
return updateFlagsForResolve(flags, userId, callingUid,
- wantInstantApps, false /*onlyExposedExplicitly*/);
+ wantInstantApps, matchSystemOnly, false /*onlyExposedExplicitly*/);
}
int updateFlagsForResolve(int flags, int userId, int callingUid,
- boolean wantInstantApps, boolean onlyExposedExplicitly) {
+ boolean wantInstantApps, boolean onlyExposedExplicitly, boolean matchSystemOnly) {
// Safe mode means we shouldn't match any third-party components
- if (mSafeMode) {
+ if (mSafeMode || matchSystemOnly) {
flags |= PackageManager.MATCH_SYSTEM_ONLY;
}
if (getInstantAppPackageName(callingUid) != null) {
@@ -6164,7 +6171,8 @@ public class PackageManagerService extends IPackageManager.Stub
if (!mUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
- flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart);
+ flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart,
+ intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/, "resolve intent");
@@ -6196,7 +6204,8 @@ public class PackageManagerService extends IPackageManager.Stub
intent = updateIntentForResolve(intent);
final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver());
final int flags = updateFlagsForResolve(
- 0, userId, callingUid, false /*includeInstantApps*/);
+ 0, userId, callingUid, false /*includeInstantApps*/,
+ intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, flags,
userId);
synchronized (mLock) {
@@ -6517,7 +6526,8 @@ public class PackageManagerService extends IPackageManager.Stub
android.provider.Settings.Global.getInt(mContext.getContentResolver(),
android.provider.Settings.Global.DEVICE_PROVISIONED, 0) == 1;
flags = updateFlagsForResolve(
- flags, userId, callingUid, false /*includeInstantApps*/);
+ flags, userId, callingUid, false /*includeInstantApps*/,
+ intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
intent = updateIntentForResolve(intent);
// writer
synchronized (mLock) {
@@ -6729,7 +6739,8 @@ public class PackageManagerService extends IPackageManager.Stub
}
synchronized (mLock) {
int flags = updateFlagsForResolve(0, parent.id, callingUid,
- false /*includeInstantApps*/);
+ false /*includeInstantApps*/,
+ intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
CrossProfileDomainInfo xpDomainInfo = getCrossProfileDomainPreferredLpr(
intent, resolvedType, flags, sourceUserId, parent.id);
return xpDomainInfo != null;
@@ -6815,7 +6826,8 @@ public class PackageManagerService extends IPackageManager.Stub
}
flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart,
- comp != null || pkgName != null /*onlyExposedExplicitly*/);
+ comp != null || pkgName != null /*onlyExposedExplicitly*/,
+ intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
if (comp != null) {
final List<ResolveInfo> list = new ArrayList<>(1);
final ActivityInfo ai = getActivityInfo(comp, flags, userId);
@@ -7600,7 +7612,8 @@ public class PackageManagerService extends IPackageManager.Stub
String resolvedType, int flags, int userId) {
if (!mUserManager.exists(userId)) return Collections.emptyList();
final int callingUid = Binder.getCallingUid();
- flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/);
+ flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
+ intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/,
"query intent activity options");
@@ -7786,7 +7799,8 @@ public class PackageManagerService extends IPackageManager.Stub
false /*requireFullPermission*/, false /*checkShell*/,
"query intent receivers");
final String instantAppPkgName = getInstantAppPackageName(callingUid);
- flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/);
+ flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
+ intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
@@ -7876,7 +7890,8 @@ public class PackageManagerService extends IPackageManager.Stub
private ResolveInfo resolveServiceInternal(Intent intent, String resolvedType, int flags,
int userId, int callingUid) {
if (!mUserManager.exists(userId)) return null;
- flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/);
+ flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
+ false /* matchSystemOnly */);
List<ResolveInfo> query = queryIntentServicesInternal(
intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/);
if (query != null) {
@@ -7907,7 +7922,8 @@ public class PackageManagerService extends IPackageManager.Stub
false /*checkShell*/,
"query intent receivers");
final String instantAppPkgName = getInstantAppPackageName(callingUid);
- flags = updateFlagsForResolve(flags, userId, callingUid, includeInstantApps);
+ flags = updateFlagsForResolve(flags, userId, callingUid, includeInstantApps,
+ false /* matchSystemOnly */);
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
@@ -8044,7 +8060,8 @@ public class PackageManagerService extends IPackageManager.Stub
if (!mUserManager.exists(userId)) return Collections.emptyList();
final int callingUid = Binder.getCallingUid();
final String instantAppPkgName = getInstantAppPackageName(callingUid);
- flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/);
+ flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
+ false /* matchSystemOnly */);
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
@@ -16458,9 +16475,56 @@ public class PackageManagerService extends IPackageManager.Stub
// BackgroundDexOptService will remove it from its blacklist.
// TODO: Layering violation
BackgroundDexOptService.notifyPackageChanged(packageName);
+
+ notifyPackageChangeObserversOnUpdate(reconciledPkg);
}
}
+ private void notifyPackageChangeObserversOnUpdate(ReconciledPackage reconciledPkg) {
+ final PackageSetting pkgSetting = reconciledPkg.pkgSetting;
+ final PackageInstalledInfo pkgInstalledInfo = reconciledPkg.installResult;
+ final PackageRemovedInfo pkgRemovedInfo = pkgInstalledInfo.removedInfo;
+
+ PackageChangeEvent pkgChangeEvent = new PackageChangeEvent();
+ pkgChangeEvent.packageName = pkgSetting.pkg.getPackageName();
+ pkgChangeEvent.version = pkgSetting.versionCode;
+ pkgChangeEvent.lastUpdateTimeMillis = pkgSetting.lastUpdateTime;
+ pkgChangeEvent.newInstalled = (pkgRemovedInfo == null || !pkgRemovedInfo.isUpdate);
+ pkgChangeEvent.dataRemoved = (pkgRemovedInfo != null && pkgRemovedInfo.dataRemoved);
+ pkgChangeEvent.isDeleted = false;
+
+ notifyPackageChangeObservers(pkgChangeEvent);
+ }
+
+ private void notifyPackageChangeObserversOnDelete(String packageName, long version) {
+ PackageChangeEvent pkgChangeEvent = new PackageChangeEvent();
+ pkgChangeEvent.packageName = packageName;
+ pkgChangeEvent.version = version;
+ pkgChangeEvent.lastUpdateTimeMillis = 0L;
+ pkgChangeEvent.newInstalled = false;
+ pkgChangeEvent.dataRemoved = false;
+ pkgChangeEvent.isDeleted = true;
+
+ notifyPackageChangeObservers(pkgChangeEvent);
+ }
+
+ private void notifyPackageChangeObservers(PackageChangeEvent event) {
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "notifyPackageChangeObservers");
+ synchronized (mPackageChangeObservers) {
+ for(IPackageChangeObserver observer : mPackageChangeObservers) {
+ try {
+ observer.onPackageChanged(event);
+ } catch(RemoteException e) {
+ Log.wtf(TAG, e);
+ }
+ }
+ }
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+
/**
* The set of data needed to successfully install the prepared package. This includes data that
* will be used to scan and reconcile the package.
@@ -17521,6 +17585,7 @@ public class PackageManagerService extends IPackageManager.Stub
} catch (RemoteException e) {
Log.i(TAG, "Observer no longer exists.");
} //end catch
+ notifyPackageChangeObserversOnDelete(packageName, versionCode);
});
}
@@ -22980,8 +23045,49 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
+ private final class PackageChangeObserverDeathRecipient implements IBinder.DeathRecipient {
+ private final IPackageChangeObserver mObserver;
+
+ PackageChangeObserverDeathRecipient(IPackageChangeObserver observer) {
+ mObserver = observer;
+ }
+
+ @Override
+ public void binderDied() {
+ synchronized (mPackageChangeObservers) {
+ mPackageChangeObservers.remove(mObserver);
+ Log.d(TAG, "Size of mPackageChangeObservers after removing dead observer is "
+ + mPackageChangeObservers.size());
+ }
+ }
+ }
+
private class PackageManagerNative extends IPackageManagerNative.Stub {
@Override
+ public void registerPackageChangeObserver(@NonNull IPackageChangeObserver observer) {
+ synchronized (mPackageChangeObservers) {
+ try {
+ observer.asBinder().linkToDeath(
+ new PackageChangeObserverDeathRecipient(observer), 0);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.getMessage());
+ }
+ mPackageChangeObservers.add(observer);
+ Log.d(TAG, "Size of mPackageChangeObservers after registry is "
+ + mPackageChangeObservers.size());
+ }
+ }
+
+ @Override
+ public void unregisterPackageChangeObserver(@NonNull IPackageChangeObserver observer) {
+ synchronized (mPackageChangeObservers) {
+ mPackageChangeObservers.remove(observer);
+ Log.d(TAG, "Size of mPackageChangeObservers after unregistry is "
+ + mPackageChangeObservers.size());
+ }
+ }
+
+ @Override
public String[] getAllPackages() {
return PackageManagerService.this.getAllPackages().toArray(new String[0]);
}
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 161f30449a52..27288d852fb2 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -30,6 +30,7 @@ import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.AppOpsManager;
import android.app.AppOpsManagerInternal;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -47,6 +48,7 @@ import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManagerInternal;
import android.permission.PermissionControllerManager;
+import android.provider.Settings;
import android.provider.Telephony;
import android.telecom.TelecomManager;
import android.util.ArrayMap;
@@ -70,7 +72,9 @@ import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.policy.PermissionPolicyInternal.OnInitializedCallback;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.ExecutionException;
/**
@@ -180,8 +184,6 @@ public final class PermissionPolicyService extends SystemService {
intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
intentFilter.addDataScheme("package");
-
- /* TODO ntmyren: enable receiver when test flakes are fixed
getContext().registerReceiverAsUser(new BroadcastReceiver() {
final List<Integer> mUserSetupUids = new ArrayList<>(200);
final Map<UserHandle, PermissionControllerManager> mPermControllerManagers =
@@ -232,7 +234,6 @@ public final class PermissionPolicyService extends SystemService {
manager.updateUserSensitiveForApp(uid);
}
}, UserHandle.ALL, intentFilter, null, null);
- */
}
/**
diff --git a/services/core/java/com/android/server/power/TEST_MAPPING b/services/core/java/com/android/server/power/TEST_MAPPING
index acf3f79e3626..74958b692eb2 100644
--- a/services/core/java/com/android/server/power/TEST_MAPPING
+++ b/services/core/java/com/android/server/power/TEST_MAPPING
@@ -3,6 +3,7 @@
{
"name": "CtsBatterySavingTestCases",
"options": [
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.LargeTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
@@ -11,6 +12,7 @@
"name": "FrameworksMockingServicesTests",
"options": [
{"include-filter": "com.android.server.power"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
},
@@ -18,6 +20,7 @@
"name": "FrameworksServicesTests",
"options": [
{"include-filter": "com.android.server.power"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{
"exclude-filter": "com.android.server.power.PowerManagerServiceTest#testWakefulnessAwake_ShouldWakeUpWhenPluggedIn"
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
index 63048f6b95b3..a9e8d3fc7a3a 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
@@ -34,6 +34,7 @@ import android.media.soundtrigger_middleware.SoundModel;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
import android.media.soundtrigger_middleware.Status;
import android.os.IBinder;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.util.Log;
@@ -139,6 +140,14 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
throw new ServiceSpecificException(((RecoverableException) e).errorCode,
e.getMessage());
}
+
+ /* Throwing an exception is not enough in this case. When the HAL behaves unexpectedly, the
+ system service and the HAL must be reset and the client must be notified. Without a full
+ reset in this catastrophic case, the state of the HAL and the system service cannot be
+ guaranteed to the client.
+ */
+ Log.wtf(TAG, "Crashing system server due to unrecoverable exception", e);
+ Process.killProcess(Process.myPid());
throw new InternalServerError(e);
}
@@ -377,7 +386,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
mCallback = callback;
mHandle = handle;
try {
- mCallback.asBinder().linkToDeath(null, 0);
+ mCallback.asBinder().linkToDeath(this, 0);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -680,7 +689,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
try {
mDelegate.detach();
mDelegate = null;
- mCallback.asBinder().unlinkToDeath(null, 0);
+ mCallback.asBinder().unlinkToDeath(this, 0);
mModules.get(mHandle).remove(this);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 6d53786ddf41..3477d82f4d71 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -4618,6 +4618,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
} catch (Exception e) {
Slog.w(TAG, "Exception thrown sending start: " + intent.getComponent(), e);
}
+ // The activity may be waiting for stop, but that is no longer appropriate if we are
+ // starting the activity again
+ mStackSupervisor.mStoppingActivities.remove(this);
}
return false;
}
@@ -4667,7 +4670,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* and {@link #shouldPauseActivity(ActivityRecord)}.
*/
private boolean shouldStartActivity() {
- return mVisibleRequested && isState(STOPPED);
+ return mVisibleRequested && (isState(STOPPED) || isState(STOPPING));
}
/**
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index e8bfe8ef63ac..c6f375ea5d18 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -36,7 +36,6 @@ import static android.app.WindowConfiguration.windowingModeToString;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
@@ -303,9 +302,6 @@ class ActivityStack extends Task {
private static final int TRANSLUCENT_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 1;
- // TODO(task-hierarchy): remove when tiles can be actual parents
- TaskTile mTile = null;
-
private final Handler mHandler;
private class ActivityStackHandler extends Handler {
@@ -551,10 +547,10 @@ class ActivityStack extends Task {
}
ActivityStack(ActivityTaskManagerService atmService, int id, int activityType,
- ActivityInfo info, Intent intent) {
+ ActivityInfo info, Intent intent, boolean createdByOrganizer) {
this(atmService, id, info, intent, null /*voiceSession*/, null /*voiceInteractor*/,
null /*taskDescription*/, null /*stack*/);
-
+ mCreatedByOrganizer = createdByOrganizer;
setActivityType(activityType);
}
@@ -601,20 +597,11 @@ class ActivityStack extends Task {
}
@Override
- public void resolveTileOverrideConfiguration(Configuration newParentConfig) {
- super.resolveTileOverrideConfiguration(newParentConfig);
- if (mTile != null) {
- // If this is a virtual child of a tile, simulate the parent-child relationship
- mTile.updateResolvedConfig(getResolvedOverrideConfiguration());
- }
- }
-
- @Override
public void onConfigurationChanged(Configuration newParentConfig) {
// Calling Task#onConfigurationChanged() for leaf task since the ops in this method are
// particularly for ActivityStack, like preventing bounds changes when inheriting certain
// windowing mode.
- if (!isRootTask() || this instanceof TaskTile) {
+ if (!isRootTask()) {
super.onConfigurationChanged(newParentConfig);
return;
}
@@ -689,6 +676,9 @@ class ActivityStack extends Task {
@Override
public void setWindowingMode(int windowingMode) {
+ // Reset the cached result of toString()
+ stringName = null;
+
// Calling Task#setWindowingMode() for leaf task since this is the a specialization of
// {@link #setWindowingMode(int)} for ActivityStack.
if (!isRootTask()) {
@@ -742,7 +732,6 @@ class ActivityStack extends Task {
final int currentOverrideMode = getRequestedOverrideWindowingMode();
final DisplayContent display = getDisplay();
final Task topTask = getTopMostTask();
- final ActivityStack splitScreenStack = display.getRootSplitScreenPrimaryTask();
int windowingMode = preferredWindowingMode;
if (preferredWindowingMode == WINDOWING_MODE_UNDEFINED
&& isTransientWindowingMode(currentMode)) {
@@ -756,14 +745,14 @@ class ActivityStack extends Task {
windowingMode = display.validateWindowingMode(windowingMode,
null /* ActivityRecord */, topTask, getActivityType());
}
- if (splitScreenStack == this
+ if (display.getRootSplitScreenPrimaryTask() == this
&& windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
// Resolution to split-screen secondary for the primary split-screen stack means
// we want to leave split-screen mode.
windowingMode = mRestoreOverrideWindowingMode;
}
- final boolean alreadyInSplitScreenMode = display.hasSplitScreenPrimaryTask();
+ final boolean alreadyInSplitScreenMode = display.isSplitScreenModeActivated();
// Don't send non-resizeable notifications if the windowing mode changed was a side effect
// of us entering split-screen mode.
@@ -831,7 +820,7 @@ class ActivityStack extends Task {
return;
}
- if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && splitScreenStack != null) {
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && alreadyInSplitScreenMode) {
// We already have a split-screen stack in this display, so just move the tasks over.
// TODO: Figure-out how to do all the stuff in
// AMS.setTaskWindowingModeSplitScreenPrimary
@@ -1063,7 +1052,7 @@ class ActivityStack extends Task {
final DisplayContent display = getDisplay();
if (inSplitScreenSecondaryWindowingMode()) {
- // If the stack is in split-screen seconardy mode, we need to make sure we move the
+ // If the stack is in split-screen secondary mode, we need to make sure we move the
// primary split-screen stack forward in the case it is currently behind a fullscreen
// stack so both halves of the split-screen appear on-top and the fullscreen stack isn't
// cutting between them.
@@ -1085,12 +1074,13 @@ class ActivityStack extends Task {
display.moveHomeStackToFront(reason + " returnToHome");
}
- final boolean movingTask = task != null;
- display.positionStackAtTop(this, !movingTask /* includingParents */, reason);
- if (movingTask) {
- // This also moves the entire hierarchy branch to top, including parents
- positionChildAtTop(task);
+ if (isRootTask()) {
+ display.positionStackAtTop(this, false /* includingParents */, reason);
}
+ if (task == null) {
+ task = this;
+ }
+ task.getParent().positionChildAt(POSITION_TOP, task, true /* includingParents */);
}
/**
@@ -1116,12 +1106,6 @@ class ActivityStack extends Task {
}
}
- @Override
- boolean isFocusable() {
- // Special check for tile which isn't really in the hierarchy
- return mTile != null ? mTile.isFocusable() : super.isFocusable();
- }
-
boolean isTopActivityFocusable() {
final ActivityRecord r = topRunningActivity();
return r != null ? r.isFocusable()
@@ -3542,6 +3526,10 @@ class ActivityStack extends Task {
@Override
void onChildPositionChanged(WindowContainer child) {
+ if (isOrganized()) {
+ mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(this, false /* force */);
+ }
+
if (!mChildren.contains(child)) {
return;
}
@@ -3572,11 +3560,6 @@ class ActivityStack extends Task {
if (oldDisplay != null && oldDisplay.isRemoving()) {
postReparent();
}
- if (mTile != null && getSurfaceControl() != null) {
- // by now, the TaskStack should already have been reparented, so we can reparent its
- // surface here
- reparentSurfaceControl(getPendingTransaction(), mTile.getSurfaceControl());
- }
}
void reparent(DisplayContent newParent, boolean onTop) {
@@ -3614,16 +3597,7 @@ class ActivityStack extends Task {
@Override
void getRelativeDisplayedPosition(Point outPos) {
- // check for tile which is "virtually" a parent.
- if (mTile != null) {
- final Rect dispBounds = getDisplayedBounds();
- outPos.set(dispBounds.left, dispBounds.top);
- final Rect parentBounds = mTile.getBounds();
- outPos.offset(-parentBounds.left, -parentBounds.top);
- } else {
- super.getRelativeDisplayedPosition(outPos);
- }
-
+ super.getRelativeDisplayedPosition(outPos);
final int outset = getStackOutset();
outPos.x -= outset;
outPos.y -= outset;
@@ -3633,16 +3607,6 @@ class ActivityStack extends Task {
if (mSurfaceControl == null) {
return;
}
- if (mTile != null) {
- // Tile controls crop, so the app needs to be able to draw its background outside of
- // the stack bounds for when the tile crop gets bigger than the stack.
- if (mLastSurfaceSize.equals(0, 0)) {
- return;
- }
- transaction.setWindowCrop(mSurfaceControl, null);
- mLastSurfaceSize.set(0, 0);
- return;
- }
final Rect stackBounds = getDisplayedBounds();
int width = stackBounds.width();
@@ -3666,9 +3630,6 @@ class ActivityStack extends Task {
@Override
void onDisplayChanged(DisplayContent dc) {
- if (mTile != null && dc != mTile.getDisplay()) {
- mTile.removeChild(this);
- }
super.onDisplayChanged(dc);
if (isRootTask()) {
updateSurfaceBounds();
@@ -3781,20 +3742,6 @@ class ActivityStack extends Task {
return super.checkCompleteDeferredRemoval();
}
- @Override
- int getOrientation() {
- return (canSpecifyOrientation()) ? super.getOrientation() : SCREEN_ORIENTATION_UNSET;
- }
-
- private boolean canSpecifyOrientation() {
- final int windowingMode = getWindowingMode();
- final int activityType = getActivityType();
- return windowingMode == WINDOWING_MODE_FULLSCREEN
- || activityType == ACTIVITY_TYPE_HOME
- || activityType == ACTIVITY_TYPE_RECENTS
- || activityType == ACTIVITY_TYPE_ASSISTANT;
- }
-
public DisplayInfo getDisplayInfo() {
return mDisplayContent.getDisplayInfo();
}
@@ -3825,49 +3772,6 @@ class ActivityStack extends Task {
return shouldSleepActivities() || mAtmService.mShuttingDown;
}
- TaskTile getTile() {
- return mTile;
- }
-
- /**
- * Don't call this directly. instead use {@link TaskTile#addChild} or
- * {@link TaskTile#removeChild}.
- */
- void setTile(TaskTile tile) {
- TaskTile origTile = mTile;
- mTile = tile;
- final ConfigurationContainer parent = getParent();
- if (parent != null) {
- onConfigurationChanged(parent.getConfiguration());
- }
-
- // Reparent to tile surface or back to original parent
- if (getSurfaceControl() == null) {
- return;
- }
- if (mTile != null) {
- // don't use reparentSurfaceControl because we need to bypass taskorg check
- mSurfaceAnimator.reparent(getPendingTransaction(), mTile.getSurfaceControl());
- } else if (mTile == null && origTile != null) {
- mSurfaceAnimator.reparent(getPendingTransaction(), getParentSurfaceControl());
- }
- }
-
- @Override
- public SurfaceControl getParentSurfaceControl() {
- // Tile is a "virtual" parent, so we need to intercept the parent surface here
- return mTile != null ? mTile.getSurfaceControl() : super.getParentSurfaceControl();
- }
-
- @Override
- void removeImmediately() {
- // TODO(task-hierarchy): remove this override when tiles are in hierarchy
- if (mTile != null) {
- mTile.removeChild(this);
- }
- super.removeImmediately();
- }
-
@Override
public void dumpDebug(ProtoOutputStream proto, long fieldId,
@WindowTraceLogLevel int logLevel) {
@@ -3912,7 +3816,7 @@ class ActivityStack extends Task {
proto.write(SURFACE_HEIGHT, mSurfaceControl.getHeight());
}
- proto.write(CREATED_BY_ORGANIZER, this instanceof TaskTile);
+ proto.write(CREATED_BY_ORGANIZER, mCreatedByOrganizer);
proto.end(token);
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 57f357d384b5..4652f49f116c 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -423,6 +423,10 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
final ActivityStack toStack = mToDisplay.getOrCreateStack(
null, mTmpOptions, task, task.getActivityType(), mOnTop);
+ if (task == toStack) {
+ // The task was reused as the root task.
+ return;
+ }
if (mOnTop) {
final boolean isTopTask = task == mTopTask;
@@ -1704,7 +1708,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
mRootWindowContainer.getLaunchStack(null, aOptions, task, onTop);
final WindowContainer parent = task.getParent();
- if (parent == stack) {
+ if (parent == stack || task == stack) {
// Nothing else to do since it is already restored in the right stack.
return true;
}
@@ -2237,7 +2241,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
final boolean isSecondaryDisplayPreferred =
(preferredDisplayId != DEFAULT_DISPLAY && preferredDisplayId != INVALID_DISPLAY);
final boolean inSplitScreenMode = actualStack != null
- && actualStack.getDisplay().hasSplitScreenPrimaryTask();
+ && actualStack.getDisplay().isSplitScreenModeActivated();
if (((!inSplitScreenMode && preferredWindowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
&& !isSecondaryDisplayPreferred) || !task.isActivityTypeStandardOrUndefined()) {
return;
@@ -2284,16 +2288,14 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
if (!task.supportsSplitScreenWindowingMode() || forceNonResizable) {
// Dismiss docked stack. If task appeared to be in docked stack but is not resizable -
// we need to move it to top of fullscreen stack, otherwise it will be covered.
-
- final ActivityStack dockedStack =
- task.getStack().getDisplay().getRootSplitScreenPrimaryTask();
- if (dockedStack != null) {
+ final DisplayContent display = task.getStack().getDisplay();
+ if (display.isSplitScreenModeActivated()) {
// Display a warning toast that we tried to put an app that doesn't support
// split-screen in split-screen.
mService.getTaskChangeNotificationController()
.notifyActivityDismissingDockedStack();
- dockedStack.getDisplay().onSplitScreenModeDismissed();
- dockedStack.getDisplay().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS,
+ display.onSplitScreenModeDismissed();
+ display.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS,
true /* notifyClients */);
}
return;
@@ -2602,7 +2604,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
"startActivityFromRecents: Task " + taskId + " not found.");
} else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
&& task.getWindowingMode() != windowingMode) {
- mService.moveTaskToSplitScreenPrimaryTile(task, true /* toTop */);
+ mService.moveTaskToSplitScreenPrimaryTask(task, true /* toTop */);
}
if (windowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 14ca7cbae4b3..da1c045dba16 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -30,7 +30,6 @@ import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WaitResult.LAUNCH_STATE_COLD;
import static android.app.WaitResult.LAUNCH_STATE_HOT;
-import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -78,6 +77,7 @@ import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_DISPLAY;
import static com.android.server.wm.Task.REPARENT_MOVE_STACK_TO_FRONT;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -1566,7 +1566,7 @@ class ActivityStarter {
}
if (!mAvoidMoveToFront && mDoResume) {
- mTargetStack.moveToFront("reuseOrNewTask");
+ mTargetStack.getStack().moveToFront("reuseOrNewTask", targetTask);
if (mOptions != null) {
if (mPreferredWindowingMode != WINDOWING_MODE_UNDEFINED) {
mTargetStack.setWindowingMode(mPreferredWindowingMode);
@@ -2364,6 +2364,7 @@ class ActivityStarter {
private void setTargetStackIfNeeded(ActivityRecord intentActivity) {
mTargetStack = intentActivity.getRootTask();
mTargetStack.mLastPausedActivity = null;
+ Task intentTask = intentActivity.getTask();
// If the target task is not in the front, then we need to bring it to the front...
// except... well, with SINGLE_TASK_LAUNCH it's not entirely clear. We'd like to have
// the same behavior as if a new instance was being started, which means not bringing it
@@ -2374,7 +2375,7 @@ class ActivityStarter {
final ActivityRecord curTop = (focusStack == null)
? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);
final Task topTask = curTop != null ? curTop.getTask() : null;
- differentTopTask = topTask != intentActivity.getTask()
+ differentTopTask = topTask != intentTask
|| (focusStack != null && topTask != focusStack.getTopMostTask());
} else {
// The existing task should always be different from those in other displays.
@@ -2391,7 +2392,6 @@ class ActivityStarter {
intentActivity.setTaskToAffiliateWith(mSourceRecord.getTask());
}
- final Task intentTask = intentActivity.getTask();
final ActivityStack launchStack =
getLaunchStack(mStartActivity, mLaunchFlags, intentTask, mOptions);
if (launchStack == null || launchStack == mTargetStack) {
@@ -2400,6 +2400,14 @@ class ActivityStarter {
// new intent has delivered.
final boolean isSplitScreenTopStack = mTargetStack.isTopSplitScreenStack();
+ // TODO(b/151572268): Figure out a better way to move tasks in above 2-levels
+ // tasks hierarchies.
+ if (mTargetStack != intentTask
+ && mTargetStack != intentTask.getParent().asTask()) {
+ intentTask.getParent().positionChildAt(POSITION_TOP, intentTask,
+ false /* includingParents */);
+ intentTask = intentTask.getParent().asTask();
+ }
// We only want to move to the front, if we aren't going to launch on a
// different stack. If we launch on a different stack, we will put the
// task on top there.
@@ -2420,8 +2428,8 @@ class ActivityStarter {
// Need to update mTargetStack because if task was moved out of it, the original stack may
// be destroyed.
mTargetStack = intentActivity.getRootTask();
- mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.getTask(),
- WINDOWING_MODE_UNDEFINED, DEFAULT_DISPLAY, mTargetStack);
+ mSupervisor.handleNonResizableTaskIfNeeded(intentTask, WINDOWING_MODE_UNDEFINED,
+ DEFAULT_DISPLAY, mTargetStack);
}
private void resumeTargetStackIfNeeded() {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index edc87e5a4d88..2263795f9442 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -548,8 +548,11 @@ public abstract class ActivityTaskManagerInternal {
/**
* Gets bitmap snapshot of the provided task id.
+ *
+ * <p>Warning! this may restore the snapshot from disk so can block, don't call in a latency
+ * sensitive environment.
*/
- public abstract ActivityManager.TaskSnapshot getTaskSnapshotNoRestore(int taskId,
+ public abstract ActivityManager.TaskSnapshot getTaskSnapshotBlocking(int taskId,
boolean isLowResolution);
/** Returns true if uid is considered foreground for activity start purposes. */
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index f7278e70cede..a5b0026398b6 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -31,12 +31,12 @@ import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
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_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -122,6 +122,7 @@ import static com.android.server.wm.RootWindowContainer.MATCH_TASK_IN_STACKS_OR_
import static com.android.server.wm.Task.LOCK_TASK_AUTH_DONT_LOCK;
import static com.android.server.wm.Task.REPARENT_KEEP_STACK_AT_FRONT;
import static com.android.server.wm.Task.REPARENT_LEAVE_STACK_IN_PLACE;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
import android.Manifest;
import android.annotation.IntDef;
@@ -144,7 +145,6 @@ import android.app.IApplicationThread;
import android.app.IAssistDataReceiver;
import android.app.INotificationManager;
import android.app.IRequestFinishCallback;
-import android.window.ITaskOrganizerController;
import android.app.ITaskStackListener;
import android.app.Notification;
import android.app.NotificationManager;
@@ -230,9 +230,9 @@ import android.util.proto.ProtoOutputStream;
import android.view.IRecentsAnimationRunner;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
+import android.view.WindowManager;
import android.window.IWindowOrganizerController;
import android.window.WindowContainerTransaction;
-import android.view.WindowManager;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -1274,9 +1274,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
a.colorMode = ActivityInfo.COLOR_MODE_DEFAULT;
a.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchActivityType(ACTIVITY_TYPE_DREAM);
+
try {
getActivityStartController().obtainStarter(intent, "dream")
.setActivityInfo(a)
+ .setActivityOptions(options.toBundle())
.setIsDream(true)
.execute();
return true;
@@ -2349,16 +2353,18 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
final ActivityStack stack = task.getStack();
- // Convert some windowing-mode changes into root-task reparents for split-screen.
- if (stack.getTile() != null) {
- stack.getDisplay().onSplitScreenModeDismissed();
- }
if (toTop) {
stack.moveToFront("setTaskWindowingMode", task);
}
- stack.setWindowingMode(windowingMode);
- stack.getDisplay().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS,
- true /* notifyClients */);
+ // Convert some windowing-mode changes into root-task reparents for split-screen.
+ if (stack.inSplitScreenWindowingMode()) {
+ stack.getDisplay().onSplitScreenModeDismissed();
+
+ } else {
+ stack.setWindowingMode(windowingMode);
+ stack.getDisplay().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS,
+ true /* notifyClients */);
+ }
return true;
} finally {
Binder.restoreCallingIdentity(ident);
@@ -2755,24 +2761,22 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
final int prevMode = task.getWindowingMode();
- moveTaskToSplitScreenPrimaryTile(task, toTop);
+ moveTaskToSplitScreenPrimaryTask(task, toTop);
return prevMode != task.getWindowingMode();
}
- void moveTaskToSplitScreenPrimaryTile(Task task, boolean toTop) {
- ActivityStack stack = task.getStack();
- TaskTile tile = null;
- for (int i = stack.getDisplay().getStackCount() - 1; i >= 0; --i) {
- tile = stack.getDisplay().getStackAt(i).asTile();
- if (tile != null && tile.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- break;
- }
+ void moveTaskToSplitScreenPrimaryTask(Task task, boolean toTop) {
+ final DisplayContent display = task.getDisplayContent();
+ final ActivityStack primarySplitTask = display.getRootSplitScreenPrimaryTask();
+ if (primarySplitTask == null) {
+ throw new IllegalStateException("Can't enter split without associated organized task");
}
- if (tile == null) {
- throw new IllegalStateException("Can't enter split without associated tile");
+
+ if (toTop) {
+ display.positionStackAt(POSITION_TOP, primarySplitTask, false /* includingParents */);
}
WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.reparent(stack.mRemoteToken, tile.mRemoteToken, toTop);
+ wct.reparent(task.getStack().mRemoteToken, primarySplitTask.mRemoteToken, toTop);
mWindowOrganizerController.applyTransaction(wct);
}
@@ -3239,7 +3243,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final ActivityStack stack = r.getRootTask();
final Task task = stack.getDisplay().createStack(stack.getWindowingMode(),
- stack.getActivityType(), !ON_TOP, ainfo, intent);
+ stack.getActivityType(), !ON_TOP, ainfo, intent,
+ false /* createdByOrganizer */);
if (!mRecentTasks.addToBottom(task)) {
// The app has too many tasks already and we can't add any more
@@ -4278,19 +4283,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
try {
synchronized (mGlobalLock) {
final DisplayContent dc = mRootWindowContainer.getDefaultDisplay();
- TaskTile primary = null;
- TaskTile secondary = null;
- for (int i = dc.getStackCount() - 1; i >= 0; --i) {
- final TaskTile t = dc.getStackAt(i).asTile();
- if (t == null) {
- continue;
- }
- if (t.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- primary = t;
- } else if (t.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
- secondary = t;
- }
- }
+ final Task primary = dc.getRootSplitScreenPrimaryTask();
+ final Task secondary = dc.getTask(t -> t.mCreatedByOrganizer && t.isRootTask()
+ && t.inSplitScreenSecondaryWindowingMode());
if (primary == null || secondary == null) {
return;
}
@@ -7477,10 +7472,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
- public ActivityManager.TaskSnapshot getTaskSnapshotNoRestore(int taskId,
- boolean isLowResolution) {
+ public ActivityManager.TaskSnapshot getTaskSnapshotBlocking(
+ int taskId, boolean isLowResolution) {
return ActivityTaskManagerService.this.getTaskSnapshot(taskId, isLowResolution,
- false /* restoreFromDisk */);
+ true /* restoreFromDisk */);
}
@Override
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 33dd9cf4ee05..1036af67c0db 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -467,6 +468,10 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
return getActivityType() == ACTIVITY_TYPE_ASSISTANT;
}
+ public boolean isActivityTypeDream() {
+ return getActivityType() == ACTIVITY_TYPE_DREAM;
+ }
+
public boolean isActivityTypeStandard() {
return getActivityType() == ACTIVITY_TYPE_STANDARD;
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 7df731b3efab..ede456949116 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -200,7 +200,6 @@ import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.IDisplayWindowInsetsController;
import android.view.ISystemGestureExclusionListener;
-import android.window.ITaskOrganizer;
import android.view.IWindow;
import android.view.InputChannel;
import android.view.InputDevice;
@@ -217,6 +216,7 @@ import android.view.ViewRootImpl;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
+import android.window.WindowContainerTransaction;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
@@ -456,6 +456,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
private final Configuration mTmpConfiguration = new Configuration();
+ private ArrayList<Task> mTmpTasks = new ArrayList<>();
+
/** Remove this display when animation on it has completed. */
private boolean mDeferredRemoval;
@@ -654,8 +656,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
private final RootWindowContainer.FindTaskResult
mTmpFindTaskResult = new RootWindowContainer.FindTaskResult();
- // When non-null, new stacks get put into this tile.
- TaskTile mLaunchTile = null;
+ // When non-null, new tasks get put into this root task.
+ Task mLaunchRootTask = null;
private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
WindowStateAnimator winAnimator = w.mWinAnimator;
@@ -2128,12 +2130,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
/** @return The primary split-screen task, and {@code null} otherwise. */
- ActivityStack getRootSplitScreenPrimaryTask() {
+ @Nullable ActivityStack getRootSplitScreenPrimaryTask() {
return mTaskContainers.getRootSplitScreenPrimaryTask();
}
- boolean hasSplitScreenPrimaryTask() {
- return getRootSplitScreenPrimaryTask() != null;
+ boolean isSplitScreenModeActivated() {
+ Task task = getRootSplitScreenPrimaryTask();
+ return task != null && task.hasChild();
}
ActivityStack getRootPinnedTask() {
@@ -2600,7 +2603,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
amendWindowTapExcludeRegion(mTouchExcludeRegion);
// TODO(multi-display): Support docked stacks on secondary displays.
- if (mDisplayId == DEFAULT_DISPLAY && getRootSplitScreenPrimaryTask() != null) {
+ if (mDisplayId == DEFAULT_DISPLAY && isSplitScreenModeActivated()) {
mDividerControllerLocked.getTouchRegion(mTmpRect);
mTmpRegion.set(mTmpRect);
mTouchExcludeRegion.op(mTmpRegion, Op.UNION);
@@ -2623,8 +2626,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// If the task is home stack and it is resizable and visible (top of its root task), we want
// to exclude the docked stack from touch so we need the entire screen area and not just a
// small portion which the home stack currently is resized to.
- if (task.isActivityTypeHome() && task.isVisible() && task.getStack().getTile() != null
- && task.isResizeable()) {
+ if (task.isActivityTypeHome() && task.isVisible() && task.isResizeable()) {
mDisplayContent.getBounds(mTmpRect);
} else {
task.getDimBounds(mTmpRect);
@@ -4328,15 +4330,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
@VisibleForTesting
ActivityStack getTopStack() {
- // TODO(task-hierarchy): Just grab index -1 once tiles are in hierarchy.
- for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) {
- final ActivityStack child = mTaskContainers.getChildAt(i);
- if (child instanceof TaskTile) {
- continue;
- }
- return child;
- }
- return null;
+ final int count = mTaskContainers.getChildCount();
+ return count > 0 ? mTaskContainers.getChildAt(count - 1) : null;
}
int getIndexOf(ActivityStack stack) {
@@ -4375,10 +4370,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
private void addStackReferenceIfNeeded(ActivityStack stack) {
- // TODO(task-hierarchy): Remove when tiles are in hierarchy.
- if (stack instanceof TaskTile) {
- return;
- }
if (stack.isActivityTypeHome()) {
if (mRootHomeTask != null) {
if (!stack.isDescendantOf(mRootHomeTask)) {
@@ -4390,27 +4381,26 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mRootHomeTask = stack;
}
}
+
+ if (!stack.isRootTask()) {
+ return;
+ }
final int windowingMode = stack.getWindowingMode();
if (windowingMode == WINDOWING_MODE_PINNED) {
if (mRootPinnedTask != null) {
- if (!stack.isDescendantOf(mRootPinnedTask)) {
- throw new IllegalArgumentException(
- "addStackReferenceIfNeeded: pinned stack=" + mRootPinnedTask
- + " already exist on display=" + this + " stack=" + stack);
- }
- } else {
- mRootPinnedTask = stack;
+ throw new IllegalArgumentException(
+ "addStackReferenceIfNeeded: pinned stack=" + mRootPinnedTask
+ + " already exist on display=" + this + " stack=" + stack);
}
+ mRootPinnedTask = stack;
} else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
if (mRootSplitScreenPrimaryTask != null) {
- if (!stack.isDescendantOf(mRootSplitScreenPrimaryTask)) {
- throw new IllegalArgumentException("addStackReferenceIfNeeded:"
- + " split-screen-primary" + " stack=" + mRootSplitScreenPrimaryTask
- + " already exist on display=" + this + " stack=" + stack);
- }
- } else {
- mRootSplitScreenPrimaryTask = stack;
+ throw new IllegalArgumentException(
+ "addStackReferenceIfNeeded: split screen primary stack="
+ + mRootSplitScreenPrimaryTask
+ + " already exist on display=" + this + " stack=" + stack);
}
+ mRootSplitScreenPrimaryTask = stack;
}
}
@@ -4495,6 +4485,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
*/
private int findPositionForStack(int requestedPosition, ActivityStack stack,
boolean adding) {
+ if (stack.isActivityTypeDream()) {
+ return POSITION_TOP;
+ }
+
if (stack.inPinnedWindowingMode()) {
return POSITION_TOP;
}
@@ -4645,10 +4639,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// Apps and their containers are not allowed to specify an orientation while using
// root tasks...except for the home stack if it is not resizable and currently
// visible (top of) its root task.
- if (mRootHomeTask != null && mRootHomeTask.isVisible()
- && mRootHomeTask.getTile() != null) {
+ if (mRootHomeTask != null && mRootHomeTask.isVisible()) {
final Task topMost = mRootHomeTask.getTopMostTask();
- final boolean resizable = topMost == null && topMost.isResizeable();
+ final boolean resizable = topMost != null && topMost.isResizeable();
if (!(resizable && mRootHomeTask.matchParentBounds())) {
final int orientation = mRootHomeTask.getOrientation();
if (orientation != SCREEN_ORIENTATION_UNSET) {
@@ -4792,17 +4785,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mSplitScreenDividerAnchor = null;
}
}
-
- @Override
- void onChildPositionChanged(WindowContainer child) {
- // TODO(task-hierarchy): Move functionality to TaskTile when it's a proper parent.
- TaskTile tile = ((ActivityStack) child).getTile();
- if (tile == null) {
- return;
- }
- mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(
- tile, false /* force */);
- }
}
private class WindowContainers extends DisplayChildWindowContainer<WindowContainer> {
@@ -4983,7 +4965,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
private boolean skipImeWindowsDuringTraversal(DisplayContent dc) {
// We skip IME windows so they're processed just above their target, except
// in split-screen mode where we process the IME containers above the docked divider.
- return dc.mInputMethodTarget != null && !dc.hasSplitScreenPrimaryTask();
+ return dc.mInputMethodTarget != null && !dc.isSplitScreenModeActivated();
}
/** Like {@link #forAllWindows}, but ignores {@link #skipImeWindowsDuringTraversal} */
@@ -5657,6 +5639,14 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mAtmService.updateSleepIfNeededLocked();
}
+ void addStackReferenceIfNeeded(ActivityStack stack) {
+ mTaskContainers.addStackReferenceIfNeeded(stack);
+ }
+
+ void removeStackReferenceIfNeeded(ActivityStack stack) {
+ mTaskContainers.removeStackReferenceIfNeeded(stack);
+ }
+
void onStackRemoved(ActivityStack stack) {
if (ActivityTaskManagerDebugConfig.DEBUG_STACK) {
Slog.v(TAG_STACK, "removeStack: detaching " + stack + " from displayId=" + mDisplayId);
@@ -5703,6 +5693,14 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
"positionStackAt: Can only have one task on display=" + this);
}
+ final boolean movingToTop = wasContained && position >= getStackCount() - 1;
+ // Reset mPreferredTopFocusableStack before positioning to top or {@link
+ // ActivityStackSupervisor#updateTopResumedActivityIfNeeded()} won't update the top
+ // resumed activity.
+ if (movingToTop && stack.isFocusable()) {
+ mPreferredTopFocusableStack = null;
+ }
+
// Since positionChildAt() is called during the creation process of pinned stacks,
// ActivityStack#getStack() can be null.
positionStackAt(position, stack, includingParents);
@@ -5712,7 +5710,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// we are looking for top focusable stack. The condition {@code wasContained} restricts the
// preferred stack is set only when moving an existing stack to top instead of adding a new
// stack that may be too early (e.g. in the middle of launching or reparenting).
- if (wasContained && position >= getStackCount() - 1 && stack.isFocusableAndVisible()) {
+ if (movingToTop && stack.isFocusableAndVisible()) {
mPreferredTopFocusableStack = stack;
} else if (mPreferredTopFocusableStack == stack) {
mPreferredTopFocusableStack = null;
@@ -5755,18 +5753,54 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
/**
* Returns an existing stack compatible with the windowing mode and activity type or creates one
* if a compatible stack doesn't exist.
+ * @see #getOrCreateStack(int, int, boolean, Intent, Task, boolean)
+ */
+ ActivityStack getOrCreateStack(int windowingMode, int activityType, boolean onTop) {
+ return getOrCreateStack(windowingMode, activityType, onTop, null /* intent */,
+ null /* candidateTask */, false /* createdByOrganizer */);
+ }
+
+ /**
+ * When two level tasks are required for given windowing mode and activity type, returns an
+ * existing compatible root task or creates a new one.
+ * For one level task, the candidate task would be reused to also be the root task or create
+ * a new root task if no candidate task.
* @see #getStack(int, int)
* @see #createStack(int, int, boolean)
*/
- ActivityStack getOrCreateStack(int windowingMode, int activityType,
- boolean onTop) {
+ ActivityStack getOrCreateStack(int windowingMode, int activityType, boolean onTop,
+ Intent intent, Task candidateTask, boolean createdByOrganizer) {
if (!alwaysCreateStack(windowingMode, activityType)) {
ActivityStack stack = getStack(windowingMode, activityType);
if (stack != null) {
return stack;
}
+ } else if (candidateTask != null) {
+ final ActivityStack stack = (ActivityStack) candidateTask;
+ final int position = onTop ? POSITION_TOP : POSITION_BOTTOM;
+ if (isSplitScreenModeActivated()) {
+ final Task splitRootSecondary = getTask(t -> t.mCreatedByOrganizer && t.isRootTask()
+ && t.inSplitScreenSecondaryWindowingMode());
+ if (stack.getParent() == null) {
+ splitRootSecondary.addChild(stack, position);
+ } else if (stack.getParent() != splitRootSecondary) {
+ stack.reparent(splitRootSecondary, position);
+ }
+ } else if (stack.getDisplay() != this || !stack.isRootTask()) {
+ if (stack.getParent() == null) {
+ addStack(stack, position);
+ } else {
+ stack.reparent(this, onTop);
+ }
+ }
+ // Update windowing mode if necessary, e.g. moving a pinned task to fullscreen.
+ if (candidateTask.getWindowingMode() != windowingMode) {
+ candidateTask.setWindowingMode(windowingMode);
+ }
+ return stack;
}
- return createStack(windowingMode, activityType, onTop);
+ return createStack(windowingMode, activityType, onTop, null /*info*/, intent,
+ createdByOrganizer);
}
/**
@@ -5784,7 +5818,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// UNDEFINED windowing mode is a valid result and means that the new stack will inherit
// it's display's windowing mode.
windowingMode = validateWindowingMode(windowingMode, r, candidateTask, activityType);
- return getOrCreateStack(windowingMode, activityType, onTop);
+ return getOrCreateStack(windowingMode, activityType, onTop, null /* intent */,
+ candidateTask, false /* createdByOrganizer */);
}
@VisibleForTesting
@@ -5793,7 +5828,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
ActivityStack createStack(int windowingMode, int activityType, boolean onTop) {
- return createStack(windowingMode, activityType, onTop, null /*info*/, null /*intent*/);
+ return createStack(windowingMode, activityType, onTop, null /* info */, null /* intent */,
+ false /* createdByOrganizer */);
}
/**
@@ -5805,25 +5841,29 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} then the stack will
* be created in {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}.
* @param onTop If true the stack will be created at the top of the display, else at the bottom.
+ * @param info The started activity info.
+ * @param intent The intent that started this task.
+ * @param createdByOrganizer @{code true} if this is created by task organizer, @{code false}
+ * otherwise.
* @return The newly created stack.
*/
ActivityStack createStack(int windowingMode, int activityType, boolean onTop, ActivityInfo info,
- Intent intent) {
+ Intent intent, boolean createdByOrganizer) {
if (mSingleTaskInstance && getStackCount() > 0) {
// Create stack on default display instead since this display can only contain 1 stack.
// TODO: Kinda a hack, but better that having the decision at each call point. Hoping
// this goes away once ActivityView is no longer using virtual displays.
return mRootWindowContainer.getDefaultDisplay().createStack(
- windowingMode, activityType, onTop, info, intent);
+ windowingMode, activityType, onTop, info, intent, createdByOrganizer);
}
- if (activityType == ACTIVITY_TYPE_UNDEFINED) {
+ if (activityType == ACTIVITY_TYPE_UNDEFINED && !createdByOrganizer) {
// Can't have an undefined stack type yet...so re-map to standard. Anyone that wants
- // anything else should be passing it in anyways...
+ // anything else should be passing it in anyways...except for the task organizer.
activityType = ACTIVITY_TYPE_STANDARD;
}
- if (activityType != ACTIVITY_TYPE_STANDARD) {
+ if (activityType != ACTIVITY_TYPE_STANDARD && activityType != ACTIVITY_TYPE_UNDEFINED) {
// For now there can be only one stack of a particular non-standard activity type on a
// display. So, get that ignoring whatever windowing mode it is currently in.
ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
@@ -5842,39 +5882,39 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
final int stackId = getNextStackId();
- return createStackUnchecked(windowingMode, activityType, stackId, onTop, info, intent);
+ return createStackUnchecked(windowingMode, activityType, stackId, onTop, info, intent,
+ createdByOrganizer);
}
- /** @return the tile to create the next stack in. */
- private TaskTile updateLaunchTile(int windowingMode) {
+ /** @return the root task to create the next task in. */
+ private Task updateLaunchRootTask(int windowingMode) {
if (!isSplitScreenWindowingMode(windowingMode)) {
- // Only split-screen windowing modes interact with tiles.
+ // Only split-screen windowing modes can do this currently...
return null;
}
for (int i = getStackCount() - 1; i >= 0; --i) {
- final TaskTile t = getStackAt(i).asTile();
- if (t == null || t.getRequestedOverrideWindowingMode() != windowingMode) {
+ final Task t = getStackAt(i);
+ if (!t.mCreatedByOrganizer || t.getRequestedOverrideWindowingMode() != windowingMode) {
continue;
}
- // If not already set, pick a launch tile which is not the one we are launching
- // into.
- if (mLaunchTile == null) {
+ // If not already set, pick a launch root which is not the one we are launching into.
+ if (mLaunchRootTask == null) {
for (int j = 0, n = getStackCount(); j < n; ++j) {
- TaskTile tt = getStackAt(j).asTile();
- if (tt != t) {
- mLaunchTile = tt;
+ final Task tt = getStackAt(j);
+ if (tt.mCreatedByOrganizer && tt != t) {
+ mLaunchRootTask = tt;
break;
}
}
}
return t;
}
- return mLaunchTile;
+ return mLaunchRootTask;
}
@VisibleForTesting
- ActivityStack createStackUnchecked(int windowingMode, int activityType,
- int stackId, boolean onTop, ActivityInfo info, Intent intent) {
+ ActivityStack createStackUnchecked(int windowingMode, int activityType, int stackId,
+ boolean onTop, ActivityInfo info, Intent intent, boolean createdByOrganizer) {
if (windowingMode == WINDOWING_MODE_PINNED && activityType != ACTIVITY_TYPE_STANDARD) {
throw new IllegalArgumentException("Stack with windowing mode cannot with non standard "
+ "activity type.");
@@ -5884,19 +5924,25 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
info.applicationInfo = new ApplicationInfo();
}
- TaskTile tile = updateLaunchTile(windowingMode);
- if (tile != null) {
- // Since this stack will be put into a tile, its windowingMode will be inherited.
+ // Task created by organizer are added as root.
+ Task launchRootTask = createdByOrganizer ? null : updateLaunchRootTask(windowingMode);
+ if (launchRootTask != null) {
+ // Since this stack will be put into a root task, its windowingMode will be inherited.
windowingMode = WINDOWING_MODE_UNDEFINED;
}
+
final ActivityStack stack = (ActivityStack) Task.create(mAtmService, stackId, activityType,
- info, intent);
- addStack(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
- stack.setWindowingMode(windowingMode, false /* animate */, false /* showRecents */,
- false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */,
- true /* creating */);
- if (tile != null) {
- tile.addChild(stack, 0 /* index */);
+ info, intent, createdByOrganizer);
+ if (launchRootTask != null) {
+ launchRootTask.addChild(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
+ if (onTop) {
+ positionStackAtTop((ActivityStack) launchRootTask, false /* includingParents */);
+ }
+ } else {
+ addStack(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
+ stack.setWindowingMode(windowingMode, false /* animate */, false /* showRecents */,
+ false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */,
+ true /* creating */);
}
return stack;
}
@@ -6031,7 +6077,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mTmpFindTaskResult.clear();
for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = getStackAt(stackNdx);
- if (!r.hasCompatibleActivityType(stack)) {
+ if (!r.hasCompatibleActivityType(stack) && stack.isLeafTask()) {
if (DEBUG_TASKS) {
Slog.d(TAG_TASKS, "Skipping stack: (mismatch activity/stack) " + stack);
}
@@ -6103,7 +6149,15 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
final int activityType = activityTypes[j];
for (int i = getStackCount() - 1; i >= 0; --i) {
final ActivityStack stack = getStackAt(i);
- if (stack.getActivityType() == activityType) {
+ // Collect the root tasks that are currently being organized.
+ if (stack.isOrganized()) {
+ for (int k = stack.getChildCount() - 1; k >= 0; --k) {
+ final ActivityStack childStack = (ActivityStack) stack.getChildAt(k);
+ if (childStack.getActivityType() == activityType) {
+ stacks.add(childStack);
+ }
+ }
+ } else if (stack.getActivityType() == activityType) {
stacks.add(stack);
}
}
@@ -6117,13 +6171,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
void onSplitScreenModeDismissed() {
mAtmService.deferWindowLayout();
try {
- mLaunchTile = null;
- for (int i = getStackCount() - 1; i >= 0; --i) {
- final TaskTile t = getStackAt(i).asTile();
- if (t != null) {
- t.removeAllChildren();
- }
- }
+ mLaunchRootTask = null;
+ moveSplitScreenTasksToFullScreen();
} finally {
final ActivityStack topFullscreenStack =
getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
@@ -6141,6 +6190,24 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
}
+ private void moveSplitScreenTasksToFullScreen() {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ mTmpTasks.clear();
+ forAllTasks(task -> {
+ if (task.mCreatedByOrganizer && task.inSplitScreenWindowingMode() && task.hasChild()) {
+ mTmpTasks.add(task);
+ }
+ });
+
+ for (int i = mTmpTasks.size() - 1; i >= 0; i--) {
+ final Task root = mTmpTasks.get(i);
+ for (int j = 0; j < root.getChildCount(); j++) {
+ wct.reparent(root.getChildAt(j).mRemoteToken, null, true /* toTop */);
+ }
+ }
+ mAtmService.mWindowOrganizerController.applyTransaction(wct);
+ }
+
/**
* Returns true if the {@param windowingMode} is supported based on other parameters passed in.
* @param windowingMode The windowing mode we are checking support for.
@@ -6253,7 +6320,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
}
- final boolean inSplitScreenMode = hasSplitScreenPrimaryTask();
+ final boolean inSplitScreenMode = isSplitScreenModeActivated();
if (!inSplitScreenMode
&& windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY) {
// Switch to the display's windowing mode if we are not in split-screen mode and we are
@@ -6278,10 +6345,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
boolean isTopNotPinnedStack(ActivityStack stack) {
- // TODO(task-hierarchy): Remove when tiles are in hierarchy.
- if (stack instanceof TaskTile) {
- return false;
- }
for (int i = getStackCount() - 1; i >= 0; --i) {
final ActivityStack current = getStackAt(i);
if (!current.inPinnedWindowingMode()) {
@@ -6503,7 +6566,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// If default display is in split-window mode, set windowing mode of the stack
// to split-screen secondary. Otherwise, set the windowing mode to undefined by
// default to let stack inherited the windowing mode from the new display.
- final int windowingMode = toDisplay.hasSplitScreenPrimaryTask()
+ final int windowingMode = toDisplay.isSplitScreenModeActivated()
? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
: WINDOWING_MODE_UNDEFINED;
stack.reparent(toDisplay, true /* onTop */);
@@ -6607,27 +6670,35 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* already top-most.
*/
ActivityStack getStackAbove(ActivityStack stack) {
- final int stackIndex = getIndexOf(stack) + 1;
- return (stackIndex < getStackCount()) ? getStackAt(stackIndex) : null;
+ final WindowContainer wc = stack.getParent();
+ final int index = wc.mChildren.indexOf(stack) + 1;
+ return (index < wc.mChildren.size()) ? (ActivityStack) wc.mChildren.get(index) : null;
}
/**
* Adjusts the {@param stack} behind the last visible stack in the display if necessary.
* Generally used in conjunction with {@link #moveStackBehindStack}.
*/
+ // TODO(b/151575894): Remove special stack movement methods.
void moveStackBehindBottomMostVisibleStack(ActivityStack stack) {
if (stack.shouldBeVisible(null)) {
// Skip if the stack is already visible
return;
}
- // Move the stack to the bottom to not affect the following visibility checks
- positionStackAtBottom(stack);
+ final boolean isRootTask = stack.isRootTask();
+ if (isRootTask) {
+ // Move the stack to the bottom to not affect the following visibility checks
+ positionStackAtBottom(stack);
+ } else {
+ stack.getParent().positionChildAt(POSITION_BOTTOM, stack, false /* includingParents */);
+ }
// Find the next position where the stack should be placed
- final int numStacks = getStackCount();
+ final int numStacks = isRootTask ? getStackCount() : stack.getParent().getChildCount();
for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) {
- final ActivityStack s = getStackAt(stackNdx);
+ final ActivityStack s = isRootTask ? getStackAt(stackNdx)
+ : (ActivityStack) stack.getParent().getChildAt(stackNdx);
if (s == stack) {
continue;
}
@@ -6636,7 +6707,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
|| winMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
if (s.shouldBeVisible(null) && isValidWindowingMode) {
// Move the provided stack to behind this stack
- positionStackAt(stack, Math.max(0, stackNdx - 1));
+ final int position = Math.max(0, stackNdx - 1);
+ if (isRootTask) {
+ positionStackAt(stack, position);
+ } else {
+ stack.getParent().positionChildAt(position, stack, false /*includingParents */);
+ }
break;
}
}
@@ -6652,15 +6728,25 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return;
}
+ final WindowContainer parent = stack.getParent();
+ if (parent == null || parent != behindStack.getParent()) {
+ return;
+ }
+
// Note that positionChildAt will first remove the given stack before inserting into the
// list, so we need to adjust the insertion index to account for the removed index
// TODO: Remove this logic when WindowContainer.positionChildAt() is updated to adjust the
// position internally
- final int stackIndex = getIndexOf(stack);
- final int behindStackIndex = getIndexOf(behindStack);
+ final int stackIndex = parent.mChildren.indexOf(stack);
+ final int behindStackIndex = parent.mChildren.indexOf(behindStack);
final int insertIndex = stackIndex <= behindStackIndex
? behindStackIndex - 1 : behindStackIndex;
- positionStackAt(stack, Math.max(0, insertIndex));
+ final int position = Math.max(0, insertIndex);
+ if (stack.isRootTask()) {
+ positionStackAt(stack, position);
+ } else {
+ parent.positionChildAt(position, stack, false /* includingParents */);
+ }
}
void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
@@ -6697,19 +6783,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return getHomeActivityForUser(mRootWindowContainer.mCurrentUser);
}
- // TODO(task-hierarchy): Remove when tiles are in hierarchy.
- void addTile(TaskTile tile) {
- mTaskContainers.addChild(tile, POSITION_BOTTOM);
- ITaskOrganizer organizer = mAtmService.mTaskOrganizerController.getTaskOrganizer(
- tile.getWindowingMode());
- tile.setTaskOrganizer(organizer);
- }
-
- // TODO(task-hierarchy): Remove when tiles are in hierarchy.
- void removeTile(TaskTile tile) {
- mTaskContainers.removeChild(tile);
- }
-
@Nullable
ActivityRecord getHomeActivityForUser(int userId) {
final ActivityStack homeStack = getRootHomeTask();
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 5e88fb0437c6..4da4a79cfd83 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1527,25 +1527,7 @@ public class DisplayPolicy {
&& (mNotificationShade.getAttrs().privateFlags
& PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0;
- // When the navigation bar isn't visible, we put up a fake input window to catch all
- // touch events. This way we can detect when the user presses anywhere to bring back the
- // nav bar and ensure the application doesn't see the event.
- if (navVisible || navAllowedHidden) {
- if (mInputConsumer != null) {
- mInputConsumer.dismiss();
- mHandler.sendMessage(
- mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer));
- mInputConsumer = null;
- }
- } else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) {
- mInputConsumer = mDisplayContent.getInputMonitor().createInputConsumer(
- mHandler.getLooper(),
- INPUT_CONSUMER_NAVIGATION,
- HideNavInputEventReceiver::new);
- // As long as mInputConsumer is active, hover events are not dispatched to the app
- // and the pointer icon is likely to become stale. Hide it to avoid confusion.
- InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL);
- }
+ updateHideNavInputEventReceiver(navVisible, navAllowedHidden);
// For purposes of positioning and showing the nav bar, if we have decided that it can't
// be hidden (because of the screen aspect ratio), then take that into account.
@@ -1567,6 +1549,28 @@ public class DisplayPolicy {
mLastNotificationShadeForcesShowingNavigation = notificationShadeForcesShowingNavigation;
}
+ void updateHideNavInputEventReceiver(boolean navVisible, boolean navAllowedHidden) {
+ // When the navigation bar isn't visible, we put up a fake input window to catch all
+ // touch events. This way we can detect when the user presses anywhere to bring back the
+ // nav bar and ensure the application doesn't see the event.
+ if (navVisible || navAllowedHidden) {
+ if (mInputConsumer != null) {
+ mInputConsumer.dismiss();
+ mHandler.sendMessage(
+ mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer));
+ mInputConsumer = null;
+ }
+ } else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) {
+ mInputConsumer = mDisplayContent.getInputMonitor().createInputConsumer(
+ mHandler.getLooper(),
+ INPUT_CONSUMER_NAVIGATION,
+ HideNavInputEventReceiver::new);
+ // As long as mInputConsumer is active, hover events are not dispatched to the app
+ // and the pointer icon is likely to become stale. Hide it to avoid confusion.
+ InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL);
+ }
+ }
+
private static void updateInsetsStateForDisplayCutout(DisplayFrames displayFrames,
InsetsState state) {
if (displayFrames.mDisplayCutout.getDisplayCutout().isEmpty()) {
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index bb0278964f85..ac6e75c717ff 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -25,6 +25,7 @@ import static android.view.InsetsController.ANIMATION_TYPE_SHOW;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.SyncRtSurfaceTransactionApplier.applyParams;
+import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_TOUCH;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
@@ -91,6 +92,12 @@ class InsetsPolicy {
|| focusedWin != getNavControlTarget(focusedWin)
|| focusedWin.getRequestedInsetsState().getSource(ITYPE_NAVIGATION_BAR)
.isVisible());
+ updateHideNavInputEventReceiver();
+ }
+
+ private void updateHideNavInputEventReceiver() {
+ mPolicy.updateHideNavInputEventReceiver(!isHidden(ITYPE_NAVIGATION_BAR),
+ mFocusedWin.mAttrs.insetsFlags.behavior != BEHAVIOR_SHOW_BARS_BY_TOUCH);
}
boolean isHidden(@InternalInsetsType int type) {
@@ -169,6 +176,7 @@ class InsetsPolicy {
if (windowState == getNavControlTarget(mFocusedWin)) {
mNavBar.setVisible(state.getSource(ITYPE_NAVIGATION_BAR).isVisible());
}
+ updateHideNavInputEventReceiver();
}
/**
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 44034edaa4bf..6b39fd2a70e3 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -406,12 +406,11 @@ class KeyguardController {
// show on top of the lock screen. In this can we want to dismiss the docked
// stack since it will be complicated/risky to try to put the activity on top
// of the lock screen in the right fullscreen configuration.
- final ActivityStack stack =
- mRootWindowContainer.getDefaultDisplay().getRootSplitScreenPrimaryTask();
- if (stack == null) {
+ final DisplayContent display = mRootWindowContainer.getDefaultDisplay();
+ if (!display.isSplitScreenModeActivated()) {
return;
}
- mRootWindowContainer.getDefaultDisplay().onSplitScreenModeDismissed();
+ display.onSplitScreenModeDismissed();
}
}
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index bd5666dd9a27..244ba82ce32c 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -21,6 +21,7 @@ import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
import static android.app.ActivityManager.RECENT_WITH_EXCLUDED;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
@@ -1298,6 +1299,7 @@ class RecentTasks {
switch (task.getActivityType()) {
case ACTIVITY_TYPE_HOME:
case ACTIVITY_TYPE_RECENTS:
+ case ACTIVITY_TYPE_DREAM:
// Ignore certain activity types completely
return false;
case ACTIVITY_TYPE_ASSISTANT:
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index adafdec40d37..9089240fe9d1 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -388,11 +388,12 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
// surfaces needs to be done immediately.
mWindowManager.executeAppTransition();
- if (targetStack.getTile() != null) {
+ final Task rootTask = targetStack.getRootTask();
+ if (rootTask.isOrganized()) {
// Client state may have changed during the recents animation, so force
// send task info so the client can synchronize its state.
mService.mTaskOrganizerController.dispatchTaskInfoChanged(
- targetStack.mTile, true /* force */);
+ rootTask, true /* force */);
}
} catch (Exception e) {
Slog.e(TAG, "Failed to clean up recents activity", e);
diff --git a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
index 45f8a15979f4..420997a5b82d 100644
--- a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
+++ b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
@@ -20,7 +20,6 @@ import static com.android.server.wm.ActivityStack.TAG_ADD_REMOVE;
import static com.android.server.wm.ActivityStack.TAG_TASKS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.wm.Task.REPARENT_LEAVE_STACK_IN_PLACE;
import android.app.ActivityOptions;
import android.content.Intent;
@@ -233,48 +232,42 @@ class ResetTargetTaskHelper {
}
final ActivityTaskManagerService atmService = mTargetStack.mAtmService;
- final ArrayList<Task> createdTasks = new ArrayList<>();
+ DisplayContent display = mTargetStack.getDisplay();
+ final boolean singleTaskInstanceDisplay = display.isSingleTaskInstance();
+ if (singleTaskInstanceDisplay) {
+ display = atmService.mRootWindowContainer.getDefaultDisplay();
+ }
+
+ final int windowingMode = mTargetStack.getWindowingMode();
+ final int activityType = mTargetStack.getActivityType();
+
while (!mPendingReparentActivities.isEmpty()) {
final ActivityRecord r = mPendingReparentActivities.remove(0);
- final ActivityRecord bottom = mTargetStack.getBottomMostActivity();
- final Task targetTask;
- if (bottom != null && r.taskAffinity.equals(bottom.getTask().affinity)) {
+ final boolean alwaysCreateTask = DisplayContent.alwaysCreateStack(windowingMode,
+ activityType);
+ final Task task = alwaysCreateTask
+ ? display.getBottomMostTask() : mTargetStack.getBottomMostTask();
+ Task targetTask = null;
+ if (task != null && r.taskAffinity.equals(task.affinity)) {
// If the activity currently at the bottom has the same task affinity as
// the one we are moving, then merge it into the same task.
- targetTask = bottom.getTask();
+ targetTask = task;
if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity "
+ r + " out to bottom task " + targetTask);
- } else {
- targetTask = mTargetStack.reuseOrCreateTask(
- r.info, null /*intent*/, false /*toTop*/);
+ }
+ if (targetTask == null) {
+ if (alwaysCreateTask) {
+ targetTask = display.getOrCreateStack(windowingMode, activityType,
+ false /* onTop */);
+ } else {
+ targetTask = mTargetStack.reuseOrCreateTask(r.info, null /*intent*/,
+ false /*toTop*/);
+ }
targetTask.affinityIntent = r.intent;
- createdTasks.add(targetTask);
- if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity "
- + r + " out to new task " + targetTask);
}
r.reparent(targetTask, 0 /* position */, "resetTargetTaskIfNeeded");
atmService.mStackSupervisor.mRecentTasks.add(targetTask);
}
-
- DisplayContent display = mTargetStack.getDisplay();
- final boolean singleTaskInstanceDisplay = display.isSingleTaskInstance();
- if (singleTaskInstanceDisplay) {
- display = atmService.mRootWindowContainer.getDefaultDisplay();
- }
-
- final int windowingMode = mTargetStack.getWindowingMode();
- final int activityType = mTargetStack.getActivityType();
- if (!singleTaskInstanceDisplay && !display.alwaysCreateStack(windowingMode, activityType)) {
- return;
- }
-
- while (!createdTasks.isEmpty()) {
- final Task targetTask = createdTasks.remove(createdTasks.size() - 1);
- final ActivityStack targetStack = display.getOrCreateStack(
- windowingMode, activityType, false /* onTop */);
- targetTask.reparent(targetStack, false /* toTop */, REPARENT_LEAVE_STACK_IN_PLACE,
- false /* animate */, true /* deferResume */, "resetTargetTask");
- }
}
private boolean takeOption(ActivityRecord p, boolean noOptions) {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index ebf1bc988b91..b2920ee1d4aa 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -18,6 +18,7 @@ package com.android.server.wm;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -1970,8 +1971,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
final int focusStackId = topFocusedStack != null
? topFocusedStack.getRootTaskId() : INVALID_TASK_ID;
// We dismiss the docked stack whenever we switch users.
- final ActivityStack dockedStack = getDefaultDisplay().getRootSplitScreenPrimaryTask();
- if (dockedStack != null) {
+ if (getDefaultDisplay().isSplitScreenModeActivated()) {
getDefaultDisplay().onSplitScreenModeDismissed();
}
// Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will
@@ -2110,20 +2110,18 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
final ActivityStack stack;
if (singleActivity) {
stack = r.getRootTask();
+ stack.setWindowingMode(WINDOWING_MODE_PINNED);
} else {
- // In the case of multiple activities, we will create a new stack for it and then
- // move the PIP activity into the stack.
- // We will then perform a windowing mode change for both scenarios.
- stack = display.createStack(
- r.getRootTask().getRequestedOverrideWindowingMode(),
- r.getActivityType(), ON_TOP, r.info, r.intent);
+ // In the case of multiple activities, we will create a new task for it and then
+ // move the PIP activity into the task.
+ stack = display.createStack(WINDOWING_MODE_PINNED, r.getActivityType(), ON_TOP,
+ r.info, r.intent, false /* createdByOrganizer */);
+
// There are multiple activities in the task and moving the top activity should
// reveal/leave the other activities in their original task.
r.reparent(stack, MAX_VALUE, "moveActivityToStack");
}
- stack.setWindowingMode(WINDOWING_MODE_PINNED);
-
// Reset the state that indicates it can enter PiP while pausing after we've moved it
// to the pinned stack
r.supportsEnterPipOnTaskSwitch = false;
@@ -2799,16 +2797,19 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
if (stack == null && r != null) {
stack = r.getRootTask();
}
+ int windowingMode = launchParams != null ? launchParams.mWindowingMode
+ : WindowConfiguration.WINDOWING_MODE_UNDEFINED;
if (stack != null) {
display = stack.getDisplay();
if (display != null && canLaunchOnDisplay(r, display.mDisplayId)) {
- int windowingMode = launchParams != null ? launchParams.mWindowingMode
- : WindowConfiguration.WINDOWING_MODE_UNDEFINED;
if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
windowingMode = display.resolveWindowingMode(r, options, candidateTask,
activityType);
}
- if (stack.isCompatible(windowingMode, activityType)) {
+ // Always allow organized tasks that created by organizer since the activity type
+ // of an organized task is decided by the activity type of its top child, which
+ // could be incompatible with the given windowing mode and activity type.
+ if (stack.isCompatible(windowingMode, activityType) || stack.mCreatedByOrganizer) {
return stack;
}
if (windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY
@@ -2826,6 +2827,10 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
if (display == null || !canLaunchOnDisplay(r, display.mDisplayId)) {
display = getDefaultDisplay();
+ if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
+ windowingMode = display.resolveWindowingMode(r, options, candidateTask,
+ activityType);
+ }
}
return display.getOrCreateStack(r, options, candidateTask, activityType, onTop);
@@ -2915,11 +2920,10 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
case ACTIVITY_TYPE_HOME: return r.isActivityTypeHome();
case ACTIVITY_TYPE_RECENTS: return r.isActivityTypeRecents();
case ACTIVITY_TYPE_ASSISTANT: return r.isActivityTypeAssistant();
+ case ACTIVITY_TYPE_DREAM: return r.isActivityTypeDream();
}
- // TODO(task-hierarchy): Find another way to differentiate tile from normal stack once it is
- // part of the hierarchy
- if (stack instanceof TaskTile) {
- // Don't launch directly into tiles.
+ if (stack.mCreatedByOrganizer) {
+ // Don't launch directly into task created by organizer...but why can't we?
return false;
}
// There is a 1-to-1 relationship between stack and task when not in
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 55dc694bb611..f19c10637c9b 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -20,6 +20,9 @@ import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.ActivityTaskManager.RESIZE_MODE_FORCED;
import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM;
import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION;
+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.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP;
@@ -114,7 +117,6 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
-import android.graphics.Point;
import android.graphics.Rect;
import android.os.Debug;
import android.os.IBinder;
@@ -129,11 +131,11 @@ import android.util.DisplayMetrics;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
-import android.window.ITaskOrganizer;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.Surface;
import android.view.SurfaceControl;
+import android.window.ITaskOrganizer;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IVoiceInteractor;
@@ -489,6 +491,17 @@ class Task extends WindowContainer<WindowContainer> {
PictureInPictureParams mPictureInPictureParams = new PictureInPictureParams.Builder().build();
/**
+ * This task was created by the task organizer which has the following implementations.
+ * <ul>
+ * <lis>The task won't be removed when it is empty. Removal has to be an explicit request
+ * from the task organizer.</li>
+ * <li>Unlike other non-root tasks, it's direct children are visible to the task
+ * organizer for ordering purposes.</li>
+ * </ul>
+ */
+ boolean mCreatedByOrganizer;
+
+ /**
* Don't use constructor directly. Use {@link #create(ActivityTaskManagerService, int,
* ActivityInfo, Intent, TaskDescription)} instead.
*/
@@ -578,6 +591,14 @@ class Task extends WindowContainer<WindowContainer> {
voiceInteractor = _voiceInteractor;
setIntent(activity, intent, info);
setMinDimensions(info);
+ // Before we began to reuse a root task (old ActivityStack) as the leaf task, we used to
+ // create a leaf task in this case. Therefore now we won't send out the task created
+ // notification when we decide to reuse it here, so we send out the notification below.
+ // The reason why the created notification sent out when root task is created doesn't work
+ // is that realActivity isn't set until setIntent() method above is called for the first
+ // time. Eventually this notification will be removed when we can populate those information
+ // when root task is created.
+ mAtmService.getTaskChangeNotificationController().notifyTaskCreated(mTaskId, realActivity);
return this;
}
@@ -586,11 +607,7 @@ class Task extends WindowContainer<WindowContainer> {
return;
}
- // TODO(xutan): Removed type check after stack and task is merged.
- // Before the real merge of stack and task, we need to avoid saving state of stacks. Once
- // the merge is finished we can just pass DisplayContent because both windowing mode and
- // bounds are set in the merged task.
- if (oldParent instanceof ActivityStack) {
+ if (isLeafTask()) {
// This task is going away, so save the last state if necessary.
saveLaunchingStateIfNeeded(((WindowContainer) oldParent).getDisplayContent());
}
@@ -1349,7 +1366,7 @@ class Task extends WindowContainer<WindowContainer> {
if (applicationType != ACTIVITY_TYPE_UNDEFINED || !hasChild()) {
return applicationType;
}
- return getChildAt(0).getActivityType();
+ return getTopChild().getActivityType();
}
@Override
@@ -1362,6 +1379,12 @@ class Task extends WindowContainer<WindowContainer> {
ProtoLog.v(WM_DEBUG_ADD_REMOVE, "addChild: %s at top.", this);
+ // A rootable task that is now being added to be the child of an organized task. Making
+ // sure the stack references is keep updated.
+ if (mTaskOrganizer != null && mCreatedByOrganizer && child.asTask() != null) {
+ mDisplayContent.addStackReferenceIfNeeded((ActivityStack) child);
+ }
+
// Make sure the list of display UID whitelists is updated
// now that this record is in a new task.
mRootWindowContainer.updateUIDsPresentOnDisplay();
@@ -1402,6 +1425,11 @@ class Task extends WindowContainer<WindowContainer> {
@Override
void removeChild(WindowContainer child) {
+ // A rootable child task that is now being removed from an organized task. Making sure
+ // the stack references is keep updated.
+ if (mTaskOrganizer != null && mCreatedByOrganizer && child.asTask() != null) {
+ mDisplayContent.removeStackReferenceIfNeeded((ActivityStack) child);
+ }
removeChild(child, "removeChild");
}
@@ -1449,8 +1477,9 @@ class Task extends WindowContainer<WindowContainer> {
mStackSupervisor.removeTask(this, false /* killProcess */,
!REMOVE_FROM_RECENTS, reason);
}
- } else if (!mReuseTask) {
+ } else if (!mReuseTask && !mCreatedByOrganizer) {
// Remove entire task if it doesn't have any activity left and it isn't marked for reuse
+ // or created by task organizer.
if (!isRootTask) {
getStack().removeChild(this, reason);
}
@@ -1862,7 +1891,12 @@ class Task extends WindowContainer<WindowContainer> {
final Task parentTask = getParent().asTask();
if (parentTask != null) {
parentTask.onActivityStateChanged(record, state, reason);
- return;
+ // We still want to update the resumed activity if the parent task is created by
+ // organizer in order to keep the information synced once got reparented out from the
+ // organized task.
+ if (!parentTask.mCreatedByOrganizer) {
+ return;
+ }
}
if (record == mResumedActivity && state != RESUMED) {
@@ -2296,18 +2330,30 @@ class Task extends WindowContainer<WindowContainer> {
return Configuration.reduceScreenLayout(sourceScreenLayout, longSize, shortSize);
}
- void resolveTileOverrideConfiguration(Configuration newParentConfig) {
+ private void resolveOrganizedOverrideConfiguration(Configuration newParentConfig) {
super.resolveOverrideConfiguration(newParentConfig);
+ if (!isOrganized()) {
+ return;
+ }
+
+ final Task root = getRootTask();
+ if (root == this) {
+ return;
+ }
+
+ // Ensure to have the same windowing mode for the child tasks that controlled by task org.
+ getResolvedOverrideConfiguration().windowConfiguration
+ .setWindowingMode(root.getWindowingMode());
}
@Override
void resolveOverrideConfiguration(Configuration newParentConfig) {
- if (!isLeafTask()) {
- resolveTileOverrideConfiguration(newParentConfig);
+ if (!isLeafTask() || mCreatedByOrganizer) {
+ resolveOrganizedOverrideConfiguration(newParentConfig);
return;
}
mTmpBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds());
- resolveTileOverrideConfiguration(newParentConfig);
+ resolveOrganizedOverrideConfiguration(newParentConfig);
int windowingMode =
getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
if (windowingMode == WINDOWING_MODE_UNDEFINED) {
@@ -2396,7 +2442,9 @@ class Task extends WindowContainer<WindowContainer> {
}
Rect updateOverrideConfigurationFromLaunchBounds() {
- final Rect bounds = getLaunchBounds();
+ // If the task is controlled by another organized task, do not set override
+ // configurations and let its parent (organized task) to control it;
+ final Rect bounds = isOrganized() && !isRootTask() ? null : getLaunchBounds();
setBounds(bounds);
if (bounds != null && !bounds.isEmpty()) {
// TODO: Review if we actually want to do this - we are setting the launch bounds
@@ -2580,7 +2628,7 @@ class Task extends WindowContainer<WindowContainer> {
// preserve POSITION_BOTTOM/POSITION_TOP positions if they are still valid.
if (suggestedPosition == POSITION_BOTTOM && minPosition == 0) {
return POSITION_BOTTOM;
- } else if (suggestedPosition == POSITION_TOP && maxPosition == (size - 1)) {
+ } else if (suggestedPosition == POSITION_TOP && maxPosition >= (size - 1)) {
return POSITION_TOP;
}
// Reset position based on minimum/maximum possible positions.
@@ -3199,6 +3247,20 @@ class Task extends WindowContainer<WindowContainer> {
}
@Override
+ int getOrientation(int candidate) {
+ return canSpecifyOrientation() ? super.getOrientation(candidate) : SCREEN_ORIENTATION_UNSET;
+ }
+
+ private boolean canSpecifyOrientation() {
+ final int windowingMode = getWindowingMode();
+ final int activityType = getActivityType();
+ return windowingMode == WINDOWING_MODE_FULLSCREEN
+ || activityType == ACTIVITY_TYPE_HOME
+ || activityType == ACTIVITY_TYPE_RECENTS
+ || activityType == ACTIVITY_TYPE_ASSISTANT;
+ }
+
+ @Override
boolean fillsParent() {
return matchParentBounds();
}
@@ -3364,14 +3426,12 @@ class Task extends WindowContainer<WindowContainer> {
info.supportsSplitScreenMultiWindow = supportsSplitScreenWindowingMode();
info.configuration.setTo(getConfiguration());
info.token = mRemoteToken;
- // Get's the first non-undefined activity type among this and children. Can't use
- // configuration.windowConfiguration because that would only be this level.
- info.topActivityType = getActivityType();
//TODO (AM refactor): Just use local once updateEffectiveIntent is run during all child
// order changes.
final Task top = getTopMostTask();
info.resizeMode = top != null ? top.mResizeMode : mResizeMode;
+ info.topActivityType = top.getActivityType();
if (mPictureInPictureParams.empty()) {
info.pictureInPictureParams = null;
@@ -3402,10 +3462,6 @@ class Task extends WindowContainer<WindowContainer> {
return this;
}
- TaskTile asTile() {
- return null;
- }
-
// TODO(task-merge): Figure-out how this should work with hierarchy tasks.
boolean shouldBeVisible(ActivityRecord starting) {
return true;
@@ -3701,8 +3757,9 @@ class Task extends WindowContainer<WindowContainer> {
}
static Task create(ActivityTaskManagerService service, int taskId, int activityType,
- ActivityInfo info, Intent intent) {
- return getTaskFactory().create(service, taskId, activityType, info, intent);
+ ActivityInfo info, Intent intent, boolean createdByOrganizer) {
+ return getTaskFactory().create(service, taskId, activityType, info, intent,
+ createdByOrganizer);
}
static Task create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
@@ -3724,8 +3781,9 @@ class Task extends WindowContainer<WindowContainer> {
*/
static class TaskFactory {
Task create(ActivityTaskManagerService service, int taskId, int activityType,
- ActivityInfo info, Intent intent) {
- return new ActivityStack(service, taskId, activityType, info, intent);
+ ActivityInfo info, Intent intent, boolean createdByOrganizer) {
+ return new ActivityStack(service, taskId, activityType, info, intent,
+ createdByOrganizer);
}
Task create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
@@ -4001,21 +4059,29 @@ class Task extends WindowContainer<WindowContainer> {
@Override
boolean isOrganized() {
final Task rootTask = getRootTask();
- // if the rootTask is a "child" of a tile, then don't consider it a root task.
- // TODO: remove this along with removing tile.
- if (((ActivityStack) rootTask).getTile() != null) {
+ if (rootTask.mTaskOrganizer == null) {
+ // You are obviously not organized...
return false;
}
- return rootTask == this && rootTask.mTaskOrganizer != null;
+ if (rootTask == this) {
+ // Root tasks can be organized.
+ return true;
+ }
+ if (rootTask.mCreatedByOrganizer && getParent() == rootTask) {
+ // Direct children of tasks added by the organizer can the organized.
+ return true;
+ }
+
+ return false;
}
@Override
protected void reparentSurfaceControl(SurfaceControl.Transaction t, SurfaceControl newParent) {
/**
- * Avoid yanking back control from the TaskOrganizer, which has presumably reparented the
- * Surface in to its own hierarchy.
+ * Avoid reparenting SurfaceControl of the organized tasks that are always on top, since
+ * the surfaces should be controlled by the organizer itself, like bubbles.
*/
- if (isOrganized()) {
+ if (isOrganized() && isAlwaysOnTop()) {
return;
}
super.reparentSurfaceControl(t, newParent);
@@ -4050,6 +4116,9 @@ class Task extends WindowContainer<WindowContainer> {
mTaskOrganizer = null;
mLastTaskOrganizerWindowingMode = -1;
onTaskOrganizerChanged();
+ if (mCreatedByOrganizer) {
+ removeImmediately();
+ }
}
/**
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 552367801678..05b721b8ee81 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
@@ -27,13 +28,14 @@ import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_WINDO
import android.annotation.Nullable;
import android.app.ActivityManager.RunningTaskInfo;
+import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
-import android.window.ITaskOrganizerController;
import android.window.ITaskOrganizer;
+import android.window.ITaskOrganizerController;
import android.window.IWindowContainer;
import com.android.internal.util.ArrayUtils;
@@ -255,11 +257,12 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
if (display == null) {
return null;
}
- final int nextId = display.getNextStackId();
- TaskTile tile = new TaskTile(mService, nextId, windowingMode);
- display.addTile(tile);
- RunningTaskInfo out = tile.getTaskInfo();
- mLastSentTaskInfos.put(tile, out);
+
+ final Task task = display.getOrCreateStack(windowingMode, ACTIVITY_TYPE_UNDEFINED,
+ false /* onTop */, new Intent(), null /* candidateTask */,
+ true /* createdByOrganizer */);
+ RunningTaskInfo out = task.getTaskInfo();
+ mLastSentTaskInfos.put(task, out);
return out;
}
} finally {
@@ -273,11 +276,13 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- TaskTile tile = TaskTile.forToken(token.asBinder());
- if (tile == null) {
- return false;
+ final Task task = WindowContainer.fromBinder(token.asBinder()).asTask();
+ if (task == null) return false;
+ if (!task.mCreatedByOrganizer) {
+ throw new IllegalArgumentException(
+ "Attempt to delete task not created by organizer task=" + task);
}
- tile.removeImmediately();
+ task.removeImmediately();
return true;
}
} finally {
@@ -358,12 +363,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
if (task == null) {
return null;
}
- ActivityStack rootTask = (ActivityStack) task.getRootTask();
- final TaskTile tile = rootTask.getTile();
- if (tile != null) {
- rootTask = tile;
- }
- return rootTask.mRemoteToken;
+ return task.getRootTask().mRemoteToken;
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -371,7 +371,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
}
@Override
- public void setLaunchRoot(int displayId, @Nullable IWindowContainer tile) {
+ public void setLaunchRoot(int displayId, @Nullable IWindowContainer token) {
enforceStackPermission("setLaunchRoot()");
final long origId = Binder.clearCallingIdentity();
try {
@@ -380,16 +380,21 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
if (display == null) {
return;
}
- TaskTile taskTile = tile == null ? null : TaskTile.forToken(tile.asBinder());
- if (taskTile == null) {
- display.mLaunchTile = null;
+ Task task = token == null
+ ? null : WindowContainer.fromBinder(token.asBinder()).asTask();
+ if (task == null) {
+ display.mLaunchRootTask = null;
return;
}
- if (taskTile.getDisplay() != display) {
+ if (!task.mCreatedByOrganizer) {
+ throw new IllegalArgumentException("Attempt to set task not created by "
+ + "organizer as launch root task=" + task);
+ }
+ if (task.getDisplayContent() != display) {
throw new RuntimeException("Can't set launch root for display " + displayId
- + " to task on display " + taskTile.getDisplay().getDisplayId());
+ + " to task on display " + task.getDisplayContent().getDisplayId());
}
- display.mLaunchTile = taskTile;
+ display.mLaunchRootTask = task;
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -411,25 +416,25 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
Slog.e(TAG, "Can't get children of " + parent + " because it is not valid.");
return null;
}
- // For now, only support returning children of persistent root tasks (of which the
- // only current implementation is TaskTile).
- if (!(container instanceof TaskTile)) {
+ final Task task = container.asTask();
+ if (task == null) {
+ Slog.e(TAG, container + " is not a task...");
+ return null;
+ }
+ // For now, only support returning children of tasks created by the organizer.
+ if (!task.mCreatedByOrganizer) {
Slog.w(TAG, "Can only get children of root tasks created via createRootTask");
return null;
}
ArrayList<RunningTaskInfo> out = new ArrayList<>();
- // Tiles aren't real parents, so we need to go through stacks on the display to
- // ensure correct ordering.
- final DisplayContent dc = container.getDisplayContent();
- for (int i = dc.getStackCount() - 1; i >= 0; --i) {
- final ActivityStack as = dc.getStackAt(i);
- if (as.getTile() == container) {
- if (activityTypes != null
- && !ArrayUtils.contains(activityTypes, as.getActivityType())) {
- continue;
- }
- out.add(as.getTaskInfo());
+ for (int i = task.getChildCount() - 1; i >= 0; --i) {
+ final Task child = task.getChildAt(i).asTask();
+ if (child == null) continue;
+ if (activityTypes != null
+ && !ArrayUtils.contains(activityTypes, child.getActivityType())) {
+ continue;
}
+ out.add(child.getTaskInfo());
}
return out;
}
@@ -451,12 +456,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
}
ArrayList<RunningTaskInfo> out = new ArrayList<>();
for (int i = dc.getStackCount() - 1; i >= 0; --i) {
- final ActivityStack task = dc.getStackAt(i);
- if (task.getTile() != null) {
- // a tile is supposed to look like a parent, so don't include their
- // "children" here. They can be accessed via getChildTasks()
- continue;
- }
+ final Task task = dc.getStackAt(i);
if (activityTypes != null
&& !ArrayUtils.contains(activityTypes, task.getActivityType())) {
continue;
diff --git a/services/core/java/com/android/server/wm/TaskTile.java b/services/core/java/com/android/server/wm/TaskTile.java
deleted file mode 100644
index 51142b1d2eb1..000000000000
--- a/services/core/java/com/android/server/wm/TaskTile.java
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-
-import android.app.ActivityManager;
-import android.app.TaskInfo;
-import android.app.WindowConfiguration;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.os.IBinder;
-import android.util.Slog;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.function.Consumer;
-
-/**
- * A Tile. Right now this acts as a proxy for manipulating non-child stacks. Eventually, this
- * can become an actual parent.
- */
-// TODO(task-hierarchy): Remove when tasks can nest >2 or when single tasks can handle their
-// own lifecycles.
-public class TaskTile extends ActivityStack {
- private static final String TAG = "TaskTile";
- final ArrayList<WindowContainer> mChildren = new ArrayList<>();
-
- private static ActivityInfo createEmptyActivityInfo() {
- ActivityInfo info = new ActivityInfo();
- info.applicationInfo = new ApplicationInfo();
- return info;
- }
-
- TaskTile(ActivityTaskManagerService atmService, int id, int windowingMode) {
- super(atmService, id, new Intent() /*intent*/, null /*affinityIntent*/, null /*affinity*/,
- null /*rootAffinity*/, null /*realActivity*/, null /*origActivity*/,
- false /*rootWasReset*/, false /*autoRemoveRecents*/, false /*askedCompatMode*/,
- 0 /*userId*/, 0 /*effectiveUid*/, null /*lastDescription*/,
- System.currentTimeMillis(), true /*neverRelinquishIdentity*/,
- new ActivityManager.TaskDescription(), id, INVALID_TASK_ID, INVALID_TASK_ID,
- 0 /*taskAffiliationColor*/, 0 /*callingUid*/, "" /*callingPackage*/,
- null /*callingFeatureId*/, RESIZE_MODE_RESIZEABLE,
- false /*supportsPictureInPicture*/, false /*_realActivitySuspended*/,
- false /*userSetupComplete*/, INVALID_MIN_SIZE, INVALID_MIN_SIZE,
- createEmptyActivityInfo(), null /*voiceSession*/, null /*voiceInteractor*/,
- null /*stack*/);
- getRequestedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
- }
-
- @Override
- void onDisplayChanged(DisplayContent dc) {
- mDisplayContent = null;
- if (dc != null) {
- dc.getPendingTransaction().merge(getPendingTransaction());
- }
- mDisplayContent = dc;
- // Virtual parent, so don't notify children.
- }
-
- @Override
- TaskTile asTile() {
- return this;
- }
-
- @Override
- protected void addChild(WindowContainer child, Comparator<WindowContainer> comparator) {
- throw new RuntimeException("Improper use of addChild() on Tile");
- }
-
- @Override
- void addChild(WindowContainer child, int index) {
- mChildren.add(child);
- if (child instanceof ActivityStack) {
- ((ActivityStack) child).setTile(this);
- }
- mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(
- this, false /* force */);
- }
-
- @Override
- void removeChild(WindowContainer child) {
- if (child instanceof ActivityStack) {
- ((ActivityStack) child).setTile(null);
- }
- mChildren.remove(child);
- mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(
- this, false /* force */);
- }
-
- void removeAllChildren() {
- for (int i = mChildren.size() - 1; i >= 0; --i) {
- final WindowContainer child = mChildren.get(i);
- if (child instanceof ActivityStack) {
- ((ActivityStack) child).setTile(null);
- }
- }
- mChildren.clear();
- mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(
- this, false /* force */);
- }
-
- @Override
- protected int getChildCount() {
- // Currently 0 as this isn't a proper hierarchy member yet.
- return 0;
- }
-
- @Override
- public void setWindowingMode(/*@WindowConfiguration.WindowingMode*/ int windowingMode) {
- Configuration c = new Configuration(getRequestedOverrideConfiguration());
- c.windowConfiguration.setWindowingMode(windowingMode);
- onRequestedOverrideConfigurationChanged(c);
- }
-
- @Override
- public void onConfigurationChanged(Configuration newParentConfig) {
- super.onConfigurationChanged(newParentConfig);
- for (int i = mChildren.size() - 1; i >= 0; --i) {
- final WindowContainer child = mChildren.get(i);
- child.onConfigurationChanged(child.getParent().getConfiguration());
- }
- }
-
- void forAllTileActivities(Consumer<ActivityRecord> callback) {
- for (int i = mChildren.size() - 1; i >= 0; --i) {
- mChildren.get(i).forAllActivities(callback, true /* traverseTopToBottom */);
- }
- }
-
- /**
- * Until this can be part of the hierarchy, the Stack level can use this utility during
- * resolveOverrideConfig to simulate inheritance.
- */
- void updateResolvedConfig(Configuration inOutResolvedConfig) {
- Rect resolveBounds = inOutResolvedConfig.windowConfiguration.getBounds();
- if (resolveBounds.isEmpty()) {
- resolveBounds.set(getRequestedOverrideBounds());
- }
- int stackMode = inOutResolvedConfig.windowConfiguration.getWindowingMode();
- if (stackMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED
- || stackMode == WindowConfiguration.WINDOWING_MODE_FULLSCREEN) {
- // Also replace FULLSCREEN because we interpret FULLSCREEN as "fill parent"
- inOutResolvedConfig.windowConfiguration.setWindowingMode(
- getRequestedOverrideWindowingMode());
- }
- if (inOutResolvedConfig.smallestScreenWidthDp
- == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
- inOutResolvedConfig.smallestScreenWidthDp =
- getRequestedOverrideConfiguration().smallestScreenWidthDp;
- }
- if (inOutResolvedConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
- inOutResolvedConfig.screenWidthDp = getRequestedOverrideConfiguration().screenWidthDp;
- }
- if (inOutResolvedConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
- inOutResolvedConfig.screenHeightDp = getRequestedOverrideConfiguration().screenHeightDp;
- }
- Rect resolveAppBounds = inOutResolvedConfig.windowConfiguration.getAppBounds();
- if (resolveAppBounds == null || resolveAppBounds.isEmpty()) {
- inOutResolvedConfig.windowConfiguration.setAppBounds(
- getRequestedOverrideConfiguration().windowConfiguration.getAppBounds());
- }
- }
-
- @Override
- void fillTaskInfo(TaskInfo info) {
- super.fillTaskInfo(info);
- WindowContainer top = null;
- // Check mChildren.isEmpty directly because hasChild() -> getChildCount() always returns 0
- if (!mChildren.isEmpty()) {
- // Find the top-most root task which is a virtual child of this Tile. Because this is a
- // virtual parent, the mChildren order here isn't changed during hierarchy operations.
- WindowContainer parent = mChildren.get(0).getParent();
- for (int i = parent.getChildCount() - 1; i >= 0; --i) {
- if (mChildren.contains(parent.getChildAt(i))) {
- top = parent.getChildAt(i);
- break;
- }
- }
- }
- final Task topTask = top == null ? null : top.getTopMostTask();
- boolean isResizable = topTask == null || topTask.isResizeable();
- info.resizeMode = isResizable ? RESIZE_MODE_RESIZEABLE : RESIZE_MODE_UNRESIZEABLE;
- info.topActivityType = top == null ? ACTIVITY_TYPE_UNDEFINED : top.getActivityType();
- }
-
- @Override
- void removeImmediately() {
- removeAllChildren();
- super.removeImmediately();
- }
-
- @Override
- void taskOrganizerDied() {
- super.taskOrganizerDied();
- removeImmediately();
- }
-
- static TaskTile forToken(IBinder token) {
- try {
- return (TaskTile) ((RemoteToken) token).getContainer();
- } catch (ClassCastException e) {
- Slog.w(TAG, "Bad tile token: " + token, e);
- return null;
- }
- }
-}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index e416e8073a75..5f21e1799958 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -159,17 +159,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
false /* preserveWindow */);
try {
for (int i = haveConfigChanges.size() - 1; i >= 0; --i) {
- final WindowContainer wc = haveConfigChanges.valueAt(i);
- final Task task = wc.asTask();
- final TaskTile tile = task != null ? task.asTile() : null;
- if (tile != null) {
- // Special case for tile. Can't override normal forAllActivities
- // because it generates duplicate calls and messes up existing
- // code-paths.
- tile.forAllTileActivities(f);
- } else {
- wc.forAllActivities(f);
- }
+ haveConfigChanges.valueAt(i).forAllActivities(f);
}
} finally {
f.recycle();
@@ -223,51 +213,65 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
private int sanitizeAndApplyHierarchyOp(WindowContainer container,
WindowContainerTransaction.HierarchyOp hop) {
- if (!(container instanceof Task)) {
+ final Task task = container.asTask();
+ if (task == null) {
throw new IllegalArgumentException("Invalid container in hierarchy op");
}
- if (container.getDisplayContent() == null) {
- Slog.w(TAG, "Container is no longer attached: " + container);
+ final DisplayContent dc = task.getDisplayContent();
+ if (dc == null) {
+ Slog.w(TAG, "Container is no longer attached: " + task);
return 0;
}
+ final ActivityStack as = (ActivityStack) task;
+
if (hop.isReparent()) {
- // special case for tiles since they are "virtual" parents
- if (container instanceof ActivityStack && ((ActivityStack) container).isRootTask()) {
- ActivityStack as = (ActivityStack) container;
- TaskTile newParent = hop.getNewParent() == null ? null
- : (TaskTile) WindowContainer.fromBinder(hop.getNewParent());
- if (as.getTile() != newParent) {
- if (as.getTile() != null) {
- as.getTile().removeChild(as);
- }
- if (newParent != null) {
- if (!as.affectedBySplitScreenResize()) {
- return 0;
- }
- newParent.addChild(as, POSITION_TOP);
+ final boolean isNonOrganizedRootableTask =
+ (task.isRootTask() && !task.mCreatedByOrganizer)
+ || task.getParent().asTask().mCreatedByOrganizer;
+ if (isNonOrganizedRootableTask) {
+ Task newParent = hop.getNewParent() == null ? null
+ : WindowContainer.fromBinder(hop.getNewParent()).asTask();
+ if (task.getParent() != newParent) {
+ if (newParent == null) {
+ // Re-parent task to display as a root task.
+ dc.moveStackToDisplay(as, hop.getToTop());
+ } else if (newParent.inMultiWindowMode() && !task.isResizeable()
+ && task.isLeafTask()) {
+ Slog.w(TAG, "Can't support task that doesn't support multi-window mode in"
+ + " multi-window mode... newParent=" + newParent + " task=" + task);
+ return 0;
+ } else {
+ // Clear the window crop on root task since it may not be updated after
+ // reparent (no longer be a root task)
+ task.getSurfaceControl().setWindowCrop(null);
+ task.reparent((ActivityStack) newParent,
+ hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
+ false /*moveParents*/, "sanitizeAndApplyHierarchyOp");
}
- }
- if (hop.getToTop()) {
- as.getDisplay().positionStackAtTop(as, false /* includingParents */);
} else {
- as.getDisplay().positionStackAtBottom(as);
+ final ActivityStack rootTask =
+ (ActivityStack) (newParent != null ? newParent : task.getRootTask());
+ if (hop.getToTop()) {
+ as.getDisplay().positionStackAtTop(rootTask, false /* includingParents */);
+ } else {
+ as.getDisplay().positionStackAtBottom(rootTask);
+ }
}
- } else if (container instanceof Task) {
+ } else {
throw new RuntimeException("Reparenting leaf Tasks is not supported now.");
}
} else {
// Ugh, of course ActivityStack has its own special reorder logic...
- if (container instanceof ActivityStack && ((ActivityStack) container).isRootTask()) {
- ActivityStack as = (ActivityStack) container;
+ if (task.isRootTask()) {
if (hop.getToTop()) {
- as.getDisplay().positionStackAtTop(as, false /* includingParents */);
+ dc.positionStackAtTop(as, false /* includingParents */);
} else {
- as.getDisplay().positionStackAtBottom(as);
+ dc.positionStackAtBottom(as);
}
} else {
- container.getParent().positionChildAt(
+ task.getParent().positionChildAt(
hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
- container, false /* includingParents */);
+ task, false /* includingParents */);
}
}
return TRANSACT_EFFECTS_LIFECYCLE;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 161152ba0d74..0338cc3096ea 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -131,6 +131,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.H.WINDOW_STATE_BLAST_SYNC_TIMEOUT;
import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
@@ -138,7 +139,6 @@ import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_REMOVING_FOCUS;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_TIMEOUT;
-import static com.android.server.wm.WindowManagerService.H.WINDOW_STATE_BLAST_SYNC_TIMEOUT;
import static com.android.server.wm.WindowStateAnimator.COMMIT_DRAW_PENDING;
import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
@@ -3338,7 +3338,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
final ActivityStack stack = task.getStack();
- if (stack == null) {
+ if (stack == null || stack.mCreatedByOrganizer) {
return;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 023a1e8ede9f..09fab3e29fe0 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4434,6 +4434,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
clearDeviceOwnerLocked(getDeviceOwnerAdminLocked(), userHandle);
}
if (isProfileOwner(adminReceiver, userHandle)) {
+ if (isProfileOwnerOfOrganizationOwnedDevice(userHandle)) {
+ mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
+ false,
+ UserHandle.of(getProfileParentId(userHandle)));
+ }
final ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver,
userHandle, /* parent */ false);
clearProfileOwnerLocked(admin, userHandle);
@@ -13070,26 +13075,25 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final boolean addingProfileRestricted = mUserManager.hasUserRestriction(
UserManager.DISALLOW_ADD_MANAGED_PROFILE, callingUserHandle);
- UserInfo parentUser = mUserManager.getProfileParent(callingUserId);
- final boolean addingProfileRestrictedOnParent = (parentUser != null)
- && mUserManager.hasUserRestriction(
- UserManager.DISALLOW_ADD_MANAGED_PROFILE,
- UserHandle.of(parentUser.id));
-
- Slog.i(LOG_TAG, String.format(
- "When checking for managed profile provisioning: Has device owner? %b, adding"
- + " profile restricted? %b, adding profile restricted on parent? %b",
- hasDeviceOwner, addingProfileRestricted, addingProfileRestrictedOnParent));
+ if (mUserManager.getUserInfo(callingUserId).isProfile()) {
+ Slog.i(LOG_TAG,
+ String.format("Calling user %d is a profile, cannot add another.",
+ callingUserId));
+ // The check is called from inside a managed profile. A managed profile cannot
+ // be provisioned from within another managed profile.
+ return CODE_CANNOT_ADD_MANAGED_PROFILE;
+ }
- // If there's a device owner, the restriction on adding a managed profile must be set
- // somewhere.
- if (hasDeviceOwner && !addingProfileRestricted && !addingProfileRestrictedOnParent) {
+ // If there's a device owner, the restriction on adding a managed profile must be set.
+ if (hasDeviceOwner && !addingProfileRestricted) {
Slog.wtf(LOG_TAG, "Has a device owner but no restriction on adding a profile.");
}
- // Do not allow adding a managed profile if there's a restriction, either on the current
- // user or its parent user.
- if (addingProfileRestricted || addingProfileRestrictedOnParent) {
+ // Do not allow adding a managed profile if there's a restriction.
+ if (addingProfileRestricted) {
+ Slog.i(LOG_TAG, String.format(
+ "Adding a profile is restricted: User %s Has device owner? %b",
+ callingUserHandle, hasDeviceOwner));
return CODE_CANNOT_ADD_MANAGED_PROFILE;
}
// If there's a restriction on removing the managed profile then we have to take it
@@ -13098,6 +13102,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
!mUserManager.hasUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
callingUserHandle);
if (!mUserManager.canAddMoreManagedProfiles(callingUserId, canRemoveProfile)) {
+ Slog.i(LOG_TAG, String.format(
+ "Cannot add more profiles: Can remove current? %b", canRemoveProfile));
return CODE_CANNOT_ADD_MANAGED_PROFILE;
}
} finally {
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 1212f2082404..109c1191523b 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -15,218 +15,227 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.frameworks.servicestests">
-
- <uses-permission android:name="android.permission.READ_LOGS" />
- <uses-permission android:name="android.permission.ACCESS_VR_MANAGER" />
- <uses-permission android:name="android.permission.ACCOUNT_MANAGER" />
- <uses-permission android:name="android.permission.WRITE_SETTINGS" />
- <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
- <uses-permission android:name="android.permission.READ_PHONE_STATE" />
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
- <uses-permission android:name="android.permission.BROADCAST_STICKY" />
- <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
- <uses-permission android:name="android.permission.MANAGE_APP_TOKENS" />
- <uses-permission android:name="android.permission.WAKE_LOCK" />
- <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
- <uses-permission android:name="android.permission.REAL_GET_TASKS" />
- <uses-permission android:name="android.permission.GET_DETAILED_TASKS" />
- <uses-permission android:name="android.permission.REORDER_TASKS" />
- <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
- <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
- <uses-permission android:name="android.permission.OBSERVE_NETWORK_POLICY" />
- <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
- <uses-permission android:name="android.permission.MANAGE_USERS" />
- <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
- <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
- <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
- <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
- <uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
- <uses-permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD" />
- <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT" />
- <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
- <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
- <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
- <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
- <uses-permission android:name="android.permission.DELETE_PACKAGES" />
- <uses-permission android:name="android.permission.GET_APP_OPS_STATS" />
- <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
+ package="com.android.frameworks.servicestests">
+
+ <uses-permission android:name="android.permission.READ_LOGS"/>
+ <uses-permission android:name="android.permission.ACCESS_VR_MANAGER"/>
+ <uses-permission android:name="android.permission.ACCOUNT_MANAGER"/>
+ <uses-permission android:name="android.permission.WRITE_SETTINGS"/>
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
+ <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+ <uses-permission android:name="android.permission.BROADCAST_STICKY"/>
+ <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS"/>
+ <uses-permission android:name="android.permission.MANAGE_APP_TOKENS"/>
+ <uses-permission android:name="android.permission.WAKE_LOCK"/>
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
+ <uses-permission android:name="android.permission.REAL_GET_TASKS"/>
+ <uses-permission android:name="android.permission.GET_DETAILED_TASKS"/>
+ <uses-permission android:name="android.permission.REORDER_TASKS"/>
+ <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY"/>
+ <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY"/>
+ <uses-permission android:name="android.permission.OBSERVE_NETWORK_POLICY"/>
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
+ <uses-permission android:name="android.permission.MANAGE_USERS"/>
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
+ <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS"/>
+ <uses-permission android:name="android.permission.MODIFY_PHONE_STATE"/>
+ <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
+ <uses-permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD"/>
+ <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT"/>
+ <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS"/>
+ <uses-permission android:name="android.permission.INSTALL_PACKAGES"/>
+ <uses-permission android:name="android.permission.CHANGE_CONFIGURATION"/>
+ <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
+ <uses-permission android:name="android.permission.DELETE_PACKAGES"/>
+ <uses-permission android:name="android.permission.GET_APP_OPS_STATS"/>
+ <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS"/>
<uses-permission android:name="android.permission.MANAGE_APP_OPS_MODES"/>
- <uses-permission android:name="android.permission.DEVICE_POWER" />
- <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES" />
- <uses-permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST" />
- <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
- <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
- <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
- <uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- <uses-permission android:name="android.permission.STORAGE_INTERNAL" />
- <uses-permission android:name="android.permission.WATCH_APPOPS" />
+ <uses-permission android:name="android.permission.DEVICE_POWER"/>
+ <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/>
+ <uses-permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST"/>
+ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
+ <uses-permission android:name="android.permission.STATUS_BAR_SERVICE"/>
+ <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER"/>
+ <uses-permission android:name="android.permission.READ_FRAME_BUFFER"/>
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+ <uses-permission android:name="android.permission.STORAGE_INTERNAL"/>
+ <uses-permission android:name="android.permission.WATCH_APPOPS"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.SUSPEND_APPS"/>
- <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE" />
- <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
+ <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/>
+ <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/>
<uses-permission android:name="android.permission.CONTROL_KEYGUARD"/>
<uses-permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE"/>
- <uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
- <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
- <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" />
+ <uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS"/>
+ <uses-permission android:name="android.permission.READ_DEVICE_CONFIG"/>
+ <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG"/>
<uses-permission android:name="android.permission.HARDWARE_TEST"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
- <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
- <uses-permission android:name="android.permission.DUMP" />
+ <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
+ <uses-permission android:name="android.permission.DUMP"/>
<uses-permission android:name="android.permission.READ_DREAM_STATE"/>
<uses-permission android:name="android.permission.WRITE_DREAM_STATE"/>
<!-- Uses API introduced in O (26) -->
<uses-sdk android:minSdkVersion="1"
- android:targetSdkVersion="26"/>
+ android:targetSdkVersion="26"/>
<application android:testOnly="true">
- <uses-library android:name="android.test.runner" />
+ <uses-library android:name="android.test.runner"/>
<service android:name="com.android.server.accounts.TestAccountType1AuthenticatorService"
- android:exported="false">
+ android:exported="false">
<intent-filter>
- <action android:name="android.accounts.AccountAuthenticator" />
+ <action android:name="android.accounts.AccountAuthenticator"/>
</intent-filter>
<meta-data android:name="android.accounts.AccountAuthenticator"
- android:resource="@xml/test_account_type1_authenticator" />
+ android:resource="@xml/test_account_type1_authenticator"/>
</service>
<service android:name="com.android.server.accounts.TestAccountType2AuthenticatorService"
- android:exported="false">
+ android:exported="false">
<intent-filter>
- <action android:name="android.accounts.AccountAuthenticator" />
+ <action android:name="android.accounts.AccountAuthenticator"/>
</intent-filter>
<meta-data android:name="android.accounts.AccountAuthenticator"
- android:resource="@xml/test_account_type2_authenticator" />
+ android:resource="@xml/test_account_type2_authenticator"/>
</service>
<receiver android:name="com.android.server.devicepolicy.ApplicationRestrictionsTest$AdminReceiver"
- android:permission="android.permission.BIND_DEVICE_ADMIN">
+ android:permission="android.permission.BIND_DEVICE_ADMIN">
<meta-data android:name="android.app.device_admin"
- android:resource="@xml/device_admin_sample" />
+ android:resource="@xml/device_admin_sample"/>
<intent-filter>
- <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+ <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/>
</intent-filter>
</receiver>
<receiver android:name="com.android.server.devicepolicy.DummyDeviceAdmins$Admin1"
- android:permission="android.permission.BIND_DEVICE_ADMIN">
+ android:permission="android.permission.BIND_DEVICE_ADMIN">
<meta-data android:name="android.app.device_admin"
- android:resource="@xml/device_admin_sample" />
+ android:resource="@xml/device_admin_sample"/>
<intent-filter>
- <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+ <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/>
</intent-filter>
</receiver>
<receiver android:name="com.android.server.devicepolicy.DummyDeviceAdmins$Admin2"
- android:permission="android.permission.BIND_DEVICE_ADMIN">
+ android:permission="android.permission.BIND_DEVICE_ADMIN">
<meta-data android:name="android.app.device_admin"
- android:resource="@xml/device_admin_sample" />
+ android:resource="@xml/device_admin_sample"/>
<intent-filter>
- <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+ <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/>
</intent-filter>
</receiver>
<receiver android:name="com.android.server.devicepolicy.DummyDeviceAdmins$Admin3"
- android:permission="android.permission.BIND_DEVICE_ADMIN">
+ android:permission="android.permission.BIND_DEVICE_ADMIN">
<meta-data android:name="android.app.device_admin"
- android:resource="@xml/device_admin_sample" />
+ android:resource="@xml/device_admin_sample"/>
<intent-filter>
- <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+ <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/>
</intent-filter>
</receiver>
<receiver android:name="com.android.server.devicepolicy.DummyDeviceAdmins$AdminNoPerm">
<meta-data android:name="android.app.device_admin"
- android:resource="@xml/device_admin_sample" />
+ android:resource="@xml/device_admin_sample"/>
<intent-filter>
- <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+ <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/>
</intent-filter>
</receiver>
<service android:name="com.android.server.job.MockPriorityJobService"
- android:permission="android.permission.BIND_JOB_SERVICE" />
+ android:permission="android.permission.BIND_JOB_SERVICE"/>
- <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity" />
- <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity2" />
- <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity3" />
+ <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity"/>
+ <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity2"/>
+ <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity3"/>
<activity android:name="com.android.server.pm.ShortcutTestActivity"
- android:enabled="true" android:exported="true" />
+ android:enabled="true"
+ android:exported="true"/>
<activity android:name="com.android.server.pm.SuspendedDetailsActivity"
- android:enabled="true"
- android:permission="android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS">
+ android:enabled="true"
+ android:permission="android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS">
<intent-filter>
- <action android:name="android.intent.action.SHOW_SUSPENDED_APP_DETAILS" />
- <category android:name="android.intent.category.DEFAULT" />
+ <action android:name="android.intent.action.SHOW_SUSPENDED_APP_DETAILS"/>
+ <category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
- <activity android:name="com.android.server.accounts.AccountAuthenticatorDummyActivity" />
- <activity android:name="com.android.server.adb.AdbDebuggingManagerTestActivity" />
+ <activity android:name="com.android.server.accounts.AccountAuthenticatorDummyActivity"/>
+ <activity android:name="com.android.server.adb.AdbDebuggingManagerTestActivity"/>
<activity-alias android:name="a.ShortcutEnabled"
- android:targetActivity="com.android.server.pm.ShortcutTestActivity"
- android:enabled="true" android:exported="true">
+ android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+ android:enabled="true"
+ android:exported="true">
</activity-alias>
<activity-alias android:name="a.ShortcutDisabled"
- android:targetActivity="com.android.server.pm.ShortcutTestActivity"
- android:enabled="false" android:exported="true">
- <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcut_5"/>
+ android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+ android:enabled="false"
+ android:exported="true">
+ <meta-data android:name="android.app.shortcuts"
+ android:resource="@xml/shortcut_5"/>
</activity-alias>
<activity-alias android:name="a.ShortcutUnexported"
- android:targetActivity="com.android.server.pm.ShortcutTestActivity"
- android:enabled="true" android:exported="false">
- <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcut_5"/>
+ android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+ android:enabled="true"
+ android:exported="false">
+ <meta-data android:name="android.app.shortcuts"
+ android:resource="@xml/shortcut_5"/>
</activity-alias>
<activity-alias android:name="a.Shortcut1"
- android:targetActivity="com.android.server.pm.ShortcutTestActivity"
- android:enabled="true" android:exported="true">
- <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcut_1"/>
+ android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+ android:enabled="true"
+ android:exported="true">
+ <meta-data android:name="android.app.shortcuts"
+ android:resource="@xml/shortcut_1"/>
</activity-alias>
<activity-alias android:name="a.ShortcutConfigActivity"
- android:targetActivity="com.android.server.pm.ShortcutTestActivity">
+ android:targetActivity="com.android.server.pm.ShortcutTestActivity">
<intent-filter>
- <action android:name="android.intent.action.CREATE_SHORTCUT" />
+ <action android:name="android.intent.action.CREATE_SHORTCUT"/>
</intent-filter>
</activity-alias>
<activity-alias android:name="a.DisabledMain"
- android:targetActivity="com.android.server.pm.ShortcutTestActivity"
- android:enabled="false" android:exported="true">
+ android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+ android:enabled="false"
+ android:exported="true">
<intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.LAUNCHER" />
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<activity-alias android:name="a.UnexportedMain"
- android:targetActivity="com.android.server.pm.ShortcutTestActivity"
- android:enabled="true" android:exported="false">
+ android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+ android:enabled="true"
+ android:exported="false">
<intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.LAUNCHER" />
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<receiver android:name="com.android.server.appwidget.DummyAppWidget">
<intent-filter>
- <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+ <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
</intent-filter>
<meta-data android:name="android.appwidget.provider"
- android:resource="@xml/dummy_appwidget_info" />
+ android:resource="@xml/dummy_appwidget_info"/>
</receiver>
</application>
- <instrumentation
- android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.frameworks.servicestests"
- android:label="Frameworks Services Tests" />
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.frameworks.servicestests"
+ android:label="Frameworks Services Tests"/>
</manifest>
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 7c6ac1717651..baf551e756e8 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -3205,6 +3205,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true))
.thenReturn(true);
setUserSetupCompleteForUser(false, UserHandle.USER_SYSTEM);
+ when(getServices().userManager.getProfileParent(UserHandle.USER_SYSTEM)).thenReturn(null);
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
}
@@ -3246,6 +3247,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true))
.thenReturn(true);
setUserSetupCompleteForUser(true, UserHandle.USER_SYSTEM);
+ when(getServices().userManager.getProfileParent(UserHandle.USER_SYSTEM)).thenReturn(null);
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
}
@@ -3617,14 +3619,14 @@ public class DevicePolicyManagerTest extends DpmTestBase {
when(getServices().ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
.thenReturn(true);
- when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(true);
+ when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(false);
when(getServices().userManager.getProfileParent(DpmMockContext.CALLER_USER_HANDLE))
.thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
when(getServices().userManager.canAddMoreManagedProfiles(DpmMockContext.CALLER_USER_HANDLE,
true)).thenReturn(true);
setUserSetupCompleteForUser(false, DpmMockContext.CALLER_USER_HANDLE);
- mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+ mContext.binder.callingUid = DpmMockContext.ANOTHER_UID;
}
public void testIsProvisioningAllowed_provisionManagedProfileWithDeviceOwner_primaryUser()
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BubbleCheckerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BubbleCheckerTest.java
index d7fc97c8722d..2578ca892520 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BubbleCheckerTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BubbleCheckerTest.java
@@ -25,6 +25,7 @@ import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
@@ -33,6 +34,7 @@ import android.app.NotificationChannel;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.pm.ShortcutInfo;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.test.suitebuilder.annotation.SmallTest;
@@ -110,8 +112,10 @@ public class BubbleCheckerTest extends UiServiceTestCase {
void setUpShortcutBubble(boolean isValid) {
when(mBubbleMetadata.getShortcutId()).thenReturn(SHORTCUT_ID);
- when(mShortcutHelper.hasValidShortcutInfo(SHORTCUT_ID, PKG, mUserHandle))
- .thenReturn(isValid);
+ ShortcutInfo info = mock(ShortcutInfo.class);
+ when(info.getId()).thenReturn(SHORTCUT_ID);
+ when(mShortcutHelper.getValidShortcutInfo(SHORTCUT_ID, PKG, mUserHandle))
+ .thenReturn(isValid ? info : null);
when(mBubbleMetadata.getIntent()).thenReturn(null);
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 3c2944560435..d2417f9d10c5 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -6372,13 +6372,41 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
ShortcutInfo si = mock(ShortcutInfo.class);
when(si.getShortLabel()).thenReturn("Hello");
+ when(si.isLongLived()).thenReturn(true);
when(mLauncherApps.getShortcuts(any(), any())).thenReturn(Arrays.asList(si));
List<ConversationChannelWrapper> conversations =
mBinderService.getConversationsForPackage(PKG_P, mUid).getList();
assertEquals(si, conversations.get(0).getShortcutInfo());
assertEquals(si, conversations.get(1).getShortcutInfo());
+ }
+ @Test
+ public void testGetConversationsForPackage_shortcut_notLongLived() throws Exception {
+ mService.setPreferencesHelper(mPreferencesHelper);
+ ArrayList<ConversationChannelWrapper> convos = new ArrayList<>();
+ ConversationChannelWrapper convo1 = new ConversationChannelWrapper();
+ NotificationChannel channel1 = new NotificationChannel("a", "a", 1);
+ channel1.setConversationId("parent1", "convo 1");
+ convo1.setNotificationChannel(channel1);
+ convos.add(convo1);
+
+ ConversationChannelWrapper convo2 = new ConversationChannelWrapper();
+ NotificationChannel channel2 = new NotificationChannel("b", "b", 1);
+ channel2.setConversationId("parent1", "convo 2");
+ convo2.setNotificationChannel(channel2);
+ convos.add(convo2);
+ when(mPreferencesHelper.getConversations(anyString(), anyInt())).thenReturn(convos);
+
+ ShortcutInfo si = mock(ShortcutInfo.class);
+ when(si.getShortLabel()).thenReturn("Hello");
+ when(si.isLongLived()).thenReturn(false);
+ when(mLauncherApps.getShortcuts(any(), any())).thenReturn(Arrays.asList(si));
+
+ List<ConversationChannelWrapper> conversations =
+ mBinderService.getConversationsForPackage(PKG_P, mUid).getList();
+ assertNull(conversations.get(0).getShortcutInfo());
+ assertNull(conversations.get(1).getShortcutInfo());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 6ec0f919ced7..ed400ea8e992 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -82,9 +82,8 @@ import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.service.voice.IVoiceInteractionSession;
import android.view.Gravity;
-import android.window.IWindowContainer;
-import android.view.SurfaceControl;
import android.window.ITaskOrganizer;
+import android.window.IWindowContainer;
import androidx.test.filters.SmallTest;
@@ -975,7 +974,7 @@ public class ActivityStarterTests extends ActivityTestsBase {
// Move activity to split-screen-primary stack and make sure it has the focus.
TestSplitOrganizer splitOrg = new TestSplitOrganizer(mService, top.getDisplayId());
- splitOrg.mPrimary.addChild(top.getRootTask(), 0 /* index */);
+ top.getRootTask().reparent(splitOrg.mPrimary, POSITION_BOTTOM);
top.getRootTask().moveToFront("testWindowingModeOptionsLaunchAdjacent");
// Activity must landed on split-screen-secondary when launch adjacent.
@@ -1001,8 +1000,8 @@ public class ActivityStarterTests extends ActivityTestsBase {
static class TestSplitOrganizer extends ITaskOrganizer.Stub {
final ActivityTaskManagerService mService;
- TaskTile mPrimary;
- TaskTile mSecondary;
+ Task mPrimary;
+ Task mSecondary;
boolean mInSplit = false;
int mDisplayId;
TestSplitOrganizer(ActivityTaskManagerService service, int displayId) {
@@ -1014,10 +1013,10 @@ public class ActivityStarterTests extends ActivityTestsBase {
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
IWindowContainer primary = mService.mTaskOrganizerController.createRootTask(
displayId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).token;
- mPrimary = TaskTile.forToken(primary.asBinder());
+ mPrimary = WindowContainer.fromBinder(primary.asBinder()).asTask();
IWindowContainer secondary = mService.mTaskOrganizerController.createRootTask(
displayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).token;
- mSecondary = TaskTile.forToken(secondary.asBinder());
+ mSecondary = WindowContainer.fromBinder(secondary.asBinder()).asTask();
}
@Override
public void onTaskAppeared(ActivityManager.RunningTaskInfo info) {
@@ -1042,7 +1041,7 @@ public class ActivityStarterTests extends ActivityTestsBase {
for (int i = dc.getStackCount() - 1; i >= 0; --i) {
if (!WindowConfiguration.isSplitScreenWindowingMode(
dc.getStackAt(i).getWindowingMode())) {
- mSecondary.addChild(dc.getStackAt(i), 0);
+ dc.getStackAt(i).reparent(mSecondary, POSITION_BOTTOM);
}
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index 4634e2d71573..716369d49036 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -464,7 +464,7 @@ class ActivityTestsBase extends SystemServiceTestsBase {
ActivityStack build() {
final int stackId = mStackId >= 0 ? mStackId : mDisplay.getNextStackId();
final ActivityStack stack = mDisplay.createStackUnchecked(mWindowingMode,
- mActivityType, stackId, mOnTop, mInfo, mIntent);
+ mActivityType, stackId, mOnTop, mInfo, mIntent, false /* createdByOrganizer */);
final ActivityStackSupervisor supervisor = mRootWindowContainer.mStackSupervisor;
if (mCreateActivity) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
index a96f40143e0f..ed635ce3f69e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
@@ -37,6 +37,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -347,45 +348,62 @@ public class TaskOrganizerTests extends WindowTestsBase {
assertEquals(ACTIVITY_TYPE_UNDEFINED, info2.topActivityType);
DisplayContent dc = mWm.mRoot.getDisplayContent(Display.DEFAULT_DISPLAY);
- List<TaskTile> infos = getTaskTiles(dc);
+ List<Task> infos = getTasksCreatedByOrganizer(dc);
assertEquals(2, infos.size());
assertTrue(mWm.mAtmService.mTaskOrganizerController.deleteRootTask(info1.token));
- infos = getTaskTiles(dc);
+ infos = getTasksCreatedByOrganizer(dc);
assertEquals(1, infos.size());
assertEquals(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, infos.get(0).getWindowingMode());
}
@Test
public void testTileAddRemoveChild() {
+ ITaskOrganizer listener = new ITaskOrganizer.Stub() {
+ @Override
+ public void onTaskAppeared(RunningTaskInfo taskInfo) { }
+
+ @Override
+ public void onTaskVanished(RunningTaskInfo container) { }
+
+ @Override
+ public void onTaskInfoChanged(RunningTaskInfo info) throws RemoteException {
+ }
+ };
+ mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener,
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
final ActivityStack stack = createTaskStackOnDisplay(
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent);
assertEquals(mDisplayContent.getWindowingMode(), stack.getWindowingMode());
- TaskTile tile1 = TaskTile.forToken(info1.token.asBinder());
- tile1.addChild(stack, 0 /* index */);
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.reparent(stack.mRemoteToken, info1.token, true /* onTop */);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
assertEquals(info1.configuration.windowConfiguration.getWindowingMode(),
stack.getWindowingMode());
// Info should reflect new membership
- List<TaskTile> tiles = getTaskTiles(mDisplayContent);
- info1 = tiles.get(0).getTaskInfo();
+ List<Task> infos = getTasksCreatedByOrganizer(mDisplayContent);
+ info1 = infos.get(0).getTaskInfo();
assertEquals(ACTIVITY_TYPE_STANDARD, info1.topActivityType);
// Children inherit configuration
Rect newSize = new Rect(10, 10, 300, 300);
- Configuration c = new Configuration(tile1.getRequestedOverrideConfiguration());
+ Task task1 = WindowContainer.fromBinder(info1.token.asBinder()).asTask();
+ Configuration c = new Configuration(task1.getRequestedOverrideConfiguration());
c.windowConfiguration.setBounds(newSize);
doNothing().when(stack).adjustForMinimalTaskDimensions(any(), any());
- tile1.onRequestedOverrideConfigurationChanged(c);
+ task1.onRequestedOverrideConfigurationChanged(c);
assertEquals(newSize, stack.getBounds());
- tile1.removeChild(stack);
+ wct = new WindowContainerTransaction();
+ wct.reparent(stack.mRemoteToken, null, true /* onTop */);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
assertEquals(mDisplayContent.getWindowingMode(), stack.getWindowingMode());
- tiles = getTaskTiles(mDisplayContent);
- info1 = tiles.get(0).getTaskInfo();
+ infos = getTasksCreatedByOrganizer(mDisplayContent);
+ info1 = infos.get(0).getTaskInfo();
assertEquals(ACTIVITY_TYPE_UNDEFINED, info1.topActivityType);
}
@@ -415,8 +433,10 @@ public class TaskOrganizerTests extends WindowTestsBase {
final ActivityStack stack = createTaskStackOnDisplay(
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent);
- TaskTile tile1 = TaskTile.forToken(info1.token.asBinder());
- tile1.addChild(stack, 0 /* index */);
+ Task task1 = WindowContainer.fromBinder(info1.token.asBinder()).asTask();
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.reparent(stack.mRemoteToken, info1.token, true /* onTop */);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
assertTrue(called[0]);
assertEquals(ACTIVITY_TYPE_STANDARD, lastReportedTiles.get(0).topActivityType);
@@ -424,19 +444,24 @@ public class TaskOrganizerTests extends WindowTestsBase {
called[0] = false;
final ActivityStack stack2 = createTaskStackOnDisplay(
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mDisplayContent);
- tile1.addChild(stack2, 0 /* index */);
+ wct = new WindowContainerTransaction();
+ wct.reparent(stack2.mRemoteToken, info1.token, true /* onTop */);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
assertTrue(called[0]);
assertEquals(ACTIVITY_TYPE_HOME, lastReportedTiles.get(0).topActivityType);
lastReportedTiles.clear();
called[0] = false;
- mDisplayContent.positionStackAtTop(stack, false /* includingParents */);
+ task1.positionChildAt(POSITION_TOP, stack, false /* includingParents */);
assertTrue(called[0]);
assertEquals(ACTIVITY_TYPE_STANDARD, lastReportedTiles.get(0).topActivityType);
lastReportedTiles.clear();
called[0] = false;
- tile1.removeAllChildren();
+ wct = new WindowContainerTransaction();
+ wct.reparent(stack.mRemoteToken, null, true /* onTop */);
+ wct.reparent(stack2.mRemoteToken, null, true /* onTop */);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
assertTrue(called[0]);
assertEquals(ACTIVITY_TYPE_UNDEFINED, lastReportedTiles.get(0).topActivityType);
}
@@ -457,9 +482,11 @@ public class TaskOrganizerTests extends WindowTestsBase {
}
};
mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(
+ listener, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(
listener, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
- mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
RunningTaskInfo info2 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
@@ -522,13 +549,11 @@ public class TaskOrganizerTests extends WindowTestsBase {
lastReportedTiles.get(info1.token.asBinder()).topActivityType);
}
- private List<TaskTile> getTaskTiles(DisplayContent dc) {
- ArrayList<TaskTile> out = new ArrayList<>();
+ private List<Task> getTasksCreatedByOrganizer(DisplayContent dc) {
+ ArrayList<Task> out = new ArrayList<>();
for (int i = dc.getStackCount() - 1; i >= 0; --i) {
- final TaskTile t = dc.getStackAt(i).asTile();
- if (t != null) {
- out.add(t);
- }
+ final Task t = dc.getStackAt(i);
+ if (t.mCreatedByOrganizer) out.add(t);
}
return out;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index a25acae3c036..56c19a47b68a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -27,6 +27,7 @@ import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
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_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.util.DisplayMetrics.DENSITY_DEFAULT;
import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_ENABLED;
@@ -151,7 +152,7 @@ public class TaskRecordTests extends ActivityTestsBase {
assertFalse(factory.mCreated);
Task.create(mService, 0 /*taskId*/, 0 /*activityType*/,
- new ActivityInfo(), new Intent());
+ new ActivityInfo(), new Intent(), false /* createdByOrganizer */);
assertTrue(factory.mCreated);
} finally {
@@ -939,6 +940,20 @@ public class TaskRecordTests extends ActivityTestsBase {
verify(persister, never()).saveTask(same(task), any());
}
+ @Test
+ public void testNotSpecifyOrientationByFloatingTask() {
+ final Task task = getTestTask();
+ final ActivityRecord activity = task.getTopMostActivity();
+ final WindowContainer<?> taskContainer = task.getParent();
+ activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE, taskContainer.getOrientation());
+
+ task.setWindowingMode(WINDOWING_MODE_PINNED);
+
+ assertEquals(SCREEN_ORIENTATION_UNSET, taskContainer.getOrientation());
+ }
+
private Task getTestTask() {
final ActivityStack stack = new StackBuilder(mRootWindowContainer).build();
return stack.getBottomMostTask();
@@ -1000,7 +1015,7 @@ public class TaskRecordTests extends ActivityTestsBase {
@Override
Task create(ActivityTaskManagerService service, int taskId, int activityType,
- ActivityInfo info, Intent intent) {
+ ActivityInfo info, Intent intent, boolean createdByOrganizer) {
mCreated = true;
return null;
}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 3196758996be..00cb6dc7400d 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -207,7 +207,13 @@ public class SoundTriggerService extends SystemService {
@Override
public void onBootPhase(int phase) {
- if (PHASE_SYSTEM_SERVICES_READY == phase) {
+ Slog.d(TAG, "onBootPhase: " + phase + " : " + isSafeMode());
+ if (PHASE_DEVICE_SPECIFIC_SERVICES_READY == phase) {
+ if (isSafeMode()) {
+ Slog.w(TAG, "not enabling SoundTriggerService in safe mode");
+ return;
+ }
+
initSoundTriggerHelper();
mLocalSoundTriggerService.setSoundTriggerHelper(mSoundTriggerHelper);
} else if (PHASE_THIRD_PARTY_APPS_CAN_START == phase) {
diff --git a/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java b/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java
index 488ee78f6230..47bf14892ccb 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java
@@ -23,6 +23,9 @@ import android.util.Log;
import com.android.server.wm.ActivityMetricsLaunchObserver;
+import java.io.StringWriter;
+import java.io.PrintWriter;
+
/**
* A validator to check the correctness of event sequence during app startup.
*
@@ -100,7 +103,8 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver {
@Override
public void onIntentStarted(@NonNull Intent intent, long timestampNs) {
if (state == State.UNKNOWN) {
- Log.wtf(TAG, "IntentStarted during UNKNOWN." + intent);
+ logWarningWithStackTrace(
+ String.format("IntentStarted during UNKNOWN. " + intent));
incAccIntentStartedEvents();
return;
}
@@ -110,7 +114,7 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver {
state != State.ACTIVITY_CANCELLED &&
state != State.ACTIVITY_FINISHED &&
state != State.REPORT_FULLY_DRAWN) {
- Log.wtf(TAG,
+ logWarningWithStackTrace(
String.format("Cannot transition from %s to %s", state, State.INTENT_STARTED));
incAccIntentStartedEvents();
incAccIntentStartedEvents();
@@ -124,12 +128,12 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver {
@Override
public void onIntentFailed() {
if (state == State.UNKNOWN) {
- Log.wtf(TAG, "IntentFailed during UNKNOWN.");
+ logWarningWithStackTrace(String.format("onIntentFailed during UNKNOWN."));
decAccIntentStartedEvents();
return;
}
if (state != State.INTENT_STARTED) {
- Log.wtf(TAG,
+ logWarningWithStackTrace(
String.format("Cannot transition from %s to %s", state, State.INTENT_FAILED));
incAccIntentStartedEvents();
return;
@@ -143,11 +147,12 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver {
public void onActivityLaunched(@NonNull @ActivityRecordProto byte[] activity,
@Temperature int temperature) {
if (state == State.UNKNOWN) {
- Log.wtf(TAG, "onActivityLaunched during UNKNOWN.");
+ logWarningWithStackTrace(
+ String.format("onActivityLaunched during UNKNOWN."));
return;
}
if (state != State.INTENT_STARTED) {
- Log.wtf(TAG,
+ logWarningWithStackTrace(
String.format("Cannot transition from %s to %s", state, State.ACTIVITY_LAUNCHED));
incAccIntentStartedEvents();
return;
@@ -160,12 +165,13 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver {
@Override
public void onActivityLaunchCancelled(@Nullable @ActivityRecordProto byte[] activity) {
if (state == State.UNKNOWN) {
- Log.wtf(TAG, "onActivityLaunchCancelled during UNKNOWN.");
+ logWarningWithStackTrace(
+ String.format("onActivityLaunchCancelled during UNKNOWN."));
decAccIntentStartedEvents();
return;
}
if (state != State.ACTIVITY_LAUNCHED) {
- Log.wtf(TAG,
+ logWarningWithStackTrace(
String.format("Cannot transition from %s to %s", state, State.ACTIVITY_CANCELLED));
incAccIntentStartedEvents();
return;
@@ -179,13 +185,14 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver {
public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] activity,
long timestampNs) {
if (state == State.UNKNOWN) {
- Log.wtf(TAG, "onActivityLaunchFinished during UNKNOWN.");
+ logWarningWithStackTrace(
+ String.format("onActivityLaunchFinished during UNKNOWN."));
decAccIntentStartedEvents();
return;
}
if (state != State.ACTIVITY_LAUNCHED) {
- Log.wtf(TAG,
+ logWarningWithStackTrace(
String.format("Cannot transition from %s to %s", state, State.ACTIVITY_FINISHED));
incAccIntentStartedEvents();
return;
@@ -199,7 +206,8 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver {
public void onReportFullyDrawn(@NonNull @ActivityRecordProto byte[] activity,
long timestampNs) {
if (state == State.UNKNOWN) {
- Log.wtf(TAG, "onReportFullyDrawn during UNKNOWN.");
+ logWarningWithStackTrace(
+ String.format("onReportFullyDrawn during UNKNOWN."));
return;
}
if (state == State.INIT) {
@@ -207,7 +215,7 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver {
}
if (state != State.ACTIVITY_FINISHED) {
- Log.wtf(TAG,
+ logWarningWithStackTrace(
String.format("Cannot transition from %s to %s", state, State.REPORT_FULLY_DRAWN));
return;
}
@@ -252,4 +260,11 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver {
Log.i(TAG,
String.format("dec AccIntentStartedEvents to %d", accIntentStartedEvents));
}
+
+ private void logWarningWithStackTrace(String log) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ new Throwable("EventSequenceValidator#getStackTrace").printStackTrace(pw);
+ Log.w(TAG, String.format("%s\n%s", log, sw));
+ }
}
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 2f9a1c8ade0d..9be97b505a3f 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -509,16 +509,68 @@ public class AppLaunch extends InstrumentationTestCase {
for (int i = 0; i < IORAP_COMPILE_CMD_TIMEOUT; ++i) {
IorapCompilationStatus status = waitForIorapCompiled(appPkgName);
if (status == IorapCompilationStatus.COMPLETE) {
+ Log.v(TAG, "compileAppForIorap: success");
+ logDumpsysIorapd(appPkgName);
return true;
} else if (status == IorapCompilationStatus.INSUFFICIENT_TRACES) {
+ Log.e(TAG, "compileAppForIorap: failed due to insufficient traces");
+ logDumpsysIorapd(appPkgName);
return false;
} // else INCOMPLETE. keep asking iorapd if it's done yet.
sleep(1000);
}
+ Log.e(TAG, "compileAppForIorap: failed due to timeout");
+ logDumpsysIorapd(appPkgName);
return false;
}
+ /** Save the contents of $(adb shell dumpsys iorapd) to the launch_logs directory. */
+ private void logDumpsysIorapd(String packageName) throws IOException {
+ InstrumentationTestRunner instrumentation =
+ (InstrumentationTestRunner)getInstrumentation();
+ Bundle args = instrumentation.getArguments();
+
+ String launchDirectory = args.getString(KEY_LAUNCH_DIRECTORY);
+
+ // Root directory for applaunch file to log the app launch output
+ // Will be useful in case of simpleperf command is used
+ File launchRootDir = null;
+ if (null != launchDirectory && !launchDirectory.isEmpty()) {
+ launchRootDir = new File(launchDirectory);
+ if (!launchRootDir.exists() && !launchRootDir.mkdirs()) {
+ throw new IOException("Unable to create the destination directory "
+ + launchRootDir + ". Try disabling selinux.");
+ }
+ } else {
+ Log.w(TAG, "logDumpsysIorapd: Missing launch-directory arg");
+ return;
+ }
+
+ File launchSubDir = new File(launchRootDir, LAUNCH_SUB_DIRECTORY);
+
+ if (!launchSubDir.exists() && !launchSubDir.mkdirs()) {
+ throw new IOException("Unable to create the lauch file sub directory "
+ + launchSubDir + ". Try disabling selinux.");
+ }
+ String path = "iorapd_dumpsys_" + packageName + "_" + System.nanoTime() + ".txt";
+ File file = new File(launchSubDir, path);
+ try (FileOutputStream outputStream = new FileOutputStream(file);
+ BufferedWriter writer = new BufferedWriter(
+ new OutputStreamWriter(outputStream));
+ ParcelFileDescriptor result = getInstrumentation().getUiAutomation().
+ executeShellCommand(IORAP_DUMPSYS_CMD);
+ BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(
+ new FileInputStream(result.getFileDescriptor())))) {
+ String line;
+ while ((line = bufferedReader.readLine()) != null) {
+ writer.write(line + "\n");
+ }
+ }
+
+ Log.v(TAG, "logDumpsysIorapd: Saved to file: " + path);
+ }
+
enum IorapCompilationStatus {
INCOMPLETE,
COMPLETE,
diff --git a/tests/RollbackTest/RollbackTest.xml b/tests/RollbackTest/RollbackTest.xml
index 269cec1ccca9..7b85cc84f1f5 100644
--- a/tests/RollbackTest/RollbackTest.xml
+++ b/tests/RollbackTest/RollbackTest.xml
@@ -23,6 +23,10 @@
<option name="run-command" value="am broadcast -a 'com.google.android.gms.phenotype.FLAG_OVERRIDE' --es package &quot;com.google.android.gms.platformconfigurator&quot; --es user '\\*' --esa flags &quot;ModuleConfig__versioned_immediate_commit_packages&quot; --esa types &quot;bytes&quot; --esa values &quot;Cm5vdGFwYWNrYWdlOgA=&quot; com.google.android.gms" />
<option name="teardown-command" value="am broadcast -a 'com.google.android.gms.phenotype.FLAG_OVERRIDE' --es action delete --es package &quot;com.google.android.gms.platformconfigurator&quot; --es user '\*' --esa flag &quot;ModuleConfig__immediate_commit_packages&quot; com.google.android.gms" />
<option name="teardown-command" value="am broadcast -a 'com.google.android.gms.phenotype.FLAG_OVERRIDE' --es action delete --es package &quot;com.google.android.gms.platformconfigurator&quot; --es user '\*' --esa flag &quot;ModuleConfig__versioned_immediate_commit_packages&quot; com.google.android.gms" />
+ <option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.A" />
+ <option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
+ <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.A" />
+ <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.tests.rollback" />
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index 5a92d6849434..cab8b4258bc8 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -75,6 +75,12 @@ public class RollbackTest {
private static final String PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS =
"enable_rollback_timeout";
+ private static boolean hasRollbackInclude(List<RollbackInfo> rollbacks, String packageName) {
+ return rollbacks.stream().anyMatch(
+ ri -> ri.getPackages().stream().anyMatch(
+ pri -> packageName.equals(pri.getPackageName())));
+ }
+
/**
* Test basic rollbacks.
*/
@@ -113,18 +119,14 @@ public class RollbackTest {
// Uninstall TestApp.A
Uninstall.packages(TestApp.A);
assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
- // TODO: There is currently a race condition between when the app is
- // uninstalled and when rollback manager deletes the rollback. Fix it
- // so that's not the case!
for (int i = 0; i < 5; ++i) {
- RollbackInfo rollback = getUniqueRollbackInfoForPackage(
- rm.getRecentlyCommittedRollbacks(), TestApp.A);
- if (rollback != null) {
+ if (hasRollbackInclude(rm.getRecentlyCommittedRollbacks(), TestApp.A)) {
Log.i(TAG, "Sleeping 1 second to wait for uninstall to take effect.");
Thread.sleep(1000);
}
}
+ assertThat(hasRollbackInclude(rm.getRecentlyCommittedRollbacks(), TestApp.A)).isFalse();
// The app should not be available for rollback.
waitForUnavailableRollback(TestApp.A);
diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
index 548af0c54b03..498cb7c1c710 100644
--- a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
+++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
@@ -18,7 +18,6 @@ package com.google.android.test.windowinsetstests;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
-
import static java.lang.Math.max;
import static java.lang.Math.min;
@@ -31,6 +30,7 @@ import android.content.Context;
import android.graphics.Insets;
import android.os.Bundle;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
@@ -44,11 +44,11 @@ import android.view.WindowInsetsController.OnControllableInsetsChangedListener;
import android.view.animation.LinearInterpolator;
import android.widget.LinearLayout;
-import androidx.appcompat.app.AppCompatActivity;
-
import java.util.ArrayList;
import java.util.List;
+import androidx.appcompat.app.AppCompatActivity;
+
public class WindowInsetsActivity extends AppCompatActivity {
private View mRoot;
@@ -191,6 +191,40 @@ public class WindowInsetsActivity extends AppCompatActivity {
mTransitions.forEach(it -> it.onFinish(animation));
}
});
+
+ findViewById(R.id.floating_action_button).setOnClickListener(
+ v -> v.getWindowInsetsController().controlWindowInsetsAnimation(ime(), -1,
+ new LinearInterpolator(), null /* cancellationSignal */,
+ new WindowInsetsAnimationControlListener() {
+ @Override
+ public void onReady(
+ WindowInsetsAnimationController controller,
+ int types) {
+ ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
+ anim.setDuration(1500);
+ anim.addUpdateListener(animation
+ -> controller.setInsetsAndAlpha(
+ controller.getShownStateInsets(),
+ (float) animation.getAnimatedValue(),
+ anim.getAnimatedFraction()));
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ controller.finish(true);
+ }
+ });
+ anim.start();
+ }
+
+ @Override
+ public void onCancelled(WindowInsetsAnimationController controller) {
+ }
+
+ @Override
+ public void onFinished(WindowInsetsAnimationController controller) {
+ }
+ }));
}
@Override
@@ -200,57 +234,6 @@ public class WindowInsetsActivity extends AppCompatActivity {
getWindow().getDecorView().post(() -> getWindow().setDecorFitsSystemWindows(false));
}
- @Override
- public void onAttachedToWindow() {
- super.onAttachedToWindow();
- getWindow().getInsetsController().addOnControllableInsetsChangedListener(
- new OnControllableInsetsChangedListener() {
-
- boolean hasControl = false;
- @Override
- public void onControllableInsetsChanged(WindowInsetsController controller,
- int types) {
- if ((types & ime()) != 0 && !hasControl) {
- hasControl = true;
- controller.controlWindowInsetsAnimation(ime(), -1,
- new LinearInterpolator(), null /* cancellationSignal */,
- new WindowInsetsAnimationControlListener() {
- @Override
- public void onReady(
- WindowInsetsAnimationController controller,
- int types) {
- ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
- anim.setDuration(1500);
- anim.addUpdateListener(animation
- -> controller.setInsetsAndAlpha(
- controller.getShownStateInsets(),
- (float) animation.getAnimatedValue(),
- anim.getAnimatedFraction()));
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- controller.finish(true);
- }
- });
- anim.start();
- }
-
- @Override
- public void onFinished(
- WindowInsetsAnimationController controller) {
- }
-
- @Override
- public void onCancelled(
- WindowInsetsAnimationController controller) {
- }
- });
- }
- }
- });
- }
-
static class Transition {
private int mEndBottom;
private int mStartBottom;
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index b2e8c378d96d..916c33981171 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -463,7 +463,9 @@ public class NetworkCapabilitiesTest {
nc1.setSSID(TEST_SSID);
nc2.combineCapabilities(nc1);
- assertTrue(TEST_SSID.equals(nc2.getSsid()));
+ if (isAtLeastR()) {
+ assertTrue(TEST_SSID.equals(nc2.getSsid()));
+ }
// Because they now have the same SSID, the following call should not throw
nc2.combineCapabilities(nc1);
@@ -601,12 +603,16 @@ public class NetworkCapabilitiesTest {
// from nc2.
assertFalse(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING));
assertTrue(nc2.hasUnwantedCapability(NET_CAPABILITY_NOT_ROAMING));
- assertTrue(TEST_SSID.equals(nc2.getSsid()));
+ if (isAtLeastR()) {
+ assertTrue(TEST_SSID.equals(nc2.getSsid()));
+ }
nc1.setSSID(DIFFERENT_TEST_SSID);
nc2.set(nc1);
assertEquals(nc1, nc2);
- assertTrue(DIFFERENT_TEST_SSID.equals(nc2.getSsid()));
+ if (isAtLeastR()) {
+ assertTrue(DIFFERENT_TEST_SSID.equals(nc2.getSsid()));
+ }
nc1.setUids(uidRange(10, 13));
nc2.set(nc1); // Overwrites, as opposed to combineCapabilities
diff --git a/wifi/Android.bp b/wifi/Android.bp
index 9d9a495d68ef..d0f1a26f7dbf 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -170,24 +170,23 @@ droidstubs {
java_library {
name: "framework-wifi-stubs-publicapi",
srcs: [":framework-wifi-stubs-srcs-publicapi"],
+ defaults: ["framework-module-stubs-lib-defaults-publicapi"],
+ // TODO(b/151134996): remove this
sdk_version: "current",
- installable: false,
}
java_library {
name: "framework-wifi-stubs-systemapi",
srcs: [":framework-wifi-stubs-srcs-systemapi"],
- sdk_version: "system_current",
libs: ["framework-annotations-lib"],
- installable: false,
+ defaults: ["framework-module-stubs-lib-defaults-systemapi"],
}
java_library {
name: "framework-wifi-stubs-module_libs_api",
srcs: [":framework-wifi-stubs-srcs-module_libs_api"],
- sdk_version: "module_current",
libs: ["framework-annotations-lib"],
- installable: false,
+ defaults: ["framework-module-stubs-lib-defaults-module_libs_api"],
}
// defaults for tests that need to build against framework-wifi's @hide APIs