Merge "[flexiglass] Simplified Dagger setup." into udc-dev
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 3249b41..4e2b6fa 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -261,6 +261,9 @@
void cancelTaskWindowTransition(int taskId);
/**
+ * Fetches the snapshot for the task with the given id, taking a new snapshot if it is not in
+ * the task snapshot cache and it is requested.
+ *
* @param taskId the id of the task to retrieve the sAutoapshots for
* @param isLowResolution if set, if the snapshot needs to be loaded from disk, this will load
* a reduced resolution of it, which is much faster
@@ -272,10 +275,14 @@
int taskId, boolean isLowResolution, boolean takeSnapshotIfNeeded);
/**
+ * Requests for a new snapshot to be taken for the task with the given id, storing it in the
+ * task snapshot cache only if requested.
+ *
* @param taskId the id of the task to take a snapshot of
+ * @param updateCache whether to store the new snapshot in the system's task snapshot cache
* @return a graphic buffer representing a screenshot of a task
*/
- android.window.TaskSnapshot takeTaskSnapshot(int taskId);
+ android.window.TaskSnapshot takeTaskSnapshot(int taskId, boolean updateCache);
/**
* Return the user id of last resumed activity.
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index d802b46..47a5db8 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -791,4 +791,6 @@
void setKeepUninstalledPackages(in List<String> packageList);
boolean[] canPackageQuery(String sourcePackageName, in String[] targetPackageNames, int userId);
+
+ boolean waitForHandler(long timeoutMillis, boolean forBackgroundHandler);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 5c79f69..d695c0c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -15310,18 +15310,6 @@
public static final String ANGLE_EGL_FEATURES = "angle_egl_features";
/**
- * Comma-separated list of package names that ANGLE may have issues with
- * @hide
- */
- public static final String ANGLE_DEFERLIST = "angle_deferlist";
-
- /**
- * Integer mode of the logic for applying `angle_deferlist`
- * @hide
- */
- public static final String ANGLE_DEFERLIST_MODE = "angle_deferlist_mode";
-
- /**
* Show the "ANGLE In Use" dialog box to the user when ANGLE is the OpenGL driver.
* The value is a boolean (1 or 0).
* @hide
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 02cd037..99a4f6b 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -23,6 +23,7 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
+import android.graphics.HardwareRenderer;
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
@@ -550,6 +551,11 @@
ThreadedRenderer.trimMemory(level);
}
+ /** @hide */
+ public void trimCaches(@HardwareRenderer.CacheTrimLevel int level) {
+ ThreadedRenderer.trimCaches(level);
+ }
+
public void dumpGfxInfo(FileDescriptor fd, String[] args) {
FileOutputStream fout = new FileOutputStream(fd);
PrintWriter pw = new FastPrintWriter(fout);
diff --git a/core/java/android/view/autofill/AutofillFeatureFlags.java b/core/java/android/view/autofill/AutofillFeatureFlags.java
index ba54686..5ad74c8 100644
--- a/core/java/android/view/autofill/AutofillFeatureFlags.java
+++ b/core/java/android/view/autofill/AutofillFeatureFlags.java
@@ -280,7 +280,7 @@
private static final boolean DEFAULT_AFAA_ON_IMPORTANT_VIEW_ENABLED = true;
private static final String DEFAULT_AFAA_DENYLIST = "";
private static final String DEFAULT_AFAA_ALLOWLIST = "";
- private static final String DEFAULT_AFAA_NON_AUTOFILLABLE_IME_ACTIONS = "2,3,4";
+ 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;
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index a554d0e..92cfa67 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -50,7 +50,6 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
-import android.provider.DeviceConfig;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
@@ -723,15 +722,13 @@
}
/**
- * Whether the auto pin feature logic is available or not.
- * @return true, if deviceConfig flag is set to true or the flag is not propagated and
- * defaultValue is true.
+ * Whether the auto pin feature is available or not.
+ * @return true. This method is always returning true due to feature flags not working
+ * properly (b/282246482). Ideally, this should check if deviceConfig flag is set to true
+ * and then return the appropriate value.
*/
public static boolean isAutoPinConfirmFeatureAvailable() {
- return DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_AUTO_PIN_CONFIRMATION,
- FLAG_ENABLE_AUTO_PIN_CONFIRMATION,
- /* defaultValue= */ true);
+ return true;
}
/** Returns if the given quality maps to an alphabetic password */
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index 128de8b..052e2f2 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -471,10 +471,6 @@
optional SettingProto updatable_driver_prerelease_opt_in_apps = 18;
optional SettingProto angle_egl_features = 19;
- // ANGLE - List of Apps that ANGLE may have issues with
- optional SettingProto angle_deferlist = 20;
- // ANGLE - Integer mode of the logic for applying `angle_deferlist`
- optional SettingProto angle_deferlist_mode = 21;
}
optional Gpu gpu = 59;
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index a99ba15..5b0dd30 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -72,9 +72,9 @@
<item name="secondary_content_alpha_material_dark" format="float" type="dimen">.7</item>
<item name="secondary_content_alpha_material_light" format="float" type="dimen">0.60</item>
- <item name="highlight_alpha_material_light" format="float" type="dimen">0.5</item>
- <item name="highlight_alpha_material_dark" format="float" type="dimen">0.5</item>
- <item name="highlight_alpha_material_colored" format="float" type="dimen">0.10</item>
+ <item name="highlight_alpha_material_light" format="float" type="dimen">0.20</item>
+ <item name="highlight_alpha_material_dark" format="float" type="dimen">0.20</item>
+ <item name="highlight_alpha_material_colored" format="float" type="dimen">0.20</item>
<!-- Primary & accent colors -->
<eat-comment />
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index 9ed3d9c..9cde187 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -144,6 +144,32 @@
public @interface DumpFlags {
}
+
+ /**
+ * Trims all Skia caches.
+ * @hide
+ */
+ public static final int CACHE_TRIM_ALL = 0;
+ /**
+ * Trims Skia font caches.
+ * @hide
+ */
+ public static final int CACHE_TRIM_FONT = 1;
+ /**
+ * Trims Skia resource caches.
+ * @hide
+ */
+ public static final int CACHE_TRIM_RESOURCES = 2;
+
+ /** @hide */
+ @IntDef(prefix = {"CACHE_TRIM_"}, value = {
+ CACHE_TRIM_ALL,
+ CACHE_TRIM_FONT,
+ CACHE_TRIM_RESOURCES
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CacheTrimLevel {}
+
/**
* Name of the file that holds the shaders cache.
*/
@@ -1131,6 +1157,20 @@
nTrimMemory(level);
}
+ /**
+ * Invoke this when all font caches should be flushed. This can cause jank on next render
+ * commands so use it only after expensive font allocation operations which would
+ * allocate large amount of temporary memory.
+ *
+ * @param level Hint about which caches to trim. See {@link #CACHE_TRIM_ALL},
+ * {@link #CACHE_TRIM_FONT}, {@link #CACHE_TRIM_RESOURCES}
+ *
+ * @hide
+ */
+ public static void trimCaches(@CacheTrimLevel int level) {
+ nTrimCaches(level);
+ }
+
/** @hide */
public static void overrideProperty(@NonNull String name, @NonNull String value) {
if (name == null || value == null) {
@@ -1497,6 +1537,8 @@
private static native void nTrimMemory(int level);
+ private static native void nTrimCaches(int level);
+
private static native void nOverrideProperty(String name, String value);
private static native void nFence(long nativeProxy);
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 4d0a058..641a2ae 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -1013,7 +1013,8 @@
}
p.setShader(shader);
p.setColorFilter(null);
- p.setColor(color);
+ // Alpha is handled by the shader (and color is a no-op because there's a shader)
+ p.setColor(0xFF000000);
return properties;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index a48be5e..91c7cc0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1346,7 +1346,7 @@
// Recreates & shows the education views. Call when a theme/config change happens.
private void updateUserEdu() {
- if (isStackEduVisible()) {
+ if (isStackEduVisible() && !mStackEduView.isHiding()) {
removeView(mStackEduView);
mStackEduView = new StackEducationView(mContext, mPositioner, mBubbleController);
addView(mStackEduView);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
index 627273f..d0598cd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
@@ -37,8 +37,7 @@
context: Context,
positioner: BubblePositioner,
controller: BubbleController
-)
- : LinearLayout(context) {
+) : LinearLayout(context) {
private val TAG = if (BubbleDebugConfig.TAG_WITH_CLASS_NAME) "BubbleStackEducationView"
else BubbleDebugConfig.TAG_BUBBLES
@@ -53,7 +52,8 @@
private val titleTextView by lazy { findViewById<TextView>(R.id.stack_education_title) }
private val descTextView by lazy { findViewById<TextView>(R.id.stack_education_description) }
- private var isHiding = false
+ var isHiding = false
+ private set
init {
LayoutInflater.from(context).inflate(R.layout.bubble_stack_user_education, this)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 9a2ec15..ab8e7e6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -709,7 +709,8 @@
return context.getSystemService(WindowManager.class)
.getMaximumWindowMetrics()
.getWindowInsets()
- .getInsets(WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout())
+ .getInsetsIgnoringVisibility(WindowInsets.Type.systemBars()
+ | WindowInsets.Type.displayCutout())
.toRect();
}
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 74ef57e..5a9c25f 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
@@ -732,10 +732,11 @@
@WMSingleton
@Provides
- static ShellController provideShellController(ShellInit shellInit,
+ static ShellController provideShellController(Context context,
+ ShellInit shellInit,
ShellCommandHandler shellCommandHandler,
@ShellMainThread ShellExecutor mainExecutor) {
- return new ShellController(shellInit, shellCommandHandler, mainExecutor);
+ return new ShellController(context, shellInit, shellCommandHandler, mainExecutor);
}
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl
index 78de5f3..3906599 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl
@@ -57,27 +57,35 @@
in Rect destinationBounds, in SurfaceControl overlay) = 2;
/**
+ * Notifies the swiping Activity to PiP onto home transition is aborted
+ *
+ * @param taskId the Task id that the Activity and overlay are currently in.
+ * @param componentName ComponentName represents the Activity
+ */
+ oneway void abortSwipePipToHome(int taskId, in ComponentName componentName) = 3;
+
+ /**
* Sets listener to get pinned stack animation callbacks.
*/
- oneway void setPipAnimationListener(IPipAnimationListener listener) = 3;
+ oneway void setPipAnimationListener(IPipAnimationListener listener) = 4;
/**
* Sets the shelf height and visibility.
*/
- oneway void setShelfHeight(boolean visible, int shelfHeight) = 4;
+ oneway void setShelfHeight(boolean visible, int shelfHeight) = 5;
/**
* Sets the next pip animation type to be the alpha animation.
*/
- oneway void setPipAnimationTypeToAlpha() = 5;
+ oneway void setPipAnimationTypeToAlpha() = 6;
/**
* Sets the height and visibility of the Launcher keep clear area.
*/
- oneway void setLauncherKeepClearAreaHeight(boolean visible, int height) = 6;
+ oneway void setLauncherKeepClearAreaHeight(boolean visible, int height) = 7;
/**
* Sets the app icon size in pixel used by Launcher
*/
- oneway void setLauncherAppIconSize(int iconSizePx) = 7;
+ oneway void setLauncherAppIconSize(int iconSizePx) = 8;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 58bc81d..db7c3fc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -462,13 +462,29 @@
// to the actual Task surface now.
// PipTransition is responsible to fade it out and cleanup when finishing the enter PIP
// transition.
- final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ final SurfaceControl.Transaction t = mSurfaceControlTransactionFactory.getTransaction();
mTaskOrganizer.reparentChildSurfaceToTask(taskId, overlay, t);
t.setLayer(overlay, Integer.MAX_VALUE);
t.apply();
}
}
+ /**
+ * Callback when launcher aborts swipe-pip-to-home operation.
+ */
+ public void abortSwipePipToHome(int taskId, ComponentName componentName) {
+ if (!mPipTransitionState.getInSwipePipToHomeTransition()) {
+ return;
+ }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "Abort swipe-pip-to-home for %s", componentName);
+ sendOnPipTransitionCancelled(TRANSITION_DIRECTION_TO_PIP);
+ // Cleanup internal states
+ mPipTransitionState.setInSwipePipToHomeTransition(false);
+ mPictureInPictureParams = null;
+ mPipTransitionState.setTransitionState(PipTransitionState.UNDEFINED);
+ }
+
public ActivityManager.RunningTaskInfo getTaskInfo() {
return mTaskInfo;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index fa21db5..bfc1fb9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -26,6 +26,7 @@
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_PIP;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.transitTypeToString;
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
@@ -92,6 +93,8 @@
private final Rect mExitDestinationBounds = new Rect();
@Nullable
private IBinder mExitTransition;
+ @Nullable
+ private IBinder mMoveToBackTransition;
private IBinder mRequestedEnterTransition;
private WindowContainerToken mRequestedEnterTask;
/** The Task window that is currently in PIP windowing mode. */
@@ -171,9 +174,10 @@
// Exiting PIP.
final int type = info.getType();
- if (transition.equals(mExitTransition)) {
+ if (transition.equals(mExitTransition) || transition.equals(mMoveToBackTransition)) {
mExitDestinationBounds.setEmpty();
mExitTransition = null;
+ mMoveToBackTransition = null;
mHasFadeOut = false;
if (mFinishCallback != null) {
callFinishCallback(null /* wct */);
@@ -201,6 +205,8 @@
startExitToSplitAnimation(info, startTransaction, finishTransaction,
finishCallback, pipTaskInfo);
break;
+ case TRANSIT_TO_BACK:
+ // pass through here is intended
case TRANSIT_REMOVE_PIP:
removePipImmediately(info, startTransaction, finishTransaction, finishCallback,
pipTaskInfo);
@@ -273,6 +279,15 @@
WindowContainerTransaction wct = new WindowContainerTransaction();
augmentRequest(transition, request, wct);
return wct;
+ } else if (request.getType() == TRANSIT_TO_BACK && request.getTriggerTask() != null
+ && request.getTriggerTask().getWindowingMode() == WINDOWING_MODE_PINNED) {
+ // if we receive a TRANSIT_TO_BACK type of request while in PiP
+ mMoveToBackTransition = transition;
+ // update the transition state to avoid {@link PipTaskOrganizer#onTaskVanished()} calls
+ mPipTransitionState.setTransitionState(PipTransitionState.EXITING_PIP);
+
+ // return an empty WindowContainerTransaction so that we don't check other handlers
+ return new WindowContainerTransaction();
} else {
return null;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 9e6bd47..63181da 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -1017,6 +1017,10 @@
mPipTaskOrganizer.stopSwipePipToHome(taskId, componentName, destinationBounds, overlay);
}
+ private void abortSwipePipToHome(int taskId, ComponentName componentName) {
+ mPipTaskOrganizer.abortSwipePipToHome(taskId, componentName);
+ }
+
private String getTransitionTag(int direction) {
switch (direction) {
case TRANSITION_DIRECTION_TO_PIP:
@@ -1313,6 +1317,12 @@
}
@Override
+ public void abortSwipePipToHome(int taskId, ComponentName componentName) {
+ executeRemoteCallWithTaskPermission(mController, "abortSwipePipToHome",
+ (controller) -> controller.abortSwipePipToHome(taskId, componentName));
+ }
+
+ @Override
public void setShelfHeight(boolean visible, int height) {
executeRemoteCallWithTaskPermission(mController, "setShelfHeight",
(controller) -> controller.setShelfHeight(visible, height));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index 387d390..a9ad3c9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -301,7 +301,7 @@
try {
for (int i = 0; i < mPausingTasks.size(); ++i) {
snapshots[i] = ActivityTaskManager.getService().takeTaskSnapshot(
- mPausingTasks.get(0).mTaskInfo.taskId);
+ mPausingTasks.get(0).mTaskInfo.taskId, false /* updateCache */);
}
} catch (RemoteException e) {
taskIds = null;
@@ -671,7 +671,8 @@
try {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
"[%d] RecentsController.screenshotTask: taskId=%d", mInstanceId, taskId);
- return ActivityTaskManager.getService().takeTaskSnapshot(taskId);
+ return ActivityTaskManager.getService().takeTaskSnapshot(taskId,
+ true /* updateCache */);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to screenshot task", e);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java
index 3f944cb..0eb7c2d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java
@@ -32,6 +32,7 @@
import android.content.res.Configuration;
import android.os.Bundle;
import android.util.ArrayMap;
+import android.view.SurfaceControlRegistry;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
@@ -52,6 +53,7 @@
public class ShellController {
private static final String TAG = ShellController.class.getSimpleName();
+ private final Context mContext;
private final ShellInit mShellInit;
private final ShellCommandHandler mShellCommandHandler;
private final ShellExecutor mMainExecutor;
@@ -72,8 +74,11 @@
private Configuration mLastConfiguration;
- public ShellController(ShellInit shellInit, ShellCommandHandler shellCommandHandler,
+ public ShellController(Context context,
+ ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
ShellExecutor mainExecutor) {
+ mContext = context;
mShellInit = shellInit;
mShellCommandHandler = shellCommandHandler;
mMainExecutor = mainExecutor;
@@ -254,6 +259,16 @@
}
}
+ private void handleInit() {
+ SurfaceControlRegistry.createProcessInstance(mContext);
+ mShellInit.init();
+ }
+
+ private void handleDump(PrintWriter pw) {
+ mShellCommandHandler.dump(pw);
+ SurfaceControlRegistry.dump(100 /* limit */, false /* runGc */, pw);
+ }
+
public void dump(@NonNull PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.println(prefix + TAG);
@@ -279,7 +294,7 @@
@Override
public void onInit() {
try {
- mMainExecutor.executeBlocking(() -> mShellInit.init());
+ mMainExecutor.executeBlocking(() -> ShellController.this.handleInit());
} catch (InterruptedException e) {
throw new RuntimeException("Failed to initialize the Shell in 2s", e);
}
@@ -344,7 +359,7 @@
@Override
public void dump(PrintWriter pw) {
try {
- mMainExecutor.executeBlocking(() -> mShellCommandHandler.dump(pw));
+ mMainExecutor.executeBlocking(() -> ShellController.this.handleDump(pw));
} catch (InterruptedException e) {
throw new RuntimeException("Failed to dump the Shell in 2s", e);
}
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 5d7b629..bb0eba6 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
@@ -18,6 +18,8 @@
import static android.view.WindowManager.TRANSIT_CHANGE;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS;
+
import android.os.IBinder;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
@@ -27,6 +29,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
@@ -36,6 +39,7 @@
import com.android.wm.shell.unfold.animation.FullscreenUnfoldTaskAnimator;
import com.android.wm.shell.unfold.animation.SplitTaskUnfoldAnimator;
import com.android.wm.shell.unfold.animation.UnfoldTaskAnimator;
+import com.android.wm.shell.util.TransitionUtil;
import java.util.ArrayList;
import java.util.List;
@@ -105,8 +109,14 @@
animator.clearTasks();
info.getChanges().forEach(change -> {
- if (change.getTaskInfo() != null
- && change.getMode() == TRANSIT_CHANGE
+ if (change.getTaskInfo() != null) {
+ ProtoLog.v(WM_SHELL_TRANSITIONS,
+ "startAnimation, check taskInfo: %s, mode: %s, isApplicableTask: %s",
+ change.getTaskInfo(), TransitionInfo.modeToString(change.getMode()),
+ animator.isApplicableTask(change.getTaskInfo()));
+ }
+ if (change.getTaskInfo() != null && (change.getMode() == TRANSIT_CHANGE
+ || TransitionUtil.isOpeningType(change.getMode()))
&& animator.isApplicableTask(change.getTaskInfo())) {
animator.onTaskAppeared(change.getTaskInfo(), change.getLeash());
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java
index 123bf3b..a4cf149 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java
@@ -213,7 +213,7 @@
@Override
public boolean isApplicableTask(TaskInfo taskInfo) {
return taskInfo.hasParentTask()
- && taskInfo.isVisible
+ && taskInfo.isRunning
&& taskInfo.realActivity != null // to filter out parents created by organizer
&& taskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW;
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index 04f2c99..85167cb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -129,7 +129,7 @@
return null;
}).when(mMockExecutor).execute(any());
mShellInit = spy(new ShellInit(mMockExecutor));
- mShellController = spy(new ShellController(mShellInit, mMockShellCommandHandler,
+ mShellController = spy(new ShellController(mContext, mShellInit, mMockShellCommandHandler,
mMockExecutor));
mPipController = new PipController(mContext, mShellInit, mMockShellCommandHandler,
mShellController, mMockDisplayController, mMockPipAnimationController,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index b542fae..2c69522 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -107,7 +107,7 @@
mMainExecutor = new TestShellExecutor();
when(mContext.getPackageManager()).thenReturn(mock(PackageManager.class));
mShellInit = spy(new ShellInit(mMainExecutor));
- mShellController = spy(new ShellController(mShellInit, mShellCommandHandler,
+ mShellController = spy(new ShellController(mContext, mShellInit, mShellCommandHandler,
mMainExecutor));
mRecentTasksControllerReal = new RecentTasksController(mContext, mShellInit,
mShellController, mShellCommandHandler, mTaskStackListener, mActivityTaskManager,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
index c37a497..9189d3d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
@@ -111,7 +111,7 @@
public void setup() {
assumeTrue(ActivityTaskManager.supportsSplitScreenMultiWindow(mContext));
MockitoAnnotations.initMocks(this);
- mShellController = spy(new ShellController(mShellInit, mShellCommandHandler,
+ mShellController = spy(new ShellController(mContext, mShellInit, mShellCommandHandler,
mMainExecutor));
mSplitScreenController = spy(new SplitScreenController(mContext, mShellInit,
mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingWindowControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingWindowControllerTests.java
index 10dec9e..a9082a6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingWindowControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingWindowControllerTests.java
@@ -81,7 +81,7 @@
doReturn(mock(Display.class)).when(mDisplayManager).getDisplay(anyInt());
doReturn(mDisplayManager).when(mContext).getSystemService(eq(DisplayManager.class));
mShellInit = spy(new ShellInit(mMainExecutor));
- mShellController = spy(new ShellController(mShellInit, mShellCommandHandler,
+ mShellController = spy(new ShellController(mContext, mShellInit, mShellCommandHandler,
mMainExecutor));
mController = new StartingWindowController(mContext, mShellInit, mShellController,
mTaskOrganizer, mMainExecutor, mTypeAlgorithm, mIconProvider, mTransactionPool);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java
index 8d92d08..7c520c3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java
@@ -78,7 +78,7 @@
mConfigChangeListener = new TestConfigurationChangeListener();
mUserChangeListener = new TestUserChangeListener();
mExecutor = new TestShellExecutor();
- mController = new ShellController(mShellInit, mShellCommandHandler, mExecutor);
+ mController = new ShellController(mContext, mShellInit, mShellCommandHandler, mExecutor);
mController.onConfigurationChanged(getConfigurationCopy());
}
diff --git a/libs/hwui/MemoryPolicy.h b/libs/hwui/MemoryPolicy.h
index 139cdde..347daf34 100644
--- a/libs/hwui/MemoryPolicy.h
+++ b/libs/hwui/MemoryPolicy.h
@@ -31,6 +31,12 @@
RUNNING_MODERATE = 5,
};
+enum class CacheTrimLevel {
+ ALL_CACHES = 0,
+ FONT_CACHE = 1,
+ RESOURCE_CACHE = 2,
+};
+
struct MemoryPolicy {
// The initial scale factor applied to the display resolution. The default is 1, but
// lower values may be used to start with a smaller initial cache size. The cache will
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index 6a7411f..d04de37 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -362,6 +362,10 @@
RenderProxy::trimMemory(level);
}
+static void android_view_ThreadedRenderer_trimCaches(JNIEnv* env, jobject clazz, jint level) {
+ RenderProxy::trimCaches(level);
+}
+
static void android_view_ThreadedRenderer_overrideProperty(JNIEnv* env, jobject clazz,
jstring name, jstring value) {
const char* nameCharArray = env->GetStringUTFChars(name, NULL);
@@ -1018,6 +1022,7 @@
(void*)android_view_ThreadedRenderer_notifyCallbackPending},
{"nNotifyExpensiveFrame", "(J)V",
(void*)android_view_ThreadedRenderer_notifyExpensiveFrame},
+ {"nTrimCaches", "(I)V", (void*)android_view_ThreadedRenderer_trimCaches},
};
static JavaVM* mJvm = nullptr;
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index c00a270..babce88 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -139,6 +139,25 @@
}
}
+void CacheManager::trimCaches(CacheTrimLevel mode) {
+ switch (mode) {
+ case CacheTrimLevel::FONT_CACHE:
+ SkGraphics::PurgeFontCache();
+ break;
+ case CacheTrimLevel::RESOURCE_CACHE:
+ SkGraphics::PurgeResourceCache();
+ break;
+ case CacheTrimLevel::ALL_CACHES:
+ SkGraphics::PurgeAllCaches();
+ if (mGrContext) {
+ mGrContext->purgeUnlockedResources(false);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
void CacheManager::trimStaleResources() {
if (!mGrContext) {
return;
diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h
index d21ac9b..5e43ac2 100644
--- a/libs/hwui/renderthread/CacheManager.h
+++ b/libs/hwui/renderthread/CacheManager.h
@@ -48,6 +48,7 @@
void configureContext(GrContextOptions* context, const void* identity, ssize_t size);
#endif
void trimMemory(TrimLevel mode);
+ void trimCaches(CacheTrimLevel mode);
void trimStaleResources();
void dumpMemoryUsage(String8& log, const RenderState* renderState = nullptr);
void getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage);
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 31b4b20..224c878 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -231,6 +231,15 @@
}
}
+void RenderProxy::trimCaches(int level) {
+ // Avoid creating a RenderThread to do a trimMemory.
+ if (RenderThread::hasInstance()) {
+ RenderThread& thread = RenderThread::getInstance();
+ const auto trimLevel = static_cast<CacheTrimLevel>(level);
+ thread.queue().post([&thread, trimLevel]() { thread.trimCaches(trimLevel); });
+ }
+}
+
void RenderProxy::purgeCaches() {
if (RenderThread::hasInstance()) {
RenderThread& thread = RenderThread::getInstance();
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 82072a6..47c1b0c 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -105,6 +105,7 @@
void destroyHardwareResources();
static void trimMemory(int level);
+ static void trimCaches(int level);
static void purgeCaches();
static void overrideProperty(const char* name, const char* value);
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 9ba67a2..eb28c08 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -521,6 +521,11 @@
cacheManager().trimMemory(level);
}
+void RenderThread::trimCaches(CacheTrimLevel level) {
+ ATRACE_CALL();
+ cacheManager().trimCaches(level);
+}
+
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index c77cd41..79e57de 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -174,6 +174,7 @@
}
void trimMemory(TrimLevel level);
+ void trimCaches(CacheTrimLevel level);
/**
* isCurrent provides a way to query, if the caller is running on
diff --git a/media/jni/soundpool/Stream.cpp b/media/jni/soundpool/Stream.cpp
index 4194a22..45dde74 100644
--- a/media/jni/soundpool/Stream.cpp
+++ b/media/jni/soundpool/Stream.cpp
@@ -282,8 +282,6 @@
priority, loop, rate, playerIId);
// initialize track
- const audio_stream_type_t streamType =
- AudioSystem::attributesToStreamType(*mStreamManager->getAttributes());
const int32_t channelCount = sound->getChannelCount();
const auto sampleRate = (uint32_t)lround(double(sound->getSampleRate()) * rate);
size_t frameCount = 0;
@@ -328,8 +326,8 @@
attributionSource.token = sp<BBinder>::make();
mCallback = sp<StreamCallback>::make(this, toggle),
// TODO b/182469354 make consistent with AudioRecord, add util for native source
- mAudioTrack = new AudioTrack(streamType, sampleRate, sound->getFormat(),
- channelMask, sound->getIMemory(), AUDIO_OUTPUT_FLAG_FAST,
+ mAudioTrack = new AudioTrack(AUDIO_STREAM_DEFAULT, sampleRate, sound->getFormat(),
+ channelMask, sound->getIMemory(), AUDIO_OUTPUT_FLAG_NONE,
mCallback,
0 /*default notification frames*/, AUDIO_SESSION_ALLOCATE,
AudioTrack::TRANSFER_DEFAULT,
diff --git a/media/jni/soundpool/StreamManager.cpp b/media/jni/soundpool/StreamManager.cpp
index acd4bad..52060f1 100644
--- a/media/jni/soundpool/StreamManager.cpp
+++ b/media/jni/soundpool/StreamManager.cpp
@@ -109,7 +109,10 @@
int32_t streams, size_t threads, const audio_attributes_t& attributes,
std::string opPackageName)
: StreamMap(streams)
- , mAttributes(attributes)
+ , mAttributes([attributes](){
+ audio_attributes_t attr = attributes;
+ attr.flags = static_cast<audio_flags_mask_t>(attr.flags | AUDIO_FLAG_LOW_LATENCY);
+ return attr; }())
, mOpPackageName(std::move(opPackageName))
, mLockStreamManagerStop(streams == 1 || kForceLockStreamManagerStop)
{
diff --git a/packages/SettingsLib/BannerMessagePreference/res/layout-v31/settingslib_banner_message.xml b/packages/SettingsLib/BannerMessagePreference/res/layout-v31/settingslib_banner_message.xml
index 6f504ef..9d53e39 100644
--- a/packages/SettingsLib/BannerMessagePreference/res/layout-v31/settingslib_banner_message.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/layout-v31/settingslib_banner_message.xml
@@ -50,6 +50,8 @@
android:id="@+id/banner_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_gravity="start"
+ android:textAlignment="viewStart"
android:paddingTop="0dp"
android:paddingBottom="4dp"
android:textAppearance="@style/Banner.Title.SettingsLib"/>
@@ -58,6 +60,8 @@
android:id="@+id/banner_subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_gravity="start"
+ android:textAlignment="viewStart"
android:paddingTop="0dp"
android:paddingBottom="4dp"
android:textAppearance="@style/Banner.Subtitle.SettingsLib"
@@ -67,6 +71,8 @@
android:id="@+id/banner_summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_gravity="start"
+ android:textAlignment="viewStart"
android:paddingTop="4dp"
android:paddingBottom="8dp"
android:textAppearance="@style/Banner.Summary.SettingsLib"/>
diff --git a/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml b/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml
index 42700b3..470a83d 100644
--- a/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml
+++ b/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml
@@ -50,6 +50,8 @@
android:id="@android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_gravity="start"
+ android:textAlignment="viewStart"
android:paddingTop="16dp"
android:paddingBottom="8dp"
android:textColor="?android:attr/textColorSecondary"
@@ -60,6 +62,8 @@
android:text="@string/settingslib_learn_more_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_gravity="start"
+ android:textAlignment="viewStart"
android:paddingBottom="8dp"
android:clickable="true"
android:visibility="gone"
diff --git a/packages/SettingsLib/FooterPreference/res/layout-v33/preference_footer.xml b/packages/SettingsLib/FooterPreference/res/layout-v33/preference_footer.xml
index a2f2510..4b5fd44 100644
--- a/packages/SettingsLib/FooterPreference/res/layout-v33/preference_footer.xml
+++ b/packages/SettingsLib/FooterPreference/res/layout-v33/preference_footer.xml
@@ -50,6 +50,8 @@
android:id="@android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_gravity="start"
+ android:textAlignment="viewStart"
android:paddingTop="16dp"
android:paddingBottom="8dp"
android:textColor="?android:attr/textColorSecondary"
@@ -62,6 +64,8 @@
android:text="@string/settingslib_learn_more_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_gravity="start"
+ android:textAlignment="viewStart"
android:paddingBottom="8dp"
android:clickable="true"
android:visibility="gone"
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml b/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml
index 23aa993..dda7517c 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml
@@ -42,6 +42,8 @@
android:id="@android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_gravity="start"
+ android:textAlignment="viewStart"
android:maxLines="2"
android:textAppearance="?android:attr/textAppearanceListItem"
android:ellipsize="marquee"/>
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference.xml b/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference.xml
index 70ce374..fedcc77 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference.xml
@@ -42,6 +42,8 @@
android:id="@android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_gravity="start"
+ android:textAlignment="viewStart"
android:maxLines="2"
android:hyphenationFrequency="normalFast"
android:lineBreakWordStyle="phrase"
diff --git a/packages/SettingsLib/TopIntroPreference/res/layout-v33/top_intro_preference.xml b/packages/SettingsLib/TopIntroPreference/res/layout-v33/top_intro_preference.xml
index 6046d91..195d45f 100644
--- a/packages/SettingsLib/TopIntroPreference/res/layout-v33/top_intro_preference.xml
+++ b/packages/SettingsLib/TopIntroPreference/res/layout-v33/top_intro_preference.xml
@@ -30,6 +30,8 @@
android:id="@android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_gravity="start"
+ android:textAlignment="viewStart"
android:clickable="false"
android:longClickable="false"
android:maxLines="10"
diff --git a/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml b/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
index 4d6e1b7..bee6bc7 100644
--- a/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
+++ b/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
@@ -30,6 +30,8 @@
android:id="@android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_gravity="start"
+ android:textAlignment="viewStart"
android:clickable="false"
android:longClickable="false"
android:maxLines="10"
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index e846480..8d4aa9a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -67,6 +67,10 @@
static final String STORAGE_MANAGER_ENABLED_PROPERTY =
"ro.storage_manager.enabled";
+ @VisibleForTesting
+ static final String INCOMPATIBLE_CHARGER_WARNING_DISABLED =
+ "incompatible_charger_warning_disabled";
+
private static Signature[] sSystemSignature;
private static String sPermissionControllerPackageName;
private static String sServicesSystemSharedLibPackageName;
@@ -652,6 +656,19 @@
/** Whether there is any incompatible chargers in the current UsbPort? */
public static boolean containsIncompatibleChargers(Context context, String tag) {
+ // Avoid the caller doesn't have permission to read the "Settings.Secure" data.
+ try {
+ // Whether the incompatible charger warning is disabled or not
+ if (Settings.Secure.getInt(context.getContentResolver(),
+ INCOMPATIBLE_CHARGER_WARNING_DISABLED, 0) == 1) {
+ Log.d(tag, "containsIncompatibleChargers: disabled");
+ return false;
+ }
+ } catch (Exception e) {
+ Log.e(tag, "containsIncompatibleChargers()", e);
+ return false;
+ }
+
final List<UsbPort> usbPortList =
context.getSystemService(UsbManager.class).getPorts();
if (usbPortList == null || usbPortList.isEmpty()) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 1aa1741..2e6bb53 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -47,6 +47,7 @@
import com.android.settingslib.utils.ThreadUtils;
import com.android.settingslib.widget.AdaptiveOutlineDrawable;
+import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
@@ -78,6 +79,7 @@
BluetoothDevice mDevice;
private HearingAidInfo mHearingAidInfo;
private int mGroupId;
+ private Timestamp mBondTimestamp;
// Need this since there is no method for getting RSSI
short mRssi;
@@ -889,15 +891,25 @@
mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_UNKNOWN);
mDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_UNKNOWN);
mDevice.setSimAccessPermission(BluetoothDevice.ACCESS_UNKNOWN);
+
+ mBondTimestamp = null;
}
refresh();
- if (bondState == BluetoothDevice.BOND_BONDED && mDevice.isBondingInitiatedLocally()) {
- connect();
+ if (bondState == BluetoothDevice.BOND_BONDED) {
+ mBondTimestamp = new Timestamp(System.currentTimeMillis());
+
+ if (mDevice.isBondingInitiatedLocally()) {
+ connect();
+ }
}
}
+ public Timestamp getBondTimestamp() {
+ return mBondTimestamp;
+ }
+
public BluetoothClass getBtClass() {
return mDevice.getBluetoothClass();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index d55144e..0db88af 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -25,6 +25,7 @@
import com.android.internal.annotations.VisibleForTesting;
+import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -37,6 +38,8 @@
private static final String TAG = "CachedBluetoothDeviceManager";
private static final boolean DEBUG = BluetoothUtils.D;
+ @VisibleForTesting static int sLateBondingTimeoutMillis = 5000; // 5s
+
private Context mContext;
private final LocalBluetoothManager mBtManager;
@@ -47,6 +50,7 @@
@VisibleForTesting
CsipDeviceManager mCsipDeviceManager;
BluetoothDevice mOngoingSetMemberPair;
+ boolean mIsLateBonding;
public CachedBluetoothDeviceManager(Context context, LocalBluetoothManager localBtManager) {
mContext = context;
@@ -309,6 +313,7 @@
// To clear the SetMemberPair flag when the Bluetooth is turning off.
mOngoingSetMemberPair = null;
+ mIsLateBonding = false;
}
}
@@ -377,15 +382,53 @@
private synchronized boolean shouldPairByCsip(BluetoothDevice device, int groupId) {
boolean isOngoingSetMemberPair = mOngoingSetMemberPair != null;
int bondState = device.getBondState();
- if (isOngoingSetMemberPair || bondState != BluetoothDevice.BOND_NONE
- || !mCsipDeviceManager.isExistedGroupId(groupId)) {
- Log.d(TAG, "isOngoingSetMemberPair: " + isOngoingSetMemberPair
- + " , device.getBondState: " + bondState);
+ boolean groupExists = mCsipDeviceManager.isExistedGroupId(groupId);
+ Log.d(TAG,
+ "isOngoingSetMemberPair=" + isOngoingSetMemberPair + ", bondState=" + bondState
+ + ", groupExists=" + groupExists + ", groupId=" + groupId);
+
+ if (isOngoingSetMemberPair || bondState != BluetoothDevice.BOND_NONE || !groupExists) {
return false;
}
return true;
}
+ private synchronized boolean checkLateBonding(int groupId) {
+ CachedBluetoothDevice firstDevice = mCsipDeviceManager.getFirstMemberDevice(groupId);
+ if (firstDevice == null) {
+ Log.d(TAG, "No first device in group: " + groupId);
+ return false;
+ }
+
+ Timestamp then = firstDevice.getBondTimestamp();
+ if (then == null) {
+ Log.d(TAG, "No bond timestamp");
+ return true;
+ }
+
+ Timestamp now = new Timestamp(System.currentTimeMillis());
+
+ long diff = (now.getTime() - then.getTime());
+ Log.d(TAG, "Time difference to first bonding: " + diff + "ms");
+
+ return diff > sLateBondingTimeoutMillis;
+ }
+
+ /**
+ * Called to check if there is an ongoing bonding for the device and it is late bonding.
+ * If the device is not matching the ongoing bonding device then false will be returned.
+ *
+ * @param device The device to check.
+ */
+ public synchronized boolean isLateBonding(BluetoothDevice device) {
+ if (!isOngoingPairByCsip(device)) {
+ Log.d(TAG, "isLateBonding: pair not ongoing or not matching device");
+ return false;
+ }
+
+ return mIsLateBonding;
+ }
+
/**
* Called when we found a set member of a group. The function will check the {@code groupId} if
* it exists and the bond state of the device is BOND_NONE, and if there isn't any ongoing pair
@@ -398,12 +441,14 @@
if (!shouldPairByCsip(device, groupId)) {
return;
}
- Log.d(TAG, "Bond " + device.getAnonymizedAddress() + " by CSIP");
+ Log.d(TAG, "Bond " + device.getAnonymizedAddress() + " groupId=" + groupId + " by CSIP ");
mOngoingSetMemberPair = device;
+ mIsLateBonding = checkLateBonding(groupId);
syncConfigFromMainDevice(device, groupId);
if (!device.createBond(BluetoothDevice.TRANSPORT_LE)) {
Log.d(TAG, "Bonding could not be started");
mOngoingSetMemberPair = null;
+ mIsLateBonding = false;
}
}
@@ -439,7 +484,7 @@
* function, and would not like to update the UI. If not, return {@code false}.
*/
public synchronized boolean onBondStateChangedIfProcess(BluetoothDevice device, int bondState) {
- if (mOngoingSetMemberPair == null || !mOngoingSetMemberPair.equals(device)) {
+ if (!isOngoingPairByCsip(device)) {
return false;
}
@@ -448,6 +493,7 @@
}
mOngoingSetMemberPair = null;
+ mIsLateBonding = false;
if (bondState != BluetoothDevice.BOND_NONE) {
if (findDevice(device) == null) {
final LocalBluetoothProfileManager profileManager = mBtManager.getProfileManager();
@@ -471,7 +517,7 @@
* {@code false}.
*/
public boolean isOngoingPairByCsip(BluetoothDevice device) {
- return !(mOngoingSetMemberPair == null) && mOngoingSetMemberPair.equals(device);
+ return mOngoingSetMemberPair != null && mOngoingSetMemberPair.equals(device);
}
private void log(String msg) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
index 8269b56..3a6da2c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
@@ -241,6 +241,17 @@
return groupDevicesList;
}
+ public CachedBluetoothDevice getFirstMemberDevice(int groupId) {
+ List<CachedBluetoothDevice> members = getGroupDevicesFromAllOfDevicesList(groupId);
+ if (members.isEmpty())
+ return null;
+
+ CachedBluetoothDevice firstMember = members.get(0);
+ log("getFirstMemberDevice: groupId=" + groupId
+ + " address=" + firstMember.getDevice().getAnonymizedAddress());
+ return firstMember;
+ }
+
@VisibleForTesting
CachedBluetoothDevice getPreferredMainDevice(int groupId,
List<CachedBluetoothDevice> groupDevicesList) {
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 dce1e20..0637e5d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
@@ -43,6 +43,7 @@
import android.telephony.ServiceState;
import android.text.TextUtils;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -65,6 +66,7 @@
@Config(shadows = {UtilsTest.ShadowSecure.class, UtilsTest.ShadowLocationManager.class})
public class UtilsTest {
private static final double[] TEST_PERCENTAGES = {0, 0.4, 0.5, 0.6, 49, 49.3, 49.8, 50, 100};
+ private static final String TAG = "UtilsTest";
private static final String PERCENTAGE_0 = "0%";
private static final String PERCENTAGE_1 = "1%";
private static final String PERCENTAGE_49 = "49%";
@@ -96,6 +98,12 @@
mAudioManager = mContext.getSystemService(AudioManager.class);
}
+ @After
+ public void reset() {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Utils.INCOMPATIBLE_CHARGER_WARNING_DISABLED, 0);
+ }
+
@Test
public void testUpdateLocationEnabled() {
int currentUserId = ActivityManager.getCurrentUser();
@@ -427,13 +435,13 @@
@Test
public void containsIncompatibleChargers_nullPorts_returnFalse() {
when(mUsbManager.getPorts()).thenReturn(null);
- assertThat(Utils.containsIncompatibleChargers(mContext, "tag")).isFalse();
+ assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isFalse();
}
@Test
public void containsIncompatibleChargers_emptyPorts_returnFalse() {
when(mUsbManager.getPorts()).thenReturn(new ArrayList<>());
- assertThat(Utils.containsIncompatibleChargers(mContext, "tag")).isFalse();
+ assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isFalse();
}
@Test
@@ -443,13 +451,13 @@
when(mUsbManager.getPorts()).thenReturn(usbPorts);
when(mUsbPort.getStatus()).thenReturn(null);
- assertThat(Utils.containsIncompatibleChargers(mContext, "tag")).isFalse();
+ assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isFalse();
}
@Test
public void containsIncompatibleChargers_returnTrue() {
setupIncompatibleCharging();
- assertThat(Utils.containsIncompatibleChargers(mContext, "tag")).isTrue();
+ assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isTrue();
}
@Test
@@ -457,7 +465,7 @@
setupIncompatibleCharging();
when(mUsbPortStatus.getComplianceWarnings()).thenReturn(new int[1]);
- assertThat(Utils.containsIncompatibleChargers(mContext, "tag")).isFalse();
+ assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isFalse();
}
@Test
@@ -465,7 +473,7 @@
setupIncompatibleCharging();
when(mUsbPort.supportsComplianceWarnings()).thenReturn(false);
- assertThat(Utils.containsIncompatibleChargers(mContext, "tag")).isFalse();
+ assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isFalse();
}
@Test
@@ -473,7 +481,16 @@
setupIncompatibleCharging();
when(mUsbPortStatus.isConnected()).thenReturn(false);
- assertThat(Utils.containsIncompatibleChargers(mContext, "tag")).isFalse();
+ assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isFalse();
+ }
+
+ @Test
+ public void containsIncompatibleChargers_disableWarning_returnFalse() {
+ setupIncompatibleCharging();
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Utils.INCOMPATIBLE_CHARGER_WARNING_DISABLED, 1);
+
+ assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isFalse();
}
private void setupIncompatibleCharging() {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 1fd84c7..a83bfda 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -785,12 +785,6 @@
Settings.Global.ANGLE_EGL_FEATURES,
GlobalSettingsProto.Gpu.ANGLE_EGL_FEATURES);
dumpSetting(s, p,
- Settings.Global.ANGLE_DEFERLIST,
- GlobalSettingsProto.Gpu.ANGLE_DEFERLIST);
- dumpSetting(s, p,
- Settings.Global.ANGLE_DEFERLIST_MODE,
- GlobalSettingsProto.Gpu.ANGLE_DEFERLIST_MODE);
- dumpSetting(s, p,
Settings.Global.SHOW_ANGLE_IN_USE_DIALOG_BOX,
GlobalSettingsProto.Gpu.SHOW_ANGLE_IN_USE_DIALOG);
dumpSetting(s, p,
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index ef4b814..873b434 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -518,8 +518,6 @@
Settings.Global.ANGLE_GL_DRIVER_SELECTION_PKGS,
Settings.Global.ANGLE_GL_DRIVER_SELECTION_VALUES,
Settings.Global.ANGLE_EGL_FEATURES,
- Settings.Global.ANGLE_DEFERLIST,
- Settings.Global.ANGLE_DEFERLIST_MODE,
Settings.Global.UPDATABLE_DRIVER_ALL_APPS,
Settings.Global.UPDATABLE_DRIVER_PRODUCTION_OPT_IN_APPS,
Settings.Global.UPDATABLE_DRIVER_PRERELEASE_OPT_IN_APPS,
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/WeatherData.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/WeatherData.kt
index 2dd146c5..a4b1cee 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/WeatherData.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/WeatherData.kt
@@ -1,6 +1,7 @@
package com.android.systemui.plugins
import android.os.Bundle
+import android.util.Log
import androidx.annotation.VisibleForTesting
class WeatherData
@@ -11,6 +12,7 @@
val temperature: Int,
) {
companion object {
+ const val DEBUG = true
private const val TAG = "WeatherData"
@VisibleForTesting const val DESCRIPTION_KEY = "description"
@VisibleForTesting const val STATE_KEY = "state"
@@ -23,20 +25,29 @@
val state =
WeatherStateIcon.fromInt(extras.getInt(STATE_KEY, INVALID_WEATHER_ICON_STATE))
val temperature = readIntFromBundle(extras, TEMPERATURE_KEY)
- return if (
+ if (
description == null ||
state == null ||
!extras.containsKey(USE_CELSIUS_KEY) ||
temperature == null
- )
- null
- else
- WeatherData(
- description = description,
- state = state,
- useCelsius = extras.getBoolean(USE_CELSIUS_KEY),
- temperature = temperature
- )
+ ) {
+ if (DEBUG) {
+ Log.w(TAG, "Weather data did not parse from $extras")
+ }
+ return null
+ } else {
+ val result =
+ WeatherData(
+ description = description,
+ state = state,
+ useCelsius = extras.getBoolean(USE_CELSIUS_KEY),
+ temperature = temperature
+ )
+ if (DEBUG) {
+ Log.i(TAG, "Weather data parsed $result from $extras")
+ }
+ return result
+ }
}
private fun readIntFromBundle(extras: Bundle, key: String): Int? =
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_security_container_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_security_container_view.xml
index 751b07a..dc58d50 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_security_container_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_security_container_view.xml
@@ -19,9 +19,7 @@
<com.android.keyguard.KeyguardSecurityContainer
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:id="@+id/keyguard_security_container"
- android:background="?androidprv:attr/materialColorSurfaceContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 271fab1..a62dead 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1730,15 +1730,15 @@
<!-- Keyboard backlight indicator-->
<dimen name="backlight_indicator_root_corner_radius">48dp</dimen>
<dimen name="backlight_indicator_root_vertical_padding">8dp</dimen>
- <dimen name="backlight_indicator_root_horizontal_padding">4dp</dimen>
+ <dimen name="backlight_indicator_root_horizontal_padding">6dp</dimen>
<dimen name="backlight_indicator_icon_width">22dp</dimen>
<dimen name="backlight_indicator_icon_height">11dp</dimen>
- <dimen name="backlight_indicator_icon_left_margin">2dp</dimen>
+ <dimen name="backlight_indicator_icon_padding">10dp</dimen>
<dimen name="backlight_indicator_step_width">52dp</dimen>
<dimen name="backlight_indicator_step_height">40dp</dimen>
- <dimen name="backlight_indicator_step_horizontal_margin">4dp</dimen>
+ <dimen name="backlight_indicator_step_horizontal_margin">2dp</dimen>
<dimen name="backlight_indicator_step_small_radius">4dp</dimen>
- <dimen name="backlight_indicator_step_large_radius">48dp</dimen>
+ <dimen name="backlight_indicator_step_large_radius">28dp</dimen>
<!-- Broadcast dialog -->
<dimen name="broadcast_dialog_title_img_margin_top">18dp</dimen>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index eaeaabe..e5c9461 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -203,5 +203,8 @@
<item type="id" name="log_access_dialog_allow_button" />
<item type="id" name="log_access_dialog_deny_button" />
+
+ <!-- keyboard backlight indicator-->
+ <item type="id" name="backlight_icon" />
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 7dc8afe..003f9b0 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -295,9 +295,8 @@
<string name="screenrecord_save_title">Screen recording saved</string>
<!-- Subtext for a notification shown after saving a screen recording to prompt the user to view it [CHAR_LIMIT=100] -->
<string name="screenrecord_save_text">Tap to view</string>
- <!-- A toast message shown when there is an error deleting a screen recording [CHAR LIMIT=NONE] -->
- <string name="screenrecord_delete_error">Error deleting screen recording</string>
- <!-- A toast message shown when the screen recording cannot be started due to insufficient permissions [CHAR LIMIT=NONE] -->
+ <!-- A toast message shown when there is an error saving a screen recording [CHAR LIMIT=NONE] -->
+ <string name="screenrecord_save_error">Error saving screen recording</string>
<!-- A toast message shown when the screen recording cannot be started due to a generic error [CHAR LIMIT=NONE] -->
<string name="screenrecord_start_error">Error starting screen recording</string>
diff --git a/packages/SystemUI/res/xml/media_session_collapsed.xml b/packages/SystemUI/res/xml/media_session_collapsed.xml
index 5129fc0..c053b33 100644
--- a/packages/SystemUI/res/xml/media_session_collapsed.xml
+++ b/packages/SystemUI/res/xml/media_session_collapsed.xml
@@ -63,8 +63,8 @@
android:layout_marginEnd="@dimen/qs_media_padding"
app:layout_constraintEnd_toStartOf="@id/action_button_guideline"
app:layout_constrainedWidth="true"
- app:layout_constraintTop_toBottomOf="@id/icon"
app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/header_artist"
app:layout_constraintHorizontal_bias="0" />
<Constraint
@@ -87,11 +87,12 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/qs_media_padding"
+ android:layout_marginBottom="@dimen/qs_media_padding"
android:layout_marginTop="0dp"
app:layout_constraintEnd_toStartOf="@id/action_button_guideline"
app:layout_constrainedWidth="true"
- app:layout_constraintTop_toBottomOf="@id/header_title"
app:layout_constraintStart_toEndOf="@id/media_explicit_indicator"
+ app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintVertical_bias="0" />
<Constraint
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
index 1fbf743..74c325d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -352,7 +352,8 @@
@Override public TaskSnapshot screenshotTask(int taskId) {
try {
- return ActivityTaskManager.getService().takeTaskSnapshot(taskId);
+ return ActivityTaskManager.getService().takeTaskSnapshot(taskId,
+ true /* updateCache */);
} catch (RemoteException e) {
Log.e(TAG, "Failed to screenshot task", e);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index a8f2804..8ea4c31a 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -23,6 +23,7 @@
import android.content.res.Resources
import android.text.format.DateFormat
import android.util.TypedValue
+import android.util.Log
import android.view.View
import android.view.View.OnAttachStateChangeListener
import android.view.ViewTreeObserver
@@ -101,7 +102,12 @@
}
updateFontSizes()
updateTimeListeners()
- cachedWeatherData?.let { value.events.onWeatherDataChanged(it) }
+ cachedWeatherData?.let {
+ if (WeatherData.DEBUG) {
+ Log.i(TAG, "Pushing cached weather data to new clock: $it")
+ }
+ value.events.onWeatherDataChanged(it)
+ }
value.smallClock.view.addOnAttachStateChangeListener(
object : OnAttachStateChangeListener {
override fun onViewAttachedToWindow(p0: View?) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 794eeda..1db0ab6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -315,6 +315,14 @@
}
/**
+ * Returns true if the large clock will block the notification shelf in AOD
+ */
+ public boolean isLargeClockBlockingNotificationShelf() {
+ ClockController clock = mKeyguardClockSwitchController.getClock();
+ return clock != null && clock.getLargeClock().getConfig().getHasCustomWeatherDataDisplay();
+ }
+
+ /**
* Updates the alignment of the KeyguardStatusView and animates the transition if requested.
*/
public void updateAlignment(
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index 3add8c8..cabe900 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -115,7 +115,7 @@
private var overlayTouchListener: TouchExplorationStateChangeListener? = null
private val coreLayoutParams = WindowManager.LayoutParams(
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
0 /* flags set in computeLayoutParams() */,
PixelFormat.TRANSLUCENT
).apply {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
index 691017b..b2bcb05 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
@@ -232,8 +232,7 @@
// check for false tap if it is a seekbar interaction
if (interactionType == MEDIA_SEEKBAR) {
- localResult[0] &= isFalseTap(mFeatureFlags.isEnabled(Flags.MEDIA_FALSING_PENALTY)
- ? FalsingManager.MODERATE_PENALTY : FalsingManager.LOW_PENALTY);
+ localResult[0] &= isFalseTap(FalsingManager.MODERATE_PENALTY);
}
logDebug("False Gesture (type: " + interactionType + "): " + localResult[0]);
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index f71a46c..e118fdf 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -88,8 +88,7 @@
// TODO(b/278873737): Tracking Bug
@JvmField
val LOAD_NOTIFICATIONS_BEFORE_THE_USER_SWITCH_IS_COMPLETE =
- unreleasedFlag(278873737, "load_notifications_before_the_user_switch_is_complete",
- teamfood = true)
+ releasedFlag(278873737, "load_notifications_before_the_user_switch_is_complete")
// TODO(b/277338665): Tracking Bug
@JvmField
@@ -218,6 +217,7 @@
)
/** Whether to use a new data source for intents to run on keyguard dismissal. */
+ // TODO(b/275069969): Tracking bug.
@JvmField
val REFACTOR_KEYGUARD_DISMISS_INTENT = unreleasedFlag(231, "refactor_keyguard_dismiss_intent")
@@ -248,6 +248,11 @@
@JvmField
val MIGRATE_INDICATION_AREA = unreleasedFlag(236, "migrate_indication_area")
+ /** Whether to listen for fingerprint authentication over keyguard occluding activities. */
+ // TODO(b/283260512): Tracking bug.
+ @JvmField
+ val FP_LISTEN_OCCLUDING_APPS = unreleasedFlag(237, "fp_listen_occluding_apps")
+
// 300 - power menu
// TODO(b/254512600): Tracking Bug
@JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
@@ -392,8 +397,6 @@
// TODO(b/254513168): Tracking Bug
@JvmField val UMO_SURFACE_RIPPLE = releasedFlag(907, "umo_surface_ripple")
- @JvmField val MEDIA_FALSING_PENALTY = releasedFlag(908, "media_falsing_media")
-
// TODO(b/261734857): Tracking Bug
@JvmField val UMO_TURBULENCE_NOISE = releasedFlag(909, "umo_turbulence_noise")
@@ -676,6 +679,10 @@
val TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK =
unreleasedFlag(2401, "trim_resources_with_background_trim_on_lock")
+ // TODO:(b/283203305): Tracking bug
+ @JvmField
+ val TRIM_FONT_CACHES_AT_UNLOCK = releasedFlag(2402, "trim_font_caches_on_unlock")
+
// 2700 - unfold transitions
// TODO(b/265764985): Tracking Bug
@Keep
@@ -724,4 +731,8 @@
// TODO(b/278761837): Tracking Bug
@JvmField
val USE_NEW_ACTIVITY_STARTER = releasedFlag(2801, name = "use_new_activity_starter")
+
+ // TODO(b/283084712): Tracking Bug
+ @JvmField
+ val IMPROVED_HUN_ANIMATIONS = unreleasedFlag(283084712, "improved_hun_animations")
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinator.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinator.kt
index 5e806b6..1f421fa 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinator.kt
@@ -21,41 +21,44 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyboard.backlight.ui.view.KeyboardBacklightDialog
+import com.android.systemui.keyboard.backlight.ui.viewmodel.BacklightDialogContentViewModel
import com.android.systemui.keyboard.backlight.ui.viewmodel.BacklightDialogViewModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
+private fun defaultCreateDialog(context: Context): (Int, Int) -> KeyboardBacklightDialog {
+ return { currentLevel: Int, maxLevel: Int ->
+ KeyboardBacklightDialog(context, currentLevel, maxLevel)
+ }
+}
+
/**
* Based on the state produced from [BacklightDialogViewModel] shows or hides keyboard backlight
* indicator
*/
@SysUISingleton
class KeyboardBacklightDialogCoordinator
-@Inject
constructor(
@Application private val applicationScope: CoroutineScope,
- private val context: Context,
private val viewModel: BacklightDialogViewModel,
+ private val createDialog: (Int, Int) -> KeyboardBacklightDialog
) {
+ @Inject
+ constructor(
+ @Application applicationScope: CoroutineScope,
+ context: Context,
+ viewModel: BacklightDialogViewModel
+ ) : this(applicationScope, viewModel, defaultCreateDialog(context))
+
var dialog: KeyboardBacklightDialog? = null
fun startListening() {
applicationScope.launch {
- viewModel.dialogContent.collect { dialogViewModel ->
- if (dialogViewModel != null) {
- if (dialog == null) {
- dialog =
- KeyboardBacklightDialog(
- context,
- initialCurrentLevel = dialogViewModel.currentValue,
- initialMaxLevel = dialogViewModel.maxValue
- )
- dialog?.show()
- } else {
- dialog?.updateState(dialogViewModel.currentValue, dialogViewModel.maxValue)
- }
+ viewModel.dialogContent.collect { contentModel ->
+ if (contentModel != null) {
+ showDialog(contentModel)
} else {
dialog?.dismiss()
dialog = null
@@ -63,4 +66,15 @@
}
}
}
+
+ private fun showDialog(model: BacklightDialogContentViewModel) {
+ if (dialog == null) {
+ dialog = createDialog(model.currentValue, model.maxValue)
+ } else {
+ dialog?.updateState(model.currentValue, model.maxValue)
+ }
+ // let's always show dialog - even if we're just updating it, it might have been dismissed
+ // externally by tapping finger outside of it
+ dialog?.show()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt
index d3678b5..7078341 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt
@@ -22,9 +22,12 @@
import android.app.Dialog
import android.content.Context
import android.graphics.drawable.ShapeDrawable
+import android.graphics.drawable.shapes.OvalShape
import android.graphics.drawable.shapes.RoundRectShape
import android.os.Bundle
import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup.MarginLayoutParams
import android.view.Window
import android.view.WindowManager
import android.widget.FrameLayout
@@ -32,9 +35,10 @@
import android.widget.LinearLayout
import android.widget.LinearLayout.LayoutParams
import android.widget.LinearLayout.LayoutParams.WRAP_CONTENT
+import androidx.annotation.IdRes
+import androidx.core.view.setPadding
import com.android.settingslib.Utils
import com.android.systemui.R
-import com.android.systemui.util.children
class KeyboardBacklightDialog(
context: Context,
@@ -51,7 +55,7 @@
private data class BacklightIconProperties(
val width: Int,
val height: Int,
- val leftMargin: Int,
+ val padding: Int,
)
private data class StepViewProperties(
@@ -71,6 +75,7 @@
private lateinit var rootProperties: RootProperties
private lateinit var iconProperties: BacklightIconProperties
private lateinit var stepProperties: StepViewProperties
+
@ColorInt
var filledRectangleColor = getColorFromStyle(com.android.internal.R.attr.materialColorPrimary)
@ColorInt
@@ -78,7 +83,16 @@
getColorFromStyle(com.android.internal.R.attr.materialColorOutlineVariant)
@ColorInt
var backgroundColor = getColorFromStyle(com.android.internal.R.attr.materialColorSurfaceBright)
- @ColorInt var iconColor = getColorFromStyle(com.android.internal.R.attr.materialColorOnPrimary)
+ @ColorInt
+ var defaultIconColor = getColorFromStyle(com.android.internal.R.attr.materialColorOnPrimary)
+ @ColorInt
+ var defaultIconBackgroundColor =
+ getColorFromStyle(com.android.internal.R.attr.materialColorPrimary)
+ @ColorInt
+ var dimmedIconColor = getColorFromStyle(com.android.internal.R.attr.materialColorOnSurface)
+ @ColorInt
+ var dimmedIconBackgroundColor =
+ getColorFromStyle(com.android.internal.R.attr.materialColorSurfaceDim)
init {
currentLevel = initialCurrentLevel
@@ -111,8 +125,7 @@
BacklightIconProperties(
width = getDimensionPixelSize(R.dimen.backlight_indicator_icon_width),
height = getDimensionPixelSize(R.dimen.backlight_indicator_icon_height),
- leftMargin =
- getDimensionPixelSize(R.dimen.backlight_indicator_icon_left_margin),
+ padding = getDimensionPixelSize(R.dimen.backlight_indicator_icon_padding),
)
stepProperties =
StepViewProperties(
@@ -139,23 +152,34 @@
if (maxLevel != max || forceRefresh) {
maxLevel = max
rootView.removeAllViews()
+ rootView.addView(buildIconTile())
buildStepViews().forEach { rootView.addView(it) }
}
currentLevel = current
- updateLevel()
+ updateIconTile()
+ updateStepColors()
}
- private fun updateLevel() {
- rootView.children.forEachIndexed(
- action = { index, v ->
- val drawable = v.background as ShapeDrawable
- if (index <= currentLevel) {
- updateColor(drawable, filledRectangleColor)
- } else {
- updateColor(drawable, emptyRectangleColor)
- }
- }
- )
+ private fun updateIconTile() {
+ val iconTile = rootView.findViewById(BACKLIGHT_ICON_ID) as ImageView
+ val backgroundDrawable = iconTile.background as ShapeDrawable
+ if (currentLevel == 0) {
+ iconTile.setColorFilter(dimmedIconColor)
+ updateColor(backgroundDrawable, dimmedIconBackgroundColor)
+ } else {
+ iconTile.setColorFilter(defaultIconColor)
+ updateColor(backgroundDrawable, defaultIconBackgroundColor)
+ }
+ }
+
+ private fun updateStepColors() {
+ (1 until rootView.childCount).forEach { index ->
+ val drawable = rootView.getChildAt(index).background as ShapeDrawable
+ updateColor(
+ drawable,
+ if (index <= currentLevel) filledRectangleColor else emptyRectangleColor,
+ )
+ }
}
private fun updateColor(drawable: ShapeDrawable, @ColorInt color: Int) {
@@ -192,9 +216,33 @@
}
private fun buildStepViews(): List<FrameLayout> {
- val stepViews = (0..maxLevel).map { i -> createStepViewAt(i) }
- stepViews[0].addView(createBacklightIconView())
- return stepViews
+ return (1..maxLevel).map { i -> createStepViewAt(i) }
+ }
+
+ private fun buildIconTile(): View {
+ val diameter = stepProperties.height
+ val circleDrawable =
+ ShapeDrawable(OvalShape()).apply {
+ intrinsicHeight = diameter
+ intrinsicWidth = diameter
+ }
+
+ return ImageView(context).apply {
+ setImageResource(R.drawable.ic_keyboard_backlight)
+ id = BACKLIGHT_ICON_ID
+ setColorFilter(defaultIconColor)
+ setPadding(iconProperties.padding)
+ layoutParams =
+ MarginLayoutParams(diameter, diameter).apply {
+ setMargins(
+ /* left= */ stepProperties.horizontalMargin,
+ /* top= */ 0,
+ /* right= */ stepProperties.horizontalMargin,
+ /* bottom= */ 0
+ )
+ }
+ background = circleDrawable
+ }
}
private fun createStepViewAt(i: Int): FrameLayout {
@@ -221,18 +269,6 @@
}
}
- private fun createBacklightIconView(): ImageView {
- return ImageView(context).apply {
- setImageResource(R.drawable.ic_keyboard_backlight)
- setColorFilter(iconColor)
- layoutParams =
- FrameLayout.LayoutParams(iconProperties.width, iconProperties.height).apply {
- gravity = Gravity.CENTER
- leftMargin = iconProperties.leftMargin
- }
- }
- }
-
private fun setWindowPosition() {
window?.apply {
setGravity(Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL)
@@ -262,30 +298,29 @@
private fun radiiForIndex(i: Int, last: Int): FloatArray {
val smallRadius = stepProperties.smallRadius
val largeRadius = stepProperties.largeRadius
- return when (i) {
- 0 -> // left radii bigger
- floatArrayOf(
- largeRadius,
- largeRadius,
- smallRadius,
- smallRadius,
- smallRadius,
- smallRadius,
- largeRadius,
- largeRadius
- )
- last -> // right radii bigger
- floatArrayOf(
- smallRadius,
- smallRadius,
- largeRadius,
- largeRadius,
- largeRadius,
- largeRadius,
- smallRadius,
- smallRadius
- )
- else -> FloatArray(8) { smallRadius } // all radii equal
+ val radii = FloatArray(8) { smallRadius }
+ if (i == 1) {
+ radii.setLeftCorners(largeRadius)
}
+ // note "first" and "last" might be the same tile
+ if (i == last) {
+ radii.setRightCorners(largeRadius)
+ }
+ return radii
+ }
+
+ private fun FloatArray.setLeftCorners(radius: Float) {
+ LEFT_CORNERS_INDICES.forEach { this[it] = radius }
+ }
+ private fun FloatArray.setRightCorners(radius: Float) {
+ RIGHT_CORNERS_INDICES.forEach { this[it] = radius }
+ }
+
+ private companion object {
+ @IdRes val BACKLIGHT_ICON_ID = R.id.backlight_icon
+
+ // indices used to define corners radii in ShapeDrawable
+ val LEFT_CORNERS_INDICES: IntArray = intArrayOf(0, 1, 6, 7)
+ val RIGHT_CORNERS_INDICES: IntArray = intArrayOf(2, 3, 4, 5)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
index 8386a05b..d8affa4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
@@ -18,6 +18,7 @@
import android.annotation.WorkerThread
import android.content.ComponentCallbacks2
+import android.graphics.HardwareRenderer
import android.os.Trace
import android.util.Log
import com.android.systemui.CoreStartable
@@ -27,12 +28,13 @@
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.utils.GlobalWindowManager
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
@@ -50,6 +52,7 @@
@Inject
constructor(
private val keyguardInteractor: KeyguardInteractor,
+ private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val globalWindowManager: GlobalWindowManager,
@Application private val applicationScope: CoroutineScope,
@Background private val bgDispatcher: CoroutineDispatcher,
@@ -58,7 +61,10 @@
override fun start() {
Log.d(LOG_TAG, "Resource trimmer registered.")
- if (!featureFlags.isEnabled(Flags.TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)) {
+ if (
+ !(featureFlags.isEnabled(Flags.TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK) ||
+ featureFlags.isEnabled(Flags.TRIM_FONT_CACHES_AT_UNLOCK))
+ ) {
return
}
@@ -78,6 +84,30 @@
.distinctUntilChanged()
.collect { onWakefulnessUpdated(it.first, it.second, it.third) }
}
+
+ applicationScope.launch(bgDispatcher) {
+ // We drop 1 to avoid triggering on initial collect().
+ keyguardTransitionInteractor.anyStateToGoneTransition.collect { transition ->
+ if (transition.transitionState == TransitionState.FINISHED) {
+ onKeyguardGone()
+ }
+ }
+ }
+ }
+
+ @WorkerThread
+ private fun onKeyguardGone() {
+ if (!featureFlags.isEnabled(Flags.TRIM_FONT_CACHES_AT_UNLOCK)) {
+ return
+ }
+
+ if (DEBUG) {
+ Log.d(LOG_TAG, "Trimming font caches since keyguard went away.")
+ }
+ // We want to clear temporary caches we've created while rendering and animating
+ // lockscreen elements, especially clocks.
+ globalWindowManager.trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN)
+ globalWindowManager.trimCaches(HardwareRenderer.CACHE_TRIM_FONT)
}
@WorkerThread
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
index 5079487..1469d96 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
@@ -786,10 +786,10 @@
// Song name
var song: CharSequence? = metadata?.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE)
- if (song == null) {
+ if (song.isNullOrBlank()) {
song = metadata?.getString(MediaMetadata.METADATA_KEY_TITLE)
}
- if (song == null) {
+ if (song.isNullOrBlank()) {
song = HybridGroupManager.resolveTitle(notif)
}
if (song.isNullOrBlank()) {
@@ -846,7 +846,7 @@
// Artist name
var artist: CharSequence? = metadata?.getString(MediaMetadata.METADATA_KEY_ARTIST)
- if (artist == null) {
+ if (artist.isNullOrBlank()) {
artist = HybridGroupManager.resolveText(notif)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
index f8e3ecb..32a7935 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
@@ -617,10 +617,7 @@
seamlessView.setContentDescription(deviceString);
seamlessView.setOnClickListener(
v -> {
- if (mFalsingManager.isFalseTap(
- mFeatureFlags.isEnabled(Flags.MEDIA_FALSING_PENALTY)
- ? FalsingManager.MODERATE_PENALTY :
- FalsingManager.LOW_PENALTY)) {
+ if (mFalsingManager.isFalseTap(FalsingManager.MODERATE_PENALTY)) {
return;
}
@@ -1130,10 +1127,7 @@
} else {
button.setEnabled(true);
button.setOnClickListener(v -> {
- if (!mFalsingManager.isFalseTap(
- mFeatureFlags.isEnabled(Flags.MEDIA_FALSING_PENALTY)
- ? FalsingManager.MODERATE_PENALTY :
- FalsingManager.LOW_PENALTY)) {
+ if (!mFalsingManager.isFalseTap(FalsingManager.MODERATE_PENALTY)) {
mLogger.logTapAction(button.getId(), mUid, mPackageName, mInstanceId);
logSmartspaceCardReported(SMARTSPACE_CARD_CLICK_EVENT);
action.run();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index b476521..b936c41 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -522,7 +522,7 @@
return mExpanded;
}
- void addTile(QSPanelControllerBase.TileRecord tileRecord) {
+ final void addTile(QSPanelControllerBase.TileRecord tileRecord) {
final QSTile.Callback callback = new QSTile.Callback() {
@Override
public void onStateChanged(QSTile.State state) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index fdab9b1..20f0352 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -199,7 +199,7 @@
mMediaHost.removeVisibilityChangeListener(mMediaHostVisibilityListener);
for (TileRecord record : mRecords) {
- record.tile.removeCallbacks();
+ record.tile.removeCallback(record.callback);
}
mRecords.clear();
mDumpManager.unregisterDumpable(mView.getDumpableTag());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
index 91c6e8b..c579f5c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
@@ -318,7 +318,6 @@
// We have a handful of different cases
qsTile !is CustomTile -> {
// The tile is not a custom tile. Make sure they are reset to the correct user
- qsTile.removeCallbacks()
if (userChanged) {
qsTile.userSwitch(user)
logger.logTileUserChanged(tileSpec, user)
@@ -327,7 +326,6 @@
}
qsTile.user == user -> {
// The tile is a custom tile for the same user, just return it
- qsTile.removeCallbacks()
qsTile
}
else -> {
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index 84f358c..e1ac0fd 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -453,9 +453,9 @@
postGroupNotification(currentUser);
mNotificationManager.notifyAsUser(null, mNotificationId, notification,
currentUser);
- } catch (IOException e) {
+ } catch (IOException | IllegalStateException e) {
Log.e(TAG, "Error saving screen recording: " + e.getMessage());
- showErrorToast(R.string.screenrecord_delete_error);
+ showErrorToast(R.string.screenrecord_save_error);
mNotificationManager.cancelAsUser(null, mNotificationId, currentUser);
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
index b8d96f7..b80a01212 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
@@ -52,8 +52,9 @@
import android.view.WindowManager;
import com.android.systemui.media.MediaProjectionCaptureTarget;
-import java.io.File;
+
import java.io.Closeable;
+import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
@@ -321,7 +322,7 @@
/**
* Store recorded video
*/
- protected SavedRecording save() throws IOException {
+ protected SavedRecording save() throws IOException, IllegalStateException {
String fileName = new SimpleDateFormat("'screen-'yyyyMMdd-HHmmss'.mp4'")
.format(new Date());
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingMuxer.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingMuxer.java
index 7ffcfd4..dc3310d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingMuxer.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingMuxer.java
@@ -52,9 +52,8 @@
/**
* RUN IN THE BACKGROUND THREAD!
*/
- public void mux() throws IOException {
- MediaMuxer muxer = null;
- muxer = new MediaMuxer(mOutFile, mFormat);
+ public void mux() throws IOException, IllegalStateException {
+ MediaMuxer muxer = new MediaMuxer(mOutFile, mFormat);
// Add extractors
for (String file: mFiles) {
MediaExtractor extractor = new MediaExtractor();
@@ -74,7 +73,10 @@
}
}
+ // This may throw IllegalStateException if no tracks were added above
+ // Let the error propagate up so we can notify the user.
muxer.start();
+
for (Pair<MediaExtractor, Integer> pair: mExtractorIndexToMuxerIndex.keySet()) {
MediaExtractor extractor = pair.first;
extractor.selectTrack(pair.second);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt
index 67e9a87..2e47ab6 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt
@@ -48,7 +48,9 @@
}
override suspend fun captureTask(taskId: Int): Bitmap? {
- val snapshot = withContext(bgContext) { atmService.takeTaskSnapshot(taskId) } ?: return null
+ val snapshot = withContext(bgContext) {
+ atmService.takeTaskSnapshot(taskId, false /* updateCache */)
+ } ?: return null
return Bitmap.wrapHardwareBuffer(snapshot.hardwareBuffer, snapshot.colorSpace)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index fbf134d..5fb3c01 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -166,21 +166,19 @@
}
override fun onUserSwitching(newUserId: Int, reply: IRemoteCallback?) {
- backgroundHandler.run {
- handleUserSwitching(newUserId)
- reply?.sendResult(null)
- }
+ handleUserSwitching(newUserId)
+ reply?.sendResult(null)
}
override fun onUserSwitchComplete(newUserId: Int) {
- backgroundHandler.run {
- handleUserSwitchComplete(newUserId)
- }
+ handleUserSwitchComplete(newUserId)
}
}, TAG)
}
+ @WorkerThread
protected open fun handleBeforeUserSwitching(newUserId: Int) {
+ Assert.isNotMainThread()
setUserIdInternal(newUserId)
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 0fdd7ca..784a360 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -1570,6 +1570,12 @@
// When media is visible, it overlaps with the large clock. Use small clock instead.
return SMALL;
}
+ // To prevent the weather clock from overlapping with the notification shelf on AOD, we use
+ // the small clock here
+ if (mKeyguardStatusViewController.isLargeClockBlockingNotificationShelf()
+ && hasVisibleNotifications() && isOnAod()) {
+ return SMALL;
+ }
return LARGE;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index df68e7e..0414a14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -23,7 +23,6 @@
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
-import android.content.ComponentCallbacks2;
import android.content.Context;
import android.content.res.ColorStateList;
import android.hardware.biometrics.BiometricSourceType;
@@ -36,7 +35,6 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewRootImpl;
-import android.view.WindowManagerGlobal;
import android.window.BackEvent;
import android.window.OnBackAnimationCallback;
import android.window.OnBackInvokedDispatcher;
@@ -985,8 +983,6 @@
mShadeViewController.resetViewGroupFade();
mCentralSurfaces.finishKeyguardFadingAway();
mBiometricUnlockController.finishKeyguardFadingAway();
- WindowManagerGlobal.getInstance().trimMemory(
- ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
}
private void wakeAndUnlockDejank() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index 9ede6ce..ed8050a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -140,7 +140,13 @@
}
protected boolean shouldHeadsUpBecomePinned(@NonNull NotificationEntry entry) {
- return hasFullScreenIntent(entry);
+ final HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.getKey());
+ if (headsUpEntry == null) {
+ // This should not happen since shouldHeadsUpBecomePinned is always called after adding
+ // the NotificationEntry into AlertingNotificationManager's mAlertEntries map.
+ return hasFullScreenIntent(entry);
+ }
+ return hasFullScreenIntent(entry) && !headsUpEntry.wasUnpinned;
}
protected boolean hasFullScreenIntent(@NonNull NotificationEntry entry) {
@@ -151,6 +157,9 @@
@NonNull HeadsUpManager.HeadsUpEntry headsUpEntry, boolean isPinned) {
mLogger.logSetEntryPinned(headsUpEntry.mEntry, isPinned);
NotificationEntry entry = headsUpEntry.mEntry;
+ if (!isPinned) {
+ headsUpEntry.wasUnpinned = true;
+ }
if (entry.isRowPinned() != isPinned) {
entry.setRowPinned(isPinned);
updatePinnedMode();
@@ -177,7 +186,9 @@
protected void onAlertEntryAdded(AlertEntry alertEntry) {
NotificationEntry entry = alertEntry.mEntry;
entry.setHeadsUp(true);
- setEntryPinned((HeadsUpEntry) alertEntry, shouldHeadsUpBecomePinned(entry));
+
+ final boolean shouldPin = shouldHeadsUpBecomePinned(entry);
+ setEntryPinned((HeadsUpEntry) alertEntry, shouldPin);
EventLogTags.writeSysuiHeadsUpStatus(entry.getKey(), 1 /* visible */);
for (OnHeadsUpChangedListener listener : mListeners) {
listener.onHeadsUpStateChanged(entry, true);
@@ -411,6 +422,7 @@
protected class HeadsUpEntry extends AlertEntry {
public boolean remoteInputActive;
protected boolean expanded;
+ protected boolean wasUnpinned;
@Override
public boolean isSticky() {
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
index c72853e..7b652c1 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
@@ -29,18 +29,28 @@
import com.android.systemui.dagger.qualifiers.LongRunning;
import com.android.systemui.dagger.qualifiers.Main;
+import dagger.Module;
+import dagger.Provides;
+
import java.util.concurrent.Executor;
import javax.inject.Named;
-import dagger.Module;
-import dagger.Provides;
-
/**
* Dagger Module for classes found within the concurrent package.
*/
@Module
public abstract class SysUIConcurrencyModule {
+
+ // Slow BG executor can potentially affect UI if UI is waiting for an updated state from this
+ // thread
+ private static final Long BG_SLOW_DISPATCH_THRESHOLD = 1000L;
+ private static final Long BG_SLOW_DELIVERY_THRESHOLD = 1000L;
+ private static final Long LONG_SLOW_DISPATCH_THRESHOLD = 2500L;
+ private static final Long LONG_SLOW_DELIVERY_THRESHOLD = 2500L;
+ private static final Long BROADCAST_SLOW_DISPATCH_THRESHOLD = 1000L;
+ private static final Long BROADCAST_SLOW_DELIVERY_THRESHOLD = 1000L;
+
/** Background Looper */
@Provides
@SysUISingleton
@@ -49,6 +59,8 @@
HandlerThread thread = new HandlerThread("SysUiBg",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
+ thread.getLooper().setSlowLogThresholdMs(BG_SLOW_DISPATCH_THRESHOLD,
+ BG_SLOW_DELIVERY_THRESHOLD);
return thread.getLooper();
}
@@ -60,6 +72,8 @@
HandlerThread thread = new HandlerThread("BroadcastRunning",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
+ thread.getLooper().setSlowLogThresholdMs(BROADCAST_SLOW_DISPATCH_THRESHOLD,
+ BROADCAST_SLOW_DELIVERY_THRESHOLD);
return thread.getLooper();
}
@@ -71,6 +85,8 @@
HandlerThread thread = new HandlerThread("SysUiLng",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
+ thread.getLooper().setSlowLogThresholdMs(LONG_SLOW_DISPATCH_THRESHOLD,
+ LONG_SLOW_DELIVERY_THRESHOLD);
return thread.getLooper();
}
diff --git a/packages/SystemUI/src/com/android/systemui/utils/GlobalWindowManager.kt b/packages/SystemUI/src/com/android/systemui/utils/GlobalWindowManager.kt
index 038fddc..4111850 100644
--- a/packages/SystemUI/src/com/android/systemui/utils/GlobalWindowManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/utils/GlobalWindowManager.kt
@@ -1,5 +1,6 @@
package com.android.systemui.utils
+import android.graphics.HardwareRenderer.CacheTrimLevel
import android.view.WindowManagerGlobal
import javax.inject.Inject
@@ -13,4 +14,9 @@
fun trimMemory(level: Int) {
WindowManagerGlobal.getInstance().trimMemory(level)
}
+
+ /** Sends a trim caches command to [WindowManagerGlobal]. */
+ fun trimCaches(@CacheTrimLevel level: Int) {
+ WindowManagerGlobal.getInstance().trimCaches(level)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
index 4cb99a2..6afbde0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
@@ -120,7 +120,6 @@
gestureCompleteListenerCaptor.capture());
mGestureFinalizedListener = gestureCompleteListenerCaptor.getValue();
- mFakeFeatureFlags.set(Flags.MEDIA_FALSING_PENALTY, true);
mFakeFeatureFlags.set(Flags.FALSING_OFF_FOR_UNFOLDED, true);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinatorTest.kt
new file mode 100644
index 0000000..7207fbf
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinatorTest.kt
@@ -0,0 +1,158 @@
+/*
+ * 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.systemui.keyboard.backlight.ui
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyboard.backlight.domain.interactor.KeyboardBacklightInteractor
+import com.android.systemui.keyboard.backlight.ui.view.KeyboardBacklightDialog
+import com.android.systemui.keyboard.backlight.ui.viewmodel.BacklightDialogViewModel
+import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository
+import com.android.systemui.keyboard.shared.model.BacklightModel
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class KeyboardBacklightDialogCoordinatorTest : SysuiTestCase() {
+
+ @Mock private lateinit var accessibilityManagerWrapper: AccessibilityManagerWrapper
+ @Mock private lateinit var dialog: KeyboardBacklightDialog
+
+ private val keyboardRepository = FakeKeyboardRepository()
+ private lateinit var underTest: KeyboardBacklightDialogCoordinator
+ private val timeoutMillis = 3000L
+ private val testScope = TestScope(StandardTestDispatcher())
+
+ private val createDialog = { value: Int, maxValue: Int ->
+ dialogCreationValue = value
+ dialogCreationMaxValue = maxValue
+ dialog
+ }
+ private var dialogCreationValue = -1
+ private var dialogCreationMaxValue = -1
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ whenever(accessibilityManagerWrapper.getRecommendedTimeoutMillis(any(), any()))
+ .thenReturn(timeoutMillis.toInt())
+ val viewModel =
+ BacklightDialogViewModel(
+ KeyboardBacklightInteractor(keyboardRepository),
+ accessibilityManagerWrapper
+ )
+ underTest =
+ KeyboardBacklightDialogCoordinator(testScope.backgroundScope, viewModel, createDialog)
+ underTest.startListening()
+ keyboardRepository.setIsAnyKeyboardConnected(true)
+ }
+
+ @Test
+ fun showsDialog_afterBacklightChange() =
+ testScope.runTest {
+ setBacklightValue(1)
+
+ verify(dialog).show()
+ }
+
+ @Test
+ fun updatesDialog_withLatestValues_afterBacklightChange() =
+ testScope.runTest {
+ setBacklightValue(value = 1, maxValue = 5)
+ setBacklightValue(value = 2, maxValue = 5)
+
+ verify(dialog).updateState(2, 5)
+ }
+
+ @Test
+ fun showsDialog_withDataFromBacklightChange() =
+ testScope.runTest {
+ setBacklightValue(value = 4, maxValue = 5)
+
+ Truth.assertThat(dialogCreationValue).isEqualTo(4)
+ Truth.assertThat(dialogCreationMaxValue).isEqualTo(5)
+ }
+
+ @Test
+ fun dismissesDialog_afterTimeout() =
+ testScope.runTest {
+ setBacklightValue(1)
+
+ advanceTimeBy(timeoutMillis + 1)
+
+ verify(dialog).dismiss()
+ }
+
+ @Test
+ fun dismissesDialog_onlyAfterTimeout_fromLastBacklightChange() =
+ testScope.runTest {
+ setBacklightValue(1)
+ advanceTimeBy(timeoutMillis * 2 / 3)
+ // majority of timeout passed
+
+ // this should restart timeout
+ setBacklightValue(2)
+ advanceTimeBy(timeoutMillis * 2 / 3)
+ verify(dialog, never()).dismiss()
+
+ advanceTimeBy(timeoutMillis * 2 / 3)
+ // finally timeout reached and dialog was dismissed
+ verify(dialog, times(1)).dismiss()
+ }
+
+ @Test
+ fun showsDialog_ifItWasAlreadyShownAndDismissedBySomethingElse() =
+ testScope.runTest {
+ setBacklightValue(1)
+ // let's pretend dialog is dismissed e.g. by user tapping on the screen
+ whenever(dialog.isShowing).thenReturn(false)
+
+ // no advancing time, we're still in timeout period
+ setBacklightValue(2)
+
+ verify(dialog, times(2)).show()
+ }
+
+ private fun TestScope.setBacklightValue(value: Int, maxValue: Int = MAX_BACKLIGHT) {
+ keyboardRepository.setBacklight(BacklightModel(value, maxValue))
+ runCurrent()
+ }
+
+ private companion object {
+ const val MAX_BACKLIGHT = 5
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogViewModelTest.kt
deleted file mode 100644
index 1fec5a4..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogViewModelTest.kt
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * 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.systemui.keyboard.backlight.ui.viewmodel
-
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.keyboard.backlight.domain.interactor.KeyboardBacklightInteractor
-import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository
-import com.android.systemui.keyboard.shared.model.BacklightModel
-import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.whenever
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.test.advanceTimeBy
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-@RunWith(JUnit4::class)
-class BacklightDialogViewModelTest : SysuiTestCase() {
-
- private val keyboardRepository = FakeKeyboardRepository()
- private lateinit var underTest: BacklightDialogViewModel
- @Mock private lateinit var accessibilityManagerWrapper: AccessibilityManagerWrapper
- private val timeoutMillis = 3000L
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
- whenever(accessibilityManagerWrapper.getRecommendedTimeoutMillis(any(), any()))
- .thenReturn(timeoutMillis.toInt())
- underTest =
- BacklightDialogViewModel(
- KeyboardBacklightInteractor(keyboardRepository),
- accessibilityManagerWrapper
- )
- keyboardRepository.setIsAnyKeyboardConnected(true)
- }
-
- @Test
- fun emitsViewModel_whenBacklightChanged() = runTest {
- keyboardRepository.setBacklight(BacklightModel(1, 5))
-
- assertThat(underTest.dialogContent.first()).isEqualTo(BacklightDialogContentViewModel(1, 5))
- }
-
- @Test
- fun emitsNull_afterTimeout() = runTest {
- val latest by collectLastValue(underTest.dialogContent)
- keyboardRepository.setBacklight(BacklightModel(1, 5))
-
- assertThat(latest).isEqualTo(BacklightDialogContentViewModel(1, 5))
- advanceTimeBy(timeoutMillis + 1)
- assertThat(latest).isNull()
- }
-
- @Test
- fun emitsNull_after5secDelay_fromLastBacklightChange() = runTest {
- val latest by collectLastValue(underTest.dialogContent)
- keyboardRepository.setIsAnyKeyboardConnected(true)
-
- keyboardRepository.setBacklight(BacklightModel(1, 5))
- assertThat(latest).isEqualTo(BacklightDialogContentViewModel(1, 5))
-
- advanceTimeBy(timeoutMillis * 2 / 3)
- // timeout yet to pass, no new emission
- keyboardRepository.setBacklight(BacklightModel(2, 5))
- assertThat(latest).isEqualTo(BacklightDialogContentViewModel(2, 5))
-
- advanceTimeBy(timeoutMillis * 2 / 3)
- // timeout refreshed because of last `setBacklight`, still content present
- assertThat(latest).isEqualTo(BacklightDialogContentViewModel(2, 5))
-
- advanceTimeBy(timeoutMillis * 2 / 3)
- // finally timeout reached and null emitted
- assertThat(latest).isNull()
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
index 367d206..548d26f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
@@ -1,6 +1,7 @@
package com.android.systemui.keyguard
import android.content.ComponentCallbacks2
+import android.graphics.HardwareRenderer
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -9,7 +10,11 @@
import com.android.systemui.keyguard.data.repository.FakeCommandQueue
import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.shared.model.WakeSleepReason
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.keyguard.shared.model.WakefulnessState
@@ -25,6 +30,7 @@
import org.mockito.Mock
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.Mockito.verifyZeroInteractions
import org.mockito.MockitoAnnotations
@@ -37,6 +43,7 @@
private val testScope = TestScope(testDispatcher)
private val keyguardRepository = FakeKeyguardRepository()
private val featureFlags = FakeFeatureFlags()
+ private val keyguardTransitionRepository = FakeKeyguardTransitionRepository()
@Mock private lateinit var globalWindowManager: GlobalWindowManager
private lateinit var resourceTrimmer: ResourceTrimmer
@@ -45,13 +52,15 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
featureFlags.set(Flags.TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK, true)
+ featureFlags.set(Flags.TRIM_FONT_CACHES_AT_UNLOCK, true)
featureFlags.set(Flags.FACE_AUTH_REFACTOR, false)
keyguardRepository.setWakefulnessModel(
WakefulnessModel(WakefulnessState.AWAKE, WakeSleepReason.OTHER, WakeSleepReason.OTHER)
)
keyguardRepository.setDozeAmount(0f)
+ keyguardRepository.setKeyguardGoingAway(false)
- val interactor =
+ val keyguardInteractor =
KeyguardInteractor(
keyguardRepository,
FakeCommandQueue(),
@@ -60,7 +69,8 @@
)
resourceTrimmer =
ResourceTrimmer(
- interactor,
+ keyguardInteractor,
+ KeyguardTransitionInteractor(keyguardTransitionRepository),
globalWindowManager,
testScope.backgroundScope,
testDispatcher,
@@ -191,4 +201,26 @@
verifyZeroInteractions(globalWindowManager)
}
}
+
+ @Test
+ fun keyguardTransitionsToGone_trimsFontCache() =
+ testScope.runTest {
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(KeyguardState.LOCKSCREEN, KeyguardState.GONE)
+ )
+ verify(globalWindowManager, times(1))
+ .trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN)
+ verify(globalWindowManager, times(1)).trimCaches(HardwareRenderer.CACHE_TRIM_FONT)
+ verifyNoMoreInteractions(globalWindowManager)
+ }
+
+ @Test
+ fun keyguardTransitionsToGone_flagDisabled_doesNotTrimFontCache() =
+ testScope.runTest {
+ featureFlags.set(Flags.TRIM_FONT_CACHES_AT_UNLOCK, false)
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(KeyguardState.LOCKSCREEN, KeyguardState.GONE)
+ )
+ verifyNoMoreInteractions(globalWindowManager)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
index fd6e457..3bcefcf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
@@ -716,6 +716,48 @@
}
@Test
+ fun testOnNotificationAdded_emptyMetadata_usesNotificationTitle() {
+ // When the app sets the metadata title fields to empty strings, but does include a
+ // non-blank notification title
+ val mockPackageManager = mock(PackageManager::class.java)
+ context.setMockPackageManager(mockPackageManager)
+ whenever(mockPackageManager.getApplicationLabel(any())).thenReturn(APP_NAME)
+ whenever(mediaFlags.isMediaTitleRequired(any(), any())).thenReturn(true)
+ whenever(controller.metadata)
+ .thenReturn(
+ metadataBuilder
+ .putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_EMPTY_TITLE)
+ .putString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE, SESSION_EMPTY_TITLE)
+ .build()
+ )
+ mediaNotification =
+ SbnBuilder().run {
+ setPkg(PACKAGE_NAME)
+ modifyNotification(context).also {
+ it.setSmallIcon(android.R.drawable.ic_media_pause)
+ it.setContentTitle(SESSION_TITLE)
+ it.setStyle(MediaStyle().apply { setMediaSession(session.sessionToken) })
+ }
+ build()
+ }
+ mediaDataManager.onNotificationAdded(KEY, mediaNotification)
+
+ // Then the media control is added using the notification's title
+ assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
+ assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+ verify(listener)
+ .onMediaDataLoaded(
+ eq(KEY),
+ eq(null),
+ capture(mediaDataCaptor),
+ eq(true),
+ eq(0),
+ eq(false)
+ )
+ assertThat(mediaDataCaptor.value.song).isEqualTo(SESSION_TITLE)
+ }
+
+ @Test
fun testOnNotificationRemoved_emptyTitle_notConverted() {
// GIVEN that the manager has a notification with a resume action and empty title.
addNotificationAndLoad()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
index ba6f536..f030a03 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
@@ -230,7 +230,6 @@
FakeFeatureFlags().apply {
this.set(Flags.UMO_SURFACE_RIPPLE, false)
this.set(Flags.UMO_TURBULENCE_NOISE, false)
- this.set(Flags.MEDIA_FALSING_PENALTY, true)
this.set(Flags.MEDIA_EXPLICIT_INDICATOR, true)
this.set(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE, false)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index 3d55c51..6720dae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -321,4 +321,30 @@
assertThat(mController.shouldUseHorizontalLayout()).isFalse();
verify(mHorizontalLayoutListener).run();
}
+
+ @Test
+ public void changeTiles_callbackRemovedOnOldOnes() {
+ // Start with one tile
+ assertThat(mController.mRecords.size()).isEqualTo(1);
+ QSPanelControllerBase.TileRecord record = mController.mRecords.get(0);
+
+ assertThat(record.tile).isEqualTo(mQSTile);
+
+ // Change to a different tile
+ when(mQSHost.getTiles()).thenReturn(List.of(mOtherTile));
+ mController.setTiles();
+
+ verify(mQSTile).removeCallback(record.callback);
+ verify(mOtherTile, never()).removeCallback(any());
+ verify(mOtherTile, never()).removeCallbacks();
+ }
+
+ @Test
+ public void onViewDetached_removesJustTheAssociatedCallback() {
+ QSPanelControllerBase.TileRecord record = mController.mRecords.get(0);
+
+ mController.onViewDetached();
+ verify(mQSTile).removeCallback(record.callback);
+ verify(mQSTile, never()).removeCallbacks();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
index 93cebe2..a60dad4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
@@ -27,6 +27,8 @@
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.qs.QSTileView
+import com.android.systemui.qs.QSPanelControllerBase.TileRecord
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSIconViewImpl
import com.android.systemui.qs.tileimpl.QSTileViewImpl
@@ -192,6 +194,18 @@
verify(accessibilityInfo, never()).addAction(actionCollapse)
}
+ @Test
+ fun addTile_callbackAdded() {
+ val tile = mock(QSTile::class.java)
+ val tileView = mock(QSTileView::class.java)
+
+ val record = TileRecord(tile, tileView)
+
+ qsPanel.addTile(record)
+
+ verify(tile).addCallback(record.callback)
+ }
+
private infix fun View.isLeftOf(other: View): Boolean {
val rect = Rect()
getBoundsOnScreen(rect)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
index 7ecb4dc..426ff67 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
@@ -589,6 +589,26 @@
.isTrue()
}
+ @Test
+ fun retainedTiles_callbackNotRemoved() =
+ testScope.runTest(USER_INFO_0) {
+ val tiles by collectLastValue(underTest.currentTiles)
+ tileSpecRepository.setTiles(USER_INFO_0.id, listOf(TileSpec.create("a")))
+
+ val tileA = tiles!![0].tile
+ val callback = mock<QSTile.Callback>()
+ tileA.addCallback(callback)
+
+ tileSpecRepository.setTiles(
+ USER_INFO_0.id,
+ listOf(TileSpec.create("a"), CUSTOM_TILE_SPEC)
+ )
+ val newTileA = tiles!![0].tile
+ assertThat(tileA).isSameInstanceAs(newTileA)
+
+ assertThat((tileA as FakeQSTile).callbacks).containsExactly(callback)
+ }
+
private fun QSTile.State.fillIn(state: Int, label: CharSequence, secondaryLabel: CharSequence) {
this.state = state
this.label = label
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt
index e509696..013c925 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt
@@ -29,6 +29,7 @@
private var tileSpec: String? = null
var destroyed = false
private val state = QSTile.State()
+ val callbacks = mutableListOf<QSTile.Callback>()
override fun getTileSpec(): String? {
return tileSpec
@@ -45,11 +46,17 @@
override fun refreshState() {}
- override fun addCallback(callback: QSTile.Callback?) {}
+ override fun addCallback(callback: QSTile.Callback) {
+ callbacks.add(callback)
+ }
- override fun removeCallback(callback: QSTile.Callback?) {}
+ override fun removeCallback(callback: QSTile.Callback) {
+ callbacks.remove(callback)
+ }
- override fun removeCallbacks() {}
+ override fun removeCallbacks() {
+ callbacks.clear()
+ }
override fun createTileView(context: Context?): QSIconView? {
return null
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
index 7c30843b..3def6ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
@@ -46,6 +46,8 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
@@ -73,6 +75,8 @@
private Handler mHandler;
@Mock
private UserContextProvider mUserContextTracker;
+ @Captor
+ private ArgumentCaptor<Runnable> mRunnableCaptor;
private KeyguardDismissUtil mKeyguardDismissUtil = new KeyguardDismissUtil() {
public void executeWhenUnlocked(ActivityStarter.OnDismissAction action,
boolean requiresShadeOpen) {
@@ -209,4 +213,19 @@
verify(mScreenMediaRecorder).release();
}
+
+ @Test
+ public void testOnErrorSaving() throws IOException {
+ // When the screen recording does not save properly
+ doThrow(new IllegalStateException("fail")).when(mScreenMediaRecorder).save();
+
+ Intent startIntent = RecordingService.getStopIntent(mContext);
+ mRecordingService.onStartCommand(startIntent, 0, 0);
+ verify(mExecutor).execute(mRunnableCaptor.capture());
+ mRunnableCaptor.getValue().run();
+
+ // Then the state is set to not recording and we cancel the notification
+ verify(mController).updateState(false);
+ verify(mNotificationManager).cancelAsUser(any(), anyInt(), any());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
index 13a2baa..487d26d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
@@ -114,6 +114,54 @@
}
@Test
+ public void testShouldHeadsUpBecomePinned_hasFSI_notUnpinned_true() {
+ // Set up NotifEntry with FSI
+ NotificationEntry notifEntry = new NotificationEntryBuilder()
+ .setSbn(createNewNotification(/* id= */ 0))
+ .build();
+ notifEntry.getSbn().getNotification().fullScreenIntent = PendingIntent.getActivity(
+ getContext(), 0, new Intent(getContext(), this.getClass()),
+ PendingIntent.FLAG_MUTABLE_UNAUDITED);
+
+ // Add notifEntry to ANM mAlertEntries map and make it NOT unpinned
+ mHeadsUpManager.showNotification(notifEntry);
+ HeadsUpManager.HeadsUpEntry headsUpEntry =
+ mHeadsUpManager.getHeadsUpEntry(notifEntry.getKey());
+ headsUpEntry.wasUnpinned = false;
+
+ assertTrue(mHeadsUpManager.shouldHeadsUpBecomePinned(notifEntry));
+ }
+
+ @Test
+ public void testShouldHeadsUpBecomePinned_wasUnpinned_false() {
+ // Set up NotifEntry with FSI
+ NotificationEntry notifEntry = new NotificationEntryBuilder()
+ .setSbn(createNewNotification(/* id= */ 0))
+ .build();
+ notifEntry.getSbn().getNotification().fullScreenIntent = PendingIntent.getActivity(
+ getContext(), 0, new Intent(getContext(), this.getClass()),
+ PendingIntent.FLAG_MUTABLE_UNAUDITED);
+
+ // Add notifEntry to ANM mAlertEntries map and make it unpinned
+ mHeadsUpManager.showNotification(notifEntry);
+ HeadsUpManager.HeadsUpEntry headsUpEntry =
+ mHeadsUpManager.getHeadsUpEntry(notifEntry.getKey());
+ headsUpEntry.wasUnpinned = true;
+
+ assertFalse(mHeadsUpManager.shouldHeadsUpBecomePinned(notifEntry));
+ }
+
+ @Test
+ public void testShouldHeadsUpBecomePinned_noFSI_false() {
+ // Set up NotifEntry with no FSI
+ NotificationEntry notifEntry = new NotificationEntryBuilder()
+ .setSbn(createNewNotification(/* id= */ 0))
+ .build();
+
+ assertFalse(mHeadsUpManager.shouldHeadsUpBecomePinned(notifEntry));
+ }
+
+ @Test
public void testShowNotification_autoDismissesWithAccessibilityTimeout() {
doReturn(TEST_A11Y_AUTO_DISMISS_TIME).when(mAccessibilityMgr)
.getRecommendedTimeoutMillis(anyInt(), anyInt());
diff --git a/services/autofill/java/com/android/server/autofill/FillResponseEventLogger.java b/services/autofill/java/com/android/server/autofill/FillResponseEventLogger.java
index fc5fb1a..a69e33a 100644
--- a/services/autofill/java/com/android/server/autofill/FillResponseEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/FillResponseEventLogger.java
@@ -26,6 +26,9 @@
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__AUTHENTICATION_TYPE__AUTHENTICATION_TYPE_UNKNOWN;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__AUTHENTICATION_TYPE__DATASET_AUTHENTICATION;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__AUTHENTICATION_TYPE__FULL_AUTHENTICATION;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__DETECTION_PREFERENCE__DETECTION_PREFER_AUTOFILL_PROVIDER;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__DETECTION_PREFERENCE__DETECTION_PREFER_PCC;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__DETECTION_PREFERENCE__DETECTION_PREFER_UNKONWN;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__DISPLAY_PRESENTATION_TYPE__DIALOG;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__DISPLAY_PRESENTATION_TYPE__INLINE;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__DISPLAY_PRESENTATION_TYPE__MENU;
@@ -40,6 +43,7 @@
import android.annotation.IntDef;
import android.annotation.Nullable;
+import android.os.SystemClock;
import android.service.autofill.Dataset;
import android.util.Slog;
import android.view.autofill.AutofillId;
@@ -57,6 +61,9 @@
public final class FillResponseEventLogger {
private static final String TAG = "FillResponseEventLogger";
+ private static final long UNINITIALIZED_TIMESTAMP = -1;
+ private long startResponseProcessingTimestamp = UNINITIALIZED_TIMESTAMP;
+
/**
* Reasons why presentation was not shown. These are wrappers around
* {@link com.android.os.AtomsProto.AutofillFillRequestReported.RequestTriggerReason}.
@@ -114,6 +121,20 @@
public @interface AuthenticationResult {
}
+
+ /**
+ * Reasons why presentation was not shown. These are wrappers around
+ * {@link com.android.os.AtomsProto.AutofillFillResponseReported.DetectionPreference}.
+ */
+ @IntDef(prefix = {"DETECTION_PREFER"}, value = {
+ DETECTION_PREFER_UNKNOWN,
+ DETECTION_PREFER_AUTOFILL_PROVIDER,
+ DETECTION_PREFER_PCC
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DetectionPreference {
+ }
+
public static final int DISPLAY_PRESENTATION_TYPE_UNKNOWN =
AUTOFILL_FILL_RESPONSE_REPORTED__DISPLAY_PRESENTATION_TYPE__UNKNOWN_AUTOFILL_DISPLAY_PRESENTATION_TYPE;
public static final int DISPLAY_PRESENTATION_TYPE_MENU =
@@ -148,6 +169,15 @@
public static final int RESPONSE_STATUS_UNKNOWN =
AUTOFILL_FILL_RESPONSE_REPORTED__RESPONSE_STATUS__RESPONSE_STATUS_UNKNOWN;
+ // Values for AutofillFillResponseReported.detection_preference
+ public static final int DETECTION_PREFER_UNKNOWN =
+ AUTOFILL_FILL_RESPONSE_REPORTED__DETECTION_PREFERENCE__DETECTION_PREFER_UNKONWN;
+ public static final int DETECTION_PREFER_AUTOFILL_PROVIDER =
+ AUTOFILL_FILL_RESPONSE_REPORTED__DETECTION_PREFERENCE__DETECTION_PREFER_AUTOFILL_PROVIDER;
+ public static final int DETECTION_PREFER_PCC =
+ AUTOFILL_FILL_RESPONSE_REPORTED__DETECTION_PREFERENCE__DETECTION_PREFER_PCC;
+
+
// Log a magic number when FillRequest failed or timeout to differentiate with FillRequest
// succeeded.
public static final int AVAILABLE_COUNT_WHEN_FILL_REQUEST_FAILED_OR_TIMEOUT = -1;
@@ -221,15 +251,21 @@
public void maybeSetAvailableCount(int val) {
mEventInternal.ifPresent(event -> {
+ event.mAvailableCount = val;
+ });
+ }
+
+ public void maybeSetTotalDatasetsProvided(int val) {
+ mEventInternal.ifPresent(event -> {
// Don't reset if it's already populated.
// This is just a technical limitation of not having complicated logic.
// Autofill Provider may return some datasets which are applicable to data types.
// In such a case, we set available count to the number of datasets provided.
// However, it's possible that those data types aren't detected by PCC, so in effect, there
// are 0 datasets. In the codebase, we treat it as null response, which may call this again
- // to set 0. But we don't want to overwrite this value.
- if (event.mAvailableCount == 0) {
- event.mAvailableCount = val;
+ // to set 0. But we don't want to overwrite already set value.
+ if (event.mTotalDatasetsProvided == -1) {
+ event.mTotalDatasetsProvided = val;
}
});
}
@@ -321,12 +357,20 @@
});
}
+ public void startResponseProcessingTime() {
+ startResponseProcessingTimestamp = SystemClock.elapsedRealtime();
+ }
+
/**
* Set latency_response_processing_millis as long as mEventInternal presents.
*/
- public void maybeSetLatencyResponseProcessingMillis(int val) {
+ public void maybeSetLatencyResponseProcessingMillis() {
mEventInternal.ifPresent(event -> {
- event.mLatencyResponseProcessingMillis = val;
+ if (startResponseProcessingTimestamp == UNINITIALIZED_TIMESTAMP && sVerbose) {
+ Slog.v(TAG, "uninitialized startResponseProcessingTimestamp");
+ }
+ event.mLatencyResponseProcessingMillis
+ = SystemClock.elapsedRealtime() - startResponseProcessingTimestamp;
});
}
@@ -351,11 +395,13 @@
/**
* Set available_pcc_count.
*/
- public void maybeSetAvailableDatasetsPccCount(@Nullable List<Dataset> datasetList) {
+ public void maybeSetDatasetsCountAfterPotentialPccFiltering(@Nullable List<Dataset> datasetList) {
mEventInternal.ifPresent(event -> {
int pccOnlyCount = 0;
int pccCount = 0;
+ int totalCount = 0;
if (datasetList != null) {
+ totalCount = datasetList.size();
for (int i = 0; i < datasetList.size(); i++) {
Dataset dataset = datasetList.get(i);
if (dataset != null) {
@@ -371,9 +417,18 @@
}
event.mAvailablePccOnlyCount = pccOnlyCount;
event.mAvailablePccCount = pccCount;
+ event.mAvailableCount = totalCount;
});
}
+ /**
+ * Set detection_pref
+ */
+ public void maybeSetDetectionPreference(@DetectionPreference int detectionPreference) {
+ mEventInternal.ifPresent(event -> {
+ event.mDetectionPref = detectionPreference;
+ });
+ }
/**
* Log an AUTOFILL_FILL_RESPONSE_REPORTED event.
@@ -402,7 +457,9 @@
+ " mResponseStatus=" + event.mResponseStatus
+ " mLatencyResponseProcessingMillis=" + event.mLatencyResponseProcessingMillis
+ " mAvailablePccCount=" + event.mAvailablePccCount
- + " mAvailablePccOnlyCount=" + event.mAvailablePccOnlyCount);
+ + " mAvailablePccOnlyCount=" + event.mAvailablePccOnlyCount
+ + " mTotalDatasetsProvided=" + event.mTotalDatasetsProvided
+ + " mDetectionPref=" + event.mDetectionPref);
}
FrameworkStatsLog.write(
AUTOFILL_FILL_RESPONSE_REPORTED,
@@ -421,7 +478,9 @@
event.mResponseStatus,
event.mLatencyResponseProcessingMillis,
event.mAvailablePccCount,
- event.mAvailablePccOnlyCount);
+ event.mAvailablePccOnlyCount,
+ event.mTotalDatasetsProvided,
+ event.mDetectionPref);
mEventInternal = Optional.empty();
}
@@ -431,16 +490,19 @@
int mDisplayPresentationType = DISPLAY_PRESENTATION_TYPE_UNKNOWN;
int mAvailableCount = 0;
int mSaveUiTriggerIds = -1;
- int mLatencyFillResponseReceivedMillis = 0;
+ int mLatencyFillResponseReceivedMillis = (int) UNINITIALIZED_TIMESTAMP;
int mAuthenticationType = AUTHENTICATION_TYPE_UNKNOWN;
int mAuthenticationResult = AUTHENTICATION_RESULT_UNKNOWN;
int mAuthenticationFailureReason = -1;
- int mLatencyAuthenticationUiDisplayMillis = 0;
- int mLatencyDatasetDisplayMillis = 0;
+ int mLatencyAuthenticationUiDisplayMillis = (int) UNINITIALIZED_TIMESTAMP;
+ int mLatencyDatasetDisplayMillis = (int) UNINITIALIZED_TIMESTAMP;
int mResponseStatus = RESPONSE_STATUS_UNKNOWN;
- int mLatencyResponseProcessingMillis = 0;
- int mAvailablePccCount;
- int mAvailablePccOnlyCount;
+ long mLatencyResponseProcessingMillis = UNINITIALIZED_TIMESTAMP;
+ int mAvailablePccCount = -1;
+ int mAvailablePccOnlyCount = -1;
+ int mTotalDatasetsProvided = -1;
+ @DetectionPreference
+ int mDetectionPref = DETECTION_PREFER_UNKNOWN;
FillResponseEventInternal() {
}
diff --git a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
index b2f9a93..11b45db 100644
--- a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
@@ -16,8 +16,6 @@
package com.android.server.autofill;
-import static android.service.autofill.Dataset.PICK_REASON_PCC_DETECTION_ONLY;
-import static android.service.autofill.Dataset.PICK_REASON_PCC_DETECTION_PREFERRED_WITH_PROVIDER;
import static android.service.autofill.FillEventHistory.Event.UI_TYPE_DIALOG;
import static android.service.autofill.FillEventHistory.Event.UI_TYPE_INLINE;
import static android.service.autofill.FillEventHistory.Event.UI_TYPE_MENU;
@@ -27,6 +25,9 @@
import static android.view.autofill.AutofillManager.COMMIT_REASON_VIEW_CLICKED;
import static android.view.autofill.AutofillManager.COMMIT_REASON_VIEW_COMMITTED;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__DETECTION_PREFERENCE__DETECTION_PREFER_AUTOFILL_PROVIDER;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__DETECTION_PREFERENCE__DETECTION_PREFER_PCC;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__DETECTION_PREFERENCE__DETECTION_PREFER_UNKONWN;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__AUTHENTICATION_RESULT__AUTHENTICATION_FAILURE;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__AUTHENTICATION_RESULT__AUTHENTICATION_RESULT_UNKNOWN;
@@ -140,6 +141,19 @@
@Retention(RetentionPolicy.SOURCE)
public @interface DatasetPickedReason {}
+ /**
+ * The type of detection that was preferred. These are wrappers around
+ * {@link com.android.os.AtomsProto.AutofillPresentationEventReported.DetectionPreference}.
+ */
+ @IntDef(prefix = {"DETECTION_PREFER"}, value = {
+ DETECTION_PREFER_UNKNOWN,
+ DETECTION_PREFER_AUTOFILL_PROVIDER,
+ DETECTION_PREFER_PCC
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DetectionPreference {
+ }
+
public static final int NOT_SHOWN_REASON_ANY_SHOWN =
AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__ANY_SHOWN;
public static final int NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED =
@@ -187,6 +201,15 @@
AUTOFILL_PRESENTATION_EVENT_REPORTED__SELECTED_DATASET_PICKED_REASON__PICK_REASON_PCC_DETECTION_ONLY;
public static final int PICK_REASON_PCC_DETECTION_PREFERRED_WITH_PROVIDER =
AUTOFILL_PRESENTATION_EVENT_REPORTED__SELECTED_DATASET_PICKED_REASON__PICK_REASON_PCC_DETECTION_PREFERRED_WITH_PROVIDER;
+
+
+ // Values for AutofillFillResponseReported.detection_preference
+ public static final int DETECTION_PREFER_UNKNOWN =
+ AUTOFILL_FILL_RESPONSE_REPORTED__DETECTION_PREFERENCE__DETECTION_PREFER_UNKONWN;
+ public static final int DETECTION_PREFER_AUTOFILL_PROVIDER =
+ AUTOFILL_FILL_RESPONSE_REPORTED__DETECTION_PREFERENCE__DETECTION_PREFER_AUTOFILL_PROVIDER;
+ public static final int DETECTION_PREFER_PCC =
+ AUTOFILL_FILL_RESPONSE_REPORTED__DETECTION_PREFERENCE__DETECTION_PREFER_PCC;
private final int mSessionId;
private Optional<PresentationStatsEventInternal> mEventInternal;
@@ -463,6 +486,15 @@
});
}
+ /**
+ * Set detection_pref
+ */
+ public void maybeSetDetectionPreference(@DetectionPreference int detectionPreference) {
+ mEventInternal.ifPresent(event -> {
+ event.mDetectionPreference = detectionPreference;
+ });
+ }
+
private int convertDatasetPickReason(@Dataset.DatasetEligibleReason int val) {
switch (val) {
case 0:
@@ -514,7 +546,8 @@
+ " mLatencyDatasetDisplayMillis=" + event.mLatencyDatasetDisplayMillis
+ " mAvailablePccCount=" + event.mAvailablePccCount
+ " mAvailablePccOnlyCount=" + event.mAvailablePccOnlyCount
- + " mSelectedDatasetPickedReason=" + event.mSelectedDatasetPickedReason);
+ + " mSelectedDatasetPickedReason=" + event.mSelectedDatasetPickedReason
+ + " mDetectionPreference=" + event.mDetectionPreference);
}
// TODO(b/234185326): Distinguish empty responses from other no presentation reasons.
@@ -550,7 +583,8 @@
event.mLatencyDatasetDisplayMillis,
event.mAvailablePccCount,
event.mAvailablePccOnlyCount,
- event.mSelectedDatasetPickedReason);
+ event.mSelectedDatasetPickedReason,
+ event.mDetectionPreference);
mEventInternal = Optional.empty();
}
@@ -582,6 +616,7 @@
int mAvailablePccCount = -1;
int mAvailablePccOnlyCount = -1;
@DatasetPickedReason int mSelectedDatasetPickedReason = PICK_REASON_UNKNOWN;
+ @DetectionPreference int mDetectionPreference = DETECTION_PREFER_UNKNOWN;
PresentationStatsEventInternal() {}
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 5d6eab7..0a8f474 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -52,6 +52,9 @@
import static com.android.server.autofill.FillRequestEventLogger.TRIGGER_REASON_PRE_TRIGGER;
import static com.android.server.autofill.FillRequestEventLogger.TRIGGER_REASON_SERVED_FROM_CACHED_RESPONSE;
import static com.android.server.autofill.FillResponseEventLogger.AVAILABLE_COUNT_WHEN_FILL_REQUEST_FAILED_OR_TIMEOUT;
+import static com.android.server.autofill.FillResponseEventLogger.DETECTION_PREFER_AUTOFILL_PROVIDER;
+import static com.android.server.autofill.FillResponseEventLogger.DETECTION_PREFER_UNKNOWN;
+import static com.android.server.autofill.FillResponseEventLogger.DETECTION_PREFER_PCC;
import static com.android.server.autofill.FillResponseEventLogger.HAVE_SAVE_TRIGGER_ID;
import static com.android.server.autofill.FillResponseEventLogger.RESPONSE_STATUS_FAILURE;
import static com.android.server.autofill.FillResponseEventLogger.RESPONSE_STATUS_SESSION_DESTROYED;
@@ -1460,6 +1463,7 @@
mFillResponseEventLogger.maybeSetRequestId(requestId);
mFillResponseEventLogger.maybeSetAppPackageUid(uid);
mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_SUCCESS);
+ mFillResponseEventLogger.startResponseProcessingTime();
// Time passed since session was created
final long fillRequestReceivedRelativeTimestamp =
SystemClock.elapsedRealtime() - mLatencyBaseTime;
@@ -1467,6 +1471,7 @@
(int) (fillRequestReceivedRelativeTimestamp));
mFillResponseEventLogger.maybeSetLatencyFillResponseReceivedMillis(
(int) (fillRequestReceivedRelativeTimestamp));
+ mFillResponseEventLogger.maybeSetDetectionPreference(getDetectionPreferenceForLogging());
synchronized (mLock) {
if (mDestroyed) {
@@ -1485,6 +1490,7 @@
Slog.w(TAG, "onFillRequestSuccess(): no request log for id " + requestId);
}
if (response == null) {
+ mFillResponseEventLogger.maybeSetTotalDatasetsProvided(0);
if (requestLog != null) {
requestLog.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS, -1);
}
@@ -1584,13 +1590,16 @@
}
}
- mFillResponseEventLogger.maybeSetAvailableCount(
- datasetList == null ? 0 : datasetList.size());
+ int datasetCount = (datasetList == null) ? 0 : datasetList.size();
+ mFillResponseEventLogger.maybeSetTotalDatasetsProvided(datasetCount);
+ // It's possible that this maybe overwritten later on after PCC filtering.
+ mFillResponseEventLogger.maybeSetAvailableCount(datasetCount);
// TODO(b/266379948): Ideally wait for PCC request to finish for a while more
// (say 100ms) before proceeding further on.
processResponseLockedForPcc(response, response.getClientState(), requestFlags);
+ mFillResponseEventLogger.maybeSetLatencyResponseProcessingMillis();
}
@@ -1993,7 +2002,7 @@
null,
dataset.getId(),
dataset.getAuthentication());
- dataset.setEligibleReasonReason(pickReason);
+ newDataset.setEligibleReasonReason(pickReason);
eligibleDatasets.add(newDataset);
Set<Dataset> newDatasets;
for (AutofillId autofillId : datasetAutofillIds) {
@@ -2036,7 +2045,10 @@
mFillResponseEventLogger.maybeSetRequestId(requestId);
mFillResponseEventLogger.maybeSetAppPackageUid(uid);
mFillResponseEventLogger.maybeSetAvailableCount(
- AVAILABLE_COUNT_WHEN_FILL_REQUEST_FAILED_OR_TIMEOUT);
+ AVAILABLE_COUNT_WHEN_FILL_REQUEST_FAILED_OR_TIMEOUT);
+ mFillResponseEventLogger.maybeSetTotalDatasetsProvided(
+ AVAILABLE_COUNT_WHEN_FILL_REQUEST_FAILED_OR_TIMEOUT);
+ mFillResponseEventLogger.maybeSetDetectionPreference(getDetectionPreferenceForLogging());
final long fillRequestReceivedRelativeTimestamp =
SystemClock.elapsedRealtime() - mLatencyBaseTime;
mFillResponseEventLogger.maybeSetLatencyFillResponseReceivedMillis(
@@ -3887,8 +3899,7 @@
// View is triggering autofill.
mCurrentViewId = viewState.id;
viewState.update(value, virtualBounds, flags);
- mPresentationStatsEventLogger.startNewEvent();
- mPresentationStatsEventLogger.maybeSetAutofillServiceUid(getAutofillServiceUid());
+ startNewEventForPresentationStatsEventLogger();
mPresentationStatsEventLogger.maybeSetIsNewRequest(true);
if (!isRequestSupportFillDialog(flags)) {
mSessionFlags.mFillDialogDisabled = true;
@@ -4021,9 +4032,7 @@
}
// If previous request was FillDialog request, a logger event was already started
if (!wasPreviouslyFillDialog) {
- mPresentationStatsEventLogger.startNewEvent();
- mPresentationStatsEventLogger.maybeSetAutofillServiceUid(
- getAutofillServiceUid());
+ startNewEventForPresentationStatsEventLogger();
}
if (requestNewFillResponseOnViewEnteredIfNecessaryLocked(id, viewState, flags)) {
// If a new request was issued even if previously it was fill dialog request,
@@ -4032,9 +4041,7 @@
// lock guarded, we should be safe.
if (wasPreviouslyFillDialog) {
mPresentationStatsEventLogger.logAndEndEvent();
- mPresentationStatsEventLogger.startNewEvent();
- mPresentationStatsEventLogger.maybeSetAutofillServiceUid(
- getAutofillServiceUid());
+ startNewEventForPresentationStatsEventLogger();
}
return;
}
@@ -4966,7 +4973,7 @@
List<Dataset> datasetList = newResponse.getDatasets();
mPresentationStatsEventLogger.maybeSetAvailableCount(datasetList, mCurrentViewId);
- mFillResponseEventLogger.maybeSetAvailableDatasetsPccCount(datasetList);
+ mFillResponseEventLogger.maybeSetDatasetsCountAfterPotentialPccFiltering(datasetList);
setViewStatesLocked(newResponse, ViewState.STATE_FILLABLE, false);
updateFillDialogTriggerIdsLocked();
@@ -5154,6 +5161,25 @@
};
}
+ private int getDetectionPreferenceForLogging() {
+ if (mService.isPccClassificationEnabled()) {
+ if (mService.getMaster().preferProviderOverPcc()) {
+ return DETECTION_PREFER_AUTOFILL_PROVIDER;
+ }
+ return DETECTION_PREFER_PCC;
+ }
+ return DETECTION_PREFER_UNKNOWN;
+ }
+
+ private void startNewEventForPresentationStatsEventLogger() {
+ synchronized (mLock) {
+ mPresentationStatsEventLogger.startNewEvent();
+ mPresentationStatsEventLogger.maybeSetDetectionPreference(
+ getDetectionPreferenceForLogging());
+ mPresentationStatsEventLogger.maybeSetAutofillServiceUid(getAutofillServiceUid());
+ }
+ }
+
private void startAuthentication(int authenticationId, IntentSender intent,
Intent fillInIntent, boolean authenticateInline) {
try {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ca4a253..3d02c96 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -17236,12 +17236,6 @@
}
}
- void onProcessFreezableChangedLocked(ProcessRecord app) {
- if (mEnableModernQueue) {
- mBroadcastQueues[0].onProcessFreezableChangedLocked(app);
- }
- }
-
@VisibleForTesting
public final class LocalService extends ActivityManagerInternal
implements ActivityManagerLocal {
@@ -18347,16 +18341,17 @@
@Override
public boolean hasRunningForegroundService(int uid, int foregroundServicetype) {
synchronized (ActivityManagerService.this) {
- return mProcessList.searchEachLruProcessesLOSP(true, app -> {
- if (app.uid != uid) {
- return null;
- }
-
+ final UidRecord uidRec = mProcessList.mActiveUids.get(uid);
+ if (uidRec == null) {
+ return false;
+ }
+ for (int i = uidRec.getNumOfProcs() - 1; i >= 0; i--) {
+ final ProcessRecord app = uidRec.getProcessRecordByIndex(i);
if ((app.mServices.containsAnyForegroundServiceTypes(foregroundServicetype))) {
- return Boolean.TRUE;
+ return true;
}
- return null;
- }) != null;
+ }
+ return false;
}
}
@@ -19043,8 +19038,11 @@
long delayedDurationMs) {
Objects.requireNonNull(targetPackage);
Preconditions.checkArgumentNonnegative(delayedDurationMs);
- Preconditions.checkState(mEnableModernQueue, "Not valid in legacy queue");
enforceCallingPermission(permission.DUMP, "forceDelayBroadcastDelivery()");
+ // Ignore request if modern queue is not enabled
+ if (!mEnableModernQueue) {
+ return;
+ }
for (BroadcastQueue queue : mBroadcastQueues) {
queue.forceDelayBroadcastDelivery(targetPackage, delayedDurationMs);
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index 142a5dc..3ac2b2b 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -201,7 +201,7 @@
private boolean mLastDeferredStates;
private boolean mUidForeground;
- private boolean mProcessFreezable;
+ private boolean mUidCached;
private boolean mProcessInstrumented;
private boolean mProcessPersistent;
@@ -411,7 +411,7 @@
*/
@CheckResult
public boolean setProcessAndUidState(@Nullable ProcessRecord app, boolean uidForeground,
- boolean processFreezable) {
+ boolean uidCached) {
this.app = app;
// Since we may have just changed our PID, invalidate cached strings
@@ -420,13 +420,13 @@
boolean didSomething = false;
if (app != null) {
+ didSomething |= setUidCached(uidCached);
didSomething |= setUidForeground(uidForeground);
- didSomething |= setProcessFreezable(processFreezable);
didSomething |= setProcessInstrumented(app.getActiveInstrumentation() != null);
didSomething |= setProcessPersistent(app.isPersistent());
} else {
+ didSomething |= setUidCached(uidCached);
didSomething |= setUidForeground(false);
- didSomething |= setProcessFreezable(false);
didSomething |= setProcessInstrumented(false);
didSomething |= setProcessPersistent(false);
}
@@ -450,13 +450,13 @@
}
/**
- * Update if this process is in the "freezable" state, typically signaling that
+ * Update if this process is in the "cached" state, typically signaling that
* broadcast dispatch should be paused or delayed.
*/
@CheckResult
- private boolean setProcessFreezable(boolean freezable) {
- if (mProcessFreezable != freezable) {
- mProcessFreezable = freezable;
+ private boolean setUidCached(boolean uidCached) {
+ if (mUidCached != uidCached) {
+ mUidCached = uidCached;
invalidateRunnableAt();
return true;
} else {
@@ -1115,7 +1115,7 @@
} else if (mCountManifest > 0) {
mRunnableAt = runnableAt;
mRunnableAtReason = REASON_CONTAINS_MANIFEST;
- } else if (mProcessFreezable) {
+ } else if (mUidCached) {
if (r.deferUntilActive) {
// All enqueued broadcasts are deferrable, defer
if (mCountDeferred == mCountEnqueued) {
@@ -1185,7 +1185,7 @@
// When all we have pending is deferred broadcasts, and we're cached,
// then we want everything to be marked deferred
final boolean wantDeferredStates = (mCountDeferred > 0)
- && (mCountDeferred == mCountEnqueued) && mProcessFreezable;
+ && (mCountDeferred == mCountEnqueued) && mUidCached;
if (mLastDeferredStates != wantDeferredStates) {
mLastDeferredStates = wantDeferredStates;
@@ -1372,9 +1372,9 @@
if (mUidForeground) {
sb.append("FG");
}
- if (mProcessFreezable) {
+ if (mUidCached) {
if (sb.length() > 0) sb.append("|");
- sb.append("FRZN");
+ sb.append("CACHED");
}
if (mProcessInstrumented) {
if (sb.length() > 0) sb.append("|");
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 93bde6f..8e76e5b 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -173,13 +173,6 @@
public abstract void onApplicationCleanupLocked(@NonNull ProcessRecord app);
/**
- * Signal from OS internals that the given process is in a freezable state and will be
- * freezed soon after.
- */
- @GuardedBy("mService")
- public abstract void onProcessFreezableChangedLocked(@NonNull ProcessRecord app);
-
- /**
* Signal from OS internals that the given package (or some subset of that
* package) has been disabled or uninstalled, and that any pending
* broadcasts should be cleaned up.
diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
index ce635f1..e389821 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
@@ -18,6 +18,7 @@
import static android.app.ActivityManager.RESTRICTION_LEVEL_RESTRICTED_BUCKET;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_START_RECEIVER;
+import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_APP_FREEZING_DELAYED;
import static android.os.Process.ZYGOTE_POLICY_FLAG_EMPTY;
import static android.os.Process.ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE;
import static android.text.TextUtils.formatSimple;
@@ -384,6 +385,16 @@
maybeReportBroadcastDispatchedEventLocked(r, r.curReceiver.applicationInfo.uid);
r.intent.setComponent(r.curComponent);
+ // See if we need to delay the freezer based on BroadcastOptions
+ if (r.options != null
+ && r.options.getTemporaryAppAllowlistDuration() > 0
+ && r.options.getTemporaryAppAllowlistType()
+ == TEMPORARY_ALLOW_LIST_TYPE_APP_FREEZING_DELAYED) {
+ mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(app,
+ CachedAppOptimizer.UNFREEZE_REASON_START_RECEIVER,
+ r.options.getTemporaryAppAllowlistDuration());
+ }
+
boolean started = false;
try {
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
@@ -449,10 +460,6 @@
skipCurrentOrPendingReceiverLocked(app);
}
- public void onProcessFreezableChangedLocked(ProcessRecord app) {
- // Not supported; ignore
- }
-
public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
boolean didSomething = false;
final BroadcastRecord br = mPendingBroadcast;
@@ -934,8 +941,13 @@
Slog.v(TAG, "Broadcast temp allowlist uid=" + uid + " duration=" + duration
+ " type=" + type + " : " + b.toString());
}
- mService.tempAllowlistUidLocked(uid, duration, reasonCode, b.toString(), type,
- r.callingUid);
+
+ // Only add to temp allowlist if it's not the APP_FREEZING_DELAYED type. That will be
+ // handled when the broadcast is actually being scheduled on the app thread.
+ if (type != TEMPORARY_ALLOW_LIST_TYPE_APP_FREEZING_DELAYED) {
+ mService.tempAllowlistUidLocked(uid, duration, reasonCode, b.toString(), type,
+ r.callingUid);
+ }
}
private void processNextBroadcast(boolean fromMsg) {
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index 331efaa..d6e692c 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -223,6 +223,16 @@
@GuardedBy("mService")
private final SparseBooleanArray mUidForeground = new SparseBooleanArray();
+ /**
+ * Map from UID to its last known "cached" state.
+ * <p>
+ * We manually maintain this data structure since the lifecycle of
+ * {@link ProcessRecord} and {@link BroadcastProcessQueue} can be
+ * mismatched.
+ */
+ @GuardedBy("mService")
+ private final SparseBooleanArray mUidCached = new SparseBooleanArray();
+
private final BroadcastConstants mConstants;
private final BroadcastConstants mFgConstants;
private final BroadcastConstants mBgConstants;
@@ -565,13 +575,6 @@
}
@Override
- public void onProcessFreezableChangedLocked(@NonNull ProcessRecord app) {
- synchronized (mService) {
- refreshProcessQueueLocked(app);
- }
- }
-
- @Override
public int getPreferredSchedulingGroupLocked(@NonNull ProcessRecord app) {
final BroadcastProcessQueue queue = getProcessQueue(app);
if ((queue != null) && getRunningIndexOf(queue) >= 0) {
@@ -1302,6 +1305,7 @@
};
broadcastPredicate = BROADCAST_PREDICATE_ANY;
+ cleanupUserStateLocked(mUidCached, userId);
cleanupUserStateLocked(mUidForeground, userId);
}
return forEachMatchingBroadcast(queuePredicate, broadcastPredicate,
@@ -1444,7 +1448,20 @@
refreshProcessQueuesLocked(uid);
}
}
- }, ActivityManager.UID_OBSERVER_PROCSTATE, ActivityManager.PROCESS_STATE_TOP, "android");
+
+ @Override
+ public void onUidCachedChanged(int uid, boolean cached) {
+ synchronized (mService) {
+ if (cached) {
+ mUidCached.put(uid, true);
+ } else {
+ mUidCached.delete(uid);
+ }
+ refreshProcessQueuesLocked(uid);
+ }
+ }
+ }, ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_CACHED,
+ ActivityManager.PROCESS_STATE_TOP, "android");
// Kick off periodic health checks
mLocalHandler.sendEmptyMessage(MSG_CHECK_HEALTH);
@@ -1633,9 +1650,10 @@
// warm via this operation, we're going to immediately promote it to
// be running, and any side effect of this operation will then apply
// after it's finished and is returned to the runnable list.
- final ProcessRecord app = mService.getProcessRecordLocked(queue.processName, queue.uid);
- queue.setProcessAndUidState(app, mUidForeground.get(queue.uid, false),
- isProcessFreezable(app));
+ queue.setProcessAndUidState(
+ mService.getProcessRecordLocked(queue.processName, queue.uid),
+ mUidForeground.get(queue.uid, false),
+ mUidCached.get(queue.uid, false));
}
}
@@ -1647,21 +1665,11 @@
private void setQueueProcess(@NonNull BroadcastProcessQueue queue,
@Nullable ProcessRecord app) {
if (queue.setProcessAndUidState(app, mUidForeground.get(queue.uid, false),
- isProcessFreezable(app))) {
+ mUidCached.get(queue.uid, false))) {
updateRunnableList(queue);
}
}
- @GuardedBy("mService")
- private boolean isProcessFreezable(@Nullable ProcessRecord app) {
- if (app == null) {
- return false;
- }
- synchronized (mService.mProcLock) {
- return app.mOptRecord.isPendingFreeze() || app.mOptRecord.isFrozen();
- }
- }
-
/**
* Refresh the process queues with the latest process state so that runnableAt
* can be updated.
@@ -1679,20 +1687,6 @@
}
/**
- * Refresh the process queue corresponding to {@code app} with the latest process state
- * so that runnableAt can be updated.
- */
- @GuardedBy("mService")
- private void refreshProcessQueueLocked(@NonNull ProcessRecord app) {
- final BroadcastProcessQueue queue = getProcessQueue(app.processName, app.uid);
- if (queue == null || queue.app == null || queue.app.getPid() != app.getPid()) {
- return;
- }
- setQueueProcess(queue, queue.app);
- enqueueUpdateRunningList();
- }
-
- /**
* Inform other parts of OS that the given broadcast queue has started
* running, typically for internal bookkeeping.
*/
@@ -2011,6 +2005,12 @@
ipw.decreaseIndent();
ipw.println();
+ ipw.println("Cached UIDs:");
+ ipw.increaseIndent();
+ ipw.println(mUidCached);
+ ipw.decreaseIndent();
+ ipw.println();
+
ipw.println("Foreground UIDs:");
ipw.increaseIndent();
ipw.println(mUidForeground);
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index e5a1759..7773190 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -300,7 +300,6 @@
static final int COMPACT_NATIVE_MSG = 5;
static final int UID_FROZEN_STATE_CHANGED_MSG = 6;
static final int DEADLOCK_WATCHDOG_MSG = 7;
- static final int REPORT_PROCESS_FREEZABLE_CHANGED = 8;
// When free swap falls below this percentage threshold any full (file + anon)
// compactions will be downgraded to file only compactions to reduce pressure
@@ -1353,7 +1352,6 @@
}
}
}
- postProcessFreezableChangedMessage(app);
mFreezeHandler.sendMessageDelayed(
mFreezeHandler.obtainMessage(SET_FROZEN_PROCESS_MSG, DO_FREEZE, 0, app),
delayMillis);
@@ -1391,7 +1389,6 @@
uidRec.setFrozen(false);
postUidFrozenMessage(uidRec.getUid(), false);
}
- postProcessFreezableChangedMessage(app);
opt.setFreezerOverride(false);
if (pid == 0 || !opt.isFrozen()) {
@@ -2069,21 +2066,6 @@
0, uidObj));
}
- private void reportProcessFreezableChanged(ProcessRecord app) {
- synchronized (mAm) {
- mAm.onProcessFreezableChangedLocked(app);
- }
- }
-
- @GuardedBy("mAm")
- private void postProcessFreezableChangedMessage(ProcessRecord app) {
- if (app.getPid() == 0) {
- return;
- }
- mFreezeHandler.obtainMessage(REPORT_PROCESS_FREEZABLE_CHANGED, 0, 0, app)
- .sendToTarget();
- }
-
private final class FreezeHandler extends Handler implements
ProcLocksReader.ProcLocksReaderCallback {
private FreezeHandler() {
@@ -2093,7 +2075,7 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case SET_FROZEN_PROCESS_MSG: {
+ case SET_FROZEN_PROCESS_MSG:
ProcessRecord proc = (ProcessRecord) msg.obj;
synchronized (mAm) {
freezeProcess(proc);
@@ -2103,8 +2085,8 @@
removeMessages(DEADLOCK_WATCHDOG_MSG);
sendEmptyMessageDelayed(DEADLOCK_WATCHDOG_MSG, FREEZE_DEADLOCK_TIMEOUT_MS);
}
- } break;
- case REPORT_UNFREEZE_MSG: {
+ break;
+ case REPORT_UNFREEZE_MSG:
int pid = msg.arg1;
int frozenDuration = msg.arg2;
Pair<String, Integer> obj = (Pair<String, Integer>) msg.obj;
@@ -2112,13 +2094,13 @@
int reason = obj.second;
reportUnfreeze(pid, frozenDuration, processName, reason);
- } break;
- case UID_FROZEN_STATE_CHANGED_MSG: {
+ break;
+ case UID_FROZEN_STATE_CHANGED_MSG:
final boolean frozen = (msg.arg1 == 1);
final int uid = (int) msg.obj;
reportOneUidFrozenStateChanged(uid, frozen);
- } break;
- case DEADLOCK_WATCHDOG_MSG: {
+ break;
+ case DEADLOCK_WATCHDOG_MSG:
try {
// post-check to prevent deadlock
if (DEBUG_FREEZER) {
@@ -2128,11 +2110,7 @@
} catch (IOException e) {
Slog.w(TAG_AM, "Unable to check file locks");
}
- } break;
- case REPORT_PROCESS_FREEZABLE_CHANGED: {
- final ProcessRecord app = (ProcessRecord) msg.obj;
- reportProcessFreezableChanged(app);
- } break;
+ break;
default:
return;
}
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index 844f175..7482e64 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -97,10 +97,6 @@
sGlobalSettingToTypeMap.put(
Settings.Global.ANGLE_EGL_FEATURES, String.class);
sGlobalSettingToTypeMap.put(
- Settings.Global.ANGLE_DEFERLIST, String.class);
- sGlobalSettingToTypeMap.put(
- Settings.Global.ANGLE_DEFERLIST_MODE, String.class);
- sGlobalSettingToTypeMap.put(
Settings.Global.SHOW_ANGLE_IN_USE_DIALOG_BOX, String.class);
sGlobalSettingToTypeMap.put(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, int.class);
sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_APP, String.class);
diff --git a/services/core/java/com/android/server/am/DropboxRateLimiter.java b/services/core/java/com/android/server/am/DropboxRateLimiter.java
index 727d4df9..b5c7215 100644
--- a/services/core/java/com/android/server/am/DropboxRateLimiter.java
+++ b/services/core/java/com/android/server/am/DropboxRateLimiter.java
@@ -40,7 +40,7 @@
// If a process is rate limited twice in a row we consider it crash-looping and rate limit it
// more aggressively.
private static final int STRICT_RATE_LIMIT_ALLOWED_ENTRIES = 1;
- private static final long STRICT_RATE_LIMIT_BUFFER_DURATION = 60 * DateUtils.MINUTE_IN_MILLIS;
+ private static final long STRICT_RATE_LIMIT_BUFFER_DURATION = 20 * DateUtils.MINUTE_IN_MILLIS;
@GuardedBy("mErrorClusterRecords")
private final ArrayMap<String, ErrorRecord> mErrorClusterRecords = new ArrayMap<>();
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index cb26c13..ab4fb46 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -42,6 +42,7 @@
import android.os.IBinder;
import android.os.PowerWhitelistManager;
import android.os.PowerWhitelistManager.ReasonCode;
+import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.TransactionTooLargeException;
@@ -382,6 +383,14 @@
})
public static BackgroundStartPrivileges getDefaultBackgroundStartPrivileges(
int callingUid) {
+ if (UserHandle.getAppId(callingUid) == Process.SYSTEM_UID) {
+ // We temporarily allow BAL for system processes, while we verify that all valid use
+ // cases are opted in explicitly to grant their BAL permission.
+ // Background: In many cases devices are running additional apps that share UID with
+ // the system. If one of these apps targets a lower SDK the change is not active, but
+ // as soon as that app is upgraded (or removed) BAL would be blocked. (b/283138430)
+ return BackgroundStartPrivileges.ALLOW_BAL;
+ }
boolean isChangeEnabledForApp = CompatChanges.isChangeEnabled(
DEFAULT_RESCIND_BAL_PRIVILEGES_FROM_PENDING_INTENT_SENDER, callingUid);
if (isChangeEnabledForApp) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index fbe7e70..4342cb9 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -3182,6 +3182,10 @@
if (isSdkSandbox) {
uid = sdkSandboxUid;
}
+ if (Process.isSdkSandboxUid(uid) && (!isSdkSandbox || sdkSandboxClientAppPackage == null)) {
+ Slog.e(TAG, "Abort creating new sandbox process as required parameters are missing.");
+ return null;
+ }
if (isolated) {
if (isolatedUid == 0) {
IsolatedUidRange uidRange = getOrCreateIsolatedUidRangeLocked(info, hostingRecord);
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 9b1149a..335d676 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1192,9 +1192,6 @@
"Killing " + toShortString() + " (adj " + mState.getSetAdj()
+ "): " + reason, info.uid);
}
- // Since the process is getting killed, reset the freezable related state.
- mOptRecord.setPendingFreeze(false);
- mOptRecord.setFrozen(false);
if (mPid > 0) {
mService.mProcessList.noteAppKill(this, reasonCode, subReason, description);
EventLog.writeEvent(EventLogTags.AM_KILL,
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 334c145..12bb5d2 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -3567,6 +3567,8 @@
if (mUserSwitchingDialog != null) {
mUserSwitchingDialog.dismiss(onDismissed);
mUserSwitchingDialog = null;
+ } else if (onDismissed != null) {
+ onDismissed.run();
}
}
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index f4c9d05..d7a5ee9 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -420,6 +420,20 @@
mBtHelper.stopBluetoothSco(eventSource);
}
+ // In BT classic for communication, the device changes from a2dp to sco device, but for
+ // LE Audio it stays the same and we must trigger the proper stream volume alignment, if
+ // LE Audio communication device is activated after the audio system has already switched to
+ // MODE_IN_CALL mode.
+ if (isBluetoothLeAudioRequested()) {
+ final int streamType = mAudioService.getBluetoothContextualVolumeStream();
+ final int leAudioVolIndex = getVssVolumeForDevice(streamType, device.getInternalType());
+ final int leAudioMaxVolIndex = getMaxVssVolumeForStream(streamType);
+ if (AudioService.DEBUG_COMM_RTE) {
+ Log.v(TAG, "setCommunicationRouteForClient restoring LE Audio device volume lvl.");
+ }
+ postSetLeAudioVolumeIndex(leAudioVolIndex, leAudioMaxVolIndex, streamType);
+ }
+
updateCommunicationRoute(eventSource);
}
@@ -633,6 +647,16 @@
}
/**
+ * Helper method on top of isDeviceRequestedForCommunication() indicating if
+ * Bluetooth LE Audio communication device is currently requested or not.
+ * @return true if Bluetooth LE Audio device is requested, false otherwise.
+ */
+ /*package*/ boolean isBluetoothLeAudioRequested() {
+ return isDeviceRequestedForCommunication(AudioDeviceInfo.TYPE_BLE_HEADSET)
+ || isDeviceRequestedForCommunication(AudioDeviceInfo.TYPE_BLE_SPEAKER);
+ }
+
+ /**
* Indicates if preferred route selection for communication is Bluetooth SCO.
* @return true if Bluetooth SCO is preferred , false otherwise.
*/
diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
index 055c63d..46c77e8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
@@ -206,17 +206,6 @@
}
}
- protected final void vibrateError() {
- Vibrator vibrator = getContext().getSystemService(Vibrator.class);
- if (vibrator != null && mShouldVibrate) {
- vibrator.vibrate(Process.myUid(),
- getContext().getOpPackageName(),
- ERROR_VIBRATION_EFFECT,
- getClass().getSimpleName() + "::error",
- HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES);
- }
- }
-
@Override
public boolean isInterruptable() {
return true;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
index 9dc1782..a529fb9 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
@@ -72,7 +72,7 @@
boolean isStrongBiometric, SensorPrivacyManager sensorPrivacyManager) {
super(context, lazyDaemon, token, listener, options.getUserId(),
options.getOpPackageName(), 0 /* cookie */, options.getSensorId(),
- true /* shouldVibrate */, logger, biometricContext);
+ false /* shouldVibrate */, logger, biometricContext);
setRequestId(requestId);
mIsStrongBiometric = isStrongBiometric;
mSensorPrivacyManager = sensorPrivacyManager;
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 38631c8..5324acd 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -72,6 +72,7 @@
import android.view.ContentRecordingSession;
import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
@@ -111,7 +112,11 @@
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
static final long MEDIA_PROJECTION_PREVENTS_REUSING_CONSENT = 266201607L; // buganizer id
- private final Object mLock = new Object(); // Protects the list of media projections
+ // Protects access to state at service level & IMediaProjection level.
+ // Invocation order while holding locks must follow below to avoid deadlock:
+ // WindowManagerService -> MediaProjectionManagerService -> DisplayManagerService
+ // See mediaprojection.md
+ private final Object mLock = new Object();
private final Map<IBinder, IBinder.DeathRecipient> mDeathEaters;
private final CallbackDelegate mCallbackDelegate;
@@ -127,7 +132,9 @@
private final MediaRouterCallback mMediaRouterCallback;
private MediaRouter.RouteInfo mMediaRouteInfo;
+ @GuardedBy("mLock")
private IBinder mProjectionToken;
+ @GuardedBy("mLock")
private MediaProjection mProjectionGrant;
public MediaProjectionManagerService(Context context) {
@@ -232,7 +239,10 @@
return;
}
- if ((serviceTypes & ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION) != 0) {
+ if (mActivityManagerInternal.hasRunningForegroundService(
+ uid, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION)) {
+ // If there is any process within this UID running a FGS
+ // with the mediaProjection type, that's Okay.
return;
}
@@ -311,9 +321,11 @@
*/
@VisibleForTesting
boolean setContentRecordingSession(@Nullable ContentRecordingSession incomingSession) {
+ // NEVER lock while calling into WindowManagerService, since WindowManagerService is
+ // ALWAYS locked when it invokes MediaProjectionManagerService.
+ final boolean setSessionSucceeded = mWmInternal.setContentRecordingSession(incomingSession);
synchronized (mLock) {
- if (!mWmInternal.setContentRecordingSession(
- incomingSession)) {
+ if (!setSessionSucceeded) {
// Unable to start mirroring, so tear down this projection.
if (mProjectionGrant != null) {
mProjectionGrant.stop();
@@ -356,13 +368,20 @@
*/
@VisibleForTesting
void requestConsentForInvalidProjection() {
+ Intent reviewConsentIntent;
+ int uid;
synchronized (mLock) {
- Slog.v(TAG, "Reusing token: Reshow dialog for due to invalid projection.");
- // Trigger the permission dialog again in SysUI
- // Do not handle the result; SysUI will update us when the user has consented.
- mContext.startActivityAsUser(buildReviewGrantedConsentIntent(),
- UserHandle.getUserHandleForUid(mProjectionGrant.uid));
+ reviewConsentIntent = buildReviewGrantedConsentIntentLocked();
+ uid = mProjectionGrant.uid;
}
+ // NEVER lock while calling into a method that eventually acquires the WindowManagerService
+ // lock, since WindowManagerService is ALWAYS locked when it invokes
+ // MediaProjectionManagerService.
+ Slog.v(TAG, "Reusing token: Reshow dialog for due to invalid projection.");
+ // Trigger the permission dialog again in SysUI
+ // Do not handle the result; SysUI will update us when the user has consented.
+ mContext.startActivityAsUser(reviewConsentIntent,
+ UserHandle.getUserHandleForUid(uid));
}
/**
@@ -372,7 +391,7 @@
* <p>Consent dialog result handled in
* {@link BinderService#setUserReviewGrantedConsentResult(int)}.
*/
- private Intent buildReviewGrantedConsentIntent() {
+ private Intent buildReviewGrantedConsentIntentLocked() {
final String permissionDialogString = mContext.getResources().getString(
R.string.config_mediaProjectionPermissionDialogComponent);
final ComponentName mediaProjectionPermissionDialogComponent =
@@ -385,7 +404,8 @@
}
/**
- * Handles result of dialog shown from {@link BinderService#buildReviewGrantedConsentIntent()}.
+ * Handles result of dialog shown from
+ * {@link BinderService#buildReviewGrantedConsentIntentLocked()}.
*
* <p>Tears down session if user did not consent, or starts mirroring if user did consent.
*/
@@ -487,23 +507,26 @@
MediaProjection getProjectionInternal(int uid, String packageName) {
final long callingToken = Binder.clearCallingIdentity();
try {
- // Supposedly the package has re-used the user's consent; confirm the provided details
- // against the current projection token before re-using the current projection.
- if (mProjectionGrant == null || mProjectionGrant.mSession == null
- || !mProjectionGrant.mSession.isWaitingForConsent()) {
- Slog.e(TAG, "Reusing token: Not possible to reuse the current projection "
- + "instance");
- return null;
- }
+ synchronized (mLock) {
+ // Supposedly the package has re-used the user's consent; confirm the provided
+ // details against the current projection token before re-using the current
+ // projection.
+ if (mProjectionGrant == null || mProjectionGrant.mSession == null
+ || !mProjectionGrant.mSession.isWaitingForConsent()) {
+ Slog.e(TAG, "Reusing token: Not possible to reuse the current projection "
+ + "instance");
+ return null;
+ }
// The package matches, go ahead and re-use the token for this request.
- if (mProjectionGrant.uid == uid
- && Objects.equals(mProjectionGrant.packageName, packageName)) {
- Slog.v(TAG, "Reusing token: getProjection can reuse the current projection");
- return mProjectionGrant;
- } else {
- Slog.e(TAG, "Reusing token: Not possible to reuse the current projection "
- + "instance due to package details mismatching");
- return null;
+ if (mProjectionGrant.uid == uid
+ && Objects.equals(mProjectionGrant.packageName, packageName)) {
+ Slog.v(TAG, "Reusing token: getProjection can reuse the current projection");
+ return mProjectionGrant;
+ } else {
+ Slog.e(TAG, "Reusing token: Not possible to reuse the current projection "
+ + "instance due to package details mismatching");
+ return null;
+ }
}
} finally {
Binder.restoreCallingIdentity(callingToken);
@@ -623,8 +646,10 @@
}
final long token = Binder.clearCallingIdentity();
try {
- if (mProjectionGrant != null) {
- mProjectionGrant.stop();
+ synchronized (mLock) {
+ if (mProjectionGrant != null) {
+ mProjectionGrant.stop();
+ }
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -638,13 +663,17 @@
throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to notify "
+ "on captured content resize");
}
- if (!isCurrentProjection(mProjectionGrant)) {
- return;
+ synchronized (mLock) {
+ if (!isCurrentProjection(mProjectionGrant)) {
+ return;
+ }
}
final long token = Binder.clearCallingIdentity();
try {
- if (mProjectionGrant != null && mCallbackDelegate != null) {
- mCallbackDelegate.dispatchResize(mProjectionGrant, width, height);
+ synchronized (mLock) {
+ if (mProjectionGrant != null && mCallbackDelegate != null) {
+ mCallbackDelegate.dispatchResize(mProjectionGrant, width, height);
+ }
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -658,13 +687,17 @@
throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to notify "
+ "on captured content visibility changed");
}
- if (!isCurrentProjection(mProjectionGrant)) {
- return;
+ synchronized (mLock) {
+ if (!isCurrentProjection(mProjectionGrant)) {
+ return;
+ }
}
final long token = Binder.clearCallingIdentity();
try {
- if (mProjectionGrant != null && mCallbackDelegate != null) {
- mCallbackDelegate.dispatchVisibilityChanged(mProjectionGrant, isVisible);
+ synchronized (mLock) {
+ if (mProjectionGrant != null && mCallbackDelegate != null) {
+ mCallbackDelegate.dispatchVisibilityChanged(mProjectionGrant, isVisible);
+ }
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -709,9 +742,11 @@
throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION to set session "
+ "details.");
}
- if (!isCurrentProjection(projection)) {
- throw new SecurityException("Unable to set ContentRecordingSession on "
- + "non-current MediaProjection");
+ synchronized (mLock) {
+ if (!isCurrentProjection(projection)) {
+ throw new SecurityException("Unable to set ContentRecordingSession on "
+ + "non-current MediaProjection");
+ }
}
final long origId = Binder.clearCallingIdentity();
try {
@@ -729,10 +764,12 @@
throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION to check if the given"
+ "projection is valid.");
}
- if (!isCurrentProjection(projection)) {
- Slog.v(TAG, "Reusing token: Won't request consent again for a token that "
- + "isn't current");
- return;
+ synchronized (mLock) {
+ if (!isCurrentProjection(projection)) {
+ Slog.v(TAG, "Reusing token: Won't request consent again for a token that "
+ + "isn't current");
+ return;
+ }
}
// Remove calling app identity before performing any privileged operations.
diff --git a/services/core/java/com/android/server/media/projection/mediaprojection.md b/services/core/java/com/android/server/media/projection/mediaprojection.md
new file mode 100644
index 0000000..bccdf34
--- /dev/null
+++ b/services/core/java/com/android/server/media/projection/mediaprojection.md
@@ -0,0 +1,30 @@
+# MediaProjection
+
+## Locking model
+`MediaProjectionManagerService` needs to have consistent lock ordering with its interactions with
+`WindowManagerService` to prevent deadlock.
+
+### TLDR
+`MediaProjectionManagerService` must lock when updating its own fields.
+
+Calls must follow the below invocation order while holding locks:
+
+`WindowManagerService -> MediaProjectionManagerService -> DisplayManagerService`
+
+### Justification
+
+`MediaProjectionManagerService` calls into `WindowManagerService` in the below cases. While handling
+each invocation, `WindowManagerService` acquires its own lock:
+* setting a `ContentRecordingSession`
+ * starting a new `MediaProjection` recording session through
+`MediaProjection#createVirtualDisplay`
+ * indicating the user has granted consent to reuse the consent token
+
+`WindowManagerService` calls into `MediaProjectionManagerService`, always while holding
+`WindowManagerGlobalLock`:
+* `ContentRecorder` handling various events such as resizing recorded content
+
+
+Since `WindowManagerService -> MediaProjectionManagerService` is guaranteed to always hold the
+`WindowManagerService` lock, we must ensure that `MediaProjectionManagerService ->
+WindowManagerService` is NEVER holding the `MediaProjectionManagerService` lock.
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
index cd457b7..feb75ef 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
@@ -74,7 +74,8 @@
notificationReported.is_ongoing,
notificationReported.is_foreground_service,
notificationReported.timeout_millis,
- notificationReported.is_non_dismissible);
+ notificationReported.is_non_dismissible,
+ notificationReported.post_duration_millis);
}
@Override
diff --git a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
index 0bb05aa..1b34c70 100644
--- a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
+++ b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
@@ -231,6 +231,14 @@
return;
}
+ // the installers without INSTALL_PACKAGES perm can't perform
+ // the installation in background. So we can just filter out them.
+ if (mPermissionManager.checkPermission(installerPackageName,
+ android.Manifest.permission.INSTALL_PACKAGES,
+ userId) != PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+
// convert up-time to current time.
final long installTimestamp = System.currentTimeMillis()
- (SystemClock.uptimeMillis() - appInfo.createTimestamp);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ae520c0..bba8043 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -279,6 +279,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
@@ -6295,6 +6296,36 @@
}
}
+ /**
+ * Wait for the handler to finish handling all pending messages.
+ * @param timeoutMillis Maximum time in milliseconds to wait.
+ * @param forBackgroundHandler Whether to wait for the background handler instead.
+ * @return True if all the waiting messages in the handler has been handled.
+ * False if timeout.
+ */
+ @Override
+ public boolean waitForHandler(long timeoutMillis, boolean forBackgroundHandler) {
+ final CountDownLatch latch = new CountDownLatch(1);
+ if (forBackgroundHandler) {
+ mBackgroundHandler.post(latch::countDown);
+ } else {
+ mHandler.post(latch::countDown);
+ }
+ final long endTimeMillis = System.currentTimeMillis() + timeoutMillis;
+ while (latch.getCount() > 0) {
+ try {
+ final long remainingTimeMillis = endTimeMillis - System.currentTimeMillis();
+ if (remainingTimeMillis <= 0) {
+ return false;
+ }
+ return latch.await(remainingTimeMillis, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ // ignore and retry
+ }
+ }
+ return true;
+ }
+
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 05bfec4..e1f010f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -354,6 +354,10 @@
return runSetSilentUpdatesPolicy();
case "get-app-metadata":
return runGetAppMetadata();
+ case "wait-for-handler":
+ return runWaitForHandler(/* forBackgroundHandler= */ false);
+ case "wait-for-background-handler":
+ return runWaitForHandler(/* forBackgroundHandler= */ true);
default: {
if (ART_SERVICE_COMMANDS.contains(cmd)) {
if (DexOptHelper.useArtService()) {
@@ -3601,6 +3605,40 @@
return 1;
}
+ private int runWaitForHandler(boolean forBackgroundHandler) {
+ final PrintWriter pw = getOutPrintWriter();
+ long timeoutMillis = 60000; // default timeout is 60 seconds
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "--timeout":
+ timeoutMillis = Long.parseLong(getNextArgRequired());
+ break;
+ default:
+ pw.println("Error: Unknown option: " + opt);
+ return -1;
+ }
+ }
+ if (timeoutMillis <= 0) {
+ pw.println("Error: --timeout value must be positive: " + timeoutMillis);
+ return -1;
+ }
+ final boolean success;
+ try {
+ success = mInterface.waitForHandler(timeoutMillis, forBackgroundHandler);
+ } catch (RemoteException e) {
+ pw.println("Failure [" + e.getClass().getName() + " - " + e.getMessage() + "]");
+ return -1;
+ }
+ if (success) {
+ pw.println("Success");
+ return 0;
+ } else {
+ pw.println("Timeout. PackageManager handlers are still busy.");
+ return -1;
+ }
+ }
+
private int runArtServiceCommand() {
try (var in = ParcelFileDescriptor.dup(getInFileDescriptor());
var out = ParcelFileDescriptor.dup(getOutFileDescriptor());
@@ -4427,6 +4465,18 @@
pw.println(" --reset: restore the installer and throttle time to the default, and");
pw.println(" clear tracks of silent updates in the system.");
pw.println("");
+ pw.println(" wait-for-handler --timeout <MILLIS>");
+ pw.println(" Wait for a given amount of time till the package manager handler finishes");
+ pw.println(" handling all pending messages.");
+ pw.println(" --timeout: wait for a given number of milliseconds. If the handler(s)");
+ pw.println(" fail to finish before the timeout, the command returns error.");
+ pw.println("");
+ pw.println(" wait-for-background-handler --timeout <MILLIS>");
+ pw.println(" Wait for a given amount of time till the package manager's background");
+ pw.println(" handler finishes handling all pending messages.");
+ pw.println(" --timeout: wait for a given number of milliseconds. If the handler(s)");
+ pw.println(" fail to finish before the timeout, the command returns error.");
+ pw.println("");
if (DexOptHelper.useArtService()) {
printArtServiceHelp();
} else {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index f41d964..3e7ae33 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -2810,29 +2810,55 @@
+ pkg.getPackageName());
}
- if ((bp.isNormal() && shouldGrantNormalPermission)
- || (bp.isSignature()
- && (!bp.isPrivileged() || CollectionUtils.contains(
- isPrivilegedPermissionAllowlisted, permName))
- && (CollectionUtils.contains(shouldGrantSignaturePermission,
- permName)
- || (((bp.isPrivileged() && CollectionUtils.contains(
- shouldGrantPrivilegedPermissionIfWasGranted,
- permName)) || bp.isDevelopment() || bp.isRole())
- && origState.isPermissionGranted(permName))))
- || (bp.isInternal()
- && (!bp.isPrivileged() || CollectionUtils.contains(
- isPrivilegedPermissionAllowlisted, permName))
- && (CollectionUtils.contains(shouldGrantInternalPermission,
- permName)
- || (((bp.isPrivileged() && CollectionUtils.contains(
- shouldGrantPrivilegedPermissionIfWasGranted,
- permName)) || bp.isDevelopment() || bp.isRole())
- && origState.isPermissionGranted(permName))))) {
- // Grant an install permission.
- if (uidState.grantPermission(bp)) {
- installPermissionsChangedForUser = true;
+ if (bp.isNormal() || bp.isSignature() || bp.isInternal()) {
+ if ((bp.isNormal() && shouldGrantNormalPermission)
+ || (bp.isSignature()
+ && (!bp.isPrivileged() || CollectionUtils.contains(
+ isPrivilegedPermissionAllowlisted, permName))
+ && (CollectionUtils.contains(shouldGrantSignaturePermission,
+ permName)
+ || (((bp.isPrivileged() && CollectionUtils.contains(
+ shouldGrantPrivilegedPermissionIfWasGranted,
+ permName)) || bp.isDevelopment()
+ || bp.isRole())
+ && origState.isPermissionGranted(
+ permName))))
+ || (bp.isInternal()
+ && (!bp.isPrivileged() || CollectionUtils.contains(
+ isPrivilegedPermissionAllowlisted, permName))
+ && (CollectionUtils.contains(shouldGrantInternalPermission,
+ permName)
+ || (((bp.isPrivileged() && CollectionUtils.contains(
+ shouldGrantPrivilegedPermissionIfWasGranted,
+ permName)) || bp.isDevelopment()
+ || bp.isRole())
+ && origState.isPermissionGranted(
+ permName))))) {
+ // Grant an install permission.
+ if (uidState.grantPermission(bp)) {
+ installPermissionsChangedForUser = true;
+ }
+ } else {
+ if (DEBUG_PERMISSIONS) {
+ boolean wasGranted = uidState.isPermissionGranted(bp.getName());
+ if (wasGranted || bp.isAppOp()) {
+ Slog.i(TAG, (wasGranted ? "Un-granting" : "Not granting")
+ + " permission " + perm
+ + " from package " + friendlyName
+ + " (protectionLevel=" + bp.getProtectionLevel()
+ + " flags=0x"
+ + Integer.toHexString(PackageInfoUtils.appInfoFlags(pkg,
+ ps))
+ + ")");
+ }
+ }
+ if (uidState.revokePermission(bp)) {
+ installPermissionsChangedForUser = true;
+ }
}
+ PermissionState origPermState = origState.getPermissionState(perm);
+ int flags = origPermState != null ? origPermState.getFlags() : 0;
+ uidState.updatePermissionFlags(bp, MASK_PERMISSION_FLAGS_ALL, flags);
} else if (bp.isRuntime()) {
boolean hardRestricted = bp.isHardRestricted();
boolean softRestricted = bp.isSoftRestricted();
@@ -2956,22 +2982,8 @@
uidState.updatePermissionFlags(bp, MASK_PERMISSION_FLAGS_ALL,
flags);
} else {
- if (DEBUG_PERMISSIONS) {
- boolean wasGranted = uidState.isPermissionGranted(bp.getName());
- if (wasGranted || bp.isAppOp()) {
- Slog.i(TAG, (wasGranted ? "Un-granting" : "Not granting")
- + " permission " + perm
- + " from package " + friendlyName
- + " (protectionLevel=" + bp.getProtectionLevel()
- + " flags=0x"
- + Integer.toHexString(PackageInfoUtils.appInfoFlags(pkg,
- ps))
- + ")");
- }
- }
- if (uidState.removePermissionState(bp.getName())) {
- installPermissionsChangedForUser = true;
- }
+ Slog.wtf(LOG_TAG, "Unknown permission protection " + bp.getProtection()
+ + " for permission " + bp.getName());
}
}
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 9cdceef..d63a908 100755
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -269,6 +269,10 @@
@Override
public void onTvMessage(int deviceId, int type, Bundle data) {
synchronized (mLock) {
+ String inputId = mHardwareInputIdMap.get(deviceId);
+ if (inputId == null) {
+ return;
+ }
SomeArgs args = SomeArgs.obtain();
args.arg1 = mHardwareInputIdMap.get(deviceId);
args.arg2 = data;
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 9cfdd5f..a05bd2d 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -4152,6 +4152,10 @@
synchronized (mLock) {
UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
TvInputState inputState = userState.inputMap.get(inputId);
+ if (inputState == null) {
+ Slog.e(TAG, "failed to send TV message - unknown input id " + inputId);
+ return;
+ }
ServiceState serviceState = userState.serviceStateMap.get(inputState.info
.getComponent());
for (IBinder token : serviceState.sessionTokens) {
diff --git a/services/core/java/com/android/server/uri/UriPermission.java b/services/core/java/com/android/server/uri/UriPermission.java
index 6db781a..f3eeab0 100644
--- a/services/core/java/com/android/server/uri/UriPermission.java
+++ b/services/core/java/com/android/server/uri/UriPermission.java
@@ -207,7 +207,9 @@
if (mReadOwners != null && includingOwners) {
ownedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
for (UriPermissionOwner r : mReadOwners) {
- r.removeReadPermission(this);
+ if (r != null) {
+ r.removeReadPermission(this);
+ }
}
mReadOwners = null;
}
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 6b90181..aafff2c 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -18,6 +18,7 @@
import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
import static android.app.Activity.FULLSCREEN_MODE_REQUEST_ENTER;
+import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.ActivityTaskManager.INVALID_WINDOWING_MODE;
import static android.app.FullscreenRequestHandler.REMOTE_CALLBACK_RESULT_KEY;
@@ -818,6 +819,13 @@
null /*startTask */, null /* remoteTransition */,
null /* displayChange */);
r.mTransitionController.setReady(r.getDisplayContent());
+ if (under != null && under.returningOptions != null
+ && under.returningOptions.getAnimationType()
+ == ANIM_SCENE_TRANSITION) {
+ // Pass along the scene-transition animation-type
+ transition.setOverrideAnimation(TransitionInfo.AnimationOptions
+ .makeSceneTransitionAnimOptions(), null, null);
+ }
} else {
transition.abort();
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 839a039..d84c013 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -3560,7 +3560,7 @@
// Note that RecentsAnimation will handle task snapshot while switching apps with
// the best capture timing (e.g. IME window capture),
// No need additional task capture while task is controlled by RecentsAnimation.
- if (mAtmService.mWindowManager.mTaskSnapshotController != null
+ if (!mTransitionController.isShellTransitionsEnabled()
&& !task.isAnimatingByRecents()) {
final ArraySet<Task> tasks = Sets.newArraySet(task);
mAtmService.mWindowManager.mTaskSnapshotController.snapshotTasks(tasks);
@@ -10608,6 +10608,7 @@
return false;
}
if (!isVisibleRequested()) return true;
+ if (mPendingRelaunchCount > 0) return false;
// Wait for attach. That is the earliest time where we know if there will be an associated
// display rotation. If we don't wait, the starting-window can finishDrawing first and
// cause the display rotation to end-up in a following transition.
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index cff6554..1360a95 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3789,7 +3789,7 @@
TaskSnapshot taskSnapshot = mWindowManager.mTaskSnapshotController.getSnapshot(taskId,
task.mUserId, true /* restoreFromDisk */, isLowResolution);
if (taskSnapshot == null && takeSnapshotIfNeeded) {
- taskSnapshot = takeTaskSnapshot(taskId);
+ taskSnapshot = takeTaskSnapshot(taskId, false /* updateCache */);
}
return taskSnapshot;
} finally {
@@ -3798,7 +3798,7 @@
}
@Override
- public TaskSnapshot takeTaskSnapshot(int taskId) {
+ public TaskSnapshot takeTaskSnapshot(int taskId, boolean updateCache) {
mAmInternal.enforceCallingPermission(READ_FRAME_BUFFER, "takeTaskSnapshot()");
final long ident = Binder.clearCallingIdentity();
try {
@@ -3809,8 +3809,13 @@
Slog.w(TAG, "takeTaskSnapshot: taskId=" + taskId + " not found or not visible");
return null;
}
- return mWindowManager.mTaskSnapshotController.captureSnapshot(
- task, true /* snapshotHome */);
+ if (updateCache) {
+ return mWindowManager.mTaskSnapshotController.recordSnapshot(task,
+ true /* snapshotHome */);
+ } else {
+ return mWindowManager.mTaskSnapshotController.captureSnapshot(task,
+ true /* snapshotHome */);
+ }
}
} finally {
Binder.restoreCallingIdentity(ident);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 0121513..0171c20 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -1074,6 +1074,12 @@
// Remove the process record so it won't be considered as alive.
mService.mProcessNames.remove(wpc.mName, wpc.mUid);
mService.mProcessMap.remove(wpc.getPid());
+ } else if (r.intent.isSandboxActivity(mService.mContext)) {
+ Slog.e(TAG, "Abort sandbox activity launching as no sandbox process to host it.");
+ r.finishIfPossible("No sandbox process for the activity", false /* oomAdj */);
+ r.launchFailed = true;
+ r.detachFromProcess();
+ return;
}
r.notifyUnknownVisibilityLaunchedForKeyguardTransition();
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index b67ccd2..cea886f 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -440,7 +440,8 @@
if (app != null) {
mDisplayContent.removeImeSurfaceImmediately();
if (app.getTask() != null) {
- mDisplayContent.mAtmService.takeTaskSnapshot(app.getTask().mTaskId);
+ mDisplayContent.mAtmService.takeTaskSnapshot(app.getTask().mTaskId,
+ true /* updateCache */);
}
}
} else {
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 4d0bff9..7e20b3b 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -28,6 +28,7 @@
import android.os.Environment;
import android.os.Handler;
import android.util.ArraySet;
+import android.util.IntArray;
import android.util.Slog;
import android.view.Display;
import android.window.ScreenCapture;
@@ -37,8 +38,6 @@
import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
import com.android.server.wm.BaseAppSnapshotPersister.PersistInfoProvider;
-import com.google.android.collect.Sets;
-
import java.util.Set;
/**
@@ -58,7 +57,7 @@
static final String SNAPSHOTS_DIRNAME = "snapshots";
private final TaskSnapshotPersister mPersister;
- private final ArraySet<Task> mSkipClosingAppSnapshotTasks = new ArraySet<>();
+ private final IntArray mSkipClosingAppSnapshotTasks = new IntArray();
private final ArraySet<Task> mTmpTasks = new ArraySet<>();
private final Handler mHandler = new Handler();
@@ -135,26 +134,6 @@
}
/**
- * Called when the visibility of an app changes outside of the regular app transition flow.
- */
- void notifyAppVisibilityChanged(ActivityRecord appWindowToken, boolean visible) {
- if (!visible) {
- handleClosingApps(Sets.newArraySet(appWindowToken));
- }
- }
-
- private void handleClosingApps(ArraySet<ActivityRecord> closingApps) {
- if (shouldDisableSnapshots()) {
- return;
- }
- // We need to take a snapshot of the task if and only if all activities of the task are
- // either closing or hidden.
- getClosingTasks(closingApps, mTmpTasks);
- snapshotTasks(mTmpTasks);
- mSkipClosingAppSnapshotTasks.clear();
- }
-
- /**
* Adds the given {@param tasks} to the list of tasks which should not have their snapshots
* taken upon the next processing of the set of closing apps. The caller is responsible for
* calling {@link #snapshotTasks} to ensure that the task has an up-to-date snapshot.
@@ -164,20 +143,23 @@
if (shouldDisableSnapshots()) {
return;
}
- mSkipClosingAppSnapshotTasks.addAll(tasks);
+ for (Task task : tasks) {
+ mSkipClosingAppSnapshotTasks.add(task.mTaskId);
+ }
}
void snapshotTasks(ArraySet<Task> tasks) {
snapshotTasks(tasks, false /* allowSnapshotHome */);
}
- void recordSnapshot(Task task, boolean allowSnapshotHome) {
+ TaskSnapshot recordSnapshot(Task task, boolean allowSnapshotHome) {
final boolean snapshotHome = allowSnapshotHome && task.isActivityTypeHome();
final TaskSnapshot snapshot = recordSnapshotInner(task, allowSnapshotHome);
if (!snapshotHome && snapshot != null) {
mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot);
task.onSnapshotChanged(snapshot);
}
+ return snapshot;
}
private void snapshotTasks(ArraySet<Task> tasks, boolean allowSnapshotHome) {
@@ -271,31 +253,16 @@
return source.getTaskDescription();
}
- /**
- * Retrieves all closing tasks based on the list of closing apps during an app transition.
- */
- @VisibleForTesting
- void getClosingTasks(ArraySet<ActivityRecord> closingApps, ArraySet<Task> outClosingTasks) {
- outClosingTasks.clear();
- for (int i = closingApps.size() - 1; i >= 0; i--) {
- final ActivityRecord activity = closingApps.valueAt(i);
- final Task task = activity.getTask();
- if (task == null) continue;
-
- getClosingTasksInner(task, outClosingTasks);
- }
- }
-
void getClosingTasksInner(Task task, ArraySet<Task> outClosingTasks) {
// Since RecentsAnimation will handle task snapshot while switching apps with the
// best capture timing (e.g. IME window capture),
// No need additional task capture while task is controlled by RecentsAnimation.
if (isAnimatingByRecents(task)) {
- mSkipClosingAppSnapshotTasks.add(task);
+ mSkipClosingAppSnapshotTasks.add(task.mTaskId);
}
// If the task of the app is not visible anymore, it means no other app in that task
// is opening. Thus, the task is closing.
- if (!task.isVisible() && !mSkipClosingAppSnapshotTasks.contains(task)) {
+ if (!task.isVisible() && mSkipClosingAppSnapshotTasks.indexOf(task.mTaskId) < 0) {
outClosingTasks.add(task);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 09312ba..7e34d15 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -845,11 +845,14 @@
case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: {
final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
final Task task = wc != null ? wc.asTask() : null;
- if (task != null) {
+ if (task == null) {
+ throw new IllegalArgumentException("Cannot set non-task as launch root: " + wc);
+ } else if (task.getTaskDisplayArea() == null) {
+ throw new IllegalArgumentException("Cannot set a task without display area as "
+ + "launch root: " + wc);
+ } else {
task.getDisplayArea().setLaunchRootTask(task,
hop.getWindowingModes(), hop.getActivityTypes());
- } else {
- throw new IllegalArgumentException("Cannot set non-task as launch root: " + wc);
}
break;
}
diff --git a/services/core/jni/tvinput/JTvInputHal.cpp b/services/core/jni/tvinput/JTvInputHal.cpp
index 6782b5c..74fdabf 100644
--- a/services/core/jni/tvinput/JTvInputHal.cpp
+++ b/services/core/jni/tvinput/JTvInputHal.cpp
@@ -108,7 +108,7 @@
status.getServiceSpecificError());
return UNKNOWN_ERROR;
}
- connection.mSourceHandle = NativeHandle::create(makeFromAidl(sidebandStream), true);
+ connection.mSourceHandle = NativeHandle::create(dupFromAidl(sidebandStream), true);
}
connection.mSurface = surface;
if (connection.mSurface != nullptr) {
diff --git a/services/credentials/java/com/android/server/credentials/metrics/ApiName.java b/services/credentials/java/com/android/server/credentials/metrics/ApiName.java
index cb6a5d0..d828349 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/ApiName.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/ApiName.java
@@ -30,6 +30,7 @@
import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_INITIAL_PHASE_REPORTED__API_NAME__API_NAME_REGISTER_CREDENTIAL_DESCRIPTION;
import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_INITIAL_PHASE_REPORTED__API_NAME__API_NAME_SET_ENABLED_PROVIDERS;
import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_INITIAL_PHASE_REPORTED__API_NAME__API_NAME_UNKNOWN;
+import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_INITIAL_PHASE_REPORTED__API_NAME__API_NAME_UNREGISTER_CREDENTIAL_DESCRIPTION;
import android.credentials.ui.RequestInfo;
import android.util.Slog;
@@ -61,7 +62,7 @@
),
UNREGISTER_CREDENTIAL_DESCRIPTION(
- CREDENTIAL_MANAGER_INITIAL_PHASE_REPORTED__API_NAME__API_NAME_REGISTER_CREDENTIAL_DESCRIPTION
+ CREDENTIAL_MANAGER_INITIAL_PHASE_REPORTED__API_NAME__API_NAME_UNREGISTER_CREDENTIAL_DESCRIPTION
);
private static final String TAG = "ApiName";
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index cf7c718..79c0349 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -1092,12 +1092,9 @@
if (policies.get(admin).getValue() != null
&& policies.get(admin).getValue().getPackageName().equals(packageName)) {
try {
- if (packageManager.getPackageInfo(
- packageName, 0, userId) == null
- || packageManager.getReceiverInfo(policies.get(admin).getValue(),
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- userId) == null) {
+ if (packageManager.getPackageInfo(packageName, 0, userId) == null
+ || packageManager.getActivityInfo(
+ policies.get(admin).getValue(), 0, userId) == null) {
Slogf.e(TAG, String.format(
"Persistent preferred activity in package %s not found for "
+ "user %d, removing policy for admin",
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
index 2b44615..f47954b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -393,9 +393,9 @@
List.of(makeMockRegisteredReceiver()), false);
enqueueOrReplaceBroadcast(queue, airplaneRecord, 0);
- queue.setProcessAndUidState(mProcess, false, false);
+ queue.setProcessAndUidState(null, false, false);
final long notCachedRunnableAt = queue.getRunnableAt();
- queue.setProcessAndUidState(mProcess, false, true);
+ queue.setProcessAndUidState(null, false, true);
final long cachedRunnableAt = queue.getRunnableAt();
assertThat(cachedRunnableAt).isGreaterThan(notCachedRunnableAt);
assertFalse(queue.isRunnable());
@@ -420,9 +420,9 @@
List.of(makeMockRegisteredReceiver()), false);
enqueueOrReplaceBroadcast(queue, airplaneRecord, 0);
- queue.setProcessAndUidState(mProcess, false, false);
+ queue.setProcessAndUidState(null, false, false);
final long notCachedRunnableAt = queue.getRunnableAt();
- queue.setProcessAndUidState(mProcess, false, true);
+ queue.setProcessAndUidState(null, false, true);
final long cachedRunnableAt = queue.getRunnableAt();
assertThat(cachedRunnableAt).isGreaterThan(notCachedRunnableAt);
assertTrue(queue.isRunnable());
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 615b4c1..ad5f0d7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -399,12 +399,6 @@
UserHandle.USER_SYSTEM);
}
- private ProcessRecord makeActiveProcessRecord(String packageName, String processName)
- throws Exception {
- return makeActiveProcessRecord(packageName, processName, ProcessBehavior.NORMAL,
- UserHandle.USER_SYSTEM);
- }
-
private ProcessRecord makeActiveProcessRecord(String packageName,
ProcessBehavior behavior) throws Exception {
return makeActiveProcessRecord(packageName, packageName, behavior, UserHandle.USER_SYSTEM);
@@ -602,11 +596,6 @@
BackgroundStartPrivileges.NONE, false, null);
}
- private void setProcessFreezable(ProcessRecord app, boolean pendingFreeze, boolean frozen) {
- app.mOptRecord.setPendingFreeze(pendingFreeze);
- app.mOptRecord.setFrozen(frozen);
- }
-
private void assertHealth() {
if (mImpl == Impl.MODERN) {
// If this fails, it'll throw a clear reason message
@@ -1670,10 +1659,8 @@
final ProcessRecord receiverYellowApp = makeActiveProcessRecord(PACKAGE_YELLOW);
final ProcessRecord receiverOrangeApp = makeActiveProcessRecord(PACKAGE_ORANGE);
- setProcessFreezable(receiverGreenApp, true, false);
- mQueue.onProcessFreezableChangedLocked(receiverGreenApp);
- setProcessFreezable(receiverBlueApp, false, true);
- mQueue.onProcessFreezableChangedLocked(receiverBlueApp);
+ mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_GREEN), true);
+ mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_BLUE), true);
final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK);
final BroadcastOptions opts = BroadcastOptions.makeBasic()
@@ -1717,14 +1704,12 @@
eq(UserHandle.USER_SYSTEM), anyInt(), anyInt(), any());
// Shift blue to be active and confirm that deferred broadcast is delivered
- setProcessFreezable(receiverBlueApp, false, false);
- mQueue.onProcessFreezableChangedLocked(receiverBlueApp);
+ mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_BLUE), false);
waitForIdle();
verifyScheduleRegisteredReceiver(times(1), receiverBlueApp, timeTick);
// Shift green to be active and confirm that deferred broadcast is delivered
- setProcessFreezable(receiverGreenApp, false, false);
- mQueue.onProcessFreezableChangedLocked(receiverGreenApp);
+ mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_GREEN), false);
waitForIdle();
verifyScheduleRegisteredReceiver(times(1), receiverGreenApp, timeTick);
}
@@ -2121,12 +2106,9 @@
final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
final ProcessRecord receiverYellowApp = makeActiveProcessRecord(PACKAGE_YELLOW);
- setProcessFreezable(receiverGreenApp, true, true);
- mQueue.onProcessFreezableChangedLocked(receiverGreenApp);
- setProcessFreezable(receiverBlueApp, true, false);
- mQueue.onProcessFreezableChangedLocked(receiverBlueApp);
- setProcessFreezable(receiverYellowApp, false, false);
- mQueue.onProcessFreezableChangedLocked(receiverYellowApp);
+ mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_GREEN), true);
+ mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_BLUE), true);
+ mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_YELLOW), false);
final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
final BroadcastOptions opts = BroadcastOptions.makeBasic()
@@ -2149,50 +2131,11 @@
verifyScheduleRegisteredReceiver(times(1), receiverYellowApp, airplane);
// Shift green to be active and confirm that deferred broadcast is delivered
- setProcessFreezable(receiverGreenApp, false, false);
- mQueue.onProcessFreezableChangedLocked(receiverGreenApp);
+ mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_GREEN), false);
waitForIdle();
verifyScheduleRegisteredReceiver(times(1), receiverGreenApp, airplane);
}
- /**
- * Verify broadcasts to a runtime receiver in cached process is deferred even when a different
- * process in the same package is not cached.
- */
- @Test
- public void testDeferralPolicy_UntilActive_WithMultiProcessUid() throws Exception {
- // Legacy stack doesn't support deferral
- Assume.assumeTrue(mImpl == Impl.MODERN);
-
- final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
- final ProcessRecord receiverGreenApp1 = makeActiveProcessRecord(PACKAGE_GREEN);
- final ProcessRecord receiverGreenApp2 = makeActiveProcessRecord(PACKAGE_GREEN,
- PACKAGE_GREEN + "_proc2");
-
- setProcessFreezable(receiverGreenApp1, true, true);
- mQueue.onProcessFreezableChangedLocked(receiverGreenApp1);
-
- final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- final BroadcastOptions opts = BroadcastOptions.makeBasic()
- .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE);
- enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, opts,
- List.of(makeRegisteredReceiver(receiverGreenApp1),
- makeRegisteredReceiver(receiverGreenApp2))));
- waitForIdle();
-
- // 1st process in Green package is ignored since it is in a cached state
- // but the 2nd process should still receive the broadcast.
- verifyScheduleRegisteredReceiver(never(), receiverGreenApp1, airplane);
- verifyScheduleRegisteredReceiver(times(1), receiverGreenApp2, airplane);
-
- // Shift the 1st process in Green package to be active and confirm that deferred broadcast
- // is delivered
- setProcessFreezable(receiverGreenApp1, false, false);
- mQueue.onProcessFreezableChangedLocked(receiverGreenApp1);
- waitForIdle();
- verifyScheduleRegisteredReceiver(times(1), receiverGreenApp1, airplane);
- }
-
@Test
public void testBroadcastDelivery_uidForeground() throws Exception {
// Legacy stack doesn't support prioritization to foreground app.
diff --git a/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java b/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java
index 01563e2..36a565e 100644
--- a/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java
@@ -140,19 +140,19 @@
// Repeated crashes after the last reset being rate limited should be restricted faster.
assertTrue(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
- // We now need to wait 61 minutes for the buffer should be empty again.
- mClock.setOffsetMillis(83 * 60 * 1000);
+ // We now need to wait 21 minutes for the buffer should be empty again.
+ mClock.setOffsetMillis(43 * 60 * 1000);
assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
- // After yet another 61 minutes, this time without triggering rate limiting, the strict
+ // After yet another 21 minutes, this time without triggering rate limiting, the strict
// limiting should be turnd off.
- mClock.setOffsetMillis(144 * 60 * 1000);
+ mClock.setOffsetMillis(64 * 60 * 1000);
assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
// As rate limiting was not triggered in the last reset, after another 11 minutes the
// buffer should still act as normal.
- mClock.setOffsetMillis(155 * 60 * 1000);
+ mClock.setOffsetMillis(75 * 60 * 1000);
// The first 6 entries should not be rate limited.
assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
index c26eee9..ade3e82 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
@@ -24,6 +24,7 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.same;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.hardware.biometrics.common.AuthenticateReason;
@@ -34,6 +35,7 @@
import android.os.IBinder;
import android.os.PowerManager;
import android.os.RemoteException;
+import android.os.Vibrator;
import android.platform.test.annotations.Presubmit;
import android.testing.TestableContext;
@@ -75,6 +77,8 @@
@Mock
private IBinder mToken;
@Mock
+ private Vibrator mVibrator;
+ @Mock
private ClientMonitorCallbackConverter mClientMonitorCallbackConverter;
@Mock
private BiometricLogger mBiometricLogger;
@@ -94,6 +98,8 @@
@Before
public void setup() {
+ mContext.addMockSystemService(Vibrator.class, mVibrator);
+
when(mBiometricContext.updateContext(any(), anyBoolean())).thenAnswer(
i -> i.getArgument(0));
}
@@ -147,6 +153,16 @@
verify(mBiometricContext).unsubscribe(same(mOperationContextCaptor.getValue()));
}
+ @Test
+ public void doesNotPlayHapticOnInteractionDetected() throws Exception {
+ final FaceDetectClient client = createClient();
+ client.start(mCallback);
+ client.onInteractionDetected();
+ client.stopHalOperation();
+
+ verifyZeroInteractions(mVibrator);
+ }
+
private FaceDetectClient createClient() throws RemoteException {
return createClient(100 /* version */);
}
diff --git a/services/tests/servicestests/src/com/android/server/credentials/MetricUtilitiesTest.java b/services/tests/servicestests/src/com/android/server/credentials/MetricUtilitiesTest.java
new file mode 100644
index 0000000..b46f1a6
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/credentials/MetricUtilitiesTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.credentials;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.credentials.metrics.InitialPhaseMetric;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Given the secondary-system nature of the MetricUtilities, we expect absolutely nothing to
+ * throw an error. If one presents itself, that is problematic.
+ *
+ * atest FrameworksServicesTests:com.android.server.credentials.MetricUtilitiesTest
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public final class MetricUtilitiesTest {
+
+ @Test
+ public void logApiCalledInitialPhase_nullInitPhaseMetricAndNegativeSequence_success() {
+ MetricUtilities.logApiCalledInitialPhase(null, -1);
+ }
+
+ @Test
+ public void logApiCalledInitialPhase_invalidInitPhaseMetricAndPositiveSequence_success() {
+ MetricUtilities.logApiCalledInitialPhase(new InitialPhaseMetric(-1), 1);
+ }
+
+ @Test
+ public void logApiCalledInitialPhase_validInitPhaseMetric_success() {
+ InitialPhaseMetric validInitPhaseMetric = new InitialPhaseMetric(
+ MetricUtilities.getHighlyUniqueInteger());
+ MetricUtilities.logApiCalledInitialPhase(validInitPhaseMetric, 1);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/credentials/metrics/ApiNameTest.java b/services/tests/servicestests/src/com/android/server/credentials/metrics/ApiNameTest.java
new file mode 100644
index 0000000..8093b18
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/credentials/metrics/ApiNameTest.java
@@ -0,0 +1,48 @@
+/*
+ * 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.credentials.metrics;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ApiNameTest {
+
+ @Test
+ public void getMetricCode_matchesWestWorldMetricCode_success() {
+ // com.android.os is only visible in cts tests, so we need to mimic it for server side unit
+ // tests. aidegen can identify it but builds will not. Thus, this is the workaround.
+ Set<Integer> expectedApiNames = new HashSet<>();
+ for (int i = 0; i < 10; i++) {
+ expectedApiNames.add(i);
+ }
+ Set<Integer> actualServerApiNames = Arrays.stream(ApiName.values())
+ .map(ApiName::getMetricCode).collect(Collectors.toSet());
+
+ assertThat(actualServerApiNames).containsExactlyElementsIn(expectedApiNames);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/credentials/metrics/ApiStatusTest.java b/services/tests/servicestests/src/com/android/server/credentials/metrics/ApiStatusTest.java
new file mode 100644
index 0000000..3bf4814
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/credentials/metrics/ApiStatusTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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.credentials.metrics;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ApiStatusTest {
+
+ @Test
+ public void getMetricCode_matchesWestWorldMetricCode_success() {
+ Set<Integer> expectedApiStatus = new HashSet<>();
+ for (int i = 1; i < 5; i++) {
+ expectedApiStatus.add(i);
+ }
+ Set<Integer> actualServerApiStatus = Arrays.stream(ApiStatus.values())
+ .map(ApiStatus::getMetricCode).collect(Collectors.toSet());
+
+ assertThat(actualServerApiStatus).containsExactlyElementsIn(expectedApiStatus);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/credentials/metrics/EntryEnumTest.java b/services/tests/servicestests/src/com/android/server/credentials/metrics/EntryEnumTest.java
new file mode 100644
index 0000000..3104cc2
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/credentials/metrics/EntryEnumTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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.credentials.metrics;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class EntryEnumTest {
+
+ @Test
+ public void getMetricCode_matchesWestWorldMetricCode_success() {
+ Set<Integer> expectedEntryEnum = new HashSet<>();
+ for (int i = 0; i < 5; i++) {
+ expectedEntryEnum.add(i);
+ }
+ Set<Integer> actualServerEntryEnum = Arrays.stream(EntryEnum.values())
+ .map(EntryEnum::getMetricCode).collect(Collectors.toSet());
+
+ assertThat(actualServerEntryEnum).containsExactlyElementsIn(expectedEntryEnum);
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 7092b0b..6e52af1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -298,8 +298,6 @@
assertTrue(mController.isAnimatingTask(activity.getTask()));
spyOn(mWm.mTaskSnapshotController);
- doNothing().when(mWm.mTaskSnapshotController).notifyAppVisibilityChanged(any(),
- anyBoolean());
doReturn(mMockTaskSnapshot).when(mWm.mTaskSnapshotController).getSnapshot(anyInt(),
anyInt(), eq(false) /* restoreFromDisk */, eq(false) /* isLowResolution */);
mController.setDeferredCancel(true /* deferred */, true /* screenshot */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java b/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
index 5eebe74..fc5e9ca 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
@@ -18,6 +18,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
@@ -119,6 +120,22 @@
assertTrue(mockWC.onSyncFinishedDrawing());
bse.onSurfacePlacement();
verify(listener, times(1)).onTransactionReady(eq(id), notNull());
+
+ // The sync is not finished for a relaunching activity.
+ id = startSyncSet(bse, listener);
+ final ActivityRecord r = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final WindowState w = mock(WindowState.class);
+ doReturn(true).when(w).isVisibleRequested();
+ doReturn(true).when(w).fillsParent();
+ doReturn(true).when(w).isSyncFinished(any());
+ r.mChildren.add(w);
+ bse.addToSyncSet(id, r);
+ r.onSyncFinishedDrawing();
+ assertTrue(r.isSyncFinished(r.getSyncGroup()));
+ r.startRelaunching();
+ assertFalse(r.isSyncFinished(r.getSyncGroup()));
+ r.finishRelaunching();
+ assertTrue(r.isSyncFinished(r.getSyncGroup()));
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index 4c7b0aa0..91256ee 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -75,7 +75,7 @@
final ArraySet<ActivityRecord> closingApps = new ArraySet<>();
closingApps.add(closingWindow.mActivityRecord);
final ArraySet<Task> closingTasks = new ArraySet<>();
- mWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks);
+ getClosingTasks(closingApps, closingTasks);
assertEquals(1, closingTasks.size());
assertEquals(closingWindow.mActivityRecord.getTask(), closingTasks.valueAt(0));
}
@@ -93,7 +93,7 @@
final ArraySet<ActivityRecord> closingApps = new ArraySet<>();
closingApps.add(closingWindow.mActivityRecord);
final ArraySet<Task> closingTasks = new ArraySet<>();
- mWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks);
+ getClosingTasks(closingApps, closingTasks);
assertEquals(0, closingTasks.size());
}
@@ -108,10 +108,23 @@
final ArraySet<Task> closingTasks = new ArraySet<>();
mWm.mTaskSnapshotController.addSkipClosingAppSnapshotTasks(
Sets.newArraySet(closingWindow.mActivityRecord.getTask()));
- mWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks);
+ getClosingTasks(closingApps, closingTasks);
assertEquals(0, closingTasks.size());
}
+ /** Retrieves all closing tasks based on the list of closing apps during an app transition. */
+ private void getClosingTasks(ArraySet<ActivityRecord> closingApps,
+ ArraySet<Task> outClosingTasks) {
+ outClosingTasks.clear();
+ for (int i = closingApps.size() - 1; i >= 0; i--) {
+ final ActivityRecord activity = closingApps.valueAt(i);
+ final Task task = activity.getTask();
+ if (task == null) continue;
+
+ mWm.mTaskSnapshotController.getClosingTasksInner(task, outClosingTasks);
+ }
+ }
+
@Test
public void testGetSnapshotMode() {
final WindowState disabledWindow = createWindow(null,