summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java6
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java6
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java70
-rw-r--r--core/api/test-current.txt2
-rw-r--r--core/java/android/animation/Animator.java4
-rw-r--r--core/java/android/animation/AnimatorInflater.java23
-rw-r--r--core/java/android/animation/AnimatorSet.java92
-rw-r--r--core/java/android/animation/ValueAnimator.java28
-rw-r--r--core/java/android/content/pm/UserProperties.java2
-rw-r--r--core/java/android/content/res/ConfigurationBoundResourceCache.java12
-rw-r--r--core/java/android/content/res/DrawableCache.java24
-rw-r--r--core/java/android/content/res/ResourcesImpl.java32
-rw-r--r--core/java/android/content/res/ThemedResourceCache.java47
-rw-r--r--core/java/android/os/UserManager.java8
-rw-r--r--core/java/android/view/ViewRootImpl.java6
-rw-r--r--core/java/android/view/WindowManager.java2
-rw-r--r--core/java/android/view/autofill/AutofillFeatureFlags.java6
-rw-r--r--core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java37
-rw-r--r--core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java77
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java25
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt31
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt64
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt9
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt9
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt30
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt56
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt9
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt25
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt25
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt5
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt5
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt28
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt14
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt40
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt5
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt50
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt52
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt13
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java9
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/Utils.java8
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java17
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java23
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java13
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt12
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt23
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt4
-rw-r--r--packages/SystemUI/res/values/strings.xml2
-rw-r--r--packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/DejankUtils.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/SwipeHelper.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/bouncer/ui/BouncerMessageView.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/UnfoldHapticsPlayer.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt79
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java58
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java79
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java77
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt9
-rw-r--r--services/autofill/java/com/android/server/autofill/LogFieldClassificationScoreOnResultListener.java79
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java145
-rw-r--r--services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java21
-rw-r--r--services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java4
-rw-r--r--services/core/java/com/android/server/am/UidObserverController.java32
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java4
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceInventory.java2
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodMenuController.java13
-rw-r--r--[-rwxr-xr-x]services/core/java/com/android/server/notification/NotificationManagerService.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java2
-rw-r--r--services/core/java/com/android/server/pm/SuspendPackageHelper.java4
-rw-r--r--services/core/java/com/android/server/wm/ActivityStartInterceptor.java18
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java20
-rw-r--r--services/tests/PackageManagerServiceTests/server/Android.bp4
-rw-r--r--services/tests/PackageManagerServiceTests/server/AndroidTest.xml8
-rw-r--r--services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java3
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt3
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt5
-rw-r--r--services/tests/servicestests/Android.bp1
-rw-r--r--services/tests/servicestests/AndroidTest.xml5
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/credentials/MetricUtilitiesTest.java116
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java39
-rw-r--r--tests/InputMethodStressTest/Android.bp1
103 files changed, 1602 insertions, 698 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 4f5eb37d871d..cbc9263a2c3d 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1631,7 +1631,8 @@ public class JobSchedulerService extends com.android.server.SystemService
jobStatus.getEstimatedNetworkDownloadBytes(),
jobStatus.getEstimatedNetworkUploadBytes(),
jobStatus.getWorkCount(),
- ActivityManager.processStateAmToProto(mUidProcStates.get(jobStatus.getUid())));
+ ActivityManager.processStateAmToProto(mUidProcStates.get(jobStatus.getUid())),
+ jobStatus.getNamespaceHash());
// If the job is immediately ready to run, then we can just immediately
// put it in the pending list and try to schedule it. This is especially
@@ -2059,7 +2060,8 @@ public class JobSchedulerService extends com.android.server.SystemService
cancelled.getEstimatedNetworkDownloadBytes(),
cancelled.getEstimatedNetworkUploadBytes(),
cancelled.getWorkCount(),
- ActivityManager.processStateAmToProto(mUidProcStates.get(cancelled.getUid())));
+ ActivityManager.processStateAmToProto(mUidProcStates.get(cancelled.getUid())),
+ cancelled.getNamespaceHash());
}
// If this is a replacement, bring in the new version of the job
if (incomingJob != null) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 90f1523104ee..0b08b6faf971 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -499,7 +499,8 @@ public final class JobServiceContext implements ServiceConnection {
job.getEstimatedNetworkDownloadBytes(),
job.getEstimatedNetworkUploadBytes(),
job.getWorkCount(),
- ActivityManager.processStateAmToProto(mService.getUidProcState(job.getUid())));
+ ActivityManager.processStateAmToProto(mService.getUidProcState(job.getUid())),
+ job.getNamespaceHash());
sEnqueuedJwiAtJobStart.logSampleWithUid(job.getUid(), job.getWorkCount());
final String sourcePackage = job.getSourcePackageName();
if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
@@ -1557,7 +1558,8 @@ public final class JobServiceContext implements ServiceConnection {
completedJob.getEstimatedNetworkUploadBytes(),
completedJob.getWorkCount(),
ActivityManager
- .processStateAmToProto(mService.getUidProcState(completedJob.getUid())));
+ .processStateAmToProto(mService.getUidProcState(completedJob.getUid())),
+ completedJob.getNamespaceHash());
if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_SYSTEM_SERVER, "JobScheduler",
getId());
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index a8b4c695889c..edd531d13965 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -43,6 +43,7 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.MediaStore;
import android.text.format.DateFormat;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.Pair;
@@ -51,6 +52,7 @@ import android.util.Slog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
@@ -65,10 +67,12 @@ import com.android.server.job.JobStatusShortInfoProto;
import dalvik.annotation.optimization.NeverCompile;
import java.io.PrintWriter;
+import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Objects;
+import java.util.Random;
import java.util.function.Predicate;
/**
@@ -88,6 +92,13 @@ public final class JobStatus {
private static final String TAG = "JobScheduler.JobStatus";
static final boolean DEBUG = JobSchedulerService.DEBUG;
+ private static MessageDigest sMessageDigest;
+ /** Cache of namespace to hash to reduce how often we need to generate the namespace hash. */
+ @GuardedBy("sNamespaceHashCache")
+ private static final ArrayMap<String, String> sNamespaceHashCache = new ArrayMap<>();
+ /** Maximum size of {@link #sNamespaceHashCache}. */
+ private static final int MAX_NAMESPACE_CACHE_SIZE = 128;
+
private static final int NUM_CONSTRAINT_CHANGE_HISTORY = 10;
public static final long NO_LATEST_RUNTIME = Long.MAX_VALUE;
@@ -231,6 +242,8 @@ public final class JobStatus {
final String sourceTag;
@Nullable
private final String mNamespace;
+ @Nullable
+ private final String mNamespaceHash;
/** An ID that can be used to uniquely identify the job when logging statsd metrics. */
private final long mLoggingJobId;
@@ -570,6 +583,7 @@ public final class JobStatus {
this.callingUid = callingUid;
this.standbyBucket = standbyBucket;
mNamespace = namespace;
+ mNamespaceHash = generateNamespaceHash(namespace);
mLoggingJobId = generateLoggingId(namespace, job.getId());
int tempSourceUid = -1;
@@ -814,6 +828,56 @@ public final class JobStatus {
return ((long) namespace.hashCode()) << 31 | jobId;
}
+ @Nullable
+ private static String generateNamespaceHash(@Nullable String namespace) {
+ if (namespace == null) {
+ return null;
+ }
+ if (namespace.trim().isEmpty()) {
+ // Input is composed of all spaces (or nothing at all).
+ return namespace;
+ }
+ synchronized (sNamespaceHashCache) {
+ final int idx = sNamespaceHashCache.indexOfKey(namespace);
+ if (idx >= 0) {
+ return sNamespaceHashCache.valueAt(idx);
+ }
+ }
+ String hash = null;
+ try {
+ // .hashCode() can result in conflicts that would make distinguishing between
+ // namespaces hard and reduce the accuracy of certain metrics. Use SHA-256
+ // to generate the hash since the probability of collision is extremely low.
+ if (sMessageDigest == null) {
+ sMessageDigest = MessageDigest.getInstance("SHA-256");
+ }
+ final byte[] digest = sMessageDigest.digest(namespace.getBytes());
+ // Convert to hexadecimal representation
+ StringBuilder hexBuilder = new StringBuilder(digest.length);
+ for (byte byteChar : digest) {
+ hexBuilder.append(String.format("%02X", byteChar));
+ }
+ hash = hexBuilder.toString();
+ } catch (Exception e) {
+ Slog.wtf(TAG, "Couldn't hash input", e);
+ }
+ if (hash == null) {
+ // If we get to this point, something went wrong with the MessageDigest above.
+ // Don't return the raw input value (which would defeat the purpose of hashing).
+ return "failed_namespace_hash";
+ }
+ hash = hash.intern();
+ synchronized (sNamespaceHashCache) {
+ if (sNamespaceHashCache.size() >= MAX_NAMESPACE_CACHE_SIZE) {
+ // Drop a random mapping instead of dropping at a predefined index to avoid
+ // potentially always dropping the same mapping.
+ sNamespaceHashCache.removeAt((new Random()).nextInt(MAX_NAMESPACE_CACHE_SIZE));
+ }
+ sNamespaceHashCache.put(namespace, hash);
+ }
+ return hash;
+ }
+
public void enqueueWorkLocked(JobWorkItem work) {
if (pendingWork == null) {
pendingWork = new ArrayList<>();
@@ -1117,10 +1181,16 @@ public final class JobStatus {
return true;
}
+ @Nullable
public String getNamespace() {
return mNamespace;
}
+ @Nullable
+ public String getNamespaceHash() {
+ return mNamespaceHash;
+ }
+
public String getSourceTag() {
return sourceTag;
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 9eb9d66cb71a..9bd29700a12d 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -3568,6 +3568,8 @@ package android.view {
field public static final int ACCESSIBILITY_TITLE_CHANGED = 33554432; // 0x2000000
field public static final int FLAG_SLIPPERY = 536870912; // 0x20000000
field public CharSequence accessibilityTitle;
+ field public float preferredMaxDisplayRefreshRate;
+ field public float preferredMinDisplayRefreshRate;
field public int privateFlags;
}
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index 12026aa3f72a..4cad58521c09 100644
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -568,13 +568,13 @@ public abstract class Animator implements Cloneable {
* repetition. lastPlayTime is similar and is used to calculate how many repeats have been
* done between the two times.
*/
- void animateValuesInRange(long currentPlayTime, long lastPlayTime, boolean notify) {}
+ void animateValuesInRange(long currentPlayTime, long lastPlayTime) {}
/**
* Internal use only. This animates any animation that has ended since lastPlayTime.
* If an animation hasn't been finished, no change will be made.
*/
- void animateSkipToEnds(long currentPlayTime, long lastPlayTime, boolean notify) {}
+ void animateSkipToEnds(long currentPlayTime, long lastPlayTime) {}
/**
* Internal use only. Adds all start times (after delay) to and end times to times.
diff --git a/core/java/android/animation/AnimatorInflater.java b/core/java/android/animation/AnimatorInflater.java
index f69bbfd3c53f..2198fcd70637 100644
--- a/core/java/android/animation/AnimatorInflater.java
+++ b/core/java/android/animation/AnimatorInflater.java
@@ -111,19 +111,20 @@ public class AnimatorInflater {
float pathErrorScale) throws NotFoundException {
final ConfigurationBoundResourceCache<Animator> animatorCache = resources
.getAnimatorCache();
- Animator animator = animatorCache.getInstance(id, resources, theme);
- if (animator != null) {
+ ConfigurationBoundResourceCache.Entry<Animator> animatorEntry =
+ animatorCache.getInstance(id, resources, theme);
+ if (animatorEntry.hasValue()) {
if (DBG_ANIMATOR_INFLATER) {
Log.d(TAG, "loaded animator from cache, " + resources.getResourceName(id));
}
- return animator;
+ return animatorEntry.getValue();
} else if (DBG_ANIMATOR_INFLATER) {
Log.d(TAG, "cache miss for animator " + resources.getResourceName(id));
}
XmlResourceParser parser = null;
try {
parser = resources.getAnimation(id);
- animator = createAnimatorFromXml(resources, theme, parser, pathErrorScale);
+ Animator animator = createAnimatorFromXml(resources, theme, parser, pathErrorScale);
if (animator != null) {
animator.appendChangingConfigurations(getChangingConfigs(resources, id));
final ConstantState<Animator> constantState = animator.createConstantState();
@@ -131,7 +132,7 @@ public class AnimatorInflater {
if (DBG_ANIMATOR_INFLATER) {
Log.d(TAG, "caching animator for res " + resources.getResourceName(id));
}
- animatorCache.put(id, theme, constantState);
+ animatorCache.put(id, theme, constantState, animatorEntry.getGeneration());
// create a new animator so that cached version is never used by the user
animator = constantState.newInstance(resources, theme);
}
@@ -160,20 +161,22 @@ public class AnimatorInflater {
final ConfigurationBoundResourceCache<StateListAnimator> cache = resources
.getStateListAnimatorCache();
final Theme theme = context.getTheme();
- StateListAnimator animator = cache.getInstance(id, resources, theme);
- if (animator != null) {
- return animator;
+ ConfigurationBoundResourceCache.Entry<StateListAnimator> animatorEntry =
+ cache.getInstance(id, resources, theme);
+ if (animatorEntry.hasValue()) {
+ return animatorEntry.getValue();
}
XmlResourceParser parser = null;
try {
parser = resources.getAnimation(id);
- animator = createStateListAnimatorFromXml(context, parser, Xml.asAttributeSet(parser));
+ StateListAnimator animator =
+ createStateListAnimatorFromXml(context, parser, Xml.asAttributeSet(parser));
if (animator != null) {
animator.appendChangingConfigurations(getChangingConfigs(resources, id));
final ConstantState<StateListAnimator> constantState = animator
.createConstantState();
if (constantState != null) {
- cache.put(id, theme, constantState);
+ cache.put(id, theme, constantState, animatorEntry.getGeneration());
// return a clone so that the animator in constant state is never used.
animator = constantState.newInstance(resources, theme);
}
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index 60659dc12342..70c3d7ae3f82 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -825,8 +825,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
private void animateBasedOnPlayTime(
long currentPlayTime,
long lastPlayTime,
- boolean inReverse,
- boolean notify
+ boolean inReverse
) {
if (currentPlayTime < 0 || lastPlayTime < -1) {
throw new UnsupportedOperationException("Error: Play time should never be negative.");
@@ -857,8 +856,8 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
while (index < endIndex) {
long playTime = startEndTimes[index];
if (lastPlayTime != playTime) {
- animateSkipToEnds(playTime, lastPlayTime, notify);
- animateValuesInRange(playTime, lastPlayTime, notify);
+ animateSkipToEnds(playTime, lastPlayTime);
+ animateValuesInRange(playTime, lastPlayTime);
lastPlayTime = playTime;
}
index++;
@@ -868,15 +867,15 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
index--;
long playTime = startEndTimes[index];
if (lastPlayTime != playTime) {
- animateSkipToEnds(playTime, lastPlayTime, notify);
- animateValuesInRange(playTime, lastPlayTime, notify);
+ animateSkipToEnds(playTime, lastPlayTime);
+ animateValuesInRange(playTime, lastPlayTime);
lastPlayTime = playTime;
}
}
}
if (currentPlayTime != lastPlayTime) {
- animateSkipToEnds(currentPlayTime, lastPlayTime, notify);
- animateValuesInRange(currentPlayTime, lastPlayTime, notify);
+ animateSkipToEnds(currentPlayTime, lastPlayTime);
+ animateValuesInRange(currentPlayTime, lastPlayTime);
}
}
@@ -896,13 +895,11 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
}
@Override
- void animateSkipToEnds(long currentPlayTime, long lastPlayTime, boolean notify) {
+ void animateSkipToEnds(long currentPlayTime, long lastPlayTime) {
initAnimation();
if (lastPlayTime > currentPlayTime) {
- if (notify) {
- notifyStartListeners(true);
- }
+ notifyStartListeners(true);
for (int i = mEvents.size() - 1; i >= 0; i--) {
AnimationEvent event = mEvents.get(i);
Node node = event.mNode;
@@ -916,31 +913,25 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
if (currentPlayTime <= start && start < lastPlayTime) {
animator.animateSkipToEnds(
0,
- lastPlayTime - node.mStartTime,
- notify
+ lastPlayTime - node.mStartTime
);
- if (notify) {
- mPlayingSet.remove(node);
- }
+ mPlayingSet.remove(node);
} else if (start <= currentPlayTime && currentPlayTime <= end) {
animator.animateSkipToEnds(
currentPlayTime - node.mStartTime,
- lastPlayTime - node.mStartTime,
- notify
+ lastPlayTime - node.mStartTime
);
- if (notify && !mPlayingSet.contains(node)) {
+ if (!mPlayingSet.contains(node)) {
mPlayingSet.add(node);
}
}
}
}
- if (currentPlayTime <= 0 && notify) {
+ if (currentPlayTime <= 0) {
notifyEndListeners(true);
}
} else {
- if (notify) {
- notifyStartListeners(false);
- }
+ notifyStartListeners(false);
int eventsSize = mEvents.size();
for (int i = 0; i < eventsSize; i++) {
AnimationEvent event = mEvents.get(i);
@@ -955,45 +946,39 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
if (lastPlayTime < end && end <= currentPlayTime) {
animator.animateSkipToEnds(
end - node.mStartTime,
- lastPlayTime - node.mStartTime,
- notify
+ lastPlayTime - node.mStartTime
);
- if (notify) {
- mPlayingSet.remove(node);
- }
+ mPlayingSet.remove(node);
} else if (start <= currentPlayTime && currentPlayTime <= end) {
animator.animateSkipToEnds(
currentPlayTime - node.mStartTime,
- lastPlayTime - node.mStartTime,
- notify
+ lastPlayTime - node.mStartTime
);
- if (notify && !mPlayingSet.contains(node)) {
+ if (!mPlayingSet.contains(node)) {
mPlayingSet.add(node);
}
}
}
}
- if (currentPlayTime >= getTotalDuration() && notify) {
+ if (currentPlayTime >= getTotalDuration()) {
notifyEndListeners(false);
}
}
}
@Override
- void animateValuesInRange(long currentPlayTime, long lastPlayTime, boolean notify) {
+ void animateValuesInRange(long currentPlayTime, long lastPlayTime) {
initAnimation();
- if (notify) {
- if (lastPlayTime < 0 || (lastPlayTime == 0 && currentPlayTime > 0)) {
- notifyStartListeners(false);
- } else {
- long duration = getTotalDuration();
- if (duration >= 0
- && (lastPlayTime > duration || (lastPlayTime == duration
- && currentPlayTime < duration))
- ) {
- notifyStartListeners(true);
- }
+ if (lastPlayTime < 0 || (lastPlayTime == 0 && currentPlayTime > 0)) {
+ notifyStartListeners(false);
+ } else {
+ long duration = getTotalDuration();
+ if (duration >= 0
+ && (lastPlayTime > duration || (lastPlayTime == duration
+ && currentPlayTime < duration))
+ ) {
+ notifyStartListeners(true);
}
}
@@ -1014,8 +999,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
) {
animator.animateValuesInRange(
currentPlayTime - node.mStartTime,
- Math.max(-1, lastPlayTime - node.mStartTime),
- notify
+ Math.max(-1, lastPlayTime - node.mStartTime)
);
}
}
@@ -1111,7 +1095,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
}
}
mSeekState.setPlayTime(playTime, mReversing);
- animateBasedOnPlayTime(playTime, lastPlayTime, mReversing, true);
+ animateBasedOnPlayTime(playTime, lastPlayTime, mReversing);
}
/**
@@ -1144,16 +1128,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
private void initChildren() {
if (!isInitialized()) {
mChildrenInitialized = true;
-
- // We have to initialize all the start values so that they are based on the previous
- // values.
- long[] times = ensureChildStartAndEndTimes();
-
- long previousTime = -1;
- for (long time : times) {
- animateBasedOnPlayTime(time, previousTime, false, false);
- previousTime = time;
- }
+ skipToEndValue(false);
}
}
@@ -1489,6 +1464,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
anim.mPauseTime = -1;
anim.mSeekState = new SeekState();
anim.mSelfPulse = true;
+ anim.mStartListenersCalled = false;
anim.mPlayingSet = new ArrayList<Node>();
anim.mNodeMap = new ArrayMap<Animator, Node>();
anim.mNodes = new ArrayList<Node>(nodeCount);
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index ead238f75ba4..5de7f387b206 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -1417,21 +1417,19 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
* will be called.
*/
@Override
- void animateValuesInRange(long currentPlayTime, long lastPlayTime, boolean notify) {
+ void animateValuesInRange(long currentPlayTime, long lastPlayTime) {
if (currentPlayTime < 0 || lastPlayTime < -1) {
throw new UnsupportedOperationException("Error: Play time should never be negative.");
}
initAnimation();
long duration = getTotalDuration();
- if (notify) {
- if (lastPlayTime < 0 || (lastPlayTime == 0 && currentPlayTime > 0)) {
- notifyStartListeners(false);
- } else if (lastPlayTime > duration
- || (lastPlayTime == duration && currentPlayTime < duration)
- ) {
- notifyStartListeners(true);
- }
+ if (lastPlayTime < 0 || (lastPlayTime == 0 && currentPlayTime > 0)) {
+ notifyStartListeners(false);
+ } else if (lastPlayTime > duration
+ || (lastPlayTime == duration && currentPlayTime < duration)
+ ) {
+ notifyStartListeners(true);
}
if (duration >= 0) {
lastPlayTime = Math.min(duration, lastPlayTime);
@@ -1448,7 +1446,7 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
iteration = Math.min(iteration, mRepeatCount);
lastIteration = Math.min(lastIteration, mRepeatCount);
- if (notify && iteration != lastIteration) {
+ if (iteration != lastIteration) {
notifyListeners(AnimatorCaller.ON_REPEAT, false);
}
}
@@ -1464,7 +1462,7 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
}
@Override
- void animateSkipToEnds(long currentPlayTime, long lastPlayTime, boolean notify) {
+ void animateSkipToEnds(long currentPlayTime, long lastPlayTime) {
boolean inReverse = currentPlayTime < lastPlayTime;
boolean doSkip;
if (currentPlayTime <= 0 && lastPlayTime > 0) {
@@ -1474,13 +1472,9 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
doSkip = duration >= 0 && currentPlayTime >= duration && lastPlayTime < duration;
}
if (doSkip) {
- if (notify) {
- notifyStartListeners(inReverse);
- }
+ notifyStartListeners(inReverse);
skipToEndValue(inReverse);
- if (notify) {
- notifyEndListeners(inReverse);
- }
+ notifyEndListeners(inReverse);
}
}
diff --git a/core/java/android/content/pm/UserProperties.java b/core/java/android/content/pm/UserProperties.java
index 77b1954c0f1b..2669040403ea 100644
--- a/core/java/android/content/pm/UserProperties.java
+++ b/core/java/android/content/pm/UserProperties.java
@@ -184,7 +184,7 @@ public final class UserProperties implements Parcelable {
* device policies from its parent profile.
*
*<p> All the user restrictions and device policies would be not propagated to the profile
- * with this property value. The {(TODO:b/256978256) @link DevicePolicyEngine}
+ * with this property value. The {@link com.android.server.devicepolicy.DevicePolicyEngine}
* uses this property to determine and propagate only select ones to the given profile.
*
* @hide
diff --git a/core/java/android/content/res/ConfigurationBoundResourceCache.java b/core/java/android/content/res/ConfigurationBoundResourceCache.java
index 5e10a5768358..4da3c18883bd 100644
--- a/core/java/android/content/res/ConfigurationBoundResourceCache.java
+++ b/core/java/android/content/res/ConfigurationBoundResourceCache.java
@@ -37,16 +37,16 @@ public class ConfigurationBoundResourceCache<T> extends ThemedResourceCache<Cons
* @param key a key that uniquely identifies the drawable resource
* @param resources a Resources object from which to create new instances.
* @param theme the theme where the resource will be used
- * @return a new instance of the resource, or {@code null} if not in
+ * @return an Entry wrapping a new instance of the resource, or {@code null} if not in
* the cache
*/
- public T getInstance(long key, Resources resources, Resources.Theme theme) {
- final ConstantState<T> entry = get(key, theme);
- if (entry != null) {
- return entry.newInstance(resources, theme);
+ public Entry<T> getInstance(long key, Resources resources, Resources.Theme theme) {
+ final Entry<ConstantState<T>> e = get(key, theme);
+ if (e.hasValue()) {
+ return new Entry<>(e.getValue().newInstance(resources, theme), e.getGeneration());
}
- return null;
+ return new Entry<>(null, e.getGeneration());
}
@Override
diff --git a/core/java/android/content/res/DrawableCache.java b/core/java/android/content/res/DrawableCache.java
index d0ebe3304065..b51d14a3c472 100644
--- a/core/java/android/content/res/DrawableCache.java
+++ b/core/java/android/content/res/DrawableCache.java
@@ -40,14 +40,32 @@ class DrawableCache extends ThemedResourceCache<Drawable.ConstantState> {
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public Drawable getInstance(long key, Resources resources, Resources.Theme theme) {
- final Drawable.ConstantState entry = get(key, theme);
- if (entry != null) {
- return entry.newDrawable(resources, theme);
+ final Entry<Drawable.ConstantState> entry = get(key, theme);
+ if (entry.getValue() != null) {
+ return entry.getValue().newDrawable(resources, theme);
}
return null;
}
+ /**
+ * If the resource is cached, creates and returns a new instance of it.
+ *
+ * @param key a key that uniquely identifies the drawable resource
+ * @param resources a Resources object from which to create new instances.
+ * @param theme the theme where the resource will be used
+ * @return an Entry wrapping a a new instance of the resource, or {@code null} if not in
+ * the cache
+ */
+ public Entry<Drawable> getDrawable(long key, Resources resources, Resources.Theme theme) {
+ final Entry<Drawable.ConstantState> e = get(key, theme);
+ if (e.hasValue()) {
+ return new Entry<>(e.getValue().newDrawable(resources, theme), e.getGeneration());
+ }
+
+ return new Entry<>(null, e.getGeneration());
+ }
+
@Override
public boolean shouldInvalidateEntry(Drawable.ConstantState entry, int configChanges) {
return Configuration.needNewResources(configChanges, entry.getChangingConfigurations());
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 3a2863e5636d..25154d5c1623 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -650,15 +650,21 @@ public class ResourcesImpl {
key = (((long) value.assetCookie) << 32) | value.data;
}
+ int cacheGeneration;
// First, check whether we have a cached version of this drawable
// that was inflated against the specified theme. Skip the cache if
// we're currently preloading or we're not using the cache.
if (!mPreloading && useCache) {
- final Drawable cachedDrawable = caches.getInstance(key, wrapper, theme);
- if (cachedDrawable != null) {
- cachedDrawable.setChangingConfigurations(value.changingConfigurations);
- return cachedDrawable;
+ final ThemedResourceCache.Entry<Drawable> cachedDrawable =
+ caches.getDrawable(key, wrapper, theme);
+ if (cachedDrawable.hasValue()) {
+ cachedDrawable.getValue().setChangingConfigurations(
+ value.changingConfigurations);
+ return cachedDrawable.getValue();
}
+ cacheGeneration = cachedDrawable.getGeneration();
+ } else {
+ cacheGeneration = ThemedResourceCache.UNDEFINED_GENERATION;
}
// Next, check preloaded drawables. Preloaded drawables may contain
@@ -702,7 +708,8 @@ public class ResourcesImpl {
if (dr != null) {
dr.setChangingConfigurations(value.changingConfigurations);
if (useCache) {
- cacheDrawable(value, isColorDrawable, caches, theme, canApplyTheme, key, dr);
+ cacheDrawable(value, isColorDrawable, caches, theme, canApplyTheme, key, dr,
+ cacheGeneration);
if (needsNewDrawableAfterCache) {
Drawable.ConstantState state = dr.getConstantState();
if (state != null) {
@@ -733,7 +740,7 @@ public class ResourcesImpl {
}
private void cacheDrawable(TypedValue value, boolean isColorDrawable, DrawableCache caches,
- Resources.Theme theme, boolean usesTheme, long key, Drawable dr) {
+ Resources.Theme theme, boolean usesTheme, long key, Drawable dr, int cacheGeneration) {
final Drawable.ConstantState cs = dr.getConstantState();
if (cs == null) {
return;
@@ -761,7 +768,7 @@ public class ResourcesImpl {
}
} else {
synchronized (mAccessLock) {
- caches.put(key, theme, cs, usesTheme);
+ caches.put(key, theme, cs, cacheGeneration, usesTheme);
}
}
}
@@ -1002,14 +1009,16 @@ public class ResourcesImpl {
TypedValue value, int id) {
final long key = (((long) value.assetCookie) << 32) | value.data;
final ConfigurationBoundResourceCache<ComplexColor> cache = mComplexColorCache;
- ComplexColor complexColor = cache.getInstance(key, wrapper, theme);
- if (complexColor != null) {
- return complexColor;
+ ThemedResourceCache.Entry<ComplexColor> complexColorEntry =
+ cache.getInstance(key, wrapper, theme);
+ if (complexColorEntry.hasValue()) {
+ return complexColorEntry.getValue();
}
final android.content.res.ConstantState<ComplexColor> factory =
sPreloadedComplexColors.get(key);
+ ComplexColor complexColor = null;
if (factory != null) {
complexColor = factory.newInstance(wrapper, theme);
}
@@ -1026,7 +1035,8 @@ public class ResourcesImpl {
sPreloadedComplexColors.put(key, complexColor.getConstantState());
}
} else {
- cache.put(key, theme, complexColor.getConstantState());
+ cache.put(key, theme, complexColor.getConstantState(),
+ complexColorEntry.getGeneration());
}
}
return complexColor;
diff --git a/core/java/android/content/res/ThemedResourceCache.java b/core/java/android/content/res/ThemedResourceCache.java
index 3270944ce7e3..e7fd2755a941 100644
--- a/core/java/android/content/res/ThemedResourceCache.java
+++ b/core/java/android/content/res/ThemedResourceCache.java
@@ -33,11 +33,37 @@ import java.lang.ref.WeakReference;
* @param <T> type of data to cache
*/
abstract class ThemedResourceCache<T> {
+ public static final int UNDEFINED_GENERATION = -1;
@UnsupportedAppUsage
private ArrayMap<ThemeKey, LongSparseArray<WeakReference<T>>> mThemedEntries;
private LongSparseArray<WeakReference<T>> mUnthemedEntries;
private LongSparseArray<WeakReference<T>> mNullThemedEntries;
+ private int mGeneration;
+
+ public static class Entry<S> {
+ private S mValue;
+ private int mGeneration;
+
+
+ public S getValue() {
+ return mValue;
+ }
+
+ public boolean hasValue() {
+ return mValue != null;
+ }
+
+ public int getGeneration() {
+ return mGeneration;
+ }
+
+ Entry(S value, int generation) {
+ this.mValue = value;
+ this.mGeneration = generation;
+ }
+ }
+
/**
* Adds a new theme-dependent entry to the cache.
*
@@ -45,9 +71,10 @@ abstract class ThemedResourceCache<T> {
* @param theme the theme against which this entry was inflated, or
* {@code null} if the entry has no theme applied
* @param entry the entry to cache
+ * @param generation The generation of the cache to compare against before storing
*/
- public void put(long key, @Nullable Theme theme, @NonNull T entry) {
- put(key, theme, entry, true);
+ public void put(long key, @Nullable Theme theme, @NonNull T entry, int generation) {
+ put(key, theme, entry, generation, true);
}
/**
@@ -57,10 +84,12 @@ abstract class ThemedResourceCache<T> {
* @param theme the theme against which this entry was inflated, or
* {@code null} if the entry has no theme applied
* @param entry the entry to cache
+ * @param generation The generation of the cache to compare against before storing
* @param usesTheme {@code true} if the entry is affected theme changes,
* {@code false} otherwise
*/
- public void put(long key, @Nullable Theme theme, @NonNull T entry, boolean usesTheme) {
+ public void put(long key, @Nullable Theme theme, @NonNull T entry, int generation,
+ boolean usesTheme) {
if (entry == null) {
return;
}
@@ -72,7 +101,8 @@ abstract class ThemedResourceCache<T> {
} else {
entries = getThemedLocked(theme, true);
}
- if (entries != null) {
+ if (entries != null
+ && ((generation == mGeneration) || (generation == UNDEFINED_GENERATION))) {
entries.put(key, new WeakReference<>(entry));
}
}
@@ -86,7 +116,7 @@ abstract class ThemedResourceCache<T> {
* @return a cached entry, or {@code null} if not in the cache
*/
@Nullable
- public T get(long key, @Nullable Theme theme) {
+ public Entry get(long key, @Nullable Theme theme) {
// The themed (includes null-themed) and unthemed caches are mutually
// exclusive, so we'll give priority to whichever one we think we'll
// hit first. Since most of the framework drawables are themed, that's
@@ -96,7 +126,7 @@ abstract class ThemedResourceCache<T> {
if (themedEntries != null) {
final WeakReference<T> themedEntry = themedEntries.get(key);
if (themedEntry != null) {
- return themedEntry.get();
+ return new Entry(themedEntry.get(), mGeneration);
}
}
@@ -104,12 +134,12 @@ abstract class ThemedResourceCache<T> {
if (unthemedEntries != null) {
final WeakReference<T> unthemedEntry = unthemedEntries.get(key);
if (unthemedEntry != null) {
- return unthemedEntry.get();
+ return new Entry(unthemedEntry.get(), mGeneration);
}
}
}
- return null;
+ return new Entry(null, mGeneration);
}
/**
@@ -121,6 +151,7 @@ abstract class ThemedResourceCache<T> {
@UnsupportedAppUsage
public void onConfigurationChange(@Config int configChanges) {
prune(configChanges);
+ mGeneration++;
}
/**
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 7d68b44581de..84f1880213b8 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -736,7 +736,7 @@ public class UserManager {
* The default value is <code>false</code>.
*
* <p>Holders of the permission
- * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_USERS}
+ * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_MODIFY_USERS}
* can set this restriction using the DevicePolicyManager APIs mentioned below.
*
* <p>Key for user restrictions.
@@ -941,7 +941,7 @@ public class UserManager {
* this restriction will be set as a base restriction which cannot be removed by any admin.
*
* <p>Holders of the permission
- * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_USERS}
+ * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_MODIFY_USERS}
* can set this restriction using the DevicePolicyManager APIs mentioned below.
*
* <p>Key for user restrictions.
@@ -1451,7 +1451,7 @@ public class UserManager {
* affected. The default value is <code>false</code>.
*
* <p>Holders of the permission
- * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_USERS}
+ * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_MODIFY_USERS}
* can set this restriction using the DevicePolicyManager APIs mentioned below.
*
* <p>Key for user restrictions.
@@ -1597,7 +1597,7 @@ public class UserManager {
* set.
*
* <p>Holders of the permission
- * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_USERS}
+ * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_MODIFY_USERS}
* can set this restriction using the DevicePolicyManager APIs mentioned below.
*
* <p>The default value is <code>false</code>.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 4cbb0409dafc..1bbb7b4bd671 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2426,7 +2426,7 @@ public final class ViewRootImpl implements ViewParent,
*
* @hide
*/
- void notifyRendererOfExpensiveFrame() {
+ public void notifyRendererOfExpensiveFrame() {
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mThreadedRenderer.notifyExpensiveFrame();
}
@@ -7017,6 +7017,10 @@ public final class ViewRootImpl implements ViewParent,
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
boolean handled = mHandwritingInitiator.onTouchEvent(event);
+ if (handled) {
+ // If handwriting is started, toolkit doesn't receive ACTION_UP.
+ mLastClickToolType = event.getToolType(event.getActionIndex());
+ }
mAttachInfo.mUnbufferedDispatchRequested = false;
mAttachInfo.mHandlingPointerEvent = true;
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index e40f8e78dafc..6714c393d64b 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -3763,6 +3763,7 @@ public interface WindowManager extends ViewManager {
* This value is ignored if {@link #preferredDisplayModeId} is set.
* @hide
*/
+ @TestApi
public float preferredMinDisplayRefreshRate;
/**
@@ -3771,6 +3772,7 @@ public interface WindowManager extends ViewManager {
* This value is ignored if {@link #preferredDisplayModeId} is set.
* @hide
*/
+ @TestApi
public float preferredMaxDisplayRefreshRate;
/** Indicates whether this window wants the HDR conversion is disabled. */
diff --git a/core/java/android/view/autofill/AutofillFeatureFlags.java b/core/java/android/view/autofill/AutofillFeatureFlags.java
index 5ad74c8803f4..461b78ce4fc2 100644
--- a/core/java/android/view/autofill/AutofillFeatureFlags.java
+++ b/core/java/android/view/autofill/AutofillFeatureFlags.java
@@ -283,6 +283,9 @@ public class AutofillFeatureFlags {
private static final String DEFAULT_AFAA_NON_AUTOFILLABLE_IME_ACTIONS = "3,4";
private static final boolean DEFAULT_AFAA_SHOULD_ENABLE_AUTOFILL_ON_ALL_VIEW_TYPES = true;
private static final boolean DEFAULT_AFAA_SHOULD_ENABLE_MULTILINE_FILTER = true;
+ private static final boolean
+ DEFAULT_AFAA_SHOULD_INCLUDE_ALL_AUTOFILL_TYPE_NOT_NONE_VIEWS_IN_ASSIST_STRUCTURE = true;
+ // END AUTOFILL FOR ALL APPS DEFAULTS
private AutofillFeatureFlags() {};
@@ -414,7 +417,8 @@ public class AutofillFeatureFlags {
public static boolean shouldIncludeAllViewsAutofillTypeNotNoneInAssistStructrue() {
return DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_AUTOFILL,
- DEVICE_CONFIG_INCLUDE_ALL_AUTOFILL_TYPE_NOT_NONE_VIEWS_IN_ASSIST_STRUCTURE, false);
+ DEVICE_CONFIG_INCLUDE_ALL_AUTOFILL_TYPE_NOT_NONE_VIEWS_IN_ASSIST_STRUCTURE,
+ DEFAULT_AFAA_SHOULD_INCLUDE_ALL_AUTOFILL_TYPE_NOT_NONE_VIEWS_IN_ASSIST_STRUCTURE);
}
/**
diff --git a/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java b/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java
index a7538701807a..0525443ecf82 100644
--- a/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java
+++ b/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java
@@ -21,6 +21,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import android.util.Property;
import android.view.View;
import androidx.test.annotation.UiThreadTest;
@@ -576,6 +577,42 @@ public class AnimatorSetActivityTest {
});
}
+ @Test
+ public void testInitializeWithoutReadingValues() throws Throwable {
+ // Some consumers crash while reading values before the animator starts
+ Property<int[], Integer> property = new Property<>(Integer.class, "firstValue") {
+ @Override
+ public Integer get(int[] target) {
+ throw new IllegalStateException("Shouldn't be called");
+ }
+
+ @Override
+ public void set(int[] target, Integer value) {
+ target[0] = value;
+ }
+ };
+
+ int[] target1 = new int[1];
+ int[] target2 = new int[1];
+ int[] target3 = new int[1];
+ ObjectAnimator animator1 = ObjectAnimator.ofInt(target1, property, 0, 100);
+ ObjectAnimator animator2 = ObjectAnimator.ofInt(target2, property, 0, 100);
+ ObjectAnimator animator3 = ObjectAnimator.ofInt(target3, property, 0, 100);
+ AnimatorSet set = new AnimatorSet();
+ set.playSequentially(animator1, animator2, animator3);
+
+ mActivityRule.runOnUiThread(() -> {
+ set.setCurrentPlayTime(900);
+ assertEquals(100, target1[0]);
+ assertEquals(100, target2[0]);
+ assertEquals(100, target3[0]);
+ set.setCurrentPlayTime(0);
+ assertEquals(0, target1[0]);
+ assertEquals(0, target2[0]);
+ assertEquals(0, target3[0]);
+ });
+ }
+
/**
* Check that the animator list contains exactly the given animators and nothing else.
*/
diff --git a/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java b/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java
index 4f8b85554f5c..b5f18c26dc97 100644
--- a/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java
+++ b/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java
@@ -45,36 +45,40 @@ public class ConfigurationBoundResourceCacheTest
@SmallTest
public void testGetEmpty() {
final Resources res = getActivity().getResources();
- assertNull(mCache.getInstance(-1, res, null));
+ assertNull(mCache.getInstance(-1, res, null).getValue());
}
@SmallTest
public void testSetGet() {
- mCache.put(1, null, new DummyFloatConstantState(5f));
+ mCache.put(1, null, new DummyFloatConstantState(5f),
+ ThemedResourceCache.UNDEFINED_GENERATION);
final Resources res = getActivity().getResources();
- assertEquals(5f, mCache.getInstance(1, res, null));
- assertNotSame(5f, mCache.getInstance(1, res, null));
- assertEquals(null, mCache.getInstance(1, res, getActivity().getTheme()));
+ assertEquals(5f, mCache.getInstance(1, res, null).getValue());
+ assertNotSame(5f, mCache.getInstance(1, res, null).getValue());
+ assertEquals(false, mCache.getInstance(1, res, getActivity().getTheme()).hasValue());
}
@SmallTest
public void testSetGetThemed() {
- mCache.put(1, getActivity().getTheme(), new DummyFloatConstantState(5f));
+ mCache.put(1, getActivity().getTheme(), new DummyFloatConstantState(5f),
+ ThemedResourceCache.UNDEFINED_GENERATION);
final Resources res = getActivity().getResources();
- assertEquals(null, mCache.getInstance(1, res, null));
- assertEquals(5f, mCache.getInstance(1, res, getActivity().getTheme()));
- assertNotSame(5f, mCache.getInstance(1, res, getActivity().getTheme()));
+ assertEquals(false, mCache.getInstance(1, res, null).hasValue());
+ assertEquals(5f, mCache.getInstance(1, res, getActivity().getTheme()).getValue());
+ assertNotSame(5f, mCache.getInstance(1, res, getActivity().getTheme()).getValue());
}
@SmallTest
public void testMultiThreadPutGet() {
- mCache.put(1, getActivity().getTheme(), new DummyFloatConstantState(5f));
- mCache.put(1, null, new DummyFloatConstantState(10f));
+ mCache.put(1, getActivity().getTheme(), new DummyFloatConstantState(5f),
+ ThemedResourceCache.UNDEFINED_GENERATION);
+ mCache.put(1, null, new DummyFloatConstantState(10f),
+ ThemedResourceCache.UNDEFINED_GENERATION);
final Resources res = getActivity().getResources();
- assertEquals(10f, mCache.getInstance(1, res, null));
- assertNotSame(10f, mCache.getInstance(1, res, null));
- assertEquals(5f, mCache.getInstance(1, res, getActivity().getTheme()));
- assertNotSame(5f, mCache.getInstance(1, res, getActivity().getTheme()));
+ assertEquals(10f, mCache.getInstance(1, res, null).getValue());
+ assertNotSame(10f, mCache.getInstance(1, res, null).getValue());
+ assertEquals(5f, mCache.getInstance(1, res, getActivity().getTheme()).getValue());
+ assertNotSame(5f, mCache.getInstance(1, res, getActivity().getTheme()).getValue());
}
@SmallTest
@@ -86,16 +90,17 @@ public class ConfigurationBoundResourceCacheTest
res.getValue(R.dimen.resource_cache_test_generic, staticValue, true);
float staticDim = TypedValue.complexToDimension(staticValue.data, res.getDisplayMetrics());
mCache.put(key, getActivity().getTheme(),
- new DummyFloatConstantState(staticDim, staticValue.changingConfigurations));
+ new DummyFloatConstantState(staticDim, staticValue.changingConfigurations),
+ ThemedResourceCache.UNDEFINED_GENERATION);
final Configuration cfg = res.getConfiguration();
Configuration newCnf = new Configuration(cfg);
newCnf.orientation = cfg.orientation == Configuration.ORIENTATION_LANDSCAPE ?
Configuration.ORIENTATION_PORTRAIT
: Configuration.ORIENTATION_LANDSCAPE;
int changes = calcConfigChanges(res, newCnf);
- assertEquals(staticDim, mCache.getInstance(key, res, getActivity().getTheme()));
+ assertEquals(staticDim, mCache.getInstance(key, res, getActivity().getTheme()).getValue());
mCache.onConfigurationChange(changes);
- assertEquals(staticDim, mCache.getInstance(key, res, getActivity().getTheme()));
+ assertEquals(staticDim, mCache.getInstance(key, res, getActivity().getTheme()).getValue());
}
@SmallTest
@@ -108,7 +113,8 @@ public class ConfigurationBoundResourceCacheTest
float changingDim = TypedValue.complexToDimension(changingValue.data,
res.getDisplayMetrics());
mCache.put(key, getActivity().getTheme(),
- new DummyFloatConstantState(changingDim, changingValue.changingConfigurations));
+ new DummyFloatConstantState(changingDim, changingValue.changingConfigurations),
+ ThemedResourceCache.UNDEFINED_GENERATION);
final Configuration cfg = res.getConfiguration();
Configuration newCnf = new Configuration(cfg);
@@ -116,9 +122,10 @@ public class ConfigurationBoundResourceCacheTest
Configuration.ORIENTATION_PORTRAIT
: Configuration.ORIENTATION_LANDSCAPE;
int changes = calcConfigChanges(res, newCnf);
- assertEquals(changingDim, mCache.getInstance(key, res, getActivity().getTheme()));
+ assertEquals(changingDim,
+ mCache.getInstance(key, res, getActivity().getTheme()).getValue());
mCache.onConfigurationChange(changes);
- assertNull(mCache.get(key, getActivity().getTheme()));
+ assertNull(mCache.get(key, getActivity().getTheme()).getValue());
}
@SmallTest
@@ -133,9 +140,11 @@ public class ConfigurationBoundResourceCacheTest
float changingDim = TypedValue.complexToDimension(changingValue.data,
res.getDisplayMetrics());
mCache.put(R.dimen.resource_cache_test_generic, getActivity().getTheme(),
- new DummyFloatConstantState(staticDim, staticValue.changingConfigurations));
+ new DummyFloatConstantState(staticDim, staticValue.changingConfigurations),
+ ThemedResourceCache.UNDEFINED_GENERATION);
mCache.put(R.dimen.resource_cache_test_orientation_dependent, getActivity().getTheme(),
- new DummyFloatConstantState(changingDim, changingValue.changingConfigurations));
+ new DummyFloatConstantState(changingDim, changingValue.changingConfigurations),
+ ThemedResourceCache.UNDEFINED_GENERATION);
final Configuration cfg = res.getConfiguration();
Configuration newCnf = new Configuration(cfg);
newCnf.orientation = cfg.orientation == Configuration.ORIENTATION_LANDSCAPE ?
@@ -143,15 +152,15 @@ public class ConfigurationBoundResourceCacheTest
: Configuration.ORIENTATION_LANDSCAPE;
int changes = calcConfigChanges(res, newCnf);
assertEquals(staticDim, mCache.getInstance(R.dimen.resource_cache_test_generic, res,
- getActivity().getTheme()));
+ getActivity().getTheme()).getValue());
assertEquals(changingDim,
mCache.getInstance(R.dimen.resource_cache_test_orientation_dependent, res,
- getActivity().getTheme()));
+ getActivity().getTheme()).getValue());
mCache.onConfigurationChange(changes);
assertEquals(staticDim, mCache.getInstance(R.dimen.resource_cache_test_generic, res,
- getActivity().getTheme()));
+ getActivity().getTheme()).getValue());
assertNull(mCache.getInstance(R.dimen.resource_cache_test_orientation_dependent, res,
- getActivity().getTheme()));
+ getActivity().getTheme()).getValue());
}
@SmallTest
@@ -173,10 +182,12 @@ public class ConfigurationBoundResourceCacheTest
res.getDisplayMetrics());
final Resources.Theme theme = i == 0 ? getActivity().getTheme() : null;
mCache.put(R.dimen.resource_cache_test_generic, theme,
- new DummyFloatConstantState(staticDim, staticValues[i].changingConfigurations));
+ new DummyFloatConstantState(staticDim, staticValues[i].changingConfigurations),
+ ThemedResourceCache.UNDEFINED_GENERATION);
mCache.put(R.dimen.resource_cache_test_orientation_dependent, theme,
new DummyFloatConstantState(changingDim,
- changingValues[i].changingConfigurations));
+ changingValues[i].changingConfigurations),
+ ThemedResourceCache.UNDEFINED_GENERATION);
}
final Configuration cfg = res.getConfiguration();
Configuration newCnf = new Configuration(cfg);
@@ -187,18 +198,18 @@ public class ConfigurationBoundResourceCacheTest
for (int i = 0; i < 2; i++) {
final Resources.Theme theme = i == 0 ? getActivity().getTheme() : null;
assertEquals(staticDim,
- mCache.getInstance(R.dimen.resource_cache_test_generic, res, theme));
+ mCache.getInstance(R.dimen.resource_cache_test_generic, res, theme).getValue());
assertEquals(changingDim,
mCache.getInstance(R.dimen.resource_cache_test_orientation_dependent, res,
- theme));
+ theme).getValue());
}
mCache.onConfigurationChange(changes);
for (int i = 0; i < 2; i++) {
final Resources.Theme theme = i == 0 ? getActivity().getTheme() : null;
assertEquals(staticDim,
- mCache.getInstance(R.dimen.resource_cache_test_generic, res, theme));
+ mCache.getInstance(R.dimen.resource_cache_test_generic, res, theme).getValue());
assertNull(mCache.getInstance(R.dimen.resource_cache_test_orientation_dependent, res,
- theme));
+ theme).getValue());
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index 281cae5e4ffa..abe2db094a5c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -70,6 +70,7 @@ public class PipResizeGestureHandler {
private final PipBoundsAlgorithm mPipBoundsAlgorithm;
private final PipMotionHelper mMotionHelper;
private final PipBoundsState mPipBoundsState;
+ private final PipTouchState mPipTouchState;
private final PipTaskOrganizer mPipTaskOrganizer;
private final PhonePipMenuController mPhonePipMenuController;
private final PipDismissTargetHandler mPipDismissTargetHandler;
@@ -104,7 +105,6 @@ public class PipResizeGestureHandler {
private boolean mAllowGesture;
private boolean mIsAttached;
private boolean mIsEnabled;
- private boolean mEnableTouch;
private boolean mEnablePinchResize;
private boolean mEnableDragCornerResize;
private boolean mIsSysUiStateValid;
@@ -122,7 +122,8 @@ public class PipResizeGestureHandler {
public PipResizeGestureHandler(Context context, PipBoundsAlgorithm pipBoundsAlgorithm,
PipBoundsState pipBoundsState, PipMotionHelper motionHelper,
- PipTaskOrganizer pipTaskOrganizer, PipDismissTargetHandler pipDismissTargetHandler,
+ PipTouchState pipTouchState, PipTaskOrganizer pipTaskOrganizer,
+ PipDismissTargetHandler pipDismissTargetHandler,
Function<Rect, Rect> movementBoundsSupplier, Runnable updateMovementBoundsRunnable,
PipUiEventLogger pipUiEventLogger, PhonePipMenuController menuActivityController,
ShellExecutor mainExecutor) {
@@ -132,6 +133,7 @@ public class PipResizeGestureHandler {
mPipBoundsAlgorithm = pipBoundsAlgorithm;
mPipBoundsState = pipBoundsState;
mMotionHelper = motionHelper;
+ mPipTouchState = pipTouchState;
mPipTaskOrganizer = pipTaskOrganizer;
mPipDismissTargetHandler = pipDismissTargetHandler;
mMovementBoundsSupplier = movementBoundsSupplier;
@@ -139,7 +141,6 @@ public class PipResizeGestureHandler {
mPhonePipMenuController = menuActivityController;
mPipUiEventLogger = pipUiEventLogger;
mPinchResizingAlgorithm = new PipPinchResizingAlgorithm();
- mEnableTouch = true;
mUpdateResizeBoundsCallback = (rect) -> {
mUserResizeBounds.set(rect);
@@ -250,8 +251,8 @@ public class PipResizeGestureHandler {
return;
}
- if (!mEnableTouch) {
- // No need to handle anything if touches are not enabled for resizing.
+ if (!mPipTouchState.getAllowInputEvents()) {
+ // No need to handle anything if touches are not enabled
return;
}
@@ -588,13 +589,13 @@ public class PipResizeGestureHandler {
mLastResizeBounds, movementBounds);
mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds, snapFraction);
- // disable the resizing until the final bounds are updated
- mEnableTouch = false;
+ // disable any touch events beyond resizing too
+ mPipTouchState.setAllowInputEvents(false);
mPipTaskOrganizer.scheduleAnimateResizePip(startBounds, mLastResizeBounds,
PINCH_RESIZE_SNAP_DURATION, mAngle, mUpdateResizeBoundsCallback, () -> {
- // reset the pinch resizing to its default state
- mEnableTouch = true;
+ // enable touch events
+ mPipTouchState.setAllowInputEvents(true);
});
} else {
mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 466da0e85358..2c4f76b1f34b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -199,11 +199,6 @@ public class PipTouchHandler {
mMotionHelper = pipMotionHelper;
mPipDismissTargetHandler = new PipDismissTargetHandler(context, pipUiEventLogger,
mMotionHelper, mainExecutor);
- mPipResizeGestureHandler =
- new PipResizeGestureHandler(context, pipBoundsAlgorithm, pipBoundsState,
- mMotionHelper, pipTaskOrganizer, mPipDismissTargetHandler,
- this::getMovementBounds, this::updateMovementBounds, pipUiEventLogger,
- menuController, mainExecutor);
mTouchState = new PipTouchState(ViewConfiguration.get(context),
() -> {
if (mPipBoundsState.isStashed()) {
@@ -220,6 +215,11 @@ public class PipTouchHandler {
},
menuController::hideMenu,
mainExecutor);
+ mPipResizeGestureHandler =
+ new PipResizeGestureHandler(context, pipBoundsAlgorithm, pipBoundsState,
+ mMotionHelper, mTouchState, pipTaskOrganizer, mPipDismissTargetHandler,
+ this::getMovementBounds, this::updateMovementBounds, pipUiEventLogger,
+ menuController, mainExecutor);
mConnection = new PipAccessibilityInteractionConnection(mContext, pipBoundsState,
mMotionHelper, pipTaskOrganizer, mPipBoundsAlgorithm.getSnapAlgorithm(),
this::onAccessibilityShowMenu, this::updateMovementBounds,
@@ -556,6 +556,11 @@ public class PipTouchHandler {
return true;
}
+ // do not process input event if not allowed
+ if (!mTouchState.getAllowInputEvents()) {
+ return true;
+ }
+
MotionEvent ev = (MotionEvent) inputEvent;
if (!mPipBoundsState.isStashed() && mPipResizeGestureHandler.willStartResizeGesture(ev)) {
// Initialize the touch state for the gesture, but immediately reset to invalidate the
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java
index d7d69f27f9f8..7f62c629c7f2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java
@@ -37,7 +37,7 @@ public class PipTouchState {
private static final boolean DEBUG = false;
@VisibleForTesting
- public static final long DOUBLE_TAP_TIMEOUT = 200;
+ public static final long DOUBLE_TAP_TIMEOUT = ViewConfiguration.getDoubleTapTimeout();
static final long HOVER_EXIT_TIMEOUT = 50;
private final ShellExecutor mMainExecutor;
@@ -55,6 +55,9 @@ public class PipTouchState {
private final PointF mLastDelta = new PointF();
private final PointF mVelocity = new PointF();
private boolean mAllowTouches = true;
+
+ // Set to false to block both PipTouchHandler and PipResizeGestureHandler's input processing
+ private boolean mAllowInputEvents = true;
private boolean mIsUserInteracting = false;
// Set to true only if the multiple taps occur within the double tap timeout
private boolean mIsDoubleTap = false;
@@ -77,6 +80,20 @@ public class PipTouchState {
}
/**
+ * @return true if input processing is enabled for PiP in general.
+ */
+ public boolean getAllowInputEvents() {
+ return mAllowInputEvents;
+ }
+
+ /**
+ * @param allowInputEvents true to enable input processing for PiP in general.
+ */
+ public void setAllowInputEvents(boolean allowInputEvents) {
+ mAllowInputEvents = allowInputEvents;
+ }
+
+ /**
* Resets this state.
*/
public void reset() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 366f4f143374..ff21c9e80ef2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -24,7 +24,6 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.res.Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
@@ -2407,7 +2406,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (change.getMode() == TRANSIT_CHANGE
&& (change.getFlags() & FLAG_IS_DISPLAY) != 0) {
mSplitLayout.update(startTransaction);
- record.mContainDisplayChange = true;
}
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
@@ -2439,8 +2437,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
final StageTaskListener stage = getStageOfTask(taskInfo);
if (stage == null) {
if (change.getParent() == null && !isClosingType(change.getMode())
- && taskInfo.getWindowingMode() == WINDOWING_MODE_PINNED) {
- record.mContainShowPipChange = true;
+ && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
+ record.mContainShowFullscreenChange = true;
}
continue;
}
@@ -2459,15 +2457,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
}
final ArraySet<StageTaskListener> dismissStages = record.getShouldDismissedStage();
- if (!record.mContainDisplayChange && record.mContainShowPipChange) {
- // This occurred when split enter pip by open another fullscreen app so let
- // pip tranistion handler to handle this but also start our dismiss transition.
- // TODO(b/282894249): Should improve this case animation on pip.
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
- mSplitTransitions.startDismissTransition(wct, this, STAGE_TYPE_UNDEFINED,
- EXIT_REASON_CHILD_TASK_ENTER_PIP);
- } else if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0
+ if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0
|| dismissStages.size() == 1) {
// If the size of dismissStages == 1, one of the task is closed without prepare
// pending transition, which could happen if all activities were finished after
@@ -2480,9 +2470,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
final int dismissTop = (dismissStages.size() == 1
&& getStageType(dismissStages.valueAt(0)) == STAGE_TYPE_MAIN)
|| mMainStage.getChildCount() == 0 ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN;
- prepareExitSplitScreen(dismissTop, wct);
+ // If there is a fullscreen opening change, we should not bring stage to top.
+ prepareExitSplitScreen(record.mContainShowFullscreenChange
+ ? STAGE_TYPE_UNDEFINED : dismissTop, wct);
mSplitTransitions.startDismissTransition(wct, this, dismissTop,
- EXIT_REASON_UNKNOWN);
+ EXIT_REASON_APP_FINISHED);
// This can happen in some pathological cases. For example:
// 1. main has 2 tasks [Task A (Single-task), Task B], side has one task [Task C]
// 2. Task B closes itself and starts Task A in LAUNCH_ADJACENT at the same time
@@ -2506,8 +2498,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
static class StageChangeRecord {
- boolean mContainDisplayChange = false;
- boolean mContainShowPipChange = false;
+ boolean mContainShowFullscreenChange = false;
static class StageChange {
final StageTaskListener mStageTaskListener;
final IntArray mAddedTaskId = new IntArray();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
index bb0eba6a0fc7..c504f57216f3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
@@ -173,6 +173,17 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene
mTransition = null;
}
+ @Override
+ public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+ @NonNull TransitionFinishCallback finishCallback) {
+ if (info.getType() == TRANSIT_CHANGE) {
+ // Apply changes happening during the unfold animation immediately
+ t.apply();
+ finishCallback.onTransitionFinished(null, null);
+ }
+ }
+
@Nullable
@Override
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
index a4ac261d1946..ef7bedf49a92 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.pip
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
@@ -55,20 +54,24 @@ import org.junit.runners.Parameterized
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class AutoEnterPipOnGoToHomeTest(flicker: FlickerTest) : EnterPipViaAppUiButtonTest(flicker) {
- /** Defines the transition used to run the test */
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- setup {
- pipApp.launchViaIntent(wmHelper)
- pipApp.enableAutoEnterForPipActivity()
- }
- teardown {
- // close gracefully so that onActivityUnpinned() can be called before force exit
- pipApp.closePipWindow(wmHelper)
- pipApp.exit(wmHelper)
- }
- transitions { tapl.goHome() }
+ override val thisTransition: FlickerBuilder.() -> Unit = {
+ transitions { tapl.goHome() }
+ }
+
+ override val defaultEnterPip: FlickerBuilder.() -> Unit = {
+ setup {
+ pipApp.launchViaIntent(wmHelper)
+ pipApp.enableAutoEnterForPipActivity()
}
+ }
+
+ override val defaultTeardown: FlickerBuilder.() -> Unit = {
+ teardown {
+ // close gracefully so that onActivityUnpinned() can be called before force exit
+ pipApp.closePipWindow(wmHelper)
+ pipApp.exit(wmHelper)
+ }
+ }
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
index 98fc91b334cf..afcc1729ed16 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
@@ -54,40 +54,38 @@ import org.junit.runners.Parameterized
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
open class ClosePipBySwipingDownTest(flicker: FlickerTest) : ClosePipTransition(flicker) {
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- super.transition(this)
- transitions {
- val pipRegion = wmHelper.getWindowRegion(pipApp).bounds
- val pipCenterX = pipRegion.centerX()
- val pipCenterY = pipRegion.centerY()
- val displayCenterX = device.displayWidth / 2
- val barComponent =
- if (flicker.scenario.isTablet) {
- ComponentNameMatcher.TASK_BAR
- } else {
- ComponentNameMatcher.NAV_BAR
- }
- val barLayerHeight =
- wmHelper.currentState.layerState
- .getLayerWithBuffer(barComponent)
- ?.visibleRegion
- ?.height
- ?: error("Couldn't find Nav or Task bar layer")
- // The dismiss button doesn't appear at the complete bottom of the screen,
- // it appears above the hot seat but `hotseatBarSize` is not available outside
- // the platform
- val displayY = (device.displayHeight * 0.9).toInt() - barLayerHeight
- device.swipe(pipCenterX, pipCenterY, displayCenterX, displayY, 50)
- // Wait until the other app is no longer visible
- wmHelper
- .StateSyncBuilder()
- .withPipGone()
- .withWindowSurfaceDisappeared(pipApp)
- .withAppTransitionIdle()
- .waitForAndVerify()
- }
+ override val thisTransition: FlickerBuilder.() -> Unit = {
+ transitions {
+ val pipRegion = wmHelper.getWindowRegion(pipApp).bounds
+ val pipCenterX = pipRegion.centerX()
+ val pipCenterY = pipRegion.centerY()
+ val displayCenterX = device.displayWidth / 2
+ val barComponent =
+ if (flicker.scenario.isTablet) {
+ ComponentNameMatcher.TASK_BAR
+ } else {
+ ComponentNameMatcher.NAV_BAR
+ }
+ val barLayerHeight =
+ wmHelper.currentState.layerState
+ .getLayerWithBuffer(barComponent)
+ ?.visibleRegion
+ ?.height
+ ?: error("Couldn't find Nav or Task bar layer")
+ // The dismiss button doesn't appear at the complete bottom of the screen,
+ // it appears above the hot seat but `hotseatBarSize` is not available outside
+ // the platform
+ val displayY = (device.displayHeight * 0.9).toInt() - barLayerHeight
+ device.swipe(pipCenterX, pipCenterY, displayCenterX, displayY, 50)
+ // Wait until the other app is no longer visible
+ wmHelper
+ .StateSyncBuilder()
+ .withPipGone()
+ .withWindowSurfaceDisappeared(pipApp)
+ .withAppTransitionIdle()
+ .waitForAndVerify()
}
+ }
/** Checks that the focus doesn't change between windows during the transition */
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt
index 2417c45bf9a0..e52b71e602f9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt
@@ -28,11 +28,10 @@ import org.junit.runners.Parameterized
/** Base class for exiting pip (closing pip window) without returning to the app */
abstract class ClosePipTransition(flicker: FlickerTest) : PipTransition(flicker) {
- override val transition: FlickerBuilder.() -> Unit
- get() = buildTransition {
- setup { this.setRotation(flicker.scenario.startRotation) }
- teardown { this.setRotation(Rotation.ROTATION_0) }
- }
+ override val thisTransition: FlickerBuilder.() -> Unit = {
+ setup { this.setRotation(flicker.scenario.startRotation) }
+ teardown { this.setRotation(Rotation.ROTATION_0) }
+ }
/**
* Checks that [pipApp] window is pinned and visible at the start and then becomes unpinned and
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
index d16583271e8c..86fe583c94e6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
@@ -54,12 +54,9 @@ import org.junit.runners.Parameterized
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
open class ClosePipWithDismissButtonTest(flicker: FlickerTest) : ClosePipTransition(flicker) {
-
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- super.transition(this)
- transitions { pipApp.closePipWindow(wmHelper) }
- }
+ override val thisTransition: FlickerBuilder.() -> Unit = {
+ transitions { pipApp.closePipWindow(wmHelper) }
+ }
/**
* Checks that the focus changes between the pip menu window and the launcher when clicking the
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
index f52e877ec2b1..01d67cc35a14 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
@@ -45,20 +45,24 @@ import org.junit.runners.Parameterized
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
open class EnterPipOnUserLeaveHintTest(flicker: FlickerTest) : EnterPipTransition(flicker) {
- /** Defines the transition used to run the test */
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- setup {
- pipApp.launchViaIntent(wmHelper)
- pipApp.enableEnterPipOnUserLeaveHint()
- }
- teardown {
- // close gracefully so that onActivityUnpinned() can be called before force exit
- pipApp.closePipWindow(wmHelper)
- pipApp.exit(wmHelper)
- }
- transitions { tapl.goHome() }
+ override val thisTransition: FlickerBuilder.() -> Unit = {
+ transitions { tapl.goHome() }
+ }
+
+ override val defaultEnterPip: FlickerBuilder.() -> Unit = {
+ setup {
+ pipApp.launchViaIntent(wmHelper)
+ pipApp.enableEnterPipOnUserLeaveHint()
+ }
+ }
+
+ override val defaultTeardown: FlickerBuilder.() -> Unit = {
+ teardown {
+ // close gracefully so that onActivityUnpinned() can be called before force exit
+ pipApp.closePipWindow(wmHelper)
+ pipApp.exit(wmHelper)
}
+ }
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
index 4b4613704a16..5480144ba1ce 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
@@ -73,39 +73,39 @@ open class EnterPipToOtherOrientation(flicker: FlickerTest) : PipTransition(flic
private val startingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_90)
private val endingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0)
- /** Defines the transition used to run the test */
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- setup {
- // Launch a portrait only app on the fullscreen stack
- testApp.launchViaIntent(
+ override val thisTransition: FlickerBuilder.() -> Unit = {
+ teardown {
+ testApp.exit(wmHelper)
+ }
+ transitions {
+ // Enter PiP, and assert that the PiP is within bounds now that the device is back
+ // in portrait
+ broadcastActionTrigger.doAction(ACTION_ENTER_PIP)
+ // during rotation the status bar becomes invisible and reappears at the end
+ wmHelper
+ .StateSyncBuilder()
+ .withPipShown()
+ .withNavOrTaskBarVisible()
+ .withStatusBarVisible()
+ .waitForAndVerify()
+ }
+ }
+
+ override val defaultEnterPip: FlickerBuilder.() -> Unit = {
+ setup {
+ // Launch a portrait only app on the fullscreen stack
+ testApp.launchViaIntent(
wmHelper,
stringExtras = mapOf(EXTRA_FIXED_ORIENTATION to ORIENTATION_PORTRAIT.toString())
- )
- // Launch the PiP activity fixed as landscape
- pipApp.launchViaIntent(
+ )
+ // Launch the PiP activity fixed as landscape, but don't enter PiP
+ pipApp.launchViaIntent(
wmHelper,
stringExtras =
- mapOf(EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString())
- )
- }
- teardown {
- pipApp.exit(wmHelper)
- testApp.exit(wmHelper)
- }
- transitions {
- // Enter PiP, and assert that the PiP is within bounds now that the device is back
- // in portrait
- broadcastActionTrigger.doAction(ACTION_ENTER_PIP)
- // during rotation the status bar becomes invisible and reappears at the end
- wmHelper
- .StateSyncBuilder()
- .withPipShown()
- .withNavOrTaskBarVisible()
- .withStatusBarVisible()
- .waitForAndVerify()
- }
+ mapOf(EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString())
+ )
}
+ }
/**
* This test is not compatible with Tablets. When using [Activity.setRequestedOrientation] to
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt
index bfd57786e615..95121def07bc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt
@@ -26,12 +26,11 @@ import org.junit.Test
import org.junit.runners.Parameterized
abstract class EnterPipTransition(flicker: FlickerTest) : PipTransition(flicker) {
- /** {@inheritDoc} */
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- setup { pipApp.launchViaIntent(wmHelper) }
- teardown { pipApp.exit(wmHelper) }
+ override val defaultEnterPip: FlickerBuilder.() -> Unit = {
+ setup {
+ pipApp.launchViaIntent(wmHelper)
}
+ }
/** Checks [pipApp] window remains visible throughout the animation */
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
index f1925d8c9d85..95725b64a48a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
@@ -51,11 +51,7 @@ import org.junit.runners.Parameterized
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
open class EnterPipViaAppUiButtonTest(flicker: FlickerTest) : EnterPipTransition(flicker) {
-
- /** {@inheritDoc} */
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- super.transition(this)
- transitions { pipApp.clickEnterPipButton(wmHelper) }
- }
+ override val thisTransition: FlickerBuilder.() -> Unit = {
+ transitions { pipApp.clickEnterPipButton(wmHelper) }
+ }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
index 3e0e37dfc997..0b3d16a8087d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
@@ -53,19 +53,16 @@ import org.junit.runners.Parameterized
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
open class ExitPipToAppViaExpandButtonTest(flicker: FlickerTest) : ExitPipToAppTransition(flicker) {
-
- /** Defines the transition used to run the test */
- override val transition: FlickerBuilder.() -> Unit
- get() = buildTransition {
- setup {
- // launch an app behind the pip one
- testApp.launchViaIntent(wmHelper)
- }
- transitions {
- // This will bring PipApp to fullscreen
- pipApp.expandPipWindowToApp(wmHelper)
- // Wait until the other app is no longer visible
- wmHelper.StateSyncBuilder().withWindowSurfaceDisappeared(testApp).waitForAndVerify()
- }
+ override val thisTransition: FlickerBuilder.() -> Unit = {
+ setup {
+ // launch an app behind the pip one
+ testApp.launchViaIntent(wmHelper)
+ }
+ transitions {
+ // This will bring PipApp to fullscreen
+ pipApp.expandPipWindowToApp(wmHelper)
+ // Wait until the other app is no longer visible
+ wmHelper.StateSyncBuilder().withWindowSurfaceDisappeared(testApp).waitForAndVerify()
}
+ }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
index 603f99541a12..bb2d40becdc9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
@@ -52,19 +52,16 @@ import org.junit.runners.Parameterized
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
open class ExitPipToAppViaIntentTest(flicker: FlickerTest) : ExitPipToAppTransition(flicker) {
-
- /** Defines the transition used to run the test */
- override val transition: FlickerBuilder.() -> Unit
- get() = buildTransition {
- setup {
- // launch an app behind the pip one
- testApp.launchViaIntent(wmHelper)
- }
- transitions {
- // This will bring PipApp to fullscreen
- pipApp.exitPipToFullScreenViaIntent(wmHelper)
- // Wait until the other app is no longer visible
- wmHelper.StateSyncBuilder().withWindowSurfaceDisappeared(testApp).waitForAndVerify()
- }
+ override val thisTransition: FlickerBuilder.() -> Unit = {
+ setup {
+ // launch an app behind the pip one
+ testApp.launchViaIntent(wmHelper)
+ }
+ transitions {
+ // This will bring PipApp to fullscreen
+ pipApp.exitPipToFullScreenViaIntent(wmHelper)
+ // Wait until the other app is no longer visible
+ wmHelper.StateSyncBuilder().withWindowSurfaceDisappeared(testApp).waitForAndVerify()
}
+ }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
index bbb1c6c2ac63..fd16b6ea6ada 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
@@ -56,8 +56,9 @@ import org.junit.runners.Parameterized
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
open class ExpandPipOnDoubleClickTest(flicker: FlickerTest) : PipTransition(flicker) {
- override val transition: FlickerBuilder.() -> Unit
- get() = buildTransition { transitions { pipApp.doubleClickPipWindow(wmHelper) } }
+ override val thisTransition: FlickerBuilder.() -> Unit = {
+ transitions { pipApp.doubleClickPipWindow(wmHelper) }
+ }
/**
* Checks that the pip app window remains inside the display bounds throughout the whole
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt
index d860e00fbfff..253aa4cae5c7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt
@@ -35,8 +35,9 @@ import org.junit.runners.Parameterized
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
open class ExpandPipOnPinchOpenTest(flicker: FlickerTest) : PipTransition(flicker) {
- override val transition: FlickerBuilder.() -> Unit
- get() = buildTransition { transitions { pipApp.pinchOpenPipWindow(wmHelper, 0.25f, 30) } }
+ override val thisTransition: FlickerBuilder.() -> Unit = {
+ transitions { pipApp.pinchOpenPipWindow(wmHelper, 0.25f, 30) }
+ }
/** Checks that the visible region area of [pipApp] always increases during the animation. */
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
index d8d57d219933..094060f86691 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
@@ -56,15 +56,10 @@ import org.junit.runners.Parameterized
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class MovePipDownOnShelfHeightChange(flicker: FlickerTest) : MovePipShelfHeightTransition(flicker) {
- /** Defines the transition used to run the test */
- override val transition: FlickerBuilder.() -> Unit
- get() = buildTransition {
- teardown {
- tapl.pressHome()
- testApp.exit(wmHelper)
- }
- transitions { testApp.launchViaIntent(wmHelper) }
- }
+ override val thisTransition: FlickerBuilder.() -> Unit = {
+ teardown { testApp.exit(wmHelper) }
+ transitions { testApp.launchViaIntent(wmHelper) }
+ }
/** Checks that the visible region of [pipApp] window always moves down during the animation. */
@Presubmit @Test fun pipWindowMovesDown() = pipWindowMoves(Direction.DOWN)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt
index 6b061bbb1565..ff51c27bf116 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt
@@ -41,23 +41,21 @@ import org.junit.runners.Parameterized
open class MovePipOnImeVisibilityChangeTest(flicker: FlickerTest) : PipTransition(flicker) {
private val imeApp = ImeAppHelper(instrumentation)
- /** {@inheritDoc} */
- override val transition: FlickerBuilder.() -> Unit
- get() = buildTransition {
- setup {
- imeApp.launchViaIntent(wmHelper)
- setRotation(flicker.scenario.startRotation)
- }
- teardown { imeApp.exit(wmHelper) }
- transitions {
- // open the soft keyboard
- imeApp.openIME(wmHelper)
- createTag(TAG_IME_VISIBLE)
+ override val thisTransition: FlickerBuilder.() -> Unit = {
+ setup {
+ imeApp.launchViaIntent(wmHelper)
+ setRotation(flicker.scenario.startRotation)
+ }
+ teardown { imeApp.exit(wmHelper) }
+ transitions {
+ // open the soft keyboard
+ imeApp.openIME(wmHelper)
+ createTag(TAG_IME_VISIBLE)
- // then close it again
- imeApp.closeIME(wmHelper)
- }
+ // then close it again
+ imeApp.closeIME(wmHelper)
}
+ }
/** Ensure the pip window remains visible throughout any keyboard interactions */
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
index ae3f87967658..27b061b67a85 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
@@ -57,14 +57,12 @@ import org.junit.runners.Parameterized
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
open class MovePipUpOnShelfHeightChangeTest(flicker: FlickerTest) :
MovePipShelfHeightTransition(flicker) {
- /** Defines the transition used to run the test */
- override val transition: FlickerBuilder.() -> Unit
- get() =
- buildTransition() {
- setup { testApp.launchViaIntent(wmHelper) }
- transitions { tapl.pressHome() }
- teardown { testApp.exit(wmHelper) }
- }
+ override val thisTransition: FlickerBuilder.() -> Unit =
+ {
+ setup { testApp.launchViaIntent(wmHelper) }
+ transitions { tapl.pressHome() }
+ teardown { testApp.exit(wmHelper) }
+ }
/** Checks that the visible region of [pipApp] window always moves up during the animation. */
@Presubmit @Test fun pipWindowMovesUp() = pipWindowMoves(Direction.UP)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
index 4e2a4e700698..9f81ba8eee87 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
@@ -22,7 +22,6 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
import android.tools.device.flicker.legacy.FlickerTestFactory
-import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
import com.android.server.wm.flicker.testapp.ActivityOptions
import org.junit.FixMethodOrder
import org.junit.Test
@@ -37,28 +36,31 @@ import org.junit.runners.Parameterized
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class PipDragTest(flicker: FlickerTest) : PipTransition(flicker) {
private var isDraggedLeft: Boolean = true
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- val stringExtras = mapOf(ActivityOptions.Pip.EXTRA_ENTER_PIP to "true")
- setup {
- tapl.setEnableRotation(true)
- // Launch the PIP activity and wait for it to enter PiP mode
- RemoveAllTasksButHomeRule.removeAllTasksButHome()
- pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = stringExtras)
+ override val thisTransition: FlickerBuilder.() -> Unit = {
+ transitions { pipApp.dragPipWindowAwayFromEdgeWithoutRelease(wmHelper, 50) }
+ }
- // determine the direction of dragging to test for
- isDraggedLeft = pipApp.isCloserToRightEdge(wmHelper)
- }
- teardown {
- // release the primary pointer after dragging without release
- pipApp.releasePipAfterDragging()
+ override val defaultEnterPip: FlickerBuilder.() -> Unit = {
+ val stringExtras = mapOf(ActivityOptions.Pip.EXTRA_ENTER_PIP to "true")
+ setup {
+ tapl.setEnableRotation(true)
+ pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = stringExtras)
- pipApp.exit(wmHelper)
- tapl.setEnableRotation(false)
- }
- transitions { pipApp.dragPipWindowAwayFromEdgeWithoutRelease(wmHelper, 50) }
+ // determine the direction of dragging to test for
+ isDraggedLeft = pipApp.isCloserToRightEdge(wmHelper)
+ }
+ }
+
+ override val defaultTeardown: FlickerBuilder.() -> Unit = {
+ teardown {
+ // release the primary pointer after dragging without release
+ pipApp.releasePipAfterDragging()
+
+ pipApp.exit(wmHelper)
+ tapl.setEnableRotation(false)
}
+ }
@Postsubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
index 8eb41b4ac694..60bf5ffdc7af 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
@@ -37,8 +37,9 @@ import org.junit.runners.Parameterized
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@FlakyTest(bugId = 270677470)
class PipPinchInTest(flicker: FlickerTest) : PipTransition(flicker) {
- override val transition: FlickerBuilder.() -> Unit
- get() = buildTransition { transitions { pipApp.pinchInPipWindow(wmHelper, 0.4f, 30) } }
+ override val thisTransition: FlickerBuilder.() -> Unit = {
+ transitions { pipApp.pinchInPipWindow(wmHelper, 0.4f, 30) }
+ }
/** Checks that the visible region area of [pipApp] always decreases during the animation. */
@Postsubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
index eb1245b9ab86..17a178f78de3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
@@ -56,27 +56,37 @@ abstract class PipTransition(flicker: FlickerTest) : BaseTest(flicker) {
}
}
- /**
- * Gets a configuration that handles basic setup and teardown of pip tests and that launches the
- * Pip app for test
- *
- * @param stringExtras Arguments to pass to the PIP launch intent
- * @param extraSpec Additional segment of flicker specification
- */
- @JvmOverloads
- protected open fun buildTransition(
- stringExtras: Map<String, String> = mapOf(ActivityOptions.Pip.EXTRA_ENTER_PIP to "true"),
- extraSpec: FlickerBuilder.() -> Unit = {}
- ): FlickerBuilder.() -> Unit {
- return {
- setup {
- setRotation(Rotation.ROTATION_0)
- removeAllTasksButHome()
- pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = stringExtras)
- }
- teardown { pipApp.exit(wmHelper) }
+ /** Defines the transition used to run the test */
+ protected open val thisTransition: FlickerBuilder.() -> Unit = {}
- extraSpec(this)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ defaultSetup(this)
+ defaultEnterPip(this)
+ thisTransition(this)
+ defaultTeardown(this)
+ }
+
+ /** Defines the default setup steps required by the test */
+ protected open val defaultSetup: FlickerBuilder.() -> Unit = {
+ setup {
+ setRotation(Rotation.ROTATION_0)
+ removeAllTasksButHome()
+ }
+ }
+
+ /** Defines the default method of entering PiP */
+ protected open val defaultEnterPip: FlickerBuilder.() -> Unit = {
+ setup {
+ pipApp.launchViaIntentAndWaitForPip(wmHelper,
+ stringExtras = mapOf(ActivityOptions.Pip.EXTRA_ENTER_PIP to "true"))
+ }
+ }
+
+ /** Defines the default teardown required to clean up after the test */
+ protected open val defaultTeardown: FlickerBuilder.() -> Unit = {
+ teardown {
+ pipApp.exit(wmHelper)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
index 3850c1f6c89a..c618e5a24fdf 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
@@ -50,41 +50,41 @@ open class SetRequestedOrientationWhilePinned(flicker: FlickerTest) : PipTransit
private val startingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0)
private val endingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_90)
- /** {@inheritDoc} */
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- setup {
- // Launch the PiP activity fixed as landscape.
- pipApp.launchViaIntent(
+ override val thisTransition: FlickerBuilder.() -> Unit = {
+ transitions {
+ // Launch the activity back into fullscreen and ensure that it is now in landscape
+ pipApp.launchViaIntent(wmHelper)
+ // System bar may fade out during fixed rotation.
+ wmHelper
+ .StateSyncBuilder()
+ .withFullScreenApp(pipApp)
+ .withRotation(Rotation.ROTATION_90)
+ .withNavOrTaskBarVisible()
+ .withStatusBarVisible()
+ .waitForAndVerify()
+ }
+ }
+
+ override val defaultEnterPip: FlickerBuilder.() -> Unit = {
+ setup {
+ // Launch the PiP activity fixed as landscape.
+ pipApp.launchViaIntent(
wmHelper,
stringExtras =
- mapOf(EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString())
- )
- // Enter PiP.
- broadcastActionTrigger.doAction(ActivityOptions.Pip.ACTION_ENTER_PIP)
- // System bar may fade out during fixed rotation.
- wmHelper
+ mapOf(EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString())
+ )
+ // Enter PiP.
+ broadcastActionTrigger.doAction(ActivityOptions.Pip.ACTION_ENTER_PIP)
+ // System bar may fade out during fixed rotation.
+ wmHelper
.StateSyncBuilder()
.withPipShown()
.withRotation(Rotation.ROTATION_0)
.withNavOrTaskBarVisible()
.withStatusBarVisible()
.waitForAndVerify()
- }
- teardown { pipApp.exit(wmHelper) }
- transitions {
- // Launch the activity back into fullscreen and ensure that it is now in landscape
- pipApp.launchViaIntent(wmHelper)
- // System bar may fade out during fixed rotation.
- wmHelper
- .StateSyncBuilder()
- .withFullScreenApp(pipApp)
- .withRotation(Rotation.ROTATION_90)
- .withNavOrTaskBarVisible()
- .withStatusBarVisible()
- .waitForAndVerify()
- }
}
+ }
/**
* This test is not compatible with Tablets. When using [Activity.setRequestedOrientation] to
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
index 703784dd8c67..43d6c8f26126 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
@@ -63,14 +63,13 @@ open class ShowPipAndRotateDisplay(flicker: FlickerTest) : PipTransition(flicker
private val screenBoundsStart = WindowUtils.getDisplayBounds(flicker.scenario.startRotation)
private val screenBoundsEnd = WindowUtils.getDisplayBounds(flicker.scenario.endRotation)
- override val transition: FlickerBuilder.() -> Unit
- get() = buildTransition {
- setup {
- testApp.launchViaIntent(wmHelper)
- setRotation(flicker.scenario.startRotation)
- }
- transitions { setRotation(flicker.scenario.endRotation) }
+ override val thisTransition: FlickerBuilder.() -> Unit = {
+ setup {
+ testApp.launchViaIntent(wmHelper)
+ setRotation(flicker.scenario.startRotation)
}
+ transitions { setRotation(flicker.scenario.endRotation) }
+ }
/** Checks that [testApp] layer is within [screenBoundsStart] at the start of the transition */
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
index ada3455fae18..1dfdbf6514ba 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
@@ -29,6 +29,7 @@ import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.MotionEvent;
+import android.view.ViewConfiguration;
import androidx.test.filters.SmallTest;
@@ -90,6 +91,8 @@ public class PipResizeGestureHandlerTest extends ShellTestCase {
private PipDisplayLayoutState mPipDisplayLayoutState;
+ private PipTouchState mPipTouchState;
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -104,8 +107,12 @@ public class PipResizeGestureHandlerTest extends ShellTestCase {
final PipMotionHelper motionHelper = new PipMotionHelper(mContext, mPipBoundsState,
mPipTaskOrganizer, mPhonePipMenuController, pipSnapAlgorithm,
mMockPipTransitionController, mFloatingContentCoordinator);
+
+ mPipTouchState = new PipTouchState(ViewConfiguration.get(mContext),
+ () -> {}, () -> {}, mMainExecutor);
mPipResizeGestureHandler = new PipResizeGestureHandler(mContext, pipBoundsAlgorithm,
- mPipBoundsState, motionHelper, mPipTaskOrganizer, mPipDismissTargetHandler,
+ mPipBoundsState, motionHelper, mPipTouchState, mPipTaskOrganizer,
+ mPipDismissTargetHandler,
(Rect bounds) -> new Rect(), () -> {}, mPipUiEventLogger, mPhonePipMenuController,
mMainExecutor) {
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 8d4aa9a7b25e..c967b568042c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -688,8 +688,12 @@ public class Utils {
continue;
}
for (int complianceWarningType : complianceWarnings) {
- if (complianceWarningType != 0) {
- return true;
+ switch (complianceWarningType) {
+ case UsbPortStatus.COMPLIANCE_WARNING_OTHER:
+ case UsbPortStatus.COMPLIANCE_WARNING_DEBUG_ACCESSORY:
+ return true;
+ default:
+ break;
}
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index fe8988385453..6eb2f3834858 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -484,8 +484,9 @@ public class ApplicationsState {
if (DEBUG_LOCKING) Log.v(TAG, "getEntry about to acquire lock...");
synchronized (mEntriesMap) {
AppEntry entry = null;
- if (mEntriesMap.contains(userId)) {
- entry = mEntriesMap.get(userId).get(packageName);
+ HashMap<String, AppEntry> userEntriesMap = mEntriesMap.get(userId);
+ if (userEntriesMap != null) {
+ entry = userEntriesMap.get(packageName);
}
if (entry == null) {
ApplicationInfo info = getAppInfoLocked(packageName, userId);
@@ -735,8 +736,9 @@ public class ApplicationsState {
private AppEntry getEntryLocked(ApplicationInfo info) {
int userId = UserHandle.getUserId(info.uid);
AppEntry entry = null;
- if (mEntriesMap.contains(userId)) {
- entry = mEntriesMap.get(userId).get(info.packageName);
+ HashMap<String, AppEntry> userEntriesMap = mEntriesMap.get(userId);
+ if (userEntriesMap != null) {
+ entry = userEntriesMap.get(info.packageName);
}
if (DEBUG) {
Log.i(TAG, "Looking up entry of pkg " + info.packageName + ": " + entry);
@@ -752,8 +754,11 @@ public class ApplicationsState {
Log.i(TAG, "Creating AppEntry for " + info.packageName);
}
entry = new AppEntry(mContext, info, mCurId++);
- mEntriesMap.get(userId).put(info.packageName, entry);
- mAppEntries.add(entry);
+ userEntriesMap = mEntriesMap.get(userId);
+ if (userEntriesMap != null) {
+ userEntriesMap.put(info.packageName, entry);
+ mAppEntries.add(entry);
+ }
} else if (entry.info != info) {
entry.info = info;
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
index 0637e5d27f57..4a913c87bddf 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
@@ -455,12 +455,24 @@ public class UtilsTest {
}
@Test
- public void containsIncompatibleChargers_returnTrue() {
- setupIncompatibleCharging();
+ public void containsIncompatibleChargers_complianeWarningOther_returnTrue() {
+ setupIncompatibleCharging(UsbPortStatus.COMPLIANCE_WARNING_OTHER);
+ assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isTrue();
+ }
+
+ @Test
+ public void containsIncompatibleChargers_complianeWarningDebug_returnTrue() {
+ setupIncompatibleCharging(UsbPortStatus.COMPLIANCE_WARNING_DEBUG_ACCESSORY);
assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isTrue();
}
@Test
+ public void containsIncompatibleChargers_unexpectedWarningType_returnFalse() {
+ setupIncompatibleCharging(UsbPortStatus.COMPLIANCE_WARNING_BC_1_2);
+ assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isFalse();
+ }
+
+ @Test
public void containsIncompatibleChargers_emptyComplianceWarnings_returnFalse() {
setupIncompatibleCharging();
when(mUsbPortStatus.getComplianceWarnings()).thenReturn(new int[1]);
@@ -494,12 +506,17 @@ public class UtilsTest {
}
private void setupIncompatibleCharging() {
+ setupIncompatibleCharging(UsbPortStatus.COMPLIANCE_WARNING_OTHER);
+ }
+
+ private void setupIncompatibleCharging(int complianceWarningType) {
final List<UsbPort> usbPorts = new ArrayList<>();
usbPorts.add(mUsbPort);
when(mUsbManager.getPorts()).thenReturn(usbPorts);
when(mUsbPort.getStatus()).thenReturn(mUsbPortStatus);
when(mUsbPort.supportsComplianceWarnings()).thenReturn(true);
when(mUsbPortStatus.isConnected()).thenReturn(true);
- when(mUsbPortStatus.getComplianceWarnings()).thenReturn(new int[]{1});
+ when(mUsbPortStatus.getComplianceWarnings())
+ .thenReturn(new int[]{complianceWarningType});
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
index 1d081d7214cc..34d8148f418f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
@@ -804,7 +804,7 @@ public class ApplicationsStateRoboTest {
}
@Test
- public void getEntry_validUserId_shouldReturnEntry() {
+ public void getEntry_hasCache_shouldReturnCacheEntry() {
mApplicationsState.mEntriesMap.put(/* userId= */ 0, new HashMap<>());
addApp(PKG_1, /* id= */ 1);
@@ -813,10 +813,13 @@ public class ApplicationsStateRoboTest {
}
@Test
- public void getEntry_invalidUserId_shouldReturnNull() {
- mApplicationsState.mEntriesMap.put(/* userId= */ 0, new HashMap<>());
- addApp(PKG_1, /* id= */ 1);
+ public void getEntry_hasNoCache_shouldReturnEntry() {
+ mApplicationsState.mEntriesMap.clear();
+ ApplicationInfo appInfo = createApplicationInfo(PKG_1, /* uid= */ 0);
+ mApplicationsState.mApplications.add(appInfo);
+ mApplicationsState.mSystemModules.put(PKG_1, /* value= */ false);
- assertThat(mApplicationsState.getEntry(PKG_1, /* userId= */ -1)).isNull();
+ assertThat(mApplicationsState.getEntry(PKG_1, /* userId= */ 0).info.packageName)
+ .isEqualTo(PKG_1);
}
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
index 83e44b69812b..7e1bfb921ca9 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
@@ -18,6 +18,7 @@ package com.android.systemui.animation
import android.graphics.fonts.Font
import android.graphics.fonts.FontVariationAxis
+import android.util.Log
import android.util.LruCache
import android.util.MathUtils
import android.util.MathUtils.abs
@@ -114,6 +115,9 @@ class FontInterpolator {
tmpInterpKey.set(start, end, progress)
val cachedFont = interpCache[tmpInterpKey]
if (cachedFont != null) {
+ if (DEBUG) {
+ Log.d(LOG_TAG, "[$progress] Interp. cache hit for $tmpInterpKey")
+ }
return cachedFont
}
@@ -159,6 +163,9 @@ class FontInterpolator {
val axesCachedFont = verFontCache[tmpVarFontKey]
if (axesCachedFont != null) {
interpCache.put(InterpKey(start, end, progress), axesCachedFont)
+ if (DEBUG) {
+ Log.d(LOG_TAG, "[$progress] Axis cache hit for $tmpVarFontKey")
+ }
return axesCachedFont
}
@@ -168,6 +175,9 @@ class FontInterpolator {
val newFont = Font.Builder(start).setFontVariationSettings(newAxes.toTypedArray()).build()
interpCache.put(InterpKey(start, end, progress), newFont)
verFontCache.put(VarFontKey(start, newAxes), newFont)
+ if (DEBUG) {
+ Log.d(LOG_TAG, "[$progress] Cache MISS for $tmpInterpKey / $tmpVarFontKey")
+ }
return newFont
}
@@ -233,6 +243,8 @@ class FontInterpolator {
(v.coerceIn(min, max) / step).toInt() * step
companion object {
+ private const val LOG_TAG = "FontInterpolator"
+ private val DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG)
private val EMPTY_AXES = arrayOf<FontVariationAxis>()
// Returns true if given two font instance can be interpolated.
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
index 00108940e6ec..16ddf0c36d9d 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
@@ -26,6 +26,7 @@ import android.graphics.fonts.Font
import android.graphics.fonts.FontVariationAxis
import android.text.Layout
import android.util.LruCache
+import kotlin.math.roundToInt
private const val DEFAULT_ANIMATION_DURATION: Long = 300
private const val TYPEFACE_CACHE_MAX_ENTRIES = 5
@@ -63,9 +64,9 @@ class TypefaceVariantCacheImpl(
return it
}
- return TypefaceVariantCache
- .createVariantTypeface(baseTypeface, fvar)
- .also { cache.put(fvar, it) }
+ return TypefaceVariantCache.createVariantTypeface(baseTypeface, fvar).also {
+ cache.put(fvar, it)
+ }
}
}
@@ -74,7 +75,6 @@ class TypefaceVariantCacheImpl(
*
* Currently this class can provide text style animation for text weight and text size. For example
* the simple view that draws text with animating text size is like as follows:
- *
* <pre> <code>
* ```
* class SimpleTextAnimation : View {
@@ -97,6 +97,7 @@ class TypefaceVariantCacheImpl(
*/
class TextAnimator(
layout: Layout,
+ numberOfAnimationSteps: Int? = null, // Only do this number of discrete animation steps.
private val invalidateCallback: () -> Unit,
) {
var typefaceCache: TypefaceVariantCache = TypefaceVariantCacheImpl(layout.paint.typeface)
@@ -112,7 +113,8 @@ class TextAnimator(
ValueAnimator.ofFloat(1f).apply {
duration = DEFAULT_ANIMATION_DURATION
addUpdateListener {
- textInterpolator.progress = it.animatedValue as Float
+ textInterpolator.progress =
+ calculateProgress(it.animatedValue as Float, numberOfAnimationSteps)
invalidateCallback()
}
addListener(
@@ -123,6 +125,17 @@ class TextAnimator(
)
}
+ private fun calculateProgress(animProgress: Float, numberOfAnimationSteps: Int?): Float {
+ if (numberOfAnimationSteps != null) {
+ // This clamps the progress to the nearest value of "numberOfAnimationSteps"
+ // discrete values between 0 and 1f.
+ return (animProgress * numberOfAnimationSteps).roundToInt() /
+ numberOfAnimationSteps.toFloat()
+ }
+
+ return animProgress
+ }
+
sealed class PositionedGlyph {
/** Mutable X coordinate of the glyph position relative from drawing offset. */
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
index 465b73e6de19..648ef03895cd 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
@@ -74,7 +74,8 @@ class AnimatableClockView @JvmOverloads constructor(
private var onTextAnimatorInitialized: Runnable? = null
@VisibleForTesting var textAnimatorFactory: (Layout, () -> Unit) -> TextAnimator =
- { layout, invalidateCb -> TextAnimator(layout, invalidateCb) }
+ { layout, invalidateCb ->
+ TextAnimator(layout, NUM_CLOCK_FONT_ANIMATION_STEPS, invalidateCb) }
@VisibleForTesting var isAnimationEnabled: Boolean = true
@VisibleForTesting var timeOverrideInMillis: Long? = null
@@ -567,6 +568,7 @@ class AnimatableClockView @JvmOverloads constructor(
private const val CHARGE_ANIM_DURATION_PHASE_0: Long = 500
private const val CHARGE_ANIM_DURATION_PHASE_1: Long = 1000
private const val COLOR_ANIM_DURATION: Long = 400
+ private const val NUM_CLOCK_FONT_ANIMATION_STEPS = 30
// Constants for the animation
private val MOVE_INTERPOLATOR = Interpolators.EMPHASIZED
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 67fdb4c0c213..b48296fe54be 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -353,7 +353,7 @@
<!-- Message shown when a biometric is authenticated, waiting for the user to confirm authentication [CHAR LIMIT=40]-->
<string name="biometric_dialog_tap_confirm">Tap Confirm to complete</string>
<!-- Message shown when a biometric has authenticated with a user's face and is waiting for the user to confirm authentication [CHAR LIMIT=60]-->
- <string name="biometric_dialog_tap_confirm_with_face">Unlocked by face.</string>
+ <string name="biometric_dialog_tap_confirm_with_face">Unlocked by face</string>
<!-- Message shown when a biometric has authenticated with a user's face and is waiting for the user to confirm authentication [CHAR LIMIT=60]-->
<string name="biometric_dialog_tap_confirm_with_face_alt_1">Unlocked by face. Press to continue.</string>
<!-- Message shown when a biometric has authenticated with a user's face and is waiting for the user to confirm authentication [CHAR LIMIT=60]-->
diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt
index f96d1e3d7fef..070a45170d04 100644
--- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt
+++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt
@@ -62,6 +62,15 @@ open class ViewScreenshotTestRule(
private val isRobolectric = if (Build.FINGERPRINT.contains("robolectric")) true else false
override fun apply(base: Statement, description: Description): Statement {
+ if (isRobolectric) {
+ // In robolectric mode, we enable NATIVE graphics and unpack font and icu files.
+ // We need to use reflection, as this library is only needed and therefore
+ // only available in deviceless mode.
+ val nativeLoaderClassName = "org.robolectric.nativeruntime.DefaultNativeRuntimeLoader"
+ val defaultNativeRuntimeLoader = Class.forName(nativeLoaderClassName)
+ System.setProperty("robolectric.graphicsMode", "NATIVE")
+ defaultNativeRuntimeLoader.getMethod("injectAndLoad").invoke(null)
+ }
val ruleToApply = if (isRobolectric) roboRule else delegateRule
return ruleToApply.apply(base, description)
}
@@ -69,6 +78,7 @@ open class ViewScreenshotTestRule(
protected fun takeScreenshot(
mode: Mode = Mode.WrapContent,
viewProvider: (ComponentActivity) -> View,
+ beforeScreenshot: (ComponentActivity) -> Unit = {}
): Bitmap {
activityRule.scenario.onActivity { activity ->
// Make sure that the activity draws full screen and fits the whole display instead of
@@ -94,6 +104,7 @@ open class ViewScreenshotTestRule(
val content = activity.requireViewById<ViewGroup>(android.R.id.content)
assertEquals(1, content.childCount)
contentView = content.getChildAt(0)
+ beforeScreenshot(activity)
}
return if (isRobolectric) {
@@ -111,9 +122,10 @@ open class ViewScreenshotTestRule(
fun screenshotTest(
goldenIdentifier: String,
mode: Mode = Mode.WrapContent,
- viewProvider: (ComponentActivity) -> View,
+ beforeScreenshot: (ComponentActivity) -> Unit = {},
+ viewProvider: (ComponentActivity) -> View
) {
- val bitmap = takeScreenshot(mode, viewProvider)
+ val bitmap = takeScreenshot(mode, viewProvider, beforeScreenshot)
screenshotRule.assertBitmapAgainstGolden(
bitmap,
goldenIdentifier,
diff --git a/packages/SystemUI/src/com/android/systemui/DejankUtils.java b/packages/SystemUI/src/com/android/systemui/DejankUtils.java
index 3fce55f6e515..146d12c3e621 100644
--- a/packages/SystemUI/src/com/android/systemui/DejankUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/DejankUtils.java
@@ -29,13 +29,17 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.os.StrictMode;
import android.os.SystemProperties;
+import android.os.Trace;
import android.view.Choreographer;
+import android.view.View;
+import android.view.ViewRootImpl;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.util.Assert;
import java.util.ArrayList;
import java.util.HashSet;
+import java.util.Random;
import java.util.Stack;
import java.util.function.Supplier;
@@ -43,12 +47,14 @@ import java.util.function.Supplier;
* Utility class for methods used to dejank the UI.
*/
public class DejankUtils {
+ private static final String TRACK_NAME = "DejankUtils";
public static final boolean STRICT_MODE_ENABLED = Build.IS_ENG
|| SystemProperties.getBoolean("persist.sysui.strictmode", false);
private static final Choreographer sChoreographer = Choreographer.getInstance();
private static final Handler sHandler = new Handler();
private static final ArrayList<Runnable> sPendingRunnables = new ArrayList<>();
+ private static final Random sRandom = new Random();
private static Stack<String> sBlockingIpcs = new Stack<>();
private static boolean sTemporarilyIgnoreStrictMode;
private static final HashSet<String> sWhitelistedFrameworkClasses = new HashSet<>();
@@ -254,4 +260,30 @@ public class DejankUtils {
public static void setImmediate(boolean immediate) {
sImmediate = immediate;
}
+
+ /**
+ * Calls notifyRendererOfExpensiveFrame on the ViewRootImpl after performing null checks.
+ */
+ public static void notifyRendererOfExpensiveFrame(View view, String reason) {
+ if (view == null) return;
+ notifyRendererOfExpensiveFrame(view.getViewRootImpl(), reason);
+ }
+
+ /**
+ * Calls notifyRendererOfExpensiveFrame on the ViewRootImpl after performing null checks.
+ */
+ public static void notifyRendererOfExpensiveFrame(ViewRootImpl viewRoot, String reason) {
+ if (viewRoot == null) return;
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_APP)) {
+ int cookie = sRandom.nextInt();
+ Trace.asyncTraceForTrackBegin(
+ Trace.TRACE_TAG_APP,
+ TRACK_NAME,
+ "notifyRendererOfExpensiveFrame (" + reason + ")",
+ cookie);
+ DejankUtils.postAfterTraversal(
+ () -> Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, TRACK_NAME, cookie));
+ }
+ viewRoot.notifyRendererOfExpensiveFrame();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index c30a2146436c..8af92ce4bcb1 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -548,6 +548,10 @@ public class SwipeHelper implements Gefingerpoken, Dumpable {
if (!cancelled) {
updateSwipeProgressFromOffset(animView, canBeDismissed);
resetSwipeOfView(animView);
+ // Clear the snapped view after success, assuming it's not being swiped now
+ if (animView == mTouchedView && !mIsSwiping) {
+ mTouchedView = null;
+ }
}
onChildSnappedBack(animView, targetLeft);
});
@@ -813,13 +817,39 @@ public class SwipeHelper implements Gefingerpoken, Dumpable {
}
}
- public void resetSwipeState() {
- View swipedView = getSwipedView();
+ private void resetSwipeState() {
+ resetSwipeStates(/* resetAll= */ false);
+ }
+
+ public void resetTouchState() {
+ resetSwipeStates(/* resetAll= */ true);
+ }
+
+ /** This method resets the swipe state, and if `resetAll` is true, also resets the snap state */
+ private void resetSwipeStates(boolean resetAll) {
+ final View touchedView = mTouchedView;
+ final boolean wasSnapping = mSnappingChild;
+ final boolean wasSwiping = mIsSwiping;
mTouchedView = null;
mIsSwiping = false;
- if (swipedView != null) {
- snapChildIfNeeded(swipedView, false, 0);
- onChildSnappedBack(swipedView, 0);
+ // If we were swiping, then we resetting swipe requires resetting everything.
+ resetAll |= wasSwiping;
+ if (resetAll) {
+ mSnappingChild = false;
+ }
+ if (touchedView == null) return; // No view to reset visually
+ // When snap needs to be reset, first thing is to cancel any translation animation
+ final boolean snapNeedsReset = resetAll && wasSnapping;
+ if (snapNeedsReset) {
+ cancelTranslateAnimation(touchedView);
+ }
+ // actually reset the view to default state
+ if (resetAll) {
+ snapChildIfNeeded(touchedView, false, 0);
+ }
+ // report if a swipe or snap was reset.
+ if (wasSwiping || snapNeedsReset) {
+ onChildSnappedBack(touchedView, 0);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 43a3b9958ee5..f86e2ed6eb91 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -638,12 +638,12 @@ public class AuthContainerView extends LinearLayout
case Surface.ROTATION_90:
mPanelController.setPosition(AuthPanelController.POSITION_RIGHT);
- setScrollViewGravity(Gravity.BOTTOM | Gravity.RIGHT);
+ setScrollViewGravity(Gravity.CENTER_VERTICAL | Gravity.RIGHT);
break;
case Surface.ROTATION_270:
mPanelController.setPosition(AuthPanelController.POSITION_LEFT);
- setScrollViewGravity(Gravity.BOTTOM | Gravity.LEFT);
+ setScrollViewGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT);
break;
case Surface.ROTATION_180:
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java
index acdde3404ab5..167067e7d7e9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java
@@ -114,7 +114,13 @@ public class AuthPanelController extends ViewOutlineProvider {
}
private int getTopBound(@Position int position) {
- return Math.max(mContainerHeight - mContentHeight - mMargin, mMargin);
+ switch (position) {
+ case POSITION_LEFT:
+ case POSITION_RIGHT:
+ return Math.max((mContainerHeight - mContentHeight) / 2, mMargin);
+ default:
+ return Math.max(mContainerHeight - mContentHeight - mMargin, mMargin);
+ }
}
public void setContainerDimensions(int containerWidth, int containerHeight) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 3b40d8602e6f..b4871211be9b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1943,7 +1943,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
startKeyguardExitAnimation(0, 0);
}
- mPowerGestureIntercepted = mUpdateMonitor.isSecureCameraLaunchedOverKeyguard();
+ mPowerGestureIntercepted =
+ isOccluded && mUpdateMonitor.isSecureCameraLaunchedOverKeyguard();
if (mOccluded != isOccluded) {
mOccluded = isOccluded;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/ui/BouncerMessageView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/ui/BouncerMessageView.kt
index c0a5a51a910d..4dc52ff46707 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/ui/BouncerMessageView.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/ui/BouncerMessageView.kt
@@ -41,6 +41,8 @@ class BouncerMessageView : LinearLayout {
super.onFinishInflate()
primaryMessageView = findViewById(R.id.bouncer_primary_message_area)
secondaryMessageView = findViewById(R.id.bouncer_secondary_message_area)
+ primaryMessageView?.disable()
+ secondaryMessageView?.disable()
}
fun init(factory: KeyguardMessageAreaController.Factory) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
index abf0932c8407..529b980a6e38 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
@@ -41,6 +41,7 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.graphics.drawable.IconCompat;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.media.BluetoothMediaDevice;
import com.android.settingslib.media.MediaDevice;
import com.android.settingslib.qrcode.QrCodeGenerator;
@@ -420,7 +421,8 @@ public class MediaOutputBroadcastDialog extends MediaOutputBaseDialog {
return mMediaOutputController.getBroadcastMetadata();
}
- private void updateBroadcastInfo(boolean isBroadcastCode, String updatedString) {
+ @VisibleForTesting
+ void updateBroadcastInfo(boolean isBroadcastCode, String updatedString) {
Button positiveBtn = mAlertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
if (positiveBtn != null) {
positiveBtn.setEnabled(false);
@@ -523,16 +525,33 @@ public class MediaOutputBroadcastDialog extends MediaOutputBaseDialog {
}
private void handleUpdateFailedUi() {
- final Button positiveBtn = mAlertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
- mBroadcastErrorMessage.setVisibility(View.VISIBLE);
+ if (mAlertDialog == null) {
+ Log.d(TAG, "handleUpdateFailedUi: mAlertDialog is null");
+ return;
+ }
+ int errorMessageStringId = -1;
+ boolean enablePositiveBtn = false;
if (mRetryCount < MAX_BROADCAST_INFO_UPDATE) {
- if (positiveBtn != null) {
- positiveBtn.setEnabled(true);
- }
- mBroadcastErrorMessage.setText(R.string.media_output_broadcast_update_error);
+ enablePositiveBtn = true;
+ errorMessageStringId = R.string.media_output_broadcast_update_error;
} else {
mRetryCount = 0;
- mBroadcastErrorMessage.setText(R.string.media_output_broadcast_last_update_error);
+ errorMessageStringId = R.string.media_output_broadcast_last_update_error;
}
+
+ // update UI
+ final Button positiveBtn = mAlertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
+ if (positiveBtn != null && enablePositiveBtn) {
+ positiveBtn.setEnabled(true);
+ }
+ if (mBroadcastErrorMessage != null) {
+ mBroadcastErrorMessage.setVisibility(View.VISIBLE);
+ mBroadcastErrorMessage.setText(errorMessageStringId);
+ }
+ }
+
+ @VisibleForTesting
+ int getRetryCount() {
+ return mRetryCount;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 047fea123dc3..ca8369950e4b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -222,6 +222,8 @@ import com.android.systemui.util.Utils;
import com.android.systemui.util.time.SystemClock;
import com.android.wm.shell.animation.FlingAnimationUtils;
+import kotlin.Unit;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
@@ -232,7 +234,6 @@ import java.util.function.Consumer;
import javax.inject.Inject;
import javax.inject.Provider;
-import kotlin.Unit;
import kotlinx.coroutines.CoroutineDispatcher;
@CentralSurfacesComponent.CentralSurfacesScope
@@ -3450,6 +3451,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
@VisibleForTesting
void notifyExpandingStarted() {
if (!mExpanding) {
+ DejankUtils.notifyRendererOfExpensiveFrame(mView, "notifyExpandingStarted");
mExpanding = true;
mIsExpandingOrCollapsing = true;
mQsController.onExpandingStarted(mQsController.getFullyExpanded());
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index c42c2f4fa15a..6480164cdaf5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -63,6 +63,7 @@ import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.internal.policy.SystemBarUtils;
import com.android.keyguard.FaceAuthApiRequestReason;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.DejankUtils;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.classifier.Classifier;
@@ -958,6 +959,7 @@ public class QuickSettingsController implements Dumpable {
// TODO (b/265193930): remove dependency on NPVC
mPanelViewControllerLazy.get().cancelHeightAnimator();
// end
+ DejankUtils.notifyRendererOfExpensiveFrame(mPanelView, "onExpansionStarted");
// Reset scroll position and apply that position to the expanded height.
float height = mExpansionHeight;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
index b6970aef6cad..39d213d46c9a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
@@ -83,7 +83,8 @@ open class BlurUtils @Inject constructor(
return
}
if (lastAppliedBlur == 0 && radius != 0) {
- Trace.asyncTraceForTrackBegin(TRACE_TAG_APP, TRACK_NAME, EARLY_WAKEUP_SLICE_NAME, 0)
+ Trace.asyncTraceForTrackBegin(
+ TRACE_TAG_APP, TRACK_NAME, "eEarlyWakeup (prepareBlur)", 0)
earlyWakeupEnabled = true
createTransaction().use {
it.setEarlyWakeupStart()
@@ -110,7 +111,7 @@ open class BlurUtils @Inject constructor(
Trace.asyncTraceForTrackBegin(
TRACE_TAG_APP,
TRACK_NAME,
- EARLY_WAKEUP_SLICE_NAME,
+ "eEarlyWakeup (applyBlur)",
0
)
it.setEarlyWakeupStart()
@@ -159,6 +160,5 @@ open class BlurUtils @Inject constructor(
companion object {
const val TRACK_NAME = "BlurUtils"
- const val EARLY_WAKEUP_SLICE_NAME = "eEarlyWakeup"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index bfb6fb0fcdc7..9f397fe9ac0c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -556,6 +556,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
public void onNotificationUpdated() {
+ if (mIsSummaryWithChildren) {
+ Trace.beginSection("ExpNotRow#onNotifUpdated (summary)");
+ } else {
+ Trace.beginSection("ExpNotRow#onNotifUpdated (leaf)");
+ }
for (NotificationContentView l : mLayouts) {
l.onNotificationUpdated(mEntry);
}
@@ -591,6 +596,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mUpdateSelfBackgroundOnUpdate = false;
updateBackgroundColorsOfSelf();
}
+ Trace.endSection();
}
private void updateBackgroundColorsOfSelf() {
@@ -2588,6 +2594,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mIsSummaryWithChildren = mChildrenContainer != null
&& mChildrenContainer.getNotificationChildCount() > 0;
if (mIsSummaryWithChildren) {
+ Trace.beginSection("ExpNotRow#onChildCountChanged (summary)");
NotificationViewWrapper wrapper = mChildrenContainer.getNotificationViewWrapper();
if (wrapper == null || wrapper.getNotificationHeader() == null) {
mChildrenContainer.recreateNotificationHeader(mExpandClickListener,
@@ -2599,6 +2606,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
updateChildrenAppearance();
updateChildrenVisibility();
applyChildrenRoundness();
+ if (mIsSummaryWithChildren) {
+ Trace.endSection();
+ }
}
protected void expandNotification() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java
index 77fd05186090..3e10f2ad52e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java
@@ -22,6 +22,7 @@ import android.annotation.Nullable;
import android.app.Notification;
import android.content.Context;
import android.content.res.Resources;
+import android.os.Trace;
import android.service.notification.StatusBarNotification;
import android.util.TypedValue;
import android.view.LayoutInflater;
@@ -57,6 +58,7 @@ public class HybridGroupManager {
}
private HybridNotificationView inflateHybridView(View contentView, ViewGroup parent) {
+ Trace.beginSection("HybridGroupManager#inflateHybridView");
LayoutInflater inflater = LayoutInflater.from(mContext);
int layout = contentView instanceof ConversationLayout
? R.layout.hybrid_conversation_notification
@@ -64,6 +66,7 @@ public class HybridGroupManager {
HybridNotificationView hybrid = (HybridNotificationView)
inflater.inflate(layout, parent, false);
parent.addView(hybrid);
+ Trace.endSection();
return hybrid;
}
@@ -90,12 +93,18 @@ public class HybridGroupManager {
public HybridNotificationView bindFromNotification(HybridNotificationView reusableView,
View contentView, StatusBarNotification notification,
ViewGroup parent) {
+ boolean isNewView = false;
if (reusableView == null) {
+ Trace.beginSection("HybridGroupManager#bindFromNotification");
reusableView = inflateHybridView(contentView, parent);
+ isNewView = true;
}
CharSequence titleText = resolveTitle(notification.getNotification());
CharSequence contentText = resolveText(notification.getNotification());
reusableView.bind(titleText, contentText, contentView);
+ if (isNewView) {
+ Trace.endSection();
+ }
return reusableView;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 451d837b63a0..124df8c3b815 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -26,6 +26,7 @@ import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.RemoteException;
+import android.os.Trace;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.util.AttributeSet;
@@ -1193,6 +1194,7 @@ public class NotificationContentView extends FrameLayout implements Notification
private void updateSingleLineView() {
if (mIsChildInGroup) {
+ Trace.beginSection("NotifContentView#updateSingleLineView");
boolean isNewView = mSingleLineView == null;
mSingleLineView = mHybridGroupManager.bindFromNotification(
mSingleLineView, mContractedChild, mNotificationEntry.getSbn(), this);
@@ -1200,6 +1202,7 @@ public class NotificationContentView extends FrameLayout implements Notification
updateViewVisibility(mVisibleType, VISIBLE_TYPE_SINGLELINE,
mSingleLineView, mSingleLineView);
}
+ Trace.endSection();
} else if (mSingleLineView != null) {
removeView(mSingleLineView);
mSingleLineView = null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index d18757d3453f..f8e374de11e2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -379,6 +379,7 @@ public class NotificationChildrenContainer extends ViewGroup
}
public void recreateNotificationHeader(OnClickListener listener, boolean isConversation) {
+ Trace.beginSection("NotifChildCont#recreateHeader");
mHeaderClickListener = listener;
mIsConversation = isConversation;
StatusBarNotification notification = mContainingNotification.getEntry().getSbn();
@@ -406,6 +407,7 @@ public class NotificationChildrenContainer extends ViewGroup
recreateLowPriorityHeader(builder, isConversation);
updateHeaderVisibility(false /* animate */);
updateChildrenAppearance();
+ Trace.endSection();
}
/**
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 b81cb2be1c4d..f7c6594e3a70 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
@@ -4079,6 +4079,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
if (!mIsExpansionChanging) {
cancelActiveSwipe();
}
+ finalizeClearAllAnimation();
}
updateNotificationAnimationStates();
updateChronometers();
@@ -4174,20 +4175,30 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
runAnimationFinishedRunnables();
clearTransient();
clearHeadsUpDisappearRunning();
+ finalizeClearAllAnimation();
+ }
+ private void finalizeClearAllAnimation() {
if (mAmbientState.isClearAllInProgress()) {
setClearAllInProgress(false);
if (mShadeNeedsToClose) {
mShadeNeedsToClose = false;
- postDelayed(
- () -> {
- mShadeController.animateCollapseShade(CommandQueue.FLAG_EXCLUDE_NONE);
- },
- DELAY_BEFORE_SHADE_CLOSE /* delayMillis */);
+ if (mIsExpanded) {
+ collapseShadeDelayed();
+ }
}
}
}
+ private void collapseShadeDelayed() {
+ postDelayed(
+ () -> {
+ mShadeController.animateCollapseShade(
+ CommandQueue.FLAG_EXCLUDE_NONE);
+ },
+ DELAY_BEFORE_SHADE_CLOSE /* delayMillis */);
+ }
+
private void clearHeadsUpDisappearRunning() {
for (int i = 0; i < getChildCount(); i++) {
View view = getChildAt(i);
@@ -4395,6 +4406,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
boolean nowHiddenAtAll = mAmbientState.isHiddenAtAll();
if (nowFullyHidden != wasFullyHidden) {
updateVisibility();
+ mSwipeHelper.resetTouchState();
}
if (!wasHiddenAtAll && nowHiddenAtAll) {
resetExposedMenuView(true /* animate */, true /* animate */);
@@ -4535,6 +4547,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mFooterView.setFooterLabelVisible(mHasFilteredOutSeenNotifications);
}
+ @VisibleForTesting
public void setClearAllInProgress(boolean clearAllInProgress) {
mClearAllInProgress = clearAllInProgress;
mAmbientState.setClearAllInProgress(clearAllInProgress);
@@ -4688,6 +4701,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
if (v instanceof ExpandableNotificationRow && !mController.isShowingEmptyShadeView()) {
mController.updateShowEmptyShadeView();
updateFooter();
+ mController.updateImportantForAccessibility();
}
updateSpeedBumpIndex();
@@ -4699,6 +4713,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
if (v instanceof ExpandableNotificationRow && mController.isShowingEmptyShadeView()) {
mController.updateShowEmptyShadeView();
updateFooter();
+ mController.updateImportantForAccessibility();
}
updateSpeedBumpIndex();
@@ -4711,6 +4726,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
if (v instanceof ExpandableNotificationRow && mController.isShowingEmptyShadeView()) {
mController.updateShowEmptyShadeView();
updateFooter();
+ mController.updateImportantForAccessibility();
}
updateSpeedBumpIndex();
@@ -5151,7 +5167,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mHeadsUpAppearanceController = headsUpAppearanceController;
}
- private boolean isVisible(View child) {
+ @VisibleForTesting
+ public boolean isVisible(View child) {
boolean hasClipBounds = child.getClipBounds(mTmpRect);
return child.getVisibility() == View.VISIBLE
&& (!hasClipBounds || mTmpRect.height() > 0);
@@ -5850,7 +5867,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
private void cancelActiveSwipe() {
- mSwipeHelper.resetSwipeState();
+ mSwipeHelper.resetTouchState();
updateContinuousShadowDrawing();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 7b046d6c9256..9272c376d4fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -327,6 +327,7 @@ public class NotificationStackScrollLayoutController {
mView.updateSensitiveness(mStatusBarStateController.goingToFullShade(),
mLockscreenUserManager.isAnyProfilePublicMode());
mView.onStatePostChange(mStatusBarStateController.fromShadeLocked());
+ updateImportantForAccessibility();
}
};
@@ -1205,6 +1206,7 @@ public class NotificationStackScrollLayoutController {
if (mView.getVisibility() == View.VISIBLE) {
// Synchronize EmptyShadeView visibility with the parent container.
updateShowEmptyShadeView();
+ updateImportantForAccessibility();
}
}
@@ -1232,6 +1234,22 @@ public class NotificationStackScrollLayoutController {
}
/**
+ * Update the importantForAccessibility of NotificationStackScrollLayout.
+ * <p>
+ * We want the NSSL to be unimportant for accessibility when there's no
+ * notifications in it while the device is on lock screen, to avoid unlablel NSSL view.
+ * Otherwise, we want it to be important for accessibility to enable accessibility
+ * auto-scrolling in NSSL.
+ */
+ public void updateImportantForAccessibility() {
+ if (getVisibleNotificationCount() == 0 && mView.onKeyguard()) {
+ mView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+ } else {
+ mView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+ }
+ }
+
+ /**
* @return true if {@link StatusBarStateController} is in transition to the KEYGUARD
* and false otherwise.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 0402d4f88205..096f73f80165 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -3677,7 +3677,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private void onShadeVisibilityChanged(boolean visible) {
if (mVisible != visible) {
mVisible = visible;
- if (!visible) {
+ if (visible) {
+ DejankUtils.notifyRendererOfExpensiveFrame(
+ mNotificationShadeWindowView, "onShadeVisibilityChanged");
+ } else {
mGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, true /* force */,
true /* removeControls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index d585163aa223..02b7e9176cf2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -21,7 +21,6 @@ import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_STANDARD;
import android.app.ActivityManager;
-import android.app.Notification;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
@@ -47,7 +46,6 @@ import android.view.OnReceiveContentListener;
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.ViewGroup;
-import android.view.ViewGroupOverlay;
import android.view.ViewRootImpl;
import android.view.WindowInsets;
import android.view.WindowInsetsAnimation;
@@ -58,6 +56,7 @@ import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
+import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -436,25 +435,23 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
if (animate && parent != null && mIsFocusAnimationFlagActive) {
ViewGroup grandParent = (ViewGroup) parent.getParent();
- ViewGroupOverlay overlay = parent.getOverlay();
View actionsContainer = getActionsContainerLayout();
int actionsContainerHeight =
actionsContainer != null ? actionsContainer.getHeight() : 0;
- // After adding this RemoteInputView to the overlay of the parent (and thus removing
- // it from the parent itself), the parent will shrink in height. This causes the
- // overlay to be moved. To correct the position of the overlay we need to offset it.
- int overlayOffsetY = actionsContainerHeight - getHeight();
- overlay.add(this);
+ // When defocusing, the notification needs to shrink. Therefore, we need to free
+ // up the space that was needed for the RemoteInputView. This is done by setting
+ // a negative top margin of the height difference of the RemoteInputView and its
+ // sibling (the actions_container_layout containing the Reply button etc.)
+ final int heightToShrink = actionsContainerHeight - getHeight();
+ setTopMargin(heightToShrink);
if (grandParent != null) grandParent.setClipChildren(false);
- Animator animator = getDefocusAnimator(actionsContainer, overlayOffsetY);
- View self = this;
+ final Animator animator = getDefocusAnimator(actionsContainer);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- overlay.remove(self);
- parent.addView(self);
+ setTopMargin(0);
if (grandParent != null) grandParent.setClipChildren(true);
setVisibility(GONE);
if (mWrapper != null) {
@@ -499,6 +496,13 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
}
}
+ private void setTopMargin(int topMargin) {
+ if (!(getLayoutParams() instanceof FrameLayout.LayoutParams)) return;
+ final FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) getLayoutParams();
+ layoutParams.topMargin = topMargin;
+ setLayoutParams(layoutParams);
+ }
+
@VisibleForTesting
protected void setViewRootImpl(ViewRootImpl viewRoot) {
mTestableViewRootImpl = viewRoot;
@@ -674,12 +678,12 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
mProgressBar.setVisibility(INVISIBLE);
mResetting = true;
mSending = false;
+ mController.removeSpinning(mEntry.getKey(), mToken);
onDefocus(true /* animate */, false /* logClose */, () -> {
mEntry.remoteInputTextWhenReset = SpannedString.valueOf(mEditText.getText());
mEditText.getText().clear();
mEditText.setEnabled(isAggregatedVisible());
mSendButton.setVisibility(VISIBLE);
- mController.removeSpinning(mEntry.getKey(), mToken);
updateSendButton();
setAttachment(null);
mResetting = false;
@@ -874,7 +878,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
ValueAnimator scaleAnimator = ValueAnimator.ofFloat(FOCUS_ANIMATION_MIN_SCALE, 1f);
scaleAnimator.addUpdateListener(valueAnimator -> {
- setFocusAnimationScaleY((float) scaleAnimator.getAnimatedValue(), 0);
+ setFocusAnimationScaleY((float) scaleAnimator.getAnimatedValue());
});
scaleAnimator.setDuration(FOCUS_ANIMATION_TOTAL_DURATION);
scaleAnimator.setInterpolator(InterpolatorsAndroidX.FAST_OUT_SLOW_IN);
@@ -901,9 +905,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
* Creates an animator for the defocus animation.
*
* @param fadeInView View that will be faded in during the defocus animation.
- * @param offsetY The RemoteInputView will be offset by offsetY during the animation
*/
- private Animator getDefocusAnimator(@Nullable View fadeInView, int offsetY) {
+ private Animator getDefocusAnimator(@Nullable View fadeInView) {
final AnimatorSet animatorSet = new AnimatorSet();
final Animator alphaAnimator = ObjectAnimator.ofFloat(this, View.ALPHA, 1f, 0f);
@@ -913,14 +916,14 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
ValueAnimator scaleAnimator = ValueAnimator.ofFloat(1f, FOCUS_ANIMATION_MIN_SCALE);
scaleAnimator.addUpdateListener(valueAnimator -> {
- setFocusAnimationScaleY((float) scaleAnimator.getAnimatedValue(), offsetY);
+ setFocusAnimationScaleY((float) scaleAnimator.getAnimatedValue());
});
scaleAnimator.setDuration(FOCUS_ANIMATION_TOTAL_DURATION);
scaleAnimator.setInterpolator(InterpolatorsAndroidX.FAST_OUT_SLOW_IN);
scaleAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation, boolean isReverse) {
- setFocusAnimationScaleY(1f /* scaleY */, 0 /* verticalOffset */);
+ setFocusAnimationScaleY(1f /* scaleY */);
}
});
@@ -942,9 +945,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
* Sets affected view properties for a vertical scale animation
*
* @param scaleY desired vertical view scale
- * @param verticalOffset vertical offset to apply to the RemoteInputView during the animation
*/
- private void setFocusAnimationScaleY(float scaleY, int verticalOffset) {
+ private void setFocusAnimationScaleY(float scaleY) {
int verticalBoundOffset = (int) ((1f - scaleY) * 0.5f * mContentView.getHeight());
Rect contentBackgroundBounds = new Rect(0, verticalBoundOffset, mContentView.getWidth(),
mContentView.getHeight() - verticalBoundOffset);
@@ -955,7 +957,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
} else {
mContentBackgroundBounds = contentBackgroundBounds;
}
- setTranslationY(verticalBoundOffset + verticalOffset);
+ setTranslationY(verticalBoundOffset);
}
/** Handler for button click on send action in IME. */
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldHapticsPlayer.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldHapticsPlayer.kt
index 8214822f0335..1e73cb3b9b24 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldHapticsPlayer.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldHapticsPlayer.kt
@@ -1,6 +1,7 @@
package com.android.systemui.unfold
import android.os.SystemProperties
+import android.os.VibrationAttributes
import android.os.VibrationEffect
import android.os.Vibrator
import com.android.systemui.dagger.qualifiers.Main
@@ -22,6 +23,8 @@ constructor(
) : TransitionProgressListener {
private var isFirstAnimationAfterUnfold = false
+ private val touchVibrationAttributes =
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK)
init {
if (vibrator != null) {
@@ -71,7 +74,7 @@ constructor(
}
private fun playHaptics() {
- vibrator?.vibrate(effect)
+ vibrator?.vibrate(effect, touchVibrationAttributes)
}
private val hapticsScale: Float
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt b/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt
index 7236e0fd134a..59f2cdb745ca 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt
@@ -224,7 +224,7 @@ object UserSwitcherViewBinder {
}
sectionView.removeAllViewsInLayout()
- for (viewModel in section) {
+ section.onEachIndexed { index, viewModel ->
val view =
layoutInflater.inflate(
R.layout.user_switcher_fullscreen_popup_item,
@@ -237,6 +237,13 @@ object UserSwitcherViewBinder {
view.resources.getString(viewModel.textResourceId)
view.setOnClickListener { viewModel.onClicked() }
sectionView.addView(view)
+ // Ensure that the first item in the first section gets accessibility focus.
+ // Request for focus with a delay when view is inflated an added to the listview.
+ if (index == 0 && position == 0) {
+ view.postDelayed({
+ view.requestAccessibilityFocus()
+ }, 200)
+ }
}
return sectionView
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt
index 14ad3acf7fb0..263d3750c657 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt
@@ -26,18 +26,17 @@ import android.text.TextPaint
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
+import kotlin.math.ceil
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.eq
import org.mockito.Mockito.inOrder
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
-
-import kotlin.math.ceil
+import org.mockito.Mockito.`when`
@RunWith(AndroidTestingRunner::class)
@SmallTest
@@ -56,15 +55,13 @@ class TextAnimatorTest : SysuiTestCase() {
val paint = mock(TextPaint::class.java)
`when`(textInterpolator.targetPaint).thenReturn(paint)
- val textAnimator = TextAnimator(layout, {}).apply {
- this.textInterpolator = textInterpolator
- this.animator = valueAnimator
- }
+ val textAnimator =
+ TextAnimator(layout, null, {}).apply {
+ this.textInterpolator = textInterpolator
+ this.animator = valueAnimator
+ }
- textAnimator.setTextStyle(
- weight = 400,
- animate = true
- )
+ textAnimator.setTextStyle(weight = 400, animate = true)
// If animation is requested, the base state should be rebased and the target state should
// be updated.
@@ -88,15 +85,13 @@ class TextAnimatorTest : SysuiTestCase() {
val paint = mock(TextPaint::class.java)
`when`(textInterpolator.targetPaint).thenReturn(paint)
- val textAnimator = TextAnimator(layout, {}).apply {
- this.textInterpolator = textInterpolator
- this.animator = valueAnimator
- }
+ val textAnimator =
+ TextAnimator(layout, null, {}).apply {
+ this.textInterpolator = textInterpolator
+ this.animator = valueAnimator
+ }
- textAnimator.setTextStyle(
- weight = 400,
- animate = false
- )
+ textAnimator.setTextStyle(weight = 400, animate = false)
// If animation is not requested, the progress should be 1 which is end of animation and the
// base state is rebased to target state by calling rebase.
@@ -118,15 +113,16 @@ class TextAnimatorTest : SysuiTestCase() {
`when`(textInterpolator.targetPaint).thenReturn(paint)
val animationEndCallback = mock(Runnable::class.java)
- val textAnimator = TextAnimator(layout, {}).apply {
- this.textInterpolator = textInterpolator
- this.animator = valueAnimator
- }
+ val textAnimator =
+ TextAnimator(layout, null, {}).apply {
+ this.textInterpolator = textInterpolator
+ this.animator = valueAnimator
+ }
textAnimator.setTextStyle(
- weight = 400,
- animate = true,
- onAnimationEnd = animationEndCallback
+ weight = 400,
+ animate = true,
+ onAnimationEnd = animationEndCallback
)
// Verify animationEnd callback has been added.
@@ -144,34 +140,27 @@ class TextAnimatorTest : SysuiTestCase() {
val layout = makeLayout("Hello, World", PAINT)
val valueAnimator = mock(ValueAnimator::class.java)
val textInterpolator = mock(TextInterpolator::class.java)
- val paint = TextPaint().apply {
- typeface = Typeface.createFromFile("/system/fonts/Roboto-Regular.ttf")
- }
+ val paint =
+ TextPaint().apply {
+ typeface = Typeface.createFromFile("/system/fonts/Roboto-Regular.ttf")
+ }
`when`(textInterpolator.targetPaint).thenReturn(paint)
- val textAnimator = TextAnimator(layout, {}).apply {
- this.textInterpolator = textInterpolator
- this.animator = valueAnimator
- }
+ val textAnimator =
+ TextAnimator(layout, null, {}).apply {
+ this.textInterpolator = textInterpolator
+ this.animator = valueAnimator
+ }
- textAnimator.setTextStyle(
- weight = 400,
- animate = true
- )
+ textAnimator.setTextStyle(weight = 400, animate = true)
val prevTypeface = paint.typeface
- textAnimator.setTextStyle(
- weight = 700,
- animate = true
- )
+ textAnimator.setTextStyle(weight = 700, animate = true)
assertThat(paint.typeface).isNotSameInstanceAs(prevTypeface)
- textAnimator.setTextStyle(
- weight = 400,
- animate = true
- )
+ textAnimator.setTextStyle(weight = 400, animate = true)
assertThat(paint.typeface).isSameInstanceAs(prevTypeface)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
index 705b485ce1b4..205fa0f11a26 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.media.dialog;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.mock;
@@ -33,6 +35,8 @@ import android.media.session.MediaSessionManager;
import android.os.PowerExemptionManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.widget.ImageView;
+import android.widget.TextView;
import androidx.test.filters.SmallTest;
@@ -44,6 +48,7 @@ import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.settingslib.media.BluetoothMediaDevice;
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastSender;
@@ -58,6 +63,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@@ -68,6 +74,9 @@ import java.util.Optional;
public class MediaOutputBroadcastDialogTest extends SysuiTestCase {
private static final String TEST_PACKAGE = "test_package";
+ private static final String BROADCAST_NAME_TEST = "Broadcast_name_test";
+ private static final String BROADCAST_CODE_TEST = "112233";
+ private static final String BROADCAST_CODE_UPDATE_TEST = "11223344";
// Mock
private final MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class);
@@ -106,6 +115,9 @@ public class MediaOutputBroadcastDialogTest extends SysuiTestCase {
when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(null);
when(mLocalBluetoothProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(null);
+ when(mLocalBluetoothLeBroadcast.getProgramInfo()).thenReturn(BROADCAST_NAME_TEST);
+ when(mLocalBluetoothLeBroadcast.getBroadcastCode()).thenReturn(
+ BROADCAST_CODE_TEST.getBytes(StandardCharsets.UTF_8));
mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE,
mMediaSessionManager, mLocalBluetoothManager, mStarter,
@@ -194,4 +206,50 @@ public class MediaOutputBroadcastDialogTest extends SysuiTestCase {
verify(mLocalBluetoothLeBroadcastAssistant, times(1)).addSource(any(), any(), anyBoolean());
}
+
+ @Test
+ public void handleLeBroadcastMetadataChanged_checkBroadcastName() {
+ when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
+ mLocalBluetoothLeBroadcast);
+ final TextView broadcastName = mMediaOutputBroadcastDialog.mDialogView
+ .requireViewById(R.id.broadcast_name_summary);
+
+ mMediaOutputBroadcastDialog.handleLeBroadcastMetadataChanged();
+
+ assertThat(broadcastName.getText().toString()).isEqualTo(BROADCAST_NAME_TEST);
+ }
+
+ @Test
+ public void handleLeBroadcastMetadataChanged_checkBroadcastCode() {
+ when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
+ mLocalBluetoothLeBroadcast);
+
+ final TextView broadcastCode = mMediaOutputBroadcastDialog.mDialogView
+ .requireViewById(R.id.broadcast_code_summary);
+
+ mMediaOutputBroadcastDialog.handleLeBroadcastMetadataChanged();
+
+ assertThat(broadcastCode.getText().toString()).isEqualTo(BROADCAST_CODE_TEST);
+ }
+
+ @Test
+ public void updateBroadcastInfo_stopBroadcastFailed_handleFailedUi() {
+ ImageView broadcastCodeEdit = mMediaOutputBroadcastDialog.mDialogView
+ .requireViewById(R.id.broadcast_code_edit);
+ TextView broadcastCode = mMediaOutputBroadcastDialog.mDialogView.requireViewById(
+ R.id.broadcast_code_summary);
+ broadcastCode.setText(BROADCAST_CODE_UPDATE_TEST);
+ when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(null);
+ broadcastCodeEdit.callOnClick();
+
+ mMediaOutputBroadcastDialog.updateBroadcastInfo(true, BROADCAST_CODE_UPDATE_TEST);
+ assertThat(mMediaOutputBroadcastDialog.getRetryCount()).isEqualTo(1);
+
+ mMediaOutputBroadcastDialog.updateBroadcastInfo(true, BROADCAST_CODE_UPDATE_TEST);
+ assertThat(mMediaOutputBroadcastDialog.getRetryCount()).isEqualTo(2);
+
+ // It will be the MAX Retry Count = 3
+ mMediaOutputBroadcastDialog.updateBroadcastInfo(true, BROADCAST_CODE_UPDATE_TEST);
+ assertThat(mMediaOutputBroadcastDialog.getRetryCount()).isEqualTo(0);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 6a0e3c6d51eb..02666e40b98d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -35,6 +35,7 @@ import static org.mockito.Mockito.when;
import android.content.res.Resources;
import android.metrics.LogMaker;
import android.testing.AndroidTestingRunner;
+import android.view.View;
import androidx.test.filters.SmallTest;
@@ -430,6 +431,84 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
verify(mNotificationStackScrollLayout).setStatusBarState(KEYGUARD);
}
+ @Test
+ public void updateImportantForAccessibility_noChild_onKeyGuard_notImportantForA11y() {
+ // GIVEN: Controller is attached, active notifications is empty,
+ // and mNotificationStackScrollLayout.onKeyguard() is true
+ initController(/* viewIsAttached= */ true);
+ when(mNotificationStackScrollLayout.onKeyguard()).thenReturn(true);
+ mController.getNotifStackController().setNotifStats(NotifStats.getEmpty());
+
+ // WHEN: call updateImportantForAccessibility
+ mController.updateImportantForAccessibility();
+
+ // THEN: mNotificationStackScrollLayout should not be important for A11y
+ verify(mNotificationStackScrollLayout)
+ .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+ }
+
+ @Test
+ public void updateImportantForAccessibility_hasChild_onKeyGuard_importantForA11y() {
+ // GIVEN: Controller is attached, active notifications is not empty,
+ // and mNotificationStackScrollLayout.onKeyguard() is true
+ initController(/* viewIsAttached= */ true);
+ when(mNotificationStackScrollLayout.onKeyguard()).thenReturn(true);
+ mController.getNotifStackController().setNotifStats(
+ new NotifStats(
+ /* numActiveNotifs = */ 1,
+ /* hasNonClearableAlertingNotifs = */ false,
+ /* hasClearableAlertingNotifs = */ false,
+ /* hasNonClearableSilentNotifs = */ false,
+ /* hasClearableSilentNotifs = */ false)
+ );
+
+ // WHEN: call updateImportantForAccessibility
+ mController.updateImportantForAccessibility();
+
+ // THEN: mNotificationStackScrollLayout should be important for A11y
+ verify(mNotificationStackScrollLayout)
+ .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+ }
+
+ @Test
+ public void updateImportantForAccessibility_hasChild_notOnKeyGuard_importantForA11y() {
+ // GIVEN: Controller is attached, active notifications is not empty,
+ // and mNotificationStackScrollLayout.onKeyguard() is false
+ initController(/* viewIsAttached= */ true);
+ when(mNotificationStackScrollLayout.onKeyguard()).thenReturn(false);
+ mController.getNotifStackController().setNotifStats(
+ new NotifStats(
+ /* numActiveNotifs = */ 1,
+ /* hasNonClearableAlertingNotifs = */ false,
+ /* hasClearableAlertingNotifs = */ false,
+ /* hasNonClearableSilentNotifs = */ false,
+ /* hasClearableSilentNotifs = */ false)
+ );
+
+ // WHEN: call updateImportantForAccessibility
+ mController.updateImportantForAccessibility();
+
+ // THEN: mNotificationStackScrollLayout should be important for A11y
+ verify(mNotificationStackScrollLayout)
+ .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+ }
+
+ @Test
+ public void updateImportantForAccessibility_noChild_notOnKeyGuard_importantForA11y() {
+ // GIVEN: Controller is attached, active notifications is empty,
+ // and mNotificationStackScrollLayout.onKeyguard() is false
+ initController(/* viewIsAttached= */ true);
+ when(mNotificationStackScrollLayout.onKeyguard()).thenReturn(false);
+ mController.getNotifStackController().setNotifStats(NotifStats.getEmpty());
+
+ // WHEN: call updateImportantForAccessibility
+ mController.updateImportantForAccessibility();
+
+ // THEN: mNotificationStackScrollLayout should be important for A11y
+ verify(mNotificationStackScrollLayout)
+ .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+ }
+
private LogMaker logMatcher(int category, int type) {
return argThat(new LogMatcher(category, type));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index a4ee349f5b71..85b1ec108e98 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -41,6 +41,7 @@ import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -575,10 +576,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mStackScroller.inflateFooterView();
// add notification
- ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
- NotificationEntry entry = mock(NotificationEntry.class);
- when(row.getEntry()).thenReturn(entry);
- when(entry.isClearable()).thenReturn(true);
+ ExpandableNotificationRow row = createClearableRow();
mStackScroller.addContainerView(row);
mStackScroller.onUpdateRowStates();
@@ -648,6 +646,50 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
}
@Test
+ public void testClearNotifications_clearAllInProgress() {
+ ExpandableNotificationRow row = createClearableRow();
+ when(row.getEntry().hasFinishedInitialization()).thenReturn(true);
+ doReturn(true).when(mStackScroller).isVisible(row);
+ mStackScroller.addContainerView(row);
+
+ mStackScroller.clearNotifications(ROWS_ALL, false);
+
+ assertClearAllInProgress(true);
+ verify(mNotificationRoundnessManager).setClearAllInProgress(true);
+ }
+
+ @Test
+ public void testOnChildAnimationFinished_resetsClearAllInProgress() {
+ mStackScroller.setClearAllInProgress(true);
+
+ mStackScroller.onChildAnimationFinished();
+
+ assertClearAllInProgress(false);
+ verify(mNotificationRoundnessManager).setClearAllInProgress(false);
+ }
+
+ @Test
+ public void testShadeCollapsed_resetsClearAllInProgress() {
+ mStackScroller.setClearAllInProgress(true);
+
+ mStackScroller.setIsExpanded(false);
+
+ assertClearAllInProgress(false);
+ verify(mNotificationRoundnessManager).setClearAllInProgress(false);
+ }
+
+ @Test
+ public void testShadeExpanded_doesntChangeClearAllInProgress() {
+ mStackScroller.setClearAllInProgress(true);
+ clearInvocations(mNotificationRoundnessManager);
+
+ mStackScroller.setIsExpanded(true);
+
+ assertClearAllInProgress(true);
+ verify(mNotificationRoundnessManager, never()).setClearAllInProgress(anyBoolean());
+ }
+
+ @Test
public void testAddNotificationUpdatesSpeedBumpIndex() {
// initial state calculated == 0
assertEquals(0, mStackScroller.getSpeedBumpIndex());
@@ -794,7 +836,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
}
@Test
- public void onShadeClosesWithAnimationWillResetSwipeState() {
+ public void onShadeClosesWithAnimationWillResetTouchState() {
// GIVEN shade is expanded
mStackScroller.setIsExpanded(true);
clearInvocations(mNotificationSwipeHelper);
@@ -804,12 +846,12 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mStackScroller.setIsExpanded(false);
mStackScroller.onExpansionStopped();
- // VERIFY swipe is reset
- verify(mNotificationSwipeHelper).resetSwipeState();
+ // VERIFY touch is reset
+ verify(mNotificationSwipeHelper).resetTouchState();
}
@Test
- public void onShadeClosesWithoutAnimationWillResetSwipeState() {
+ public void onShadeClosesWithoutAnimationWillResetTouchState() {
// GIVEN shade is expanded
mStackScroller.setIsExpanded(true);
clearInvocations(mNotificationSwipeHelper);
@@ -817,8 +859,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
// WHEN closing the shade without the animation
mStackScroller.setIsExpanded(false);
- // VERIFY swipe is reset
- verify(mNotificationSwipeHelper).resetSwipeState();
+ // VERIFY touch is reset
+ verify(mNotificationSwipeHelper).resetTouchState();
}
@Test
@@ -896,6 +938,21 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mStackScroller.setStatusBarState(state);
}
+ private ExpandableNotificationRow createClearableRow() {
+ ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
+ NotificationEntry entry = mock(NotificationEntry.class);
+ when(row.canViewBeCleared()).thenReturn(true);
+ when(row.getEntry()).thenReturn(entry);
+ when(entry.isClearable()).thenReturn(true);
+
+ return row;
+ }
+
+ private void assertClearAllInProgress(boolean expected) {
+ assertEquals(expected, mStackScroller.getClearAllInProgress());
+ assertEquals(expected, mAmbientState.isClearAllInProgress());
+ }
+
private static void mockBoundsOnScreen(View view, Rect bounds) {
doAnswer(invocation -> {
Rect out = invocation.getArgument(0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt
index 3dec45b4ff9f..b9c7e6133669 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt
@@ -15,6 +15,7 @@
*/
package com.android.systemui.unfold
+import android.os.VibrationAttributes
import android.os.VibrationEffect
import android.os.Vibrator
import android.testing.AndroidTestingRunner
@@ -53,7 +54,7 @@ class UnfoldHapticsPlayerTest : SysuiTestCase() {
progressProvider.onTransitionProgress(0.5f)
progressProvider.onTransitionFinishing()
- verify(vibrator).vibrate(any<VibrationEffect>())
+ verify(vibrator).vibrate(any<VibrationEffect>(), any<VibrationAttributes>())
}
@Test
@@ -64,7 +65,7 @@ class UnfoldHapticsPlayerTest : SysuiTestCase() {
progressProvider.onTransitionProgress(0.99f)
progressProvider.onTransitionFinishing()
- verify(vibrator, never()).vibrate(any<VibrationEffect>())
+ verify(vibrator, never()).vibrate(any<VibrationEffect>(), any<VibrationAttributes>())
}
@Test
@@ -84,7 +85,7 @@ class UnfoldHapticsPlayerTest : SysuiTestCase() {
progressProvider.onTransitionFinished()
testFoldProvider.onFoldUpdate(isFolded = true)
- verify(vibrator, never()).vibrate(any<VibrationEffect>())
+ verify(vibrator, never()).vibrate(any<VibrationEffect>(), any<VibrationAttributes>())
}
@Test
@@ -112,6 +113,6 @@ class UnfoldHapticsPlayerTest : SysuiTestCase() {
progressProvider.onTransitionFinishing()
progressProvider.onTransitionFinished()
- verify(vibrator).vibrate(any<VibrationEffect>())
+ verify(vibrator).vibrate(any<VibrationEffect>(), any<VibrationAttributes>())
}
}
diff --git a/services/autofill/java/com/android/server/autofill/LogFieldClassificationScoreOnResultListener.java b/services/autofill/java/com/android/server/autofill/LogFieldClassificationScoreOnResultListener.java
new file mode 100644
index 000000000000..b4aca1530204
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/LogFieldClassificationScoreOnResultListener.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2023 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.autofill;
+
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.RemoteCallback;
+import android.service.autofill.FieldClassification;
+import android.service.autofill.FillEventHistory.Event.NoSaveReason;
+import android.util.Slog;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillManager.AutofillCommitReason;
+
+import java.util.ArrayList;
+
+class LogFieldClassificationScoreOnResultListener implements
+ RemoteCallback.OnResultListener {
+
+ private static final String TAG = "LogFieldClassificationScoreOnResultListener";
+
+ private Session mSession;
+ private final @NoSaveReason int mSaveDialogNotShowReason;
+ private final @AutofillCommitReason int mCommitReason;
+ private final int mViewsSize;
+ private final AutofillId[] mAutofillIds;
+ private final String[] mUserValues;
+ private final String[] mCategoryIds;
+ private final ArrayList<AutofillId> mDetectedFieldIds;
+ private final ArrayList<FieldClassification> mDetectedFieldClassifications;
+ LogFieldClassificationScoreOnResultListener(Session session,
+ int saveDialogNotShowReason,
+ int commitReason, int viewsSize, AutofillId[] autofillIds, String[] userValues,
+ String[] categoryIds, ArrayList<AutofillId> detectedFieldIds,
+ ArrayList<FieldClassification> detectedFieldClassifications) {
+ this.mSession = session;
+ this.mSaveDialogNotShowReason = saveDialogNotShowReason;
+ this.mCommitReason = commitReason;
+ this.mViewsSize = viewsSize;
+ this.mAutofillIds = autofillIds;
+ this.mUserValues = userValues;
+ this.mCategoryIds = categoryIds;
+ this.mDetectedFieldIds = detectedFieldIds;
+ this.mDetectedFieldClassifications = detectedFieldClassifications;
+ }
+
+ public void onResult(@Nullable Bundle result) {
+ // Create a local copy to safe guard race condition
+ Session session = mSession;
+ if (session == null) {
+ Slog.wtf(TAG, "session is null when calling onResult()");
+ return;
+ }
+ session.handleLogFieldClassificationScore(
+ result,
+ mSaveDialogNotShowReason,
+ mCommitReason,
+ mViewsSize,
+ mAutofillIds,
+ mUserValues,
+ mCategoryIds,
+ mDetectedFieldIds,
+ mDetectedFieldClassifications);
+ mSession = null;
+ }
+}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 563e431b5737..0c3f8667f4f8 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -3063,76 +3063,91 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
// Then use the results, asynchronously
- final RemoteCallback callback = new RemoteCallback((result) -> {
- if (result == null) {
- if (sDebug) Slog.d(TAG, "setFieldClassificationScore(): no results");
- logContextCommitted(null, null, saveDialogNotShowReason, commitReason);
- return;
- }
- final Scores scores = result.getParcelable(EXTRA_SCORES, android.service.autofill.AutofillFieldClassificationService.Scores.class);
- if (scores == null) {
- Slog.w(TAG, "No field classification score on " + result);
- return;
- }
- int i = 0, j = 0;
- try {
- // Iteract over all autofill fields first
- for (i = 0; i < viewsSize; i++) {
- final AutofillId autofillId = autofillIds[i];
-
- // Search the best scores for each category (as some categories could have
- // multiple user values
- ArrayMap<String, Float> scoresByField = null;
- for (j = 0; j < userValues.length; j++) {
- final String categoryId = categoryIds[j];
- final float score = scores.scores[i][j];
- if (score > 0) {
- if (scoresByField == null) {
- scoresByField = new ArrayMap<>(userValues.length);
- }
- final Float currentScore = scoresByField.get(categoryId);
- if (currentScore != null && currentScore > score) {
- if (sVerbose) {
- Slog.v(TAG, "skipping score " + score
- + " because it's less than " + currentScore);
- }
- continue;
- }
+ final RemoteCallback callback = new RemoteCallback(
+ new LogFieldClassificationScoreOnResultListener(
+ this,
+ saveDialogNotShowReason,
+ commitReason,
+ viewsSize,
+ autofillIds,
+ userValues,
+ categoryIds,
+ detectedFieldIds,
+ detectedFieldClassifications));
+
+ fcStrategy.calculateScores(callback, currentValues, userValues, categoryIds,
+ defaultAlgorithm, defaultArgs, algorithms, args);
+ }
+
+ void handleLogFieldClassificationScore(@Nullable Bundle result, int saveDialogNotShowReason,
+ int commitReason, int viewsSize, AutofillId[] autofillIds, String[] userValues,
+ String[] categoryIds, ArrayList<AutofillId> detectedFieldIds,
+ ArrayList<FieldClassification> detectedFieldClassifications) {
+ if (result == null) {
+ if (sDebug) Slog.d(TAG, "setFieldClassificationScore(): no results");
+ logContextCommitted(null, null, saveDialogNotShowReason, commitReason);
+ return;
+ }
+ final Scores scores = result.getParcelable(EXTRA_SCORES,
+ android.service.autofill.AutofillFieldClassificationService.Scores.class);
+ if (scores == null) {
+ Slog.w(TAG, "No field classification score on " + result);
+ return;
+ }
+ int i = 0, j = 0;
+ try {
+ // Iteract over all autofill fields first
+ for (i = 0; i < viewsSize; i++) {
+ final AutofillId autofillId = autofillIds[i];
+
+ // Search the best scores for each category (as some categories could have
+ // multiple user values
+ ArrayMap<String, Float> scoresByField = null;
+ for (j = 0; j < userValues.length; j++) {
+ final String categoryId = categoryIds[j];
+ final float score = scores.scores[i][j];
+ if (score > 0) {
+ if (scoresByField == null) {
+ scoresByField = new ArrayMap<>(userValues.length);
+ }
+ final Float currentScore = scoresByField.get(categoryId);
+ if (currentScore != null && currentScore > score) {
if (sVerbose) {
- Slog.v(TAG, "adding score " + score + " at index " + j + " and id "
- + autofillId);
+ Slog.v(TAG, "skipping score " + score
+ + " because it's less than " + currentScore);
}
- scoresByField.put(categoryId, score);
- } else if (sVerbose) {
- Slog.v(TAG, "skipping score 0 at index " + j + " and id " + autofillId);
+ continue;
}
+ if (sVerbose) {
+ Slog.v(TAG, "adding score " + score + " at index " + j + " and id "
+ + autofillId);
+ }
+ scoresByField.put(categoryId, score);
+ } else if (sVerbose) {
+ Slog.v(TAG, "skipping score 0 at index " + j + " and id " + autofillId);
}
- if (scoresByField == null) {
- if (sVerbose) Slog.v(TAG, "no score for autofillId=" + autofillId);
- continue;
- }
-
- // Then create the matches for that autofill id
- final ArrayList<Match> matches = new ArrayList<>(scoresByField.size());
- for (j = 0; j < scoresByField.size(); j++) {
- final String fieldId = scoresByField.keyAt(j);
- final float score = scoresByField.valueAt(j);
- matches.add(new Match(fieldId, score));
- }
- detectedFieldIds.add(autofillId);
- detectedFieldClassifications.add(new FieldClassification(matches));
- } // for i
- } catch (ArrayIndexOutOfBoundsException e) {
- wtf(e, "Error accessing FC score at [%d, %d] (%s): %s", i, j, scores, e);
- return;
- }
-
- logContextCommitted(detectedFieldIds, detectedFieldClassifications,
- saveDialogNotShowReason, commitReason);
- });
+ }
+ if (scoresByField == null) {
+ if (sVerbose) Slog.v(TAG, "no score for autofillId=" + autofillId);
+ continue;
+ }
- fcStrategy.calculateScores(callback, currentValues, userValues, categoryIds,
- defaultAlgorithm, defaultArgs, algorithms, args);
+ // Then create the matches for that autofill id
+ final ArrayList<Match> matches = new ArrayList<>(scoresByField.size());
+ for (j = 0; j < scoresByField.size(); j++) {
+ final String fieldId = scoresByField.keyAt(j);
+ final float score = scoresByField.valueAt(j);
+ matches.add(new Match(fieldId, score));
+ }
+ detectedFieldIds.add(autofillId);
+ detectedFieldClassifications.add(new FieldClassification(matches));
+ } // for i
+ } catch (ArrayIndexOutOfBoundsException e) {
+ wtf(e, "Error accessing FC score at [%d, %d] (%s): %s", i, j, scores, e);
+ return;
+ }
+ logContextCommitted(detectedFieldIds, detectedFieldClassifications,
+ saveDialogNotShowReason, commitReason);
}
/**
diff --git a/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java b/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
index 80406e6f66e4..38e7371e7075 100644
--- a/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
+++ b/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
@@ -113,6 +113,11 @@ public class ForegroundServiceTypeLoggerModule {
// We use this to get the duration an API was active after
// the stop call.
final SparseArray<Long> mLastFgsTimeStamp = new SparseArray<>();
+
+ // A map of API types to first FGS start call timestamps
+ // We use this to get the duration an API was active after
+ // the stop call.
+ final SparseArray<Long> mFirstFgsTimeStamp = new SparseArray<>();
}
// SparseArray that tracks all UIDs that have made various
@@ -146,6 +151,7 @@ public class ForegroundServiceTypeLoggerModule {
if (fgsIndex < 0) {
uidState.mRunningFgs.put(apiType, new ArrayMap<>());
fgsIndex = uidState.mRunningFgs.indexOfKey(apiType);
+ uidState.mFirstFgsTimeStamp.put(apiType, System.currentTimeMillis());
}
final ArrayMap<ComponentName, ServiceRecord> fgsList =
uidState.mRunningFgs.valueAt(fgsIndex);
@@ -237,7 +243,7 @@ public class ForegroundServiceTypeLoggerModule {
// there's no more FGS running for this type, just get rid of it
uidState.mRunningFgs.remove(apiType);
// but we need to keep track of the timestamp in case an API stops
- uidState.mLastFgsTimeStamp.put(apiType, record.mFgsExitTime);
+ uidState.mLastFgsTimeStamp.put(apiType, System.currentTimeMillis());
}
}
if (!apisFound.isEmpty()) {
@@ -454,8 +460,17 @@ public class ForegroundServiceTypeLoggerModule {
public void logFgsApiEvent(ServiceRecord r, int fgsState,
@FgsApiState int apiState,
@ForegroundServiceApiType int apiType, long timestamp) {
- final long apiDurationBeforeFgsStart = r.mFgsEnterTime - timestamp;
- final long apiDurationAfterFgsEnd = timestamp - r.mFgsExitTime;
+ long apiDurationBeforeFgsStart = r.createRealTime - timestamp;
+ long apiDurationAfterFgsEnd = timestamp - r.mFgsExitTime;
+ UidState uidState = mUids.get(r.appInfo.uid);
+ if (uidState != null) {
+ if (uidState.mFirstFgsTimeStamp.contains(apiType)) {
+ apiDurationBeforeFgsStart = uidState.mFirstFgsTimeStamp.get(apiType) - timestamp;
+ }
+ if (uidState.mLastFgsTimeStamp.contains(apiType)) {
+ apiDurationAfterFgsEnd = timestamp - uidState.mLastFgsTimeStamp.get(apiType);
+ }
+ }
final int[] apiTypes = new int[1];
apiTypes[0] = apiType;
final long[] timeStamps = new long[1];
diff --git a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
index 7841b699ec98..ffe5a6e6b958 100644
--- a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
+++ b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
@@ -18,6 +18,7 @@ package com.android.server.am;
import android.annotation.UptimeMillisLong;
import android.app.ActivityManagerInternal.OomAdjReason;
+import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -320,5 +321,8 @@ final class ProcessCachedOptimizerRecord {
pw.print(prefix); pw.print("isFreezeExempt="); pw.print(mFreezeExempt);
pw.print(" isPendingFreeze="); pw.print(mPendingFreeze);
pw.print(" " + IS_FROZEN + "="); pw.println(mFrozen);
+ pw.print(prefix); pw.print("earliestFreezableTimeMs=");
+ TimeUtils.formatDuration(mEarliestFreezableTimeMillis, nowUptime, pw);
+ pw.println();
}
}
diff --git a/services/core/java/com/android/server/am/UidObserverController.java b/services/core/java/com/android/server/am/UidObserverController.java
index a2582083c409..a6677a5185ca 100644
--- a/services/core/java/com/android/server/am/UidObserverController.java
+++ b/services/core/java/com/android/server/am/UidObserverController.java
@@ -429,21 +429,23 @@ public class UidObserverController {
}
}
- pw.println();
- pw.print(" mUidChangeDispatchCount=");
- pw.print(mUidChangeDispatchCount);
- pw.println();
- pw.println(" Slow UID dispatches:");
- for (int i = 0; i < count; i++) {
- final UidObserverRegistration reg = (UidObserverRegistration)
- mUidObservers.getRegisteredCallbackCookie(i);
- pw.print(" ");
- pw.print(mUidObservers.getRegisteredCallbackItem(i).getClass().getTypeName());
- pw.print(": ");
- pw.print(reg.mSlowDispatchCount);
- pw.print(" / Max ");
- pw.print(reg.mMaxDispatchTime);
- pw.println("ms");
+ if (dumpPackage == null) {
+ pw.println();
+ pw.print(" mUidChangeDispatchCount=");
+ pw.print(mUidChangeDispatchCount);
+ pw.println();
+ pw.println(" Slow UID dispatches:");
+ for (int i = 0; i < count; i++) {
+ final UidObserverRegistration reg = (UidObserverRegistration)
+ mUidObservers.getRegisteredCallbackCookie(i);
+ pw.print(" ");
+ pw.print(mUidObservers.getRegisteredCallbackItem(i).getClass().getTypeName());
+ pw.print(": ");
+ pw.print(reg.mSlowDispatchCount);
+ pw.print(" / Max ");
+ pw.print(reg.mMaxDispatchTime);
+ pw.println("ms");
+ }
}
}
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index b86da88d15c3..1f8ff73b2d5d 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -2210,8 +2210,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
mDeviceInventory.setPreferredDevicesForStrategyInt(
mAccessibilityStrategyId, Arrays.asList(defaultDevice));
} else {
- mDeviceInventory.removePreferredDevicesForStrategInt(mCommunicationStrategyId);
- mDeviceInventory.removePreferredDevicesForStrategInt(mAccessibilityStrategyId);
+ mDeviceInventory.removePreferredDevicesForStrategyInt(mCommunicationStrategyId);
+ mDeviceInventory.removePreferredDevicesForStrategyInt(mAccessibilityStrategyId);
}
mDeviceInventory.applyConnectedDevicesRoles();
} else {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 7b2e732702a3..d1cae490a31d 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -823,7 +823,7 @@ public class AudioDeviceInventory {
return status;
}
// Only used for internal requests
- /*package*/ int removePreferredDevicesForStrategInt(int strategy) {
+ /*package*/ int removePreferredDevicesForStrategyInt(int strategy) {
return clearDevicesRoleForStrategy(
strategy, AudioSystem.DEVICE_ROLE_PREFERRED, true /*internal */);
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
index 44ae454e7ef2..c212e8e3c82c 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
@@ -19,6 +19,7 @@ package com.android.server.inputmethod;
import static com.android.server.inputmethod.InputMethodManagerService.DEBUG;
import static com.android.server.inputmethod.InputMethodUtils.NOT_A_SUBTYPE_ID;
+import android.annotation.Nullable;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
@@ -65,8 +66,8 @@ final class InputMethodMenuController {
private boolean mShowImeWithHardKeyboard;
@GuardedBy("ImfLock.class")
- private final InputMethodDialogWindowContext mDialogWindowContext =
- new InputMethodDialogWindowContext();
+ @Nullable
+ private InputMethodDialogWindowContext mDialogWindowContext;
InputMethodMenuController(InputMethodManagerService service) {
mService = service;
@@ -124,11 +125,13 @@ final class InputMethodMenuController {
}
}
+ if (mDialogWindowContext == null) {
+ mDialogWindowContext = new InputMethodDialogWindowContext();
+ }
final Context dialogWindowContext = mDialogWindowContext.get(displayId);
mDialogBuilder = new AlertDialog.Builder(dialogWindowContext);
mDialogBuilder.setOnCancelListener(dialog -> hideInputMethodMenu());
- // TODO(b/277061090): refactor UI components should not be created while holding a lock.
final Context dialogContext = mDialogBuilder.getContext();
final TypedArray a = dialogContext.obtainStyledAttributes(null,
com.android.internal.R.styleable.DialogPreference,
@@ -196,11 +199,10 @@ final class InputMethodMenuController {
attrs.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
attrs.setTitle("Select input method");
w.setAttributes(attrs);
- // TODO(b/277062834) decouple/remove dependency on IMMS
mService.updateSystemUiLocked();
mService.sendOnNavButtonFlagsChangedLocked();
+ mSwitchingDialog.show();
}
- mSwitchingDialog.show();
}
private boolean isScreenLocked() {
@@ -274,7 +276,6 @@ final class InputMethodMenuController {
private final int mTextViewResourceId;
private final List<ImeSubtypeListItem> mItemsList;
public int mCheckedItem;
-
private ImeSubtypeListAdapter(Context context, int textViewResourceId,
List<ImeSubtypeListItem> itemsList, int checkedItem) {
super(context, textViewResourceId, itemsList);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index e56eba659d37..aeff2b0f9af6 100755..100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1843,7 +1843,7 @@ public class NotificationManagerService extends SystemService {
}
} else if (action.equals(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)) {
int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
- if (userHandle >= 0) {
+ if (userHandle >= 0 && !mDpm.isKeepProfilesRunningEnabled()) {
cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, true, userHandle,
REASON_PROFILE_TURNED_OFF, null);
mSnoozeHelper.clearData(userHandle);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index bba8043af5be..db47306ad58e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3409,7 +3409,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
}
@Nullable
- private String getDevicePolicyManagementRoleHolderPackageName(int userId) {
+ public String getDevicePolicyManagementRoleHolderPackageName(int userId) {
return Binder.withCleanCallingIdentity(() -> {
RoleManager roleManager = mContext.getSystemService(RoleManager.class);
List<String> roleHolders =
diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
index ba825774daf6..08934c69e099 100644
--- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java
+++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
@@ -724,6 +724,10 @@ public final class SuspendPackageHelper {
for (PackageInfo info : pkgInfos) {
result.add(info.packageName);
}
+
+ // Role holder may be null, but ArraySet handles it correctly.
+ result.remove(mPm.getDevicePolicyManagementRoleHolderPackageName(userId));
+
return result;
}
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index 90af4c6236aa..1eb56f1b7d1c 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -280,6 +280,10 @@ class ActivityStartInterceptor {
return false;
}
+ if (isKeepProfilesRunningEnabled() && !isPackageSuspended()) {
+ return false;
+ }
+
IntentSender target = createIntentSenderForOriginalIntent(mCallingUid,
FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT);
@@ -322,8 +326,7 @@ class ActivityStartInterceptor {
private boolean interceptSuspendedPackageIfNeeded() {
// Do not intercept if the package is not suspended
- if (mAInfo == null || mAInfo.applicationInfo == null ||
- (mAInfo.applicationInfo.flags & FLAG_SUSPENDED) == 0) {
+ if (!isPackageSuspended()) {
return false;
}
final PackageManagerInternal pmi = mService.getPackageManagerInternalLocked();
@@ -467,6 +470,17 @@ class ActivityStartInterceptor {
return true;
}
+ private boolean isPackageSuspended() {
+ return mAInfo != null && mAInfo.applicationInfo != null
+ && (mAInfo.applicationInfo.flags & FLAG_SUSPENDED) != 0;
+ }
+
+ private static boolean isKeepProfilesRunningEnabled() {
+ DevicePolicyManagerInternal dpmi =
+ LocalServices.getService(DevicePolicyManagerInternal.class);
+ return dpmi == null || dpmi.isKeepProfilesRunningEnabled();
+ }
+
/**
* Called when an activity is successfully launched.
*/
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index de1403b64477..a5b15489f292 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -49,6 +49,7 @@ import static android.Manifest.permission.MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_LOCK_TASK;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_MICROPHONE;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_MOBILE_NETWORK;
+import static android.Manifest.permission.MANAGE_DEVICE_POLICY_MODIFY_USERS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_MTE;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY;
@@ -73,7 +74,6 @@ import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SYSTEM_UPDATES;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_TIME;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER;
-import static android.Manifest.permission.MANAGE_DEVICE_POLICY_USERS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_VPN;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_WALLPAPER;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_WIFI;
@@ -13721,7 +13721,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
USER_RESTRICTION_PERMISSIONS.put(
UserManager.DISALLOW_ADD_CLONE_PROFILE, new String[]{MANAGE_DEVICE_POLICY_PROFILES});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_ADD_USER, new String[]{MANAGE_DEVICE_POLICY_USERS});
+ UserManager.DISALLOW_ADD_USER, new String[]{MANAGE_DEVICE_POLICY_MODIFY_USERS});
USER_RESTRICTION_PERMISSIONS.put(
UserManager.DISALLOW_ADD_WIFI_CONFIG, new String[]{MANAGE_DEVICE_POLICY_WIFI});
USER_RESTRICTION_PERMISSIONS.put(
@@ -13811,13 +13811,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
USER_RESTRICTION_PERMISSIONS.put(
UserManager.DISALLOW_PRINTING, new String[]{MANAGE_DEVICE_POLICY_PRINTING});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_REMOVE_USER, new String[]{MANAGE_DEVICE_POLICY_USERS});
+ UserManager.DISALLOW_REMOVE_USER, new String[]{MANAGE_DEVICE_POLICY_MODIFY_USERS});
USER_RESTRICTION_PERMISSIONS.put(
UserManager.DISALLOW_RUN_IN_BACKGROUND, new String[]{MANAGE_DEVICE_POLICY_RUN_IN_BACKGROUND});
USER_RESTRICTION_PERMISSIONS.put(
UserManager.DISALLOW_SAFE_BOOT, new String[]{MANAGE_DEVICE_POLICY_SAFE_BOOT});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_SET_USER_ICON, new String[]{MANAGE_DEVICE_POLICY_USERS});
+ UserManager.DISALLOW_SET_USER_ICON, new String[]{MANAGE_DEVICE_POLICY_MODIFY_USERS});
USER_RESTRICTION_PERMISSIONS.put(
UserManager.DISALLOW_SET_WALLPAPER, new String[]{MANAGE_DEVICE_POLICY_WALLPAPER});
USER_RESTRICTION_PERMISSIONS.put(
@@ -13843,7 +13843,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
USER_RESTRICTION_PERMISSIONS.put(
UserManager.DISALLOW_USB_FILE_TRANSFER, new String[]{MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_USER_SWITCH, new String[]{MANAGE_DEVICE_POLICY_USERS});
+ UserManager.DISALLOW_USER_SWITCH, new String[]{MANAGE_DEVICE_POLICY_MODIFY_USERS});
USER_RESTRICTION_PERMISSIONS.put(
UserManager.DISALLOW_WIFI_DIRECT, new String[]{MANAGE_DEVICE_POLICY_WIFI});
USER_RESTRICTION_PERMISSIONS.put(
@@ -23021,6 +23021,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
MANAGE_DEVICE_POLICY_LOCK_TASK,
MANAGE_DEVICE_POLICY_MICROPHONE,
MANAGE_DEVICE_POLICY_MOBILE_NETWORK,
+ MANAGE_DEVICE_POLICY_MODIFY_USERS,
MANAGE_DEVICE_POLICY_MTE,
MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION,
MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
@@ -23044,7 +23045,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
MANAGE_DEVICE_POLICY_TIME,
MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING,
MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER,
- MANAGE_DEVICE_POLICY_USERS,
MANAGE_DEVICE_POLICY_VPN,
MANAGE_DEVICE_POLICY_WALLPAPER,
MANAGE_DEVICE_POLICY_WIFI,
@@ -23065,12 +23065,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
MANAGE_DEVICE_POLICY_KEYGUARD,
MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS,
MANAGE_DEVICE_POLICY_LOCK_TASK,
+ MANAGE_DEVICE_POLICY_MODIFY_USERS,
MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
MANAGE_DEVICE_POLICY_SAFE_BOOT,
MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE,
MANAGE_DEVICE_POLICY_TIME,
- MANAGE_DEVICE_POLICY_USERS,
MANAGE_DEVICE_POLICY_WIPE_DATA
);
@@ -23156,6 +23156,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
MANAGE_DEVICE_POLICY_FUN,
MANAGE_DEVICE_POLICY_LOCK_TASK,
MANAGE_DEVICE_POLICY_MOBILE_NETWORK,
+ MANAGE_DEVICE_POLICY_MODIFY_USERS,
MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA,
MANAGE_DEVICE_POLICY_PRINTING,
MANAGE_DEVICE_POLICY_PROFILES,
@@ -23164,7 +23165,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
MANAGE_DEVICE_POLICY_SMS,
MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS,
MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER,
- MANAGE_DEVICE_POLICY_USERS,
MANAGE_DEVICE_POLICY_WINDOWS,
SET_TIME,
SET_TIME_ZONE
@@ -23354,6 +23354,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCK_TASK,
MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_MODIFY_USERS,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE,
MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
@@ -23374,8 +23376,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS,
MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_USERS,
- MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_VPN,
MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_WALLPAPER,
diff --git a/services/tests/PackageManagerServiceTests/server/Android.bp b/services/tests/PackageManagerServiceTests/server/Android.bp
index 1146271368af..3176f0673b65 100644
--- a/services/tests/PackageManagerServiceTests/server/Android.bp
+++ b/services/tests/PackageManagerServiceTests/server/Android.bp
@@ -106,6 +106,10 @@ android_test {
":PackageParserTestApp6",
],
resource_zips: [":PackageManagerServiceServerTests_apks_as_resources"],
+
+ data: [
+ ":StubTestApp",
+ ],
}
// Rules to copy all the test apks to the intermediate raw resource directory
diff --git a/services/tests/PackageManagerServiceTests/server/AndroidTest.xml b/services/tests/PackageManagerServiceTests/server/AndroidTest.xml
index 1b93527065ec..a0ef03c69e3a 100644
--- a/services/tests/PackageManagerServiceTests/server/AndroidTest.xml
+++ b/services/tests/PackageManagerServiceTests/server/AndroidTest.xml
@@ -23,6 +23,14 @@
<option name="install-arg" value="-g" />
<option name="test-file-name" value="PackageManagerServiceServerTests.apk" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="settings put global verifier_engprod 1" />
+ </target_preparer>
+
+ <!-- Load additional APKs onto device -->
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="push" value="StubTestApp.apk->/data/local/tmp/servicestests/StubTestApp.apk"/>
+ </target_preparer>
<option name="test-tag" value="PackageManagerServiceServerTests" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java
index b82ffb4d0b39..435f0d7f5f15 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -32,7 +32,6 @@ import android.app.AppGlobals;
import android.content.IIntentReceiver;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
@@ -70,7 +69,7 @@ import java.util.regex.Pattern;
@RunWith(AndroidJUnit4.class)
public class PackageManagerServiceTest {
- private static final String PACKAGE_NAME = "com.android.frameworks.servicestests";
+ private static final String PACKAGE_NAME = "com.android.server.pm.test.service.server";
private static final String TEST_DATA_PATH = "/data/local/tmp/servicestests/";
private static final String TEST_APP_APK = "StubTestApp.apk";
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt
index cfef0b23b3e7..5fd270ecb2b4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt
@@ -48,6 +48,7 @@ open class PackageHelperTestBase {
const val UNINSTALLER_PACKAGE = "com.android.test.known.uninstaller"
const val VERIFIER_PACKAGE = "com.android.test.known.verifier"
const val PERMISSION_CONTROLLER_PACKAGE = "com.android.test.known.permission"
+ const val MGMT_ROLE_HOLDER_PACKAGE = "com.android.test.know.device_management"
const val TEST_USER_ID = 0
}
@@ -119,6 +120,8 @@ open class PackageHelperTestBase {
Mockito.doReturn(arrayOf(PERMISSION_CONTROLLER_PACKAGE)).`when`(pms)
.getKnownPackageNamesInternal(any(),
eq(KnownPackages.PACKAGE_PERMISSION_CONTROLLER), eq(TEST_USER_ID))
+ Mockito.doReturn(MGMT_ROLE_HOLDER_PACKAGE).`when`(pms)
+ .getDevicePolicyManagementRoleHolderPackageName(eq(TEST_USER_ID))
}
private fun createPackageManagerService(vararg stageExistingPackages: String):
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
index f9a8ead9cd4a..5cca5fa8ea0b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
@@ -128,13 +128,14 @@ class SuspendPackageHelperTest : PackageHelperTestBase() {
fun setPackagesSuspended_forQuietMode() {
val knownPackages = arrayOf(DEVICE_ADMIN_PACKAGE, DEFAULT_HOME_PACKAGE, DIALER_PACKAGE,
INSTALLER_PACKAGE, UNINSTALLER_PACKAGE, VERIFIER_PACKAGE,
- PERMISSION_CONTROLLER_PACKAGE)
+ PERMISSION_CONTROLLER_PACKAGE, MGMT_ROLE_HOLDER_PACKAGE)
val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
knownPackages, true /* suspended */, null /* appExtras */,
null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
TEST_USER_ID, deviceOwnerUid, true /* forQuietMode */)!!
- assertThat(failedNames.size).isEqualTo(0)
+ assertThat(failedNames.size).isEqualTo(1)
+ assertThat(failedNames[0]).isEqualTo(MGMT_ROLE_HOLDER_PACKAGE)
}
@Test
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index cfeaf0b54552..16e1b457ad96 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -114,7 +114,6 @@ android_test {
":SimpleServiceTestApp1",
":SimpleServiceTestApp2",
":SimpleServiceTestApp3",
- ":StubTestApp",
":SuspendTestApp",
":MediaButtonReceiverHolderTestHelperApp",
"data/broken_shortcut.xml",
diff --git a/services/tests/servicestests/AndroidTest.xml b/services/tests/servicestests/AndroidTest.xml
index b304968f3e69..fbb0ca108ecd 100644
--- a/services/tests/servicestests/AndroidTest.xml
+++ b/services/tests/servicestests/AndroidTest.xml
@@ -44,11 +44,6 @@
<option name="teardown-command" value="rm -rf /data/local/tmp/servicestests"/>
</target_preparer>
- <!-- Load additional APKs onto device -->
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
- <option name="push" value="StubTestApp.apk->/data/local/tmp/servicestests/StubTestApp.apk"/>
- </target_preparer>
-
<option name="test-tag" value="FrameworksServicesTests" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.frameworks.servicestests" />
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
index b0fd64976c48..c98de7c4d00e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -66,6 +66,7 @@ import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.statusbar.StatusBarManagerInternal;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
@@ -307,6 +308,7 @@ public class WindowMagnificationManagerTest {
MagnificationScaleProvider.MAX_SCALE);
}
+ @Ignore("b/278816260: We could refer to b/182561174#comment4 for solution.")
@Test
public void logTrackingTypingFocus_processScroll_logDuration() {
WindowMagnificationManager spyWindowMagnificationManager = spy(mWindowMagnificationManager);
diff --git a/services/tests/servicestests/src/com/android/server/credentials/MetricUtilitiesTest.java b/services/tests/servicestests/src/com/android/server/credentials/MetricUtilitiesTest.java
index b46f1a61c745..50ea48719948 100644
--- a/services/tests/servicestests/src/com/android/server/credentials/MetricUtilitiesTest.java
+++ b/services/tests/servicestests/src/com/android/server/credentials/MetricUtilitiesTest.java
@@ -15,14 +15,27 @@
*/
package com.android.server.credentials;
+import android.content.Context;
+
+import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.credentials.metrics.BrowsedAuthenticationMetric;
+import com.android.server.credentials.metrics.CandidateAggregateMetric;
+import com.android.server.credentials.metrics.CandidateBrowsingPhaseMetric;
+import com.android.server.credentials.metrics.ChosenProviderFinalPhaseMetric;
import com.android.server.credentials.metrics.InitialPhaseMetric;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.security.cert.CertificateException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
/**
* Given the secondary-system nature of the MetricUtilities, we expect absolutely nothing to
* throw an error. If one presents itself, that is problematic.
@@ -33,6 +46,11 @@ import org.junit.runner.RunWith;
@SmallTest
public final class MetricUtilitiesTest {
+ @Before
+ public void setUp() throws CertificateException {
+ final Context context = ApplicationProvider.getApplicationContext();
+ }
+
@Test
public void logApiCalledInitialPhase_nullInitPhaseMetricAndNegativeSequence_success() {
MetricUtilities.logApiCalledInitialPhase(null, -1);
@@ -49,4 +67,102 @@ public final class MetricUtilitiesTest {
MetricUtilities.getHighlyUniqueInteger());
MetricUtilities.logApiCalledInitialPhase(validInitPhaseMetric, 1);
}
+
+ @Test
+ public void logApiCalledTotalCandidate_nullCandidateNegativeSequence_success() {
+ MetricUtilities.logApiCalledAggregateCandidate(null, -1);
+ }
+
+ @Test
+ public void logApiCalledTotalCandidate_invalidCandidatePhasePositiveSequence_success() {
+ MetricUtilities.logApiCalledAggregateCandidate(new CandidateAggregateMetric(-1), 1);
+ }
+
+ @Test
+ public void logApiCalledTotalCandidate_validPhaseMetric_success() {
+ MetricUtilities.logApiCalledAggregateCandidate(
+ new CandidateAggregateMetric(MetricUtilities.getHighlyUniqueInteger()), 1);
+ }
+
+ @Test
+ public void logApiCalledNoUidFinal_nullNoUidFinalNegativeSequenceAndStatus_success() {
+ MetricUtilities.logApiCalledNoUidFinal(null, null,
+ -1, -1);
+ }
+
+ @Test
+ public void logApiCalledNoUidFinal_invalidNoUidFinalPhasePositiveSequenceAndStatus_success() {
+ MetricUtilities.logApiCalledNoUidFinal(new ChosenProviderFinalPhaseMetric(-1, -1),
+ List.of(new CandidateBrowsingPhaseMetric()), 1, 1);
+ }
+
+ @Test
+ public void logApiCalledNoUidFinal_validNoUidFinalMetric_success() {
+ MetricUtilities.logApiCalledNoUidFinal(
+ new ChosenProviderFinalPhaseMetric(MetricUtilities.getHighlyUniqueInteger(),
+ MetricUtilities.getHighlyUniqueInteger()),
+ List.of(new CandidateBrowsingPhaseMetric()), 1, 1);
+ }
+
+ @Test
+ public void logApiCalledCandidate_nullMapNullInitFinalNegativeSequence_success() {
+ MetricUtilities.logApiCalledCandidatePhase(null, -1,
+ null);
+ }
+
+ @Test
+ public void logApiCalledCandidate_invalidProvidersCandidatePositiveSequence_success() {
+ Map<String, ProviderSession> testMap = new HashMap<>();
+ testMap.put("s", null);
+ MetricUtilities.logApiCalledCandidatePhase(testMap, 1,
+ null);
+ }
+
+ @Test
+ public void logApiCalledCandidateGet_nullMapFinalNegativeSequence_success() {
+ MetricUtilities.logApiCalledCandidateGetMetric(null, -1);
+ }
+
+ @Test
+ public void logApiCalledCandidateGet_invalidProvidersCandidatePositiveSequence_success() {
+ Map<String, ProviderSession> testMap = new HashMap<>();
+ testMap.put("s", null);
+ MetricUtilities.logApiCalledCandidateGetMetric(testMap, 1);
+ }
+
+ @Test
+ public void logApiCalledAuthMetric_nullAuthMetricNegativeSequence_success() {
+ MetricUtilities.logApiCalledAuthenticationMetric(null, -1);
+ }
+
+ @Test
+ public void logApiCalledAuthMetric_invalidAuthMetricPositiveSequence_success() {
+ MetricUtilities.logApiCalledAuthenticationMetric(new BrowsedAuthenticationMetric(-1), 1);
+ }
+
+ @Test
+ public void logApiCalledAuthMetric_nullAuthMetricPositiveSequence_success() {
+ MetricUtilities.logApiCalledAuthenticationMetric(
+ new BrowsedAuthenticationMetric(MetricUtilities.getHighlyUniqueInteger()), -1);
+ }
+
+ @Test
+ public void logApiCalledFinal_nullFinalNegativeSequenceAndStatus_success() {
+ MetricUtilities.logApiCalledFinalPhase(null, null,
+ -1, -1);
+ }
+
+ @Test
+ public void logApiCalledFinal_invalidFinalPhasePositiveSequenceAndStatus_success() {
+ MetricUtilities.logApiCalledFinalPhase(new ChosenProviderFinalPhaseMetric(-1, -1),
+ List.of(new CandidateBrowsingPhaseMetric()), 1, 1);
+ }
+
+ @Test
+ public void logApiCalledFinal_validFinalMetric_success() {
+ MetricUtilities.logApiCalledFinalPhase(
+ new ChosenProviderFinalPhaseMetric(MetricUtilities.getHighlyUniqueInteger(),
+ MetricUtilities.getHighlyUniqueInteger()),
+ List.of(new CandidateBrowsingPhaseMetric()), 1, 1);
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index 4890f3e6cbf1..bcb0c6b5c269 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -250,9 +250,22 @@ public class ActivityStartInterceptorTest {
}
@Test
- public void testInterceptQuietProfile() {
- // GIVEN that the user the activity is starting as is currently in quiet mode
+ public void testInterceptQuietProfile_keepProfilesRunningEnabled() {
+ // GIVEN that the user the activity is starting as is currently in quiet mode and
+ // profiles are kept running when in quiet mode.
when(mUserManager.isQuietModeEnabled(eq(UserHandle.of(TEST_USER_ID)))).thenReturn(true);
+ when(mDevicePolicyManager.isKeepProfilesRunningEnabled()).thenReturn(true);
+
+ // THEN calling intercept returns false because package also has to be suspended.
+ assertFalse(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null));
+ }
+
+ @Test
+ public void testInterceptQuietProfile_keepProfilesRunningDisabled() {
+ // GIVEN that the user the activity is starting as is currently in quiet mode and
+ // profiles are stopped when in quiet mode (pre-U behavior, no profile app suspension).
+ when(mUserManager.isQuietModeEnabled(eq(UserHandle.of(TEST_USER_ID)))).thenReturn(true);
+ when(mDevicePolicyManager.isKeepProfilesRunningEnabled()).thenReturn(false);
// THEN calling intercept returns true
assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null));
@@ -263,10 +276,28 @@ public class ActivityStartInterceptorTest {
}
@Test
- public void testInterceptQuietProfileWhenPackageSuspended() {
+ public void testInterceptQuietProfileWhenPackageSuspended_keepProfilesRunningEnabled() {
+ // GIVEN that the user the activity is starting as is currently in quiet mode,
+ // the package is suspended and profiles are kept running while in quiet mode.
+ suspendPackage("com.test.suspending.package");
+ when(mUserManager.isQuietModeEnabled(eq(UserHandle.of(TEST_USER_ID)))).thenReturn(true);
+ when(mDevicePolicyManager.isKeepProfilesRunningEnabled()).thenReturn(true);
+
+ // THEN calling intercept returns true
+ assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null));
+
+ // THEN the returned intent is the quiet mode intent
+ assertTrue(UnlaunchableAppActivity.createInQuietModeDialogIntent(TEST_USER_ID)
+ .filterEquals(mInterceptor.mIntent));
+ }
+
+ @Test
+ public void testInterceptQuietProfileWhenPackageSuspended_keepProfilesRunningDisabled() {
+ // GIVEN that the user the activity is starting as is currently in quiet mode,
+ // the package is suspended and profiles are stopped while in quiet mode.
suspendPackage("com.test.suspending.package");
- // GIVEN that the user the activity is starting as is currently in quiet mode
when(mUserManager.isQuietModeEnabled(eq(UserHandle.of(TEST_USER_ID)))).thenReturn(true);
+ when(mDevicePolicyManager.isKeepProfilesRunningEnabled()).thenReturn(false);
// THEN calling intercept returns true
assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null));
diff --git a/tests/InputMethodStressTest/Android.bp b/tests/InputMethodStressTest/Android.bp
index 27640a5f51bc..84845c69fb27 100644
--- a/tests/InputMethodStressTest/Android.bp
+++ b/tests/InputMethodStressTest/Android.bp
@@ -30,7 +30,6 @@ android_test {
],
test_suites: [
"general-tests",
- "vts",
],
data: [
":SimpleTestIme",