Merge "Reapply "Finalize resources for VIC"" into main
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 31c9a258..18914e1 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -275,13 +275,23 @@
}
flag {
- name: "headless_single_user_bad_device_admin_state_fix"
- namespace: "enterprise"
- description: "Fix the bad state in DPMS caused by an earlier bug related to the headless single user change"
- bug: "332477138"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
+ name: "headless_single_user_bad_device_admin_state_fix"
+ namespace: "enterprise"
+ description: "Fix the bad state in DPMS caused by an earlier bug related to the headless single user change"
+ bug: "332477138"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "onboarding_bugreport_storage_bug_fix"
+ namespace: "enterprise"
+ description: "Add a separate storage limit for deferred bugreports"
+ bug: "330177040"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
flag {
diff --git a/core/java/android/service/dreams/flags.aconfig b/core/java/android/service/dreams/flags.aconfig
index f87cb85..a42eaff 100644
--- a/core/java/android/service/dreams/flags.aconfig
+++ b/core/java/android/service/dreams/flags.aconfig
@@ -29,6 +29,16 @@
}
flag {
+ name: "dismiss_dream_on_keyguard_dismiss"
+ namespace: "systemui"
+ description: "Dismisses the dream in the keyguard-going-away transition, preventing it from being visible"
+ bug: "333829441"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "dream_tracks_focus"
namespace: "communal"
description: "This flag enables the ability for dreams to track whether or not they have focus"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 6834e6d..17121c8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -702,10 +702,12 @@
ShellInit shellInit,
ShellController shellController,
Transitions transitions,
+ TaskStackListenerImpl taskStackListener,
@ShellMainThread Handler mainHandler,
@ShellMainThread ShellExecutor mainExecutor) {
return new KeyguardTransitionHandler(
- shellInit, shellController, transitions, mainHandler, mainExecutor);
+ shellInit, shellController, transitions, taskStackListener, mainHandler,
+ mainExecutor);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
index 863a51a..9eaf7e4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
@@ -20,6 +20,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.service.dreams.Flags.dismissDreamOnKeyguardDismiss;
import static android.view.WindowManager.KEYGUARD_VISIBILITY_TRANSIT_FLAGS;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
@@ -44,10 +45,13 @@
import android.window.IRemoteTransitionFinishedCallback;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
+import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.TaskStackListenerCallback;
+import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.shared.annotations.ExternalThread;
import com.android.wm.shell.sysui.KeyguardChangeListener;
@@ -62,7 +66,8 @@
* <p>This takes the highest priority.
*/
public class KeyguardTransitionHandler
- implements Transitions.TransitionHandler, KeyguardChangeListener {
+ implements Transitions.TransitionHandler, KeyguardChangeListener,
+ TaskStackListenerCallback {
private static final String TAG = "KeyguardTransition";
private final Transitions mTransitions;
@@ -71,6 +76,7 @@
private final ShellExecutor mMainExecutor;
private final ArrayMap<IBinder, StartedTransition> mStartedTransitions = new ArrayMap<>();
+ private final TaskStackListenerImpl mTaskStackListener;
/**
* Local IRemoteTransition implementations registered by the keyguard service.
@@ -87,6 +93,8 @@
// Last value reported by {@link KeyguardChangeListener}.
private boolean mKeyguardShowing = true;
+ @Nullable
+ private WindowContainerToken mDreamToken;
private final class StartedTransition {
final TransitionInfo mInfo;
@@ -105,18 +113,23 @@
@NonNull ShellInit shellInit,
@NonNull ShellController shellController,
@NonNull Transitions transitions,
+ @NonNull TaskStackListenerImpl taskStackListener,
@NonNull Handler mainHandler,
@NonNull ShellExecutor mainExecutor) {
mTransitions = transitions;
mShellController = shellController;
mMainHandler = mainHandler;
mMainExecutor = mainExecutor;
+ mTaskStackListener = taskStackListener;
shellInit.addInitCallback(this::onInit, this);
}
private void onInit() {
mTransitions.addHandler(this);
mShellController.addKeyguardChangeListener(this);
+ if (dismissDreamOnKeyguardDismiss()) {
+ mTaskStackListener.addListener(this);
+ }
}
/**
@@ -142,6 +155,11 @@
}
@Override
+ public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
+ mDreamToken = taskInfo.getActivityType() == ACTIVITY_TYPE_DREAM ? taskInfo.token : null;
+ }
+
+ @Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@@ -271,6 +289,13 @@
@Override
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
@NonNull TransitionRequestInfo request) {
+ if (dismissDreamOnKeyguardDismiss()
+ && (request.getFlags() & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0
+ && mDreamToken != null) {
+ // Dismiss the dream in the same transaction, so that it isn't visible once the device
+ // is unlocked.
+ return new WindowContainerTransaction().removeTask(mDreamToken);
+ }
return null;
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 9e9350b..2e9075c 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -186,9 +186,14 @@
sendBroadcast = sBroadcastOnRestore.contains(name);
sendBroadcastSystemUI = sBroadcastOnRestoreSystemUI.contains(name);
- if (sendBroadcast || sendBroadcastSystemUI) {
+ if (sendBroadcast) {
// TODO: http://b/22388012
oldValue = table.lookup(cr, name, UserHandle.USER_SYSTEM);
+ } else if (sendBroadcastSystemUI) {
+ // This is only done for broadcasts sent to system ui as the consumers are known.
+ // It would probably be correct to do it for the ones sent to the system, but consumers
+ // may be depending on the current behavior.
+ oldValue = table.lookup(cr, name, context.getUserId());
}
try {
@@ -266,7 +271,7 @@
if (sendBroadcastSystemUI) {
intent.setPackage(
context.getString(com.android.internal.R.string.config_systemUi));
- context.sendBroadcastAsUser(intent, UserHandle.SYSTEM, null);
+ context.sendBroadcastAsUser(intent, context.getUser(), null);
}
}
}
diff --git a/packages/Shell/Android.bp b/packages/Shell/Android.bp
index c87916f..2531454 100644
--- a/packages/Shell/Android.bp
+++ b/packages/Shell/Android.bp
@@ -19,6 +19,9 @@
include_dirs: ["frameworks/native/cmds/dumpstate/binder"],
},
static_libs: shell_static_libs,
+ libs: [
+ "device_policy_aconfig_flags_lib",
+ ],
platform_apis: true,
certificate: "platform",
privileged: true,
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 5ac0e44..bcfd8f6 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -16,6 +16,7 @@
package com.android.shell;
+import static android.app.admin.flags.Flags.onboardingBugreportStorageBugFix;
import static android.content.pm.PackageManager.FEATURE_LEANBACK;
import static android.content.pm.PackageManager.FEATURE_TELEVISION;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
@@ -89,10 +90,10 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.google.android.collect.Lists;
-
import libcore.io.Streams;
+import com.google.android.collect.Lists;
+
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
@@ -109,6 +110,8 @@
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
@@ -442,10 +445,14 @@
}
}
- private static void sendRemoteBugreportFinishedBroadcast(Context context,
+ private void sendRemoteBugreportFinishedBroadcast(Context context,
String bugreportFileName, File bugreportFile, long nonce) {
- cleanupOldFiles(REMOTE_BUGREPORT_FILES_AMOUNT, REMOTE_MIN_KEEP_AGE,
- bugreportFile.getParentFile());
+ // Remote bugreports are stored in the same directory as normal bugreports, meaning that
+ // the remote bugreport storage limit will get applied to normal bugreports whenever a
+ // remote bugreport is triggered. The fix in cleanupOldFiles applies the normal bugreport
+ // limit to the remote bugreports as a quick fix.
+ cleanupOldFiles(
+ REMOTE_BUGREPORT_FILES_AMOUNT, REMOTE_MIN_KEEP_AGE, bugreportFile.getParentFile());
final Intent intent = new Intent(DevicePolicyManager.ACTION_REMOTE_BUGREPORT_DISPATCH);
final Uri bugreportUri = getUri(context, bugreportFile);
final String bugreportHash = generateFileHash(bugreportFileName);
@@ -496,12 +503,16 @@
return fileHash;
}
- static void cleanupOldFiles(final int minCount, final long minAge, File bugreportsDir) {
+ void cleanupOldFiles(final int minCount, final long minAge, File bugreportsDir) {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
try {
- FileUtils.deleteOlderFiles(bugreportsDir, minCount, minAge);
+ if (onboardingBugreportStorageBugFix()) {
+ cleanupOldBugreports();
+ } else {
+ FileUtils.deleteOlderFiles(bugreportsDir, minCount, minAge);
+ }
} catch (RuntimeException e) {
Log.e(TAG, "RuntimeException deleting old files", e);
}
@@ -510,6 +521,42 @@
}.execute();
}
+ private void cleanupOldBugreports() {
+ final File[] files = mBugreportsDir.listFiles();
+ if (files == null) return;
+
+ // Sort with newest files first
+ Arrays.sort(files, new Comparator<File>() {
+ @Override
+ public int compare(File lhs, File rhs) {
+ return Long.compare(rhs.lastModified(), lhs.lastModified());
+ }
+ });
+
+ int normalBugreportFilesCount = 0;
+ int deferredBugreportFilesCount = 0;
+ for (int i = 0; i < files.length; i++) {
+ final File file = files[i];
+
+ // tmp files are deferred bugreports which have their separate storage limit
+ boolean isDeferredBugreportFile = file.getName().endsWith(".tmp");
+ if (isDeferredBugreportFile) {
+ deferredBugreportFilesCount++;
+ } else {
+ normalBugreportFilesCount++;
+ }
+ // Keep files newer than minAgeMs
+ final long age = System.currentTimeMillis() - file.lastModified();
+ final int count = isDeferredBugreportFile
+ ? deferredBugreportFilesCount : normalBugreportFilesCount;
+ if (count > MIN_KEEP_COUNT && age > MIN_KEEP_AGE) {
+ if (file.delete()) {
+ Log.d(TAG, "Deleted old file " + file);
+ }
+ }
+ }
+ }
+
/**
* Main thread used to handle all requests but taking screenshots.
*/
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModelTest.kt
new file mode 100644
index 0000000..288dc48
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModelTest.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AlternateBouncerToOccludedTransitionViewModelTest : SysuiTestCase() {
+ val kosmos =
+ testKosmos().apply {
+ fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
+ }
+
+ private val testScope = kosmos.testScope
+ private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+ private val underTest by lazy { kosmos.alternateBouncerToOccludedTransitionViewModel }
+
+ @Test
+ fun deviceEntryParentViewDisappear() =
+ testScope.runTest {
+ val values by collectValues(underTest.deviceEntryParentViewAlpha)
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ listOf(
+ step(0f, TransitionState.STARTED),
+ step(0f),
+ step(0.1f),
+ step(0.2f),
+ step(0.3f),
+ step(1f),
+ ),
+ testScope,
+ )
+
+ values.forEach { assertThat(it).isEqualTo(0f) }
+ }
+
+ private fun step(value: Float, state: TransitionState = RUNNING): TransitionStep {
+ return TransitionStep(
+ from = KeyguardState.ALTERNATE_BOUNCER,
+ to = KeyguardState.OCCLUDED,
+ value = value,
+ transitionState = state,
+ ownerName = "AlternateBouncerToOccludedTransitionViewModelTest"
+ )
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index e160cfc..66f7416 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -155,7 +155,6 @@
mColorExtractor,
mDumpManager,
mKeyguardStateController,
- mKosmos.getScreenOffAnimationController(),
mAuthController,
mKosmos::getShadeInteractor,
mShadeWindowLogger,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index ae163ea..674c128 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.service.dreams.Flags.dismissDreamOnKeyguardDismiss;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_APPEARING;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
@@ -89,14 +90,14 @@
import com.android.wm.shell.shared.TransitionUtil;
import com.android.wm.shell.transition.Transitions;
-import kotlinx.coroutines.CoroutineScope;
-
import java.util.ArrayList;
import java.util.Map;
import java.util.WeakHashMap;
import javax.inject.Inject;
+import kotlinx.coroutines.CoroutineScope;
+
public class KeyguardService extends Service {
static final String TAG = "KeyguardService";
static final String PERMISSION = android.Manifest.permission.CONTROL_KEYGUARD;
@@ -223,6 +224,20 @@
initAlphaForAnimationTargets(t, apps);
initAlphaForAnimationTargets(t, wallpapers);
+ // If the keyguard is going away, hide the dream if one exists.
+ if (dismissDreamOnKeyguardDismiss()
+ && (info.getFlags() & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0) {
+ for (RemoteAnimationTarget app : apps) {
+ final boolean isDream = app.taskInfo != null
+ && app.taskInfo.getActivityType()
+ == WindowConfiguration.ACTIVITY_TYPE_DREAM;
+ if (isDream && app.mode == RemoteAnimationTarget.MODE_CLOSING) {
+ t.hide(app.leash);
+ break;
+ }
+ }
+ }
+
t.apply();
runner.onAnimationStart(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 46aec25..5d31d1e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -21,6 +21,7 @@
import static android.provider.Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT;
import static android.provider.Settings.System.LOCKSCREEN_SOUNDS_ENABLED;
import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
+import static android.service.dreams.Flags.dismissDreamOnKeyguardDismiss;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT;
@@ -3092,13 +3093,22 @@
createInteractionJankMonitorConf(
CUJ_LOCKSCREEN_UNLOCK_ANIMATION, "DismissPanel"));
+ // Filter out any closing apps, such as the dream.
+ RemoteAnimationTarget[] openingApps = apps;
+ if (dismissDreamOnKeyguardDismiss()) {
+ openingApps = Arrays.stream(apps)
+ .filter(a -> a.mode == RemoteAnimationTarget.MODE_OPENING)
+ .toArray(RemoteAnimationTarget[]::new);
+ }
+
// Pass the surface and metadata to the unlock animation controller.
RemoteAnimationTarget[] openingWallpapers = Arrays.stream(wallpapers).filter(
w -> w.mode == RemoteAnimationTarget.MODE_OPENING).toArray(
RemoteAnimationTarget[]::new);
+
mKeyguardUnlockAnimationControllerLazy.get()
.notifyStartSurfaceBehindRemoteAnimation(
- apps, openingWallpapers, startTime,
+ openingApps, openingWallpapers, startTime,
mSurfaceBehindRemoteAnimationRequested);
} else {
mInteractionJankMonitor.begin(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
index e441017..5a28f711 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
@@ -193,5 +193,6 @@
val TO_AOD_DURATION = TRANSITION_DURATION_MS
val TO_PRIMARY_BOUNCER_DURATION = TRANSITION_DURATION_MS
val TO_DOZING_DURATION = TRANSITION_DURATION_MS
+ val TO_OCCLUDED_DURATION = TRANSITION_DURATION_MS
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
index 3b21141..a8e9041 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
@@ -18,6 +18,7 @@
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToAodTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToDozingTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToGoneTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToOccludedTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToPrimaryBouncerTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.AodToGoneTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.AodToLockscreenTransitionViewModel
@@ -71,6 +72,12 @@
@Binds
@IntoSet
+ abstract fun alternateBouncerToOccluded(
+ impl: AlternateBouncerToOccludedTransitionViewModel
+ ): DeviceEntryIconTransition
+
+ @Binds
+ @IntoSet
abstract fun alternateBouncerToPrimaryBouncer(
impl: AlternateBouncerToPrimaryBouncerTransitionViewModel
): DeviceEntryIconTransition
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModel.kt
new file mode 100644
index 0000000..27febd3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModel.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor.Companion.TO_OCCLUDED_DURATION
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Breaks down ALTERNATE_BOUNCER->GONE transition into discrete steps for corresponding views to
+ * consume.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class AlternateBouncerToOccludedTransitionViewModel
+@Inject
+constructor(
+ animationFlow: KeyguardTransitionAnimationFlow,
+) : DeviceEntryIconTransition {
+ private val transitionAnimation =
+ animationFlow.setup(
+ duration = TO_OCCLUDED_DURATION,
+ from = ALTERNATE_BOUNCER,
+ to = KeyguardState.OCCLUDED,
+ )
+
+ override val deviceEntryParentViewAlpha: Flow<Float> =
+ transitionAnimation.immediatelyTransitionTo(0f)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
index 30f33a3..f8086f5 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -21,19 +21,20 @@
import android.graphics.Paint
import android.graphics.Point
import android.os.Handler
-import android.os.SystemClock
import android.util.Log
import android.util.MathUtils
import android.view.Gravity
import android.view.HapticFeedbackConstants
import android.view.MotionEvent
import android.view.VelocityTracker
+import android.view.View
import android.view.ViewConfiguration
import android.view.WindowManager
import androidx.annotation.VisibleForTesting
import androidx.core.os.postDelayed
import androidx.core.view.isVisible
import androidx.dynamicanimation.animation.DynamicAnimation
+import com.android.internal.jank.Cuj
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.util.LatencyTracker
import com.android.systemui.dagger.qualifiers.Main
@@ -41,6 +42,7 @@
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.ViewController
+import com.android.systemui.util.time.SystemClock
import java.io.PrintWriter
import javax.inject.Inject
import kotlin.math.abs
@@ -84,6 +86,7 @@
private val windowManager: WindowManager,
private val viewConfiguration: ViewConfiguration,
@Main private val mainHandler: Handler,
+ private val systemClock: SystemClock,
private val vibratorHelper: VibratorHelper,
private val configurationController: ConfigurationController,
private val latencyTracker: LatencyTracker,
@@ -102,6 +105,7 @@
private val windowManager: WindowManager,
private val viewConfiguration: ViewConfiguration,
@Main private val mainHandler: Handler,
+ private val systemClock: SystemClock,
private val vibratorHelper: VibratorHelper,
private val configurationController: ConfigurationController,
private val latencyTracker: LatencyTracker,
@@ -115,6 +119,7 @@
windowManager,
viewConfiguration,
mainHandler,
+ systemClock,
vibratorHelper,
configurationController,
latencyTracker,
@@ -158,9 +163,9 @@
private var gestureInactiveTime = 0L
private val elapsedTimeSinceInactive
- get() = SystemClock.uptimeMillis() - gestureInactiveTime
+ get() = systemClock.uptimeMillis() - gestureInactiveTime
private val elapsedTimeSinceEntry
- get() = SystemClock.uptimeMillis() - gestureEntryTime
+ get() = systemClock.uptimeMillis() - gestureEntryTime
private var pastThresholdWhileEntryOrInactiveTime = 0L
private var entryToActiveDelay = 0F
@@ -178,7 +183,7 @@
// Distance in pixels a drag can be considered for a fling event
private var minFlingDistance = 0
- private val failsafeRunnable = Runnable { onFailsafe() }
+ internal val failsafeRunnable = Runnable { onFailsafe() }
internal enum class GestureState {
/* Arrow is off the screen and invisible */
@@ -370,6 +375,7 @@
// Receiving a CANCEL implies that something else intercepted
// the gesture, i.e., the user did not cancel their gesture.
// Therefore, disappear immediately, with minimum fanfare.
+ interactionJankMonitor.cancel(Cuj.CUJ_BACK_PANEL_ARROW)
updateArrowState(GestureState.GONE)
velocityTracker = null
}
@@ -692,10 +698,10 @@
}
if (isPastThresholdForFirstTime) {
- pastThresholdWhileEntryOrInactiveTime = SystemClock.uptimeMillis()
+ pastThresholdWhileEntryOrInactiveTime = systemClock.uptimeMillis()
entryToActiveDelay = dynamicDelay()
}
- val timePastThreshold = SystemClock.uptimeMillis() - pastThresholdWhileEntryOrInactiveTime
+ val timePastThreshold = systemClock.uptimeMillis() - pastThresholdWhileEntryOrInactiveTime
return timePastThreshold > entryToActiveDelay
}
@@ -881,6 +887,16 @@
previousState = currentState
currentState = newState
+ // First, update the jank tracker
+ when (currentState) {
+ GestureState.ENTRY -> {
+ interactionJankMonitor.cancel(Cuj.CUJ_BACK_PANEL_ARROW)
+ interactionJankMonitor.begin(mView, Cuj.CUJ_BACK_PANEL_ARROW)
+ }
+ GestureState.GONE -> interactionJankMonitor.end(Cuj.CUJ_BACK_PANEL_ARROW)
+ else -> {}
+ }
+
when (currentState) {
GestureState.CANCELLED -> {
backCallback.cancelBack()
@@ -912,7 +928,7 @@
mView.isVisible = true
updateRestingArrowDimens()
- gestureEntryTime = SystemClock.uptimeMillis()
+ gestureEntryTime = systemClock.uptimeMillis()
}
GestureState.ACTIVE -> {
previousXTranslationOnActiveOffset = previousXTranslation
@@ -927,7 +943,7 @@
mView.popOffEdge(popVelocity)
}
GestureState.INACTIVE -> {
- gestureInactiveTime = SystemClock.uptimeMillis()
+ gestureInactiveTime = systemClock.uptimeMillis()
// Typically entering INACTIVE means
// totalTouchDelta <= deactivationSwipeTriggerThreshold
@@ -1041,6 +1057,11 @@
pw.println(" isLeftPanel=${mView.isLeftPanel}")
}
+ @VisibleForTesting
+ internal fun getBackPanelView(): BackPanel {
+ return mView
+ }
+
init {
if (DEBUG)
mView.drawDebugInfo = { canvas ->
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index 8b7e11c..4a636d2 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -68,7 +68,6 @@
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -131,7 +130,6 @@
mCallbacks = new ArrayList<>();
private final SysuiColorExtractor mColorExtractor;
- private final ScreenOffAnimationController mScreenOffAnimationController;
/**
* Layout params would be aggregated and dispatched all at once if this is > 0.
*
@@ -159,7 +157,6 @@
SysuiColorExtractor colorExtractor,
DumpManager dumpManager,
KeyguardStateController keyguardStateController,
- ScreenOffAnimationController screenOffAnimationController,
AuthController authController,
Lazy<ShadeInteractor> shadeInteractorLazy,
ShadeWindowLogger logger,
@@ -179,7 +176,6 @@
mKeyguardBypassController = keyguardBypassController;
mBackgroundExecutor = backgroundExecutor;
mColorExtractor = colorExtractor;
- mScreenOffAnimationController = screenOffAnimationController;
// prefix with {slow} to make sure this dumps at the END of the critical section.
dumpManager.registerCriticalDumpable("{slow}NotificationShadeWindowControllerImpl", this);
mAuthController = authController;
@@ -441,14 +437,8 @@
private void applyFocusableFlag(NotificationShadeWindowState state) {
boolean panelFocusable = state.notificationShadeFocusable && state.shadeOrQsExpanded;
if (state.bouncerShowing && (state.keyguardOccluded || state.keyguardNeedsInput)
- || ENABLE_REMOTE_INPUT && state.remoteInputActive
- // Make the panel focusable if we're doing the screen off animation, since the light
- // reveal scrim is drawing in the panel and should consume touch events so that they
- // don't go to the app behind.
- || mScreenOffAnimationController.shouldIgnoreKeyguardTouches()) {
- mLpChanged.flags &= ~LayoutParams.FLAG_NOT_FOCUSABLE;
- mLpChanged.flags &= ~LayoutParams.FLAG_ALT_FOCUSABLE_IM;
- } else if (state.glanceableHubShowing) {
+ || (ENABLE_REMOTE_INPUT && state.remoteInputActive)
+ || state.glanceableHubShowing) {
mLpChanged.flags &= ~LayoutParams.FLAG_NOT_FOCUSABLE;
mLpChanged.flags &= ~LayoutParams.FLAG_ALT_FOCUSABLE_IM;
} else if (state.isKeyguardShowingAndNotOccluded() || panelFocusable) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 59f7d61..325e7bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -262,7 +262,6 @@
mColorExtractor,
mDumpManager,
mKeyguardStateController,
- mScreenOffAnimationController,
mAuthController,
() -> mShadeInteractor,
mShadeWindowLogger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
index e6c259a..f1c97dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
@@ -17,7 +17,6 @@
package com.android.systemui.navigationbar.gestural
import android.os.Handler
-import android.os.Looper
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.HapticFeedbackConstants
@@ -28,6 +27,7 @@
import android.view.ViewConfiguration
import android.view.WindowManager
import androidx.test.filters.SmallTest
+import com.android.internal.jank.Cuj
import com.android.internal.util.LatencyTracker
import com.android.systemui.SysuiTestCase
import com.android.systemui.jank.interactionJankMonitor
@@ -35,6 +35,7 @@
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.testKosmos
+import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -43,6 +44,7 @@
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -55,6 +57,7 @@
}
private val kosmos = testKosmos()
private lateinit var mBackPanelController: BackPanelController
+ private lateinit var systemClock: FakeSystemClock
private lateinit var testableLooper: TestableLooper
private var triggerThreshold: Float = 0.0f
private val touchSlop = ViewConfiguration.get(context).scaledEdgeSlop
@@ -69,12 +72,15 @@
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
+ testableLooper = TestableLooper.get(this)
+ systemClock = FakeSystemClock()
mBackPanelController =
BackPanelController(
context,
windowManager,
ViewConfiguration.get(context),
- Handler.createAsync(checkNotNull(Looper.myLooper())),
+ Handler.createAsync(testableLooper.looper),
+ systemClock,
vibratorHelper,
configurationController,
latencyTracker,
@@ -83,7 +89,6 @@
mBackPanelController.setLayoutParams(layoutParams)
mBackPanelController.setBackCallback(backCallback)
mBackPanelController.setIsLeftPanel(true)
- testableLooper = TestableLooper.get(this)
triggerThreshold = mBackPanelController.params.staticTriggerThreshold
}
@@ -103,6 +108,7 @@
assertThat(mBackPanelController.currentState)
.isEqualTo(BackPanelController.GestureState.GONE)
+ verify(interactionJankMonitor, never()).begin(any())
}
@Test
@@ -110,23 +116,37 @@
startTouch()
// Move once to cross the touch slop
continueTouch(START_X + touchSlop.toFloat() + 1)
+ assertThat(mBackPanelController.currentState)
+ .isEqualTo(BackPanelController.GestureState.ENTRY)
+ verify(interactionJankMonitor).cancel(Cuj.CUJ_BACK_PANEL_ARROW)
+ verify(interactionJankMonitor)
+ .begin(mBackPanelController.getBackPanelView(), Cuj.CUJ_BACK_PANEL_ARROW)
// Move again to cross the back trigger threshold
continueTouch(START_X + touchSlop + triggerThreshold + 1)
// Wait threshold duration and hold touch past trigger threshold
- Thread.sleep((MAX_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION + 1).toLong())
+ moveTimeForward((MAX_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION + 1).toLong())
continueTouch(START_X + touchSlop + triggerThreshold + 1)
assertThat(mBackPanelController.currentState)
.isEqualTo(BackPanelController.GestureState.ACTIVE)
verify(backCallback).setTriggerBack(true)
- testableLooper.moveTimeForward(100)
- testableLooper.processAllMessages()
+ moveTimeForward(100)
verify(vibratorHelper)
.performHapticFeedback(any(), eq(HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE))
finishTouchActionUp(START_X + touchSlop + triggerThreshold + 1)
assertThat(mBackPanelController.currentState)
.isEqualTo(BackPanelController.GestureState.COMMITTED)
verify(backCallback).triggerBack()
+
+ // Because the Handler that is typically used for transitioning the arrow state from
+ // COMMITTED to GONE is used as an animation-end-listener on a SpringAnimation,
+ // there is no way to meaningfully test that the state becomes GONE and that the tracked
+ // jank interaction is ended. So instead, manually trigger the failsafe, which does
+ // the same thing:
+ mBackPanelController.failsafeRunnable.run()
+ assertThat(mBackPanelController.currentState)
+ .isEqualTo(BackPanelController.GestureState.GONE)
+ verify(interactionJankMonitor).end(Cuj.CUJ_BACK_PANEL_ARROW)
}
@Test
@@ -134,19 +154,22 @@
startTouch()
// Move once to cross the touch slop
continueTouch(START_X + touchSlop.toFloat() + 1)
+ assertThat(mBackPanelController.currentState)
+ .isEqualTo(BackPanelController.GestureState.ENTRY)
// Move again to cross the back trigger threshold
continueTouch(
START_X + touchSlop + triggerThreshold -
mBackPanelController.params.deactivationTriggerThreshold
)
// Wait threshold duration and hold touch before trigger threshold
- Thread.sleep((MAX_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION + 1).toLong())
+ moveTimeForward((MAX_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION + 1).toLong())
continueTouch(
START_X + touchSlop + triggerThreshold -
mBackPanelController.params.deactivationTriggerThreshold
)
clearInvocations(backCallback)
- Thread.sleep(MIN_DURATION_ACTIVE_BEFORE_INACTIVE_ANIMATION)
+ moveTimeForward(MIN_DURATION_ACTIVE_BEFORE_INACTIVE_ANIMATION)
+
// Move in the opposite direction to cross the deactivation threshold and cancel back
continueTouch(START_X)
@@ -175,4 +198,10 @@
private fun createMotionEvent(action: Int, x: Float, y: Float): MotionEvent {
return MotionEvent.obtain(0L, 0L, action, x, y, 0)
}
+
+ private fun moveTimeForward(millis: Long) {
+ systemClock.advanceTime(millis)
+ testableLooper.moveTimeForward(millis)
+ testableLooper.processAllMessages()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index db998f3..2017954 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -338,8 +338,6 @@
@Mock
private KeyguardStateController mKeyguardStateController;
@Mock
- private ScreenOffAnimationController mScreenOffAnimationController;
- @Mock
Transitions mTransitions;
@Mock
private Optional<OneHandedController> mOneHandedOptional;
@@ -491,7 +489,6 @@
mColorExtractor,
mDumpManager,
mKeyguardStateController,
- mScreenOffAnimationController,
mAuthController,
() -> mShadeInteractor,
mShadeWindowLogger,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModelKosmos.kt
new file mode 100644
index 0000000..71ad3c6
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModelKosmos.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+val Kosmos.alternateBouncerToOccludedTransitionViewModel by Fixture {
+ AlternateBouncerToOccludedTransitionViewModel(
+ animationFlow = keyguardTransitionAnimationFlow,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index d4b7937..a81ac03 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -56,7 +56,6 @@
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shade.shadeController
import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
-import com.android.systemui.statusbar.phone.screenOffAnimationController
import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
import com.android.systemui.statusbar.policy.data.repository.fakeDeviceProvisioningRepository
import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
@@ -90,7 +89,6 @@
val simBouncerInteractor by lazy { kosmos.simBouncerInteractor }
val statusBarStateController by lazy { kosmos.statusBarStateController }
val interactionJankMonitor by lazy { kosmos.interactionJankMonitor }
- val screenOffAnimationController by lazy { kosmos.screenOffAnimationController }
val fakeSceneContainerConfig by lazy { kosmos.sceneContainerConfig }
val sceneInteractor by lazy { kosmos.sceneInteractor }
val falsingCollector by lazy { kosmos.falsingCollector }
diff --git a/services/core/java/com/android/server/audio/AudioManagerShellCommand.java b/services/core/java/com/android/server/audio/AudioManagerShellCommand.java
index e330ed5..d669c8d 100644
--- a/services/core/java/com/android/server/audio/AudioManagerShellCommand.java
+++ b/services/core/java/com/android/server/audio/AudioManagerShellCommand.java
@@ -22,7 +22,10 @@
import static android.media.AudioManager.ADJUST_UNMUTE;
import android.content.Context;
+import android.media.AudioDeviceAttributes;
+import android.media.AudioDeviceVolumeManager;
import android.media.AudioManager;
+import android.media.VolumeInfo;
import android.os.ShellCommand;
import java.io.PrintWriter;
@@ -60,6 +63,8 @@
return resetSoundDoseTimeout();
case "set-volume":
return setVolume();
+ case "set-device-volume":
+ return setDeviceVolume();
case "adj-mute":
return adjMute();
case "adj-unmute":
@@ -97,6 +102,8 @@
pw.println(" Resets the sound dose timeout used for momentary exposure");
pw.println(" set-volume STREAM_TYPE VOLUME_INDEX");
pw.println(" Sets the volume for STREAM_TYPE to VOLUME_INDEX");
+ pw.println(" set-device-volume STREAM_TYPE VOLUME_INDEX NATIVE_DEVICE_TYPE");
+ pw.println(" Sets for NATIVE_DEVICE_TYPE the STREAM_TYPE volume to VOLUME_INDEX");
pw.println(" adj-mute STREAM_TYPE");
pw.println(" mutes the STREAM_TYPE");
pw.println(" adj-unmute STREAM_TYPE");
@@ -257,6 +264,23 @@
return 0;
}
+ private int setDeviceVolume() {
+ final Context context = mService.mContext;
+ final AudioDeviceVolumeManager advm = (AudioDeviceVolumeManager) context.getSystemService(
+ Context.AUDIO_DEVICE_VOLUME_SERVICE);
+ final int stream = readIntArg();
+ final int index = readIntArg();
+ final int device = readIntArg();
+
+ final VolumeInfo volume = new VolumeInfo.Builder(stream).setVolumeIndex(index).build();
+ final AudioDeviceAttributes ada = new AudioDeviceAttributes(
+ /*native type*/ device, /*address*/ "foo");
+ getOutPrintWriter().println(
+ "calling AudioDeviceVolumeManager.setDeviceVolume(" + volume + ", " + ada + ")");
+ advm.setDeviceVolume(volume, ada);
+ return 0;
+ }
+
private int adjMute() {
final Context context = mService.mContext;
final AudioManager am = context.getSystemService(AudioManager.class);
diff --git a/services/core/java/com/android/server/location/gnss/GnssConfiguration.java b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java
index a5939e9..a439f16 100644
--- a/services/core/java/com/android/server/location/gnss/GnssConfiguration.java
+++ b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java
@@ -386,7 +386,7 @@
configs = CarrierConfigManager.getDefaultConfig();
}
for (String configKey : configs.keySet()) {
- if (configKey.startsWith(CarrierConfigManager.Gps.KEY_PREFIX)) {
+ if (configKey != null && configKey.startsWith(CarrierConfigManager.Gps.KEY_PREFIX)) {
String key = configKey
.substring(CarrierConfigManager.Gps.KEY_PREFIX.length())
.toUpperCase(Locale.ROOT);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index bff3d39..9d4ab11 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -266,7 +266,6 @@
import android.os.UserManager;
import android.os.WorkSource;
import android.permission.PermissionManager;
-import android.provider.DeviceConfig;
import android.provider.Settings;
import android.provider.Settings.Secure;
import android.service.notification.Adjustment;
@@ -313,7 +312,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.compat.IPlatformCompat;
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags;
import com.android.internal.logging.InstanceId;
@@ -704,7 +702,6 @@
private ConditionProviders mConditionProviders;
private NotificationUsageStats mUsageStats;
private boolean mLockScreenAllowSecureNotifications = true;
- boolean mSystemExemptFromDismissal = false;
final ArrayMap<String, ArrayMap<Integer,
RemoteCallbackList<ICallNotificationEventCallback>>>
mCallNotificationEventCallbacks = new ArrayMap<>();
@@ -722,7 +719,6 @@
private GroupHelper mGroupHelper;
private int mAutoGroupAtCount;
private boolean mIsTelevision;
- private DeviceConfig.OnPropertiesChangedListener mDeviceConfigChangedListener;
protected NotificationAttentionHelper mAttentionHelper;
private int mWarnRemoteViewsSizeBytes;
@@ -973,18 +969,6 @@
}
protected void setDefaultAssistantForUser(int userId) {
- String overrideDefaultAssistantString = DeviceConfig.getProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE);
- if (overrideDefaultAssistantString != null) {
- ArraySet<ComponentName> approved = mAssistants.queryPackageForServices(
- overrideDefaultAssistantString,
- MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
- userId);
- for (int i = 0; i < approved.size(); i++) {
- if (allowAssistant(userId, approved.valueAt(i))) return;
- }
- }
ArraySet<ComponentName> defaults = mAssistants.getDefaultComponents();
// We should have only one default assistant by default
// allowAssistant should execute once in practice
@@ -2670,10 +2654,6 @@
mStatsManager.clearPullAtomCallback(DND_MODE_RULE);
mAppOps.stopWatchingMode(mAppOpsListener);
mAlarmManager.cancelAll();
-
- if (mDeviceConfigChangedListener != null) {
- DeviceConfig.removeOnPropertiesChangedListener(mDeviceConfigChangedListener);
- }
}
protected String[] getStringArrayResource(int key) {
@@ -2744,27 +2724,6 @@
publishLocalService(NotificationManagerInternal.class, mInternalService);
}
- void registerDeviceConfigChange() {
- mDeviceConfigChangedListener = properties -> {
- if (!DeviceConfig.NAMESPACE_SYSTEMUI.equals(properties.getNamespace())) {
- return;
- }
- for (String name : properties.getKeyset()) {
- if (SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE.equals(name)) {
- mAssistants.resetDefaultAssistantsIfNecessary();
- }
- }
- };
- mSystemExemptFromDismissal = DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER,
- /* name= */ "application_exemptions",
- /* defaultValue= */ true);
- DeviceConfig.addOnPropertiesChangedListener(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- new HandlerExecutor(mHandler),
- mDeviceConfigChangedListener);
- }
-
private void registerNotificationPreferencesPullers() {
mPullAtomCallback = new StatsPullAtomCallbackImpl();
mStatsManager.setPullAtomCallback(
@@ -2938,7 +2897,6 @@
mAssistants.onBootPhaseAppsCanStart();
mConditionProviders.onBootPhaseAppsCanStart();
mHistoryManager.onBootPhaseAppsCanStart();
- registerDeviceConfigChange();
migrateDefaultNAS();
maybeShowInitialReviewPermissionsNotification();
@@ -7738,7 +7696,7 @@
return true;
}
// Check if an app has been given system exemption
- return mSystemExemptFromDismissal && mAppOps.checkOpNoThrow(
+ return mAppOps.checkOpNoThrow(
AppOpsManager.OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, ai.uid,
ai.packageName) == MODE_ALLOWED;
}
diff --git a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
index f04b4af..8069a93 100644
--- a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
@@ -48,8 +48,15 @@
/**
* Flag to indicate whether to restrict desktop mode to supported devices.
*/
+ @VisibleForTesting
+ static final String ENFORCE_DEVICE_RESTRICTIONS_KEY =
+ "persist.wm.debug.desktop_mode_enforce_device_restrictions";
+
+ /**
+ * Flag to indicate whether to restrict desktop mode to supported devices.
+ */
private static final boolean ENFORCE_DEVICE_RESTRICTIONS = SystemProperties.getBoolean(
- "persist.wm.debug.desktop_mode_enforce_device_restrictions", true);
+ ENFORCE_DEVICE_RESTRICTIONS_KEY, true);
private StringBuilder mLogBuilder;
@@ -178,24 +185,24 @@
* Return {@code true} if desktop mode should be restricted to supported devices.
*/
@VisibleForTesting
- public boolean enforceDeviceRestrictions() {
+ static boolean enforceDeviceRestrictions() {
return ENFORCE_DEVICE_RESTRICTIONS;
}
/**
* Return {@code true} if the current device supports desktop mode.
*/
+ // TODO(b/337819319): use a companion object instead.
@VisibleForTesting
- public boolean isDesktopModeSupported(@NonNull Context context) {
+ static boolean isDesktopModeSupported(@NonNull Context context) {
return context.getResources().getBoolean(R.bool.config_isDesktopModeSupported);
}
/**
* Return {@code true} if desktop mode can be entered on the current device.
*/
- boolean canEnterDesktopMode(@NonNull Context context) {
+ static boolean canEnterDesktopMode(@NonNull Context context) {
return isDesktopModeEnabled()
&& (!enforceDeviceRestrictions() || isDesktopModeSupported(context));
}
-
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 6458eac..d555f1a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -666,15 +666,6 @@
DELEGATION_CERT_SELECTION,
});
- /**
- * System property whose value indicates whether the device is fully owned by an organization:
- * it can be either a device owner device, or a device with an organization-owned managed
- * profile.
- *
- * <p>The state is stored as a Boolean string.
- */
- private static final String PROPERTY_ORGANIZATION_OWNED = "ro.organization_owned";
-
private static final int STATUS_BAR_DISABLE_MASK =
StatusBarManager.DISABLE_EXPAND |
StatusBarManager.DISABLE_NOTIFICATION_ICONS |
@@ -2356,7 +2347,6 @@
void loadOwners() {
synchronized (getLockObject()) {
mOwners.load();
- setDeviceOwnershipSystemPropertyLocked();
if (mOwners.hasDeviceOwner()) {
setGlobalSettingDeviceOwnerType(
mOwners.getDeviceOwnerType(mOwners.getDeviceOwnerPackageName()));
@@ -2720,27 +2710,6 @@
+ defaultRestrictions);
}
- private void setDeviceOwnershipSystemPropertyLocked() {
- final boolean deviceProvisioned =
- mInjector.settingsGlobalGetInt(Settings.Global.DEVICE_PROVISIONED, 0) != 0;
- final boolean hasDeviceOwner = mOwners.hasDeviceOwner();
- final boolean hasOrgOwnedProfile = isOrganizationOwnedDeviceWithManagedProfile();
- // If the device is not provisioned and there is currently no management, do not set the
- // read-only system property yet, since device owner / org-owned profile may still be
- // provisioned.
- if (!hasDeviceOwner && !hasOrgOwnedProfile && !deviceProvisioned) {
- return;
- }
- final String value = Boolean.toString(hasDeviceOwner || hasOrgOwnedProfile);
- final String currentVal = mInjector.systemPropertiesGet(PROPERTY_ORGANIZATION_OWNED, null);
- if (TextUtils.isEmpty(currentVal)) {
- Slogf.i(LOG_TAG, "Set ro.organization_owned property to " + value);
- mInjector.systemPropertiesSet(PROPERTY_ORGANIZATION_OWNED, value);
- } else if (!value.equals(currentVal)) {
- Slogf.w(LOG_TAG, "Cannot change existing ro.organization_owned to " + value);
- }
- }
-
private void maybeStartSecurityLogMonitorOnActivityManagerReady() {
if (!mInjector.securityLogIsLoggingEnabled()) {
return;
@@ -9447,7 +9416,6 @@
mOwners.setDeviceOwner(admin, userId);
mOwners.writeDeviceOwner();
- setDeviceOwnershipSystemPropertyLocked();
//TODO(b/180371154): when provisionFullyManagedDevice is used in tests, remove this
// hard-coded default value setting.
@@ -15303,8 +15271,6 @@
private class SetupContentObserver extends ContentObserver {
private final Uri mUserSetupComplete = Settings.Secure.getUriFor(
Settings.Secure.USER_SETUP_COMPLETE);
- private final Uri mDeviceProvisioned = Settings.Global.getUriFor(
- Settings.Global.DEVICE_PROVISIONED);
private final Uri mPaired = Settings.Secure.getUriFor(Settings.Secure.DEVICE_PAIRED);
private final Uri mDefaultImeChanged = Settings.Secure.getUriFor(
Settings.Secure.DEFAULT_INPUT_METHOD);
@@ -15318,7 +15284,6 @@
void register() {
mInjector.registerContentObserver(mUserSetupComplete, false, this, UserHandle.USER_ALL);
- mInjector.registerContentObserver(mDeviceProvisioned, false, this, UserHandle.USER_ALL);
if (mIsWatch) {
mInjector.registerContentObserver(mPaired, false, this, UserHandle.USER_ALL);
}
@@ -15334,12 +15299,6 @@
public void onChange(boolean selfChange, Uri uri, int userId) {
if (mUserSetupComplete.equals(uri) || (mIsWatch && mPaired.equals(uri))) {
updateUserSetupCompleteAndPaired();
- } else if (mDeviceProvisioned.equals(uri)) {
- synchronized (getLockObject()) {
- // Set PROPERTY_DEVICE_OWNER_PRESENT, for the SUW case where setting the property
- // is delayed until device is marked as provisioned.
- setDeviceOwnershipSystemPropertyLocked();
- }
} else if (mDefaultImeChanged.equals(uri)) {
synchronized (getLockObject()) {
if (mUserIdsWithPendingChangesByOwner.contains(userId)) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
index a0d9be54..eeb4976 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
@@ -16,6 +16,9 @@
package com.android.server.devicepolicy;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -185,6 +188,9 @@
}
}
+ // TODO: when a local policy exists for a user, this callback will be invoked for this user
+ // individually as well as for USER_ALL. This can be optimized by separating local and global
+ // enforcement in the policy engine.
static boolean setUserControlDisabledPackages(
@Nullable Set<String> packages, Context context, int userId, PolicyKey policyKey) {
Binder.withCleanCallingIdentity(() -> {
@@ -201,20 +207,35 @@
return;
}
final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
- for (var pkg : packages) {
- final var appInfo = pmi.getApplicationInfo(pkg,
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- Process.myUid(), userId);
- if (appInfo != null) {
- DevicePolicyManagerService.setBgUsageAppOp(appOpsManager, appInfo);
- }
- }
+ resolveUsers(userId).forEach(
+ user -> setBgUsageAppOp(packages, pmi, user, appOpsManager));
}
});
return true;
}
+ /** Handles USER_ALL expanding it into the list of all intact users. */
+ private static List<Integer> resolveUsers(int userId) {
+ if (userId == UserHandle.USER_ALL) {
+ UserManagerInternal userManager = LocalServices.getService(UserManagerInternal.class);
+ return userManager.getUsers(/* excludeDying= */ true)
+ .stream().map(ui -> ui.id).toList();
+ } else {
+ return List.of(userId);
+ }
+ }
+
+ private static void setBgUsageAppOp(Set<String> packages, PackageManagerInternal pmi,
+ int userId, AppOpsManager appOpsManager) {
+ for (var pkg : packages) {
+ int packageFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
+ final var appInfo = pmi.getApplicationInfo(pkg, packageFlags, Process.myUid(), userId);
+ if (appInfo != null) {
+ DevicePolicyManagerService.setBgUsageAppOp(appOpsManager, appInfo);
+ }
+ }
+ }
+
static boolean addPersistentPreferredActivity(
@Nullable ComponentName preferredActivity, @NonNull Context context, int userId,
@NonNull PolicyKey policyKey) {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index ce7a0a0..74d8433 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -243,7 +243,6 @@
import android.platform.test.flag.junit.FlagsParameterization;
import android.platform.test.flag.junit.SetFlagsRule;
import android.platform.test.rule.LimitDevicesRule;
-import android.provider.DeviceConfig;
import android.provider.MediaStore;
import android.provider.Settings;
import android.service.notification.Adjustment;
@@ -280,7 +279,6 @@
import androidx.test.filters.SmallTest;
import com.android.internal.R;
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.config.sysui.TestableFlagResolver;
import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.logging.InstanceIdSequenceFake;
@@ -602,7 +600,9 @@
when(mContext.getContentResolver()).thenReturn(cr);
doNothing().when(cr).registerContentObserver(any(), anyBoolean(), any(), anyInt());
- setDpmAppOppsExemptFromDismissal(false);
+ when(mAppOpsManager.checkOpNoThrow(
+ AppOpsManager.OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, mUid,
+ mPkg)).thenReturn(AppOpsManager.MODE_IGNORED);
// Use this testable looper.
mTestableLooper = TestableLooper.get(this);
@@ -900,7 +900,6 @@
@After
public void tearDown() throws Exception {
if (mFile != null) mFile.delete();
- clearDeviceConfig();
if (mActivityIntent != null) {
mActivityIntent.cancel();
@@ -1200,19 +1199,6 @@
return answers;
}
- private void clearDeviceConfig() {
- DeviceConfig.resetToDefaults(
- Settings.RESET_MODE_PACKAGE_DEFAULTS, DeviceConfig.NAMESPACE_SYSTEMUI);
- }
-
- private void setDefaultAssistantInDeviceConfig(String componentName) {
- DeviceConfig.setProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE,
- componentName,
- false);
- }
-
private Notification.Builder getMessageStyleNotifBuilder(boolean addBubbleMetadata,
String groupKey, boolean isSummary, boolean mutable) {
// Give it a person
@@ -9092,7 +9078,6 @@
@Test
public void setDefaultAssistantForUser_fromConfigXml() {
- clearDeviceConfig();
ComponentName xmlConfig = new ComponentName("config", "xml");
ArraySet<ComponentName> components = new ArraySet<>(Arrays.asList(xmlConfig));
when(mResources
@@ -9115,51 +9100,6 @@
}
@Test
- public void setDefaultAssistantForUser_fromDeviceConfig() {
- ComponentName xmlConfig = new ComponentName("xml", "config");
- ComponentName deviceConfig = new ComponentName("device", "config");
- setDefaultAssistantInDeviceConfig(deviceConfig.flattenToString());
- when(mResources
- .getString(com.android.internal.R.string.config_defaultAssistantAccessComponent))
- .thenReturn(xmlConfig.flattenToString());
- when(mContext.getResources()).thenReturn(mResources);
- when(mAssistants.queryPackageForServices(eq(null), anyInt(), anyInt()))
- .thenReturn(new ArraySet<>(Arrays.asList(xmlConfig, deviceConfig)));
- when(mAssistants.getDefaultComponents())
- .thenReturn(new ArraySet<>(Arrays.asList(deviceConfig)));
- mService.setNotificationAssistantAccessGrantedCallback(
- mNotificationAssistantAccessGrantedCallback);
-
- mService.setDefaultAssistantForUser(0);
-
- verify(mNotificationAssistantAccessGrantedCallback)
- .onGranted(eq(deviceConfig), eq(0), eq(true), eq(false));
- }
-
- @Test
- public void setDefaultAssistantForUser_deviceConfigInvalid() {
- ComponentName xmlConfig = new ComponentName("xml", "config");
- ComponentName deviceConfig = new ComponentName("device", "config");
- setDefaultAssistantInDeviceConfig(deviceConfig.flattenToString());
- when(mResources
- .getString(com.android.internal.R.string.config_defaultAssistantAccessComponent))
- .thenReturn(xmlConfig.flattenToString());
- when(mContext.getResources()).thenReturn(mResources);
- // Only xmlConfig is valid, deviceConfig is not.
- when(mAssistants.queryPackageForServices(eq(null), anyInt(), eq(0)))
- .thenReturn(new ArraySet<>(Collections.singleton(xmlConfig)));
- when(mAssistants.getDefaultComponents())
- .thenReturn(new ArraySet<>(Arrays.asList(xmlConfig, deviceConfig)));
- mService.setNotificationAssistantAccessGrantedCallback(
- mNotificationAssistantAccessGrantedCallback);
-
- mService.setDefaultAssistantForUser(0);
-
- verify(mNotificationAssistantAccessGrantedCallback)
- .onGranted(eq(xmlConfig), eq(0), eq(true), eq(false));
- }
-
- @Test
public void clearMultipleDefaultAssistantPackagesShouldEnableOnlyOne() throws RemoteException {
ArrayMap<Boolean, ArrayList<ComponentName>> changedListeners =
generateResetComponentValues();
@@ -11006,7 +10946,6 @@
tr.addOverride(com.android.internal.R.string.config_defaultListenerAccessPackages, "");
tr.addOverride(com.android.internal.R.string.config_defaultDndAccessPackages, "");
tr.addOverride(com.android.internal.R.string.config_defaultAssistantAccessComponent, "");
- setDefaultAssistantInDeviceConfig("");
mService.loadDefaultApprovedServices(USER_SYSTEM);
@@ -13425,7 +13364,6 @@
throws Exception {
when(mDevicePolicyManager.isActiveDeviceOwner(mUid)).thenReturn(true);
// Given: a notification has the flag FLAG_ONGOING_EVENT set
- setDpmAppOppsExemptFromDismissal(false);
Notification n = new Notification.Builder(mContext, "test")
.setOngoing(true)
.build();
@@ -13451,7 +13389,6 @@
AppOpsManager.OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, mUid,
mPkg)).thenReturn(AppOpsManager.MODE_ALLOWED);
// Given: a notification has the flag FLAG_ONGOING_EVENT set
- setDpmAppOppsExemptFromDismissal(true);
Notification n = new Notification.Builder(mContext, "test")
.setOngoing(true)
.build();
@@ -13459,8 +13396,8 @@
// When: fix the notification with NotificationManagerService
mService.fixNotification(n, mPkg, "tag", 9, 0, mUid, NOT_FOREGROUND_SERVICE, true);
- // Then: the notification's flag FLAG_NO_DISMISS should be cleared
- assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS);
+ // Then: the notification's flag FLAG_NO_DISMISS should be set
+ assertNotSame(0, n.flags & Notification.FLAG_NO_DISMISS);
}
@Test
@@ -13468,9 +13405,8 @@
throws Exception {
when(mAppOpsManager.checkOpNoThrow(
AppOpsManager.OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, mUid,
- mPkg)).thenReturn(AppOpsManager.MODE_ALLOWED);
+ mPkg)).thenReturn(AppOpsManager.MODE_IGNORED);
// Given: a notification has the flag FLAG_ONGOING_EVENT set
- setDpmAppOppsExemptFromDismissal(false);
Notification n = new Notification.Builder(mContext, "test")
.setOngoing(true)
.build();
@@ -15551,14 +15487,6 @@
PendingIntent.FLAG_MUTABLE);
}
- private void setDpmAppOppsExemptFromDismissal(boolean isOn) {
- DeviceConfig.setProperty(
- DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER,
- /* name= */ "application_exemptions",
- String.valueOf(isOn),
- /* makeDefault= */ false);
- }
-
private void allowTestPackageToToast() throws Exception {
assertWithMessage("toast queue").that(mService.mToastQueue).isEmpty();
mService.isSystemUid = false;
diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
index 353ba01..89ae802 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
@@ -22,6 +22,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static com.android.server.wm.DesktopModeLaunchParamsModifier.DESKTOP_MODE_INITIAL_BOUNDS_SCALE;
+import static com.android.server.wm.DesktopModeLaunchParamsModifier.ENFORCE_DEVICE_RESTRICTIONS_KEY;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_DISPLAY;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
@@ -29,18 +30,21 @@
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+import android.content.res.Resources;
import android.graphics.Rect;
+import android.os.SystemProperties;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
+import com.android.internal.R;
import com.android.server.wm.LaunchParamsController.LaunchParamsModifier.Result;
import com.android.window.flags.Flags;
@@ -48,6 +52,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+import org.mockito.Mockito;
/**
* Tests for desktop mode task bounds.
@@ -71,8 +76,6 @@
@Before
public void setUp() throws Exception {
mActivity = new ActivityBuilder(mAtm).build();
- mTarget = spy(new DesktopModeLaunchParamsModifier(mContext));
- doReturn(true).when(mTarget).isDesktopModeSupported(any());
mCurrent = new LaunchParamsController.LaunchParams();
mCurrent.reset();
mResult = new LaunchParamsController.LaunchParams();
@@ -82,20 +85,26 @@
@Test
@DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
public void testReturnsContinueIfDesktopWindowingIsDisabled() {
+ setupDesktopModeLaunchParamsModifier();
+
assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(null).calculate());
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
public void testReturnsContinueIfDesktopWindowingIsEnabledOnUnsupportedDevice() {
- doReturn(false).when(mTarget).isDesktopModeSupported(any());
+ setupDesktopModeLaunchParamsModifier(/*isDesktopModeSupported=*/ false,
+ /*enforceDeviceRestrictions=*/ true);
+
assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(null).calculate());
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
public void testReturnsDoneIfDesktopWindowingIsEnabledAndUnsupportedDeviceOverridden() {
- doReturn(false).when(mTarget).enforceDeviceRestrictions();
+ setupDesktopModeLaunchParamsModifier(/*isDesktopModeSupported=*/ true,
+ /*enforceDeviceRestrictions=*/ false);
+
final Task task = new TaskBuilder(mSupervisor).build();
assertEquals(RESULT_DONE, new CalculateRequestBuilder().setTask(task).calculate());
}
@@ -103,12 +112,16 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
public void testReturnsSkipIfTaskIsNull() {
+ setupDesktopModeLaunchParamsModifier();
+
assertEquals(RESULT_SKIP, new CalculateRequestBuilder().setTask(null).calculate());
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
public void testReturnsSkipIfNotBoundsPhase() {
+ setupDesktopModeLaunchParamsModifier();
+
final Task task = new TaskBuilder(mSupervisor).build();
assertEquals(RESULT_SKIP, new CalculateRequestBuilder().setTask(task).setPhase(
PHASE_DISPLAY).calculate());
@@ -117,6 +130,8 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
public void testReturnsSkipIfTaskNotUsingActivityTypeStandardOrUndefined() {
+ setupDesktopModeLaunchParamsModifier();
+
final Task task = new TaskBuilder(mSupervisor).setActivityType(
ACTIVITY_TYPE_ASSISTANT).build();
assertEquals(RESULT_SKIP, new CalculateRequestBuilder().setTask(task).calculate());
@@ -125,6 +140,8 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
public void testReturnsDoneIfTaskUsingActivityTypeStandard() {
+ setupDesktopModeLaunchParamsModifier();
+
final Task task = new TaskBuilder(mSupervisor).setActivityType(
ACTIVITY_TYPE_STANDARD).build();
assertEquals(RESULT_DONE, new CalculateRequestBuilder().setTask(task).calculate());
@@ -133,6 +150,8 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
public void testReturnsDoneIfTaskUsingActivityTypeUndefined() {
+ setupDesktopModeLaunchParamsModifier();
+
final Task task = new TaskBuilder(mSupervisor).setActivityType(
ACTIVITY_TYPE_UNDEFINED).build();
assertEquals(RESULT_DONE, new CalculateRequestBuilder().setTask(task).calculate());
@@ -141,6 +160,8 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
public void testReturnsSkipIfCurrentParamsHasBounds() {
+ setupDesktopModeLaunchParamsModifier();
+
final Task task = new TaskBuilder(mSupervisor).setActivityType(
ACTIVITY_TYPE_STANDARD).build();
mCurrent.mBounds.set(/* left */ 0, /* top */ 0, /* right */ 100, /* bottom */ 100);
@@ -150,6 +171,8 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
public void testUsesDefaultBounds() {
+ setupDesktopModeLaunchParamsModifier();
+
final Task task = new TaskBuilder(mSupervisor).setActivityType(
ACTIVITY_TYPE_STANDARD).build();
final int displayHeight = 1600;
@@ -165,6 +188,8 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
public void testUsesDisplayAreaAndWindowingModeFromSource() {
+ setupDesktopModeLaunchParamsModifier();
+
final Task task = new TaskBuilder(mSupervisor).setActivityType(
ACTIVITY_TYPE_STANDARD).build();
TaskDisplayArea mockTaskDisplayArea = mock(TaskDisplayArea.class);
@@ -176,6 +201,24 @@
assertEquals(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode);
}
+ private void setupDesktopModeLaunchParamsModifier() {
+ setupDesktopModeLaunchParamsModifier(/*isDesktopModeSupported=*/ true,
+ /*enforceDeviceRestrictions=*/ true);
+ }
+
+ private void setupDesktopModeLaunchParamsModifier(boolean isDesktopModeSupported,
+ boolean enforceDeviceRestrictions) {
+ Resources mockResources = Mockito.mock(Resources.class);
+ when(mockResources.getBoolean(eq(R.bool.config_isDesktopModeSupported)))
+ .thenReturn(isDesktopModeSupported);
+ doReturn(mockResources).when(mContext).getResources();
+
+ SystemProperties.set(ENFORCE_DEVICE_RESTRICTIONS_KEY,
+ String.valueOf(enforceDeviceRestrictions));
+
+ mTarget = new DesktopModeLaunchParamsModifier(mContext);
+ }
+
private class CalculateRequestBuilder {
private Task mTask;
private int mPhase = PHASE_BOUNDS;
diff --git a/tests/FlickerTests/IME/AndroidTestTemplate.xml b/tests/FlickerTests/IME/AndroidTestTemplate.xml
index 988f76f..38442db 100644
--- a/tests/FlickerTests/IME/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/IME/AndroidTestTemplate.xml
@@ -10,6 +10,8 @@
<target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
<!-- keeps the screen on during tests -->
<option name="screen-always-on" value="on"/>
+ <!-- enable AOD -->
+ <option name="set-secure-setting" key="doze_always_on" value="1" />
<!-- prevents the phone from restarting -->
<option name="force-skip-system-props" value="true"/>
<!-- set WM tracing verbose level to all -->
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt
new file mode 100644
index 0000000..31506b5
--- /dev/null
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.ime
+
+import android.platform.test.annotations.Presubmit
+import android.platform.test.rule.UnlockScreenRule
+import android.tools.Rotation
+import android.tools.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.flicker.legacy.FlickerBuilder
+import android.tools.flicker.legacy.LegacyFlickerTest
+import android.tools.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.traces.component.ComponentNameMatcher
+import com.android.server.wm.flicker.BaseTest
+import com.android.server.wm.flicker.helpers.ImeAppHelper
+import org.junit.FixMethodOrder
+import org.junit.Ignore
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test IME window closing on lock and opening on screen unlock. To run this test: `atest
+ * FlickerTests:CloseImeWindowToHomeTest`
+ */
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class ShowImeOnUnlockScreenTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
+ private val testApp = ImeAppHelper(instrumentation)
+ private val imeOrSnapshot = ComponentNameMatcher.IME.or(ComponentNameMatcher.IME_SNAPSHOT)
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup {
+ tapl.expectedRotationCheckEnabled = false
+ testApp.launchViaIntent(wmHelper)
+ testApp.openIME(wmHelper)
+ }
+ transitions {
+ device.sleep()
+ wmHelper.StateSyncBuilder().withoutTopVisibleAppWindows().waitForAndVerify()
+ UnlockScreenRule.unlockScreen(device)
+ wmHelper.StateSyncBuilder().withImeShown().waitForAndVerify()
+ }
+ teardown { testApp.exit(wmHelper) }
+ }
+
+ @Presubmit
+ @Test
+ fun imeAndAppAnimateTogetherWhenLockingAndUnlocking() {
+ flicker.assertLayers {
+ this.isVisible(testApp)
+ .isVisible(imeOrSnapshot)
+ .then()
+ .isInvisible(testApp)
+ .isInvisible(imeOrSnapshot)
+ .then()
+ .isVisible(testApp)
+ .isVisible(imeOrSnapshot)
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. Display turns off during transition")
+ override fun navBarWindowIsAlwaysVisible() {}
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. Display turns off during transition")
+ override fun statusBarWindowIsAlwaysVisible() {}
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. Display turns off during transition")
+ override fun taskBarWindowIsAlwaysVisible() {}
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(Rotation.ROTATION_0)
+ )
+ }
+}