Merge "[multi-shade] Launcher touch integration" into udc-dev
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index 6ff4271..f946754 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -531,8 +531,9 @@
mInstantAppVisibility = o.mInstantAppVisibility;
}
- /** {@inheritDoc} */
- public String toString() {
+ /** @hide */
+ public String toLongString() {
+ // Not implemented directly as toString() due to potential memory regression
final StringBuilder sb = new StringBuilder();
sb.append("IntentFilter {");
sb.append(" pri=");
diff --git a/core/java/android/credentials/ui/CancelUiRequest.java b/core/java/android/credentials/ui/CancelUiRequest.java
index 6bd9de4..d4c249e 100644
--- a/core/java/android/credentials/ui/CancelUiRequest.java
+++ b/core/java/android/credentials/ui/CancelUiRequest.java
@@ -40,24 +40,50 @@
@NonNull
private final IBinder mToken;
+ private final boolean mShouldShowCancellationUi;
+
+ @NonNull
+ private final String mAppPackageName;
+
/** Returns the request token matching the user request that should be cancelled. */
@NonNull
public IBinder getToken() {
return mToken;
}
- public CancelUiRequest(@NonNull IBinder token) {
+ @NonNull
+ public String getAppPackageName() {
+ return mAppPackageName;
+ }
+
+ /**
+ * Returns whether the UI should render a cancellation UI upon the request. If false, the UI
+ * will be silently cancelled.
+ */
+ public boolean shouldShowCancellationUi() {
+ return mShouldShowCancellationUi;
+ }
+
+ public CancelUiRequest(@NonNull IBinder token, boolean shouldShowCancellationUi,
+ @NonNull String appPackageName) {
mToken = token;
+ mShouldShowCancellationUi = shouldShowCancellationUi;
+ mAppPackageName = appPackageName;
}
private CancelUiRequest(@NonNull Parcel in) {
mToken = in.readStrongBinder();
AnnotationValidations.validate(NonNull.class, null, mToken);
+ mShouldShowCancellationUi = in.readBoolean();
+ mAppPackageName = in.readString8();
+ AnnotationValidations.validate(NonNull.class, null, mAppPackageName);
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeStrongBinder(mToken);
+ dest.writeBoolean(mShouldShowCancellationUi);
+ dest.writeString8(mAppPackageName);
}
@Override
diff --git a/core/java/android/credentials/ui/IntentFactory.java b/core/java/android/credentials/ui/IntentFactory.java
index dcfef56..5e8372d 100644
--- a/core/java/android/credentials/ui/IntentFactory.java
+++ b/core/java/android/credentials/ui/IntentFactory.java
@@ -72,7 +72,8 @@
* @hide
*/
@NonNull
- public static Intent createCancelUiIntent(@NonNull IBinder requestToken) {
+ public static Intent createCancelUiIntent(@NonNull IBinder requestToken,
+ boolean shouldShowCancellationUi, @NonNull String appPackageName) {
Intent intent = new Intent();
ComponentName componentName =
ComponentName.unflattenFromString(
@@ -81,7 +82,8 @@
com.android.internal.R.string
.config_credentialManagerDialogComponent));
intent.setComponent(componentName);
- intent.putExtra(CancelUiRequest.EXTRA_CANCEL_UI_REQUEST, new CancelUiRequest(requestToken));
+ intent.putExtra(CancelUiRequest.EXTRA_CANCEL_UI_REQUEST,
+ new CancelUiRequest(requestToken, shouldShowCancellationUi, appPackageName));
return intent;
}
diff --git a/core/java/android/service/remotelockscreenvalidation/RemoteLockscreenValidationClientImpl.java b/core/java/android/service/remotelockscreenvalidation/RemoteLockscreenValidationClientImpl.java
index 140ef39..a5acf54 100644
--- a/core/java/android/service/remotelockscreenvalidation/RemoteLockscreenValidationClientImpl.java
+++ b/core/java/android/service/remotelockscreenvalidation/RemoteLockscreenValidationClientImpl.java
@@ -179,7 +179,7 @@
PackageManager.ComponentInfoFlags.of(PackageManager.GET_META_DATA));
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, TextUtils.formatSimple("Cannot resolve service %s",
- serviceComponent.getClass().getName()));
+ serviceComponent.getClassName()));
return null;
}
}
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index e62af74..bab4b6e 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -21,6 +21,7 @@
option java_outer_classname = "SettingsServiceProto";
import "frameworks/base/core/proto/android/providers/settings/config.proto";
+import "frameworks/base/core/proto/android/providers/settings/generation.proto";
import "frameworks/base/core/proto/android/providers/settings/global.proto";
import "frameworks/base/core/proto/android/providers/settings/secure.proto";
import "frameworks/base/core/proto/android/providers/settings/system.proto";
@@ -37,6 +38,9 @@
// Config settings
optional ConfigSettingsProto config_settings = 3;
+
+ // Generation registry stats
+ optional GenerationRegistryProto generation_registry = 4;
}
message UserSettingsProto {
diff --git a/core/proto/android/providers/settings/generation.proto b/core/proto/android/providers/settings/generation.proto
new file mode 100644
index 0000000..9dcbad2
--- /dev/null
+++ b/core/proto/android/providers/settings/generation.proto
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+package android.providers.settings;
+
+option java_multiple_files = true;
+
+import "frameworks/base/core/proto/android/privacy.proto";
+
+message GenerationRegistryProto {
+ option (android.msg_privacy).dest = DEST_EXPLICIT;
+ optional int32 num_backing_stores = 1;
+ optional int32 num_max_backing_stores = 2;
+ repeated BackingStoreProto backing_stores = 3;
+}
+
+message BackingStoreProto {
+ optional int32 key = 1;
+ optional int32 backing_store_size = 2;
+ optional int32 num_cached_entries = 3;
+ repeated CacheEntryProto cache_entries = 4;
+}
+
+message CacheEntryProto {
+ optional string name = 1;
+ optional int32 generation = 2;
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 563fb4d..87a7c3e 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -228,9 +228,10 @@
the screen. This time the double-tap can happen on the top or bottom of the screen.
To teach the user about this feature, we display an education explaining how the double-tap
works and how the app can be moved on the screen.
- This is the text we show to the user below an animated icon visualizing the double-tap
- action. [CHAR LIMIT=NONE] -->
- <string name="letterbox_reachability_reposition_text">Double-tap to move this app</string>
+ This is the text we show to the user below an icon visualizing the double-tap
+ action. The description should be split in two lines separated by "\n" like for this
+ locale. [CHAR LIMIT=NONE] -->
+ <string name="letterbox_reachability_reposition_text">Double-tap to\nmove this app</string>
<!-- Freeform window caption strings -->
<!-- Accessibility text for the maximize window button [CHAR LIMIT=NONE] -->
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
index 015d5c1..fb0a91f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
@@ -16,10 +16,13 @@
package com.android.wm.shell.desktopmode;
+import static com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.FINAL_FREEFORM_SCALE;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.RectEvaluator;
import android.animation.ValueAnimator;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Resources;
@@ -32,12 +35,12 @@
import android.view.WindowManager;
import android.view.WindowlessWindowManager;
import android.view.animation.DecelerateInterpolator;
-import android.widget.ImageView;
import com.android.wm.shell.R;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
/**
@@ -56,6 +59,9 @@
private final SyncTransactionQueue mSyncQueue;
private SurfaceControlViewHost mViewHost;
+ private View mView;
+ private boolean mIsFullscreen;
+
public DesktopModeVisualIndicator(SyncTransactionQueue syncQueue,
ActivityManager.RunningTaskInfo taskInfo, DisplayController displayController,
Context context, SurfaceControl taskSurface, ShellTaskOrganizer taskOrganizer,
@@ -67,21 +73,19 @@
mTaskSurface = taskSurface;
mTaskOrganizer = taskOrganizer;
mRootTdaOrganizer = taskDisplayAreaOrganizer;
+ createView();
}
/**
- * Create and animate the indicator for the exit desktop mode transition.
+ * Create a fullscreen indicator with no animation
*/
- public void createFullscreenIndicator() {
+ private void createView() {
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
final Resources resources = mContext.getResources();
final DisplayMetrics metrics = resources.getDisplayMetrics();
final int screenWidth = metrics.widthPixels;
final int screenHeight = metrics.heightPixels;
- final int padding = mDisplayController
- .getDisplayLayout(mTaskInfo.displayId).stableInsets().top;
- final ImageView v = new ImageView(mContext);
- v.setImageResource(R.drawable.desktop_windowing_transition_background);
+ mView = new View(mContext);
final SurfaceControl.Builder builder = new SurfaceControl.Builder();
mRootTdaOrganizer.attachToDisplayArea(mTaskInfo.displayId, builder);
mLeash = builder
@@ -101,7 +105,7 @@
mViewHost = new SurfaceControlViewHost(mContext,
mDisplayController.getDisplay(mTaskInfo.displayId), windowManager,
"FullscreenVisualIndicator");
- mViewHost.setView(v, lp);
+ mViewHost.setView(mView, lp);
// We want this indicator to be behind the dragged task, but in front of all others.
t.setRelativeLayer(mLeash, mTaskSurface, -1);
@@ -109,17 +113,56 @@
transaction.merge(t);
t.close();
});
- final Rect startBounds = new Rect(padding, padding,
- screenWidth - padding, screenHeight - padding);
- final VisualIndicatorAnimator animator = VisualIndicatorAnimator.fullscreenIndicator(v,
- startBounds);
+ }
+
+ /**
+ * Create fullscreen indicator and fades it in.
+ */
+ public void createFullscreenIndicator() {
+ mIsFullscreen = true;
+ mView.setBackgroundResource(R.drawable.desktop_windowing_transition_background);
+ final VisualIndicatorAnimator animator = VisualIndicatorAnimator.toFullscreenAnimator(
+ mView, mDisplayController.getDisplayLayout(mTaskInfo.displayId));
+ animator.start();
+ }
+
+ /**
+ * Create a fullscreen indicator. Animator fades it in while expanding the bounds outwards.
+ */
+ public void createFullscreenIndicatorWithAnimatedBounds() {
+ mIsFullscreen = true;
+ mView.setBackgroundResource(R.drawable.desktop_windowing_transition_background);
+ final VisualIndicatorAnimator animator = VisualIndicatorAnimator
+ .toFullscreenAnimatorWithAnimatedBounds(mView,
+ mDisplayController.getDisplayLayout(mTaskInfo.displayId));
+ animator.start();
+ }
+
+ /**
+ * Takes existing fullscreen indicator and animates it to freeform bounds
+ */
+ public void transitionFullscreenIndicatorToFreeform() {
+ mIsFullscreen = false;
+ final VisualIndicatorAnimator animator = VisualIndicatorAnimator.toFreeformAnimator(
+ mView, mDisplayController.getDisplayLayout(mTaskInfo.displayId));
+ animator.start();
+ }
+
+ /**
+ * Takes the existing freeform indicator and animates it to fullscreen
+ */
+ public void transitionFreeformIndicatorToFullscreen() {
+ mIsFullscreen = true;
+ final VisualIndicatorAnimator animator =
+ VisualIndicatorAnimator.toFullscreenAnimatorWithAnimatedBounds(
+ mView, mDisplayController.getDisplayLayout(mTaskInfo.displayId));
animator.start();
}
/**
* Release the indicator and its components when it is no longer needed.
*/
- public void releaseFullscreenIndicator() {
+ public void releaseVisualIndicator() {
if (mViewHost == null) return;
if (mViewHost != null) {
mViewHost.release();
@@ -136,20 +179,28 @@
});
}
}
+
+ /**
+ * Returns true if visual indicator is fullscreen
+ */
+ public boolean isFullscreen() {
+ return mIsFullscreen;
+ }
+
/**
* Animator for Desktop Mode transitions which supports bounds and alpha animation.
*/
private static class VisualIndicatorAnimator extends ValueAnimator {
private static final int FULLSCREEN_INDICATOR_DURATION = 200;
- private static final float SCALE_ADJUSTMENT_PERCENT = 0.015f;
+ private static final float FULLSCREEN_SCALE_ADJUSTMENT_PERCENT = 0.015f;
private static final float INDICATOR_FINAL_OPACITY = 0.7f;
- private final ImageView mView;
+ private final View mView;
private final Rect mStartBounds;
private final Rect mEndBounds;
private final RectEvaluator mRectEvaluator;
- private VisualIndicatorAnimator(ImageView view, Rect startBounds,
+ private VisualIndicatorAnimator(View view, Rect startBounds,
Rect endBounds) {
mView = view;
mStartBounds = new Rect(startBounds);
@@ -161,30 +212,66 @@
/**
* Create animator for visual indicator of fullscreen transition
*
- * @param view the view for this indicator
- * @param startBounds the starting bounds of the fullscreen indicator
+ * @param view the view for this indicator
+ * @param displayLayout information about the display the transitioning task is currently on
*/
- public static VisualIndicatorAnimator fullscreenIndicator(ImageView view,
- Rect startBounds) {
- view.getDrawable().setBounds(startBounds);
- int width = startBounds.width();
- int height = startBounds.height();
- Rect endBounds = new Rect((int) (startBounds.left - (SCALE_ADJUSTMENT_PERCENT * width)),
- (int) (startBounds.top - (SCALE_ADJUSTMENT_PERCENT * height)),
- (int) (startBounds.right + (SCALE_ADJUSTMENT_PERCENT * width)),
- (int) (startBounds.bottom + (SCALE_ADJUSTMENT_PERCENT * height)));
- VisualIndicatorAnimator animator = new VisualIndicatorAnimator(
- view, startBounds, endBounds);
+ public static VisualIndicatorAnimator toFullscreenAnimator(@NonNull View view,
+ @NonNull DisplayLayout displayLayout) {
+ final Rect bounds = getMaxBounds(displayLayout);
+ final VisualIndicatorAnimator animator = new VisualIndicatorAnimator(
+ view, bounds, bounds);
animator.setInterpolator(new DecelerateInterpolator());
- setupFullscreenIndicatorAnimation(animator);
+ setupIndicatorAnimation(animator);
+ return animator;
+ }
+
+
+ /**
+ * Create animator for visual indicator of fullscreen transition
+ *
+ * @param view the view for this indicator
+ * @param displayLayout information about the display the transitioning task is currently on
+ */
+ public static VisualIndicatorAnimator toFullscreenAnimatorWithAnimatedBounds(
+ @NonNull View view, @NonNull DisplayLayout displayLayout) {
+ final int padding = displayLayout.stableInsets().top;
+ Rect startBounds = new Rect(padding, padding,
+ displayLayout.width() - padding, displayLayout.height() - padding);
+ view.getBackground().setBounds(startBounds);
+
+ final VisualIndicatorAnimator animator = new VisualIndicatorAnimator(
+ view, startBounds, getMaxBounds(displayLayout));
+ animator.setInterpolator(new DecelerateInterpolator());
+ setupIndicatorAnimation(animator);
return animator;
}
/**
- * Add necessary listener for animation of fullscreen indicator
+ * Create animator for visual indicator of freeform transition
+ *
+ * @param view the view for this indicator
+ * @param displayLayout information about the display the transitioning task is currently on
*/
- private static void setupFullscreenIndicatorAnimation(
- VisualIndicatorAnimator animator) {
+ public static VisualIndicatorAnimator toFreeformAnimator(@NonNull View view,
+ @NonNull DisplayLayout displayLayout) {
+ final float adjustmentPercentage = 1f - FINAL_FREEFORM_SCALE;
+ final int width = displayLayout.width();
+ final int height = displayLayout.height();
+ Rect endBounds = new Rect((int) (adjustmentPercentage * width / 2),
+ (int) (adjustmentPercentage * height / 2),
+ (int) (displayLayout.width() - (adjustmentPercentage * width / 2)),
+ (int) (displayLayout.height() - (adjustmentPercentage * height / 2)));
+ final VisualIndicatorAnimator animator = new VisualIndicatorAnimator(
+ view, getMaxBounds(displayLayout), endBounds);
+ animator.setInterpolator(new DecelerateInterpolator());
+ setupIndicatorAnimation(animator);
+ return animator;
+ }
+
+ /**
+ * Add necessary listener for animation of indicator
+ */
+ private static void setupIndicatorAnimation(@NonNull VisualIndicatorAnimator animator) {
animator.addUpdateListener(a -> {
if (animator.mView != null) {
animator.updateBounds(a.getAnimatedFraction(), animator.mView);
@@ -196,7 +283,7 @@
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- animator.mView.getDrawable().setBounds(animator.mEndBounds);
+ animator.mView.getBackground().setBounds(animator.mEndBounds);
}
});
animator.setDuration(FULLSCREEN_INDICATOR_DURATION);
@@ -210,9 +297,12 @@
* @param fraction fraction to use, compared against previous fraction
* @param view the view to update
*/
- private void updateBounds(float fraction, ImageView view) {
+ private void updateBounds(float fraction, View view) {
+ if (mStartBounds.equals(mEndBounds)) {
+ return;
+ }
Rect currentBounds = mRectEvaluator.evaluate(fraction, mStartBounds, mEndBounds);
- view.getDrawable().setBounds(currentBounds);
+ view.getBackground().setBounds(currentBounds);
}
/**
@@ -223,5 +313,23 @@
private void updateIndicatorAlpha(float fraction, View view) {
view.setAlpha(fraction * INDICATOR_FINAL_OPACITY);
}
+
+ /**
+ * Return the max bounds of a fullscreen indicator
+ */
+ private static Rect getMaxBounds(@NonNull DisplayLayout displayLayout) {
+ final int padding = displayLayout.stableInsets().top;
+ final int width = displayLayout.width() - 2 * padding;
+ final int height = displayLayout.height() - 2 * padding;
+ Rect endBounds = new Rect((int) (padding
+ - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * width)),
+ (int) (padding
+ - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * height)),
+ (int) (displayLayout.width() - padding
+ + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * width)),
+ (int) (displayLayout.height() - padding
+ + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * height)));
+ return endBounds;
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index c35cd5a..670b24c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -122,7 +122,7 @@
}
/** Move a task to desktop */
- fun moveToDesktop(task: ActivityManager.RunningTaskInfo) {
+ fun moveToDesktop(task: RunningTaskInfo) {
ProtoLog.v(WM_SHELL_DESKTOP_MODE, "moveToDesktop: %d", task.taskId)
val wct = WindowContainerTransaction()
@@ -181,7 +181,7 @@
}
/** Move a task to fullscreen */
- fun moveToFullscreen(task: ActivityManager.RunningTaskInfo) {
+ fun moveToFullscreen(task: RunningTaskInfo) {
ProtoLog.v(WM_SHELL_DESKTOP_MODE, "moveToFullscreen: %d", task.taskId)
val wct = WindowContainerTransaction()
@@ -206,7 +206,7 @@
}
/** Move a task to the front **/
- fun moveTaskToFront(taskInfo: ActivityManager.RunningTaskInfo) {
+ fun moveTaskToFront(taskInfo: RunningTaskInfo) {
val wct = WindowContainerTransaction()
wct.reorder(taskInfo.token, true)
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
@@ -273,7 +273,7 @@
request: TransitionRequestInfo
): WindowContainerTransaction? {
// Check if we should skip handling this transition
- val task: ActivityManager.RunningTaskInfo? = request.triggerTask
+ val task: RunningTaskInfo? = request.triggerTask
val shouldHandleRequest =
when {
// Only handle open or to front transitions
@@ -382,16 +382,15 @@
taskSurface: SurfaceControl,
y: Float
) {
- val statusBarHeight = displayController
- .getDisplayLayout(taskInfo.displayId)?.stableInsets()?.top ?: 0
if (taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) {
+ val statusBarHeight = getStatusBarHeight(taskInfo)
if (y <= statusBarHeight && visualIndicator == null) {
visualIndicator = DesktopModeVisualIndicator(syncQueue, taskInfo,
displayController, context, taskSurface, shellTaskOrganizer,
rootTaskDisplayAreaOrganizer)
- visualIndicator?.createFullscreenIndicator()
+ visualIndicator?.createFullscreenIndicatorWithAnimatedBounds()
} else if (y > statusBarHeight && visualIndicator != null) {
- visualIndicator?.releaseFullscreenIndicator()
+ visualIndicator?.releaseVisualIndicator()
visualIndicator = null
}
}
@@ -407,16 +406,73 @@
taskInfo: RunningTaskInfo,
y: Float
) {
- val statusBarHeight = displayController
- .getDisplayLayout(taskInfo.displayId)?.stableInsets()?.top ?: 0
+ val statusBarHeight = getStatusBarHeight(taskInfo)
if (y <= statusBarHeight && taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) {
- visualIndicator?.releaseFullscreenIndicator()
+ visualIndicator?.releaseVisualIndicator()
visualIndicator = null
moveToFullscreenWithAnimation(taskInfo)
}
}
/**
+ * Perform checks required on drag move. Create/release fullscreen indicator and transitions
+ * indicator to freeform or fullscreen dimensions as needed.
+ *
+ * @param taskInfo the task being dragged.
+ * @param taskSurface SurfaceControl of dragged task.
+ * @param y coordinate of dragged task. Used for checks against status bar height.
+ */
+ fun onDragPositioningMoveThroughStatusBar(
+ taskInfo: RunningTaskInfo,
+ taskSurface: SurfaceControl,
+ y: Float
+ ) {
+ if (visualIndicator == null) {
+ visualIndicator = DesktopModeVisualIndicator(syncQueue, taskInfo,
+ displayController, context, taskSurface, shellTaskOrganizer,
+ rootTaskDisplayAreaOrganizer)
+ visualIndicator?.createFullscreenIndicator()
+ }
+ val indicator = visualIndicator ?: return
+ if (y >= getFreeformTransitionStatusBarDragThreshold(taskInfo)) {
+ if (indicator.isFullscreen) {
+ indicator.transitionFullscreenIndicatorToFreeform()
+ }
+ } else if (!indicator.isFullscreen) {
+ indicator.transitionFreeformIndicatorToFullscreen()
+ }
+ }
+
+ /**
+ * Perform checks required when drag ends under status bar area.
+ *
+ * @param taskInfo the task being dragged.
+ * @param y height of drag, to be checked against status bar height.
+ */
+ fun onDragPositioningEndThroughStatusBar(
+ taskInfo: RunningTaskInfo,
+ freeformBounds: Rect
+ ) {
+ moveToDesktopWithAnimation(taskInfo, freeformBounds)
+ visualIndicator?.releaseVisualIndicator()
+ visualIndicator = null
+ }
+
+
+ private fun getStatusBarHeight(taskInfo: RunningTaskInfo): Int {
+ return displayController.getDisplayLayout(taskInfo.displayId)?.stableInsets()?.top ?: 0
+ }
+
+ /**
+ * Returns the threshold at which we transition a task into freeform when dragging a
+ * fullscreen task down from the status bar
+ */
+ private fun getFreeformTransitionStatusBarDragThreshold(taskInfo: RunningTaskInfo): Int {
+ return 2 * getStatusBarHeight(taskInfo)
+ }
+
+
+ /**
* Adds a listener to find out about changes in the visibility of freeform tasks.
*
* @param listener the listener to add.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index 4c53f60..f70df83 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -188,6 +188,11 @@
return mCurrentAnimator;
}
+ /** Reset animator state to prevent it from being used after its lifetime. */
+ public void resetAnimatorState() {
+ mCurrentAnimator = null;
+ }
+
private PipTransitionAnimator setupPipTransitionAnimator(PipTransitionAnimator animator) {
animator.setSurfaceTransactionHelper(mSurfaceTransactionHelper);
animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
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 ec0e770..a939212 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
@@ -1854,6 +1854,7 @@
animator::clearContentOverlay);
}
PipAnimationController.quietCancel(animator);
+ mPipAnimationController.resetAnimatorState();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index c0dcd0b..f998217 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -79,6 +79,7 @@
public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
private static final String TAG = "DesktopModeWindowDecorViewModel";
+
private final DesktopModeWindowDecoration.Factory mDesktopModeWindowDecorFactory;
private final ActivityTaskManager mActivityTaskManager;
private final ShellTaskOrganizer mTaskOrganizer;
@@ -542,7 +543,7 @@
mTransitionDragActive = false;
final int statusBarHeight = getStatusBarHeight(
relevantDecor.mTaskInfo.displayId);
- if (ev.getY() > statusBarHeight) {
+ if (ev.getY() > 2 * statusBarHeight) {
if (DesktopModeStatus.isProto2Enabled()) {
mPauseRelayoutForTask = relevantDecor.mTaskInfo.taskId;
centerAndMoveToDesktopWithAnimation(relevantDecor, ev);
@@ -567,9 +568,11 @@
return;
}
if (mTransitionDragActive) {
- final int statusBarHeight = mDisplayController
- .getDisplayLayout(
- relevantDecor.mTaskInfo.displayId).stableInsets().top;
+ mDesktopTasksController.ifPresent(
+ c -> c.onDragPositioningMoveThroughStatusBar(relevantDecor.mTaskInfo,
+ relevantDecor.mTaskSurface, ev.getY()));
+ final int statusBarHeight = getStatusBarHeight(
+ relevantDecor.mTaskInfo.displayId);
if (ev.getY() > statusBarHeight) {
if (!mDragToDesktopAnimationStarted) {
mDragToDesktopAnimationStarted = true;
@@ -644,7 +647,8 @@
@Override
public void onAnimationEnd(Animator animation) {
mDesktopTasksController.ifPresent(
- c -> c.moveToDesktopWithAnimation(relevantDecor.mTaskInfo,
+ c -> c.onDragPositioningEndThroughStatusBar(
+ relevantDecor.mTaskInfo,
calculateFreeformBounds(FINAL_FREEFORM_SCALE)));
}
});
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index 28f9453..452455c 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -110,6 +110,11 @@
ResultReceiver::class.java
)
+ val cancellationRequest = getCancelUiRequest(intent)
+ val cancelUiRequestState = cancellationRequest?.let {
+ CancelUiRequestState(getAppLabel(context.getPackageManager(), it.appPackageName))
+ }
+
initialUiState = when (requestInfo.type) {
RequestInfo.TYPE_CREATE -> {
val defaultProviderId = userConfigRepo.getDefaultProviderId()
@@ -128,6 +133,7 @@
isPasskeyFirstUse
)!!,
getCredentialUiState = null,
+ cancelRequestState = cancelUiRequestState
)
}
RequestInfo.TYPE_GET -> {
@@ -142,6 +148,7 @@
if (autoSelectEntry == null) ProviderActivityState.NOT_APPLICABLE
else ProviderActivityState.READY_TO_LAUNCH,
isAutoSelectFlow = autoSelectEntry != null,
+ cancelRequestState = cancelUiRequestState
)
}
else -> throw IllegalStateException("Unrecognized request type: ${requestInfo.type}")
@@ -238,12 +245,12 @@
}
}
- /** Return the request token whose UI should be cancelled, or null otherwise. */
- fun getCancelUiRequestToken(intent: Intent): IBinder? {
+ /** Return the cancellation request if present. */
+ fun getCancelUiRequest(intent: Intent): CancelUiRequest? {
return intent.extras?.getParcelable(
CancelUiRequest.EXTRA_CANCEL_UI_REQUEST,
CancelUiRequest::class.java
- )?.token
+ )
}
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
index 5d72424..2efe1be 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -30,11 +30,13 @@
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.ui.res.stringResource
import androidx.lifecycle.viewmodel.compose.viewModel
import com.android.credentialmanager.common.Constants
import com.android.credentialmanager.common.DialogState
import com.android.credentialmanager.common.ProviderActivityResult
import com.android.credentialmanager.common.StartBalIntentSenderForResultContract
+import com.android.credentialmanager.common.ui.Snackbar
import com.android.credentialmanager.createflow.CreateCredentialScreen
import com.android.credentialmanager.createflow.hasContentToDisplay
import com.android.credentialmanager.getflow.GetCredentialScreen
@@ -49,10 +51,9 @@
super.onCreate(savedInstanceState)
Log.d(Constants.LOG_TAG, "Creating new CredentialSelectorActivity")
try {
- if (CredentialManagerRepo.getCancelUiRequestToken(intent) != null) {
- Log.d(
- Constants.LOG_TAG, "Received UI cancellation intent; cancelling the activity.")
- this.finish()
+ val (isCancellationRequest, shouldShowCancellationUi, _) =
+ maybeCancelUIUponRequest(intent)
+ if (isCancellationRequest && !shouldShowCancellationUi) {
return
}
val userConfigRepo = UserConfigRepo(this)
@@ -75,14 +76,15 @@
setIntent(intent)
Log.d(Constants.LOG_TAG, "Existing activity received new intent")
try {
- val cancelUiRequestToken = CredentialManagerRepo.getCancelUiRequestToken(intent)
val viewModel: CredentialSelectorViewModel by viewModels()
- if (cancelUiRequestToken != null &&
- viewModel.shouldCancelCurrentUi(cancelUiRequestToken)) {
- Log.d(
- Constants.LOG_TAG, "Received UI cancellation intent; cancelling the activity.")
- this.finish()
- return
+ val (isCancellationRequest, shouldShowCancellationUi, appDisplayName) =
+ maybeCancelUIUponRequest(intent, viewModel)
+ if (isCancellationRequest) {
+ if (shouldShowCancellationUi) {
+ viewModel.onCancellationUiRequested(appDisplayName)
+ } else {
+ return
+ }
} else {
val userConfigRepo = UserConfigRepo(this)
val credManRepo = CredentialManagerRepo(this, intent, userConfigRepo)
@@ -93,11 +95,41 @@
}
}
+ /**
+ * Cancels the UI activity if requested by the backend. Different from the other finishing
+ * helpers, this does not report anything back to the Credential Manager service backend.
+ *
+ * Can potentially show a transient snackbar before finishing, if the request specifies so.
+ *
+ * Returns <isCancellationRequest, shouldShowCancellationUi, appDisplayName>.
+ */
+ private fun maybeCancelUIUponRequest(
+ intent: Intent,
+ viewModel: CredentialSelectorViewModel? = null
+ ): Triple<Boolean, Boolean, String?> {
+ val cancelUiRequest = CredentialManagerRepo.getCancelUiRequest(intent)
+ ?: return Triple(false, false, null)
+ if (viewModel != null && !viewModel.shouldCancelCurrentUi(cancelUiRequest.token)) {
+ // Cancellation was for a different request, don't cancel the current UI.
+ return Triple(false, false, null)
+ }
+ val shouldShowCancellationUi = cancelUiRequest.shouldShowCancellationUi()
+ Log.d(
+ Constants.LOG_TAG, "Received UI cancellation intent. Should show cancellation" +
+ " ui = $shouldShowCancellationUi")
+ val appDisplayName = getAppLabel(packageManager, cancelUiRequest.appPackageName)
+ if (!shouldShowCancellationUi) {
+ this.finish()
+ }
+ return Triple(true, shouldShowCancellationUi, appDisplayName)
+ }
+
+
@ExperimentalMaterialApi
@Composable
- fun CredentialManagerBottomSheet(
+ private fun CredentialManagerBottomSheet(
credManRepo: CredentialManagerRepo,
- userConfigRepo: UserConfigRepo
+ userConfigRepo: UserConfigRepo,
) {
val viewModel: CredentialSelectorViewModel = viewModel {
CredentialSelectorViewModel(credManRepo, userConfigRepo)
@@ -113,7 +145,17 @@
val createCredentialUiState = viewModel.uiState.createCredentialUiState
val getCredentialUiState = viewModel.uiState.getCredentialUiState
- if (createCredentialUiState != null && hasContentToDisplay(createCredentialUiState)) {
+ val cancelRequestState = viewModel.uiState.cancelRequestState
+ if (cancelRequestState != null) {
+ if (cancelRequestState.appDisplayName == null) {
+ Log.d(Constants.LOG_TAG, "Received UI cancel request with an invalid package name.")
+ this.finish()
+ return
+ } else {
+ UiCancellationScreen(cancelRequestState.appDisplayName)
+ }
+ } else if (
+ createCredentialUiState != null && hasContentToDisplay(createCredentialUiState)) {
CreateCredentialScreen(
viewModel = viewModel,
createCredentialUiState = createCredentialUiState,
@@ -122,15 +164,15 @@
} else if (getCredentialUiState != null && hasContentToDisplay(getCredentialUiState)) {
if (isFallbackScreen(getCredentialUiState)) {
GetGenericCredentialScreen(
- viewModel = viewModel,
- getCredentialUiState = getCredentialUiState,
- providerActivityLauncher = launcher
+ viewModel = viewModel,
+ getCredentialUiState = getCredentialUiState,
+ providerActivityLauncher = launcher
)
} else {
GetCredentialScreen(
- viewModel = viewModel,
- getCredentialUiState = getCredentialUiState,
- providerActivityLauncher = launcher
+ viewModel = viewModel,
+ getCredentialUiState = getCredentialUiState,
+ providerActivityLauncher = launcher
)
}
} else {
@@ -172,4 +214,13 @@
)
this.finish()
}
+
+ @Composable
+ private fun UiCancellationScreen(appDisplayName: String) {
+ Snackbar(
+ contentText = stringResource(R.string.request_cancelled_by, appDisplayName),
+ onDismiss = { this@CredentialSelectorActivity.finish() },
+ dismissOnTimeout = true,
+ )
+ }
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
index e49e3f1..7eb3bf4 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -51,6 +51,11 @@
// True if the UI has one and only one auto selectable entry. Its provider activity will be
// launched immediately, and canceling it will cancel the whole UI flow.
val isAutoSelectFlow: Boolean = false,
+ val cancelRequestState: CancelUiRequestState?,
+)
+
+data class CancelUiRequestState(
+ val appDisplayName: String?,
)
class CredentialSelectorViewModel(
@@ -76,6 +81,10 @@
uiState = uiState.copy(dialogState = DialogState.COMPLETE)
}
+ fun onCancellationUiRequested(appDisplayName: String?) {
+ uiState = uiState.copy(cancelRequestState = CancelUiRequestState(appDisplayName))
+ }
+
/** Close the activity and don't report anything to the backend.
* Example use case is the no-auth-info snackbar where the activity should simply display the
* UI and then be dismissed. */
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index 783cf3b..43da980 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -64,7 +64,7 @@
import org.json.JSONObject
// TODO: remove all !! checks
-private fun getAppLabel(
+fun getAppLabel(
pm: PackageManager,
appPackageName: String
): String? {
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SnackBar.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SnackBar.kt
index 514ff90..dfff3d6 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SnackBar.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SnackBar.kt
@@ -30,20 +30,24 @@
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalAccessibilityManager
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.android.credentialmanager.R
import com.android.credentialmanager.common.material.Scrim
import com.android.credentialmanager.ui.theme.Shapes
+import kotlinx.coroutines.delay
@Composable
fun Snackbar(
contentText: String,
action: (@Composable () -> Unit)? = null,
onDismiss: () -> Unit,
+ dismissOnTimeout: Boolean = false,
) {
BoxWithConstraints {
Box(Modifier.fillMaxSize()) {
@@ -89,4 +93,20 @@
}
}
}
+ val accessibilityManager = LocalAccessibilityManager.current
+ LaunchedEffect(true) {
+ if (dismissOnTimeout) {
+ // Same as SnackbarDuration.Short
+ val originalDuration = 4000L
+ val duration = if (accessibilityManager == null) originalDuration else
+ accessibilityManager.calculateRecommendedTimeoutMillis(
+ originalDuration,
+ containsIcons = true,
+ containsText = true,
+ containsControls = action != null,
+ )
+ delay(duration)
+ onDismiss()
+ }
+ }
}
\ No newline at end of file
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
index 80030f7..02ec486 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
@@ -20,14 +20,19 @@
import android.os.Bundle;
import android.os.UserHandle;
import android.provider.Settings;
+import android.providers.settings.BackingStoreProto;
+import android.providers.settings.CacheEntryProto;
+import android.providers.settings.GenerationRegistryProto;
import android.util.ArrayMap;
import android.util.MemoryIntArray;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import java.io.IOException;
+import java.io.PrintWriter;
/**
* This class tracks changes for config/global/secure/system tables
@@ -292,4 +297,94 @@
int getMaxNumBackingStores() {
return mMaxNumBackingStore;
}
+
+ public void dumpProto(ProtoOutputStream proto) {
+ synchronized (mLock) {
+ final int numBackingStores = mKeyToBackingStoreMap.size();
+ proto.write(GenerationRegistryProto.NUM_BACKING_STORES, numBackingStores);
+ proto.write(GenerationRegistryProto.NUM_MAX_BACKING_STORES, getMaxNumBackingStores());
+
+ for (int i = 0; i < numBackingStores; i++) {
+ final long token = proto.start(GenerationRegistryProto.BACKING_STORES);
+ final int key = mKeyToBackingStoreMap.keyAt(i);
+ proto.write(BackingStoreProto.KEY, key);
+ try {
+ proto.write(BackingStoreProto.BACKING_STORE_SIZE,
+ mKeyToBackingStoreMap.valueAt(i).size());
+ } catch (IOException ignore) {
+ }
+ proto.write(BackingStoreProto.NUM_CACHED_ENTRIES,
+ mKeyToIndexMapMap.get(key).size());
+ final ArrayMap<String, Integer> indexMap = mKeyToIndexMapMap.get(key);
+ final MemoryIntArray backingStore = getBackingStoreLocked(key,
+ /* createIfNotExist= */ false);
+ if (indexMap == null || backingStore == null) {
+ continue;
+ }
+ for (String setting : indexMap.keySet()) {
+ try {
+ final int index = getKeyIndexLocked(key, setting, mKeyToIndexMapMap,
+ backingStore, /* createIfNotExist= */ false);
+ if (index < 0) {
+ continue;
+ }
+ final long cacheEntryToken = proto.start(
+ BackingStoreProto.CACHE_ENTRIES);
+ final int generation = backingStore.get(index);
+ proto.write(CacheEntryProto.NAME,
+ setting.equals(DEFAULT_MAP_KEY_FOR_UNSET_SETTINGS)
+ ? "UNSET" : setting);
+ proto.write(CacheEntryProto.GENERATION, generation);
+ proto.end(cacheEntryToken);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ proto.end(token);
+ }
+
+ }
+ }
+
+ public void dump(PrintWriter pw) {
+ pw.println("GENERATION REGISTRY");
+ pw.println("Maximum number of backing stores:" + getMaxNumBackingStores());
+ synchronized (mLock) {
+ final int numBackingStores = mKeyToBackingStoreMap.size();
+ pw.println("Number of backing stores:" + numBackingStores);
+ for (int i = 0; i < numBackingStores; i++) {
+ final int key = mKeyToBackingStoreMap.keyAt(i);
+ pw.print("_Backing store for type:"); pw.print(SettingsState.settingTypeToString(
+ SettingsState.getTypeFromKey(key)));
+ pw.print(" user:"); pw.print(SettingsState.getUserIdFromKey(key));
+ try {
+ pw.print(" size:" + mKeyToBackingStoreMap.valueAt(i).size());
+ } catch (IOException ignore) {
+ }
+ pw.println(" cachedEntries:" + mKeyToIndexMapMap.get(key).size());
+ final ArrayMap<String, Integer> indexMap = mKeyToIndexMapMap.get(key);
+ final MemoryIntArray backingStore = getBackingStoreLocked(key,
+ /* createIfNotExist= */ false);
+ if (indexMap == null || backingStore == null) {
+ continue;
+ }
+ for (String setting : indexMap.keySet()) {
+ try {
+ final int index = getKeyIndexLocked(key, setting, mKeyToIndexMapMap,
+ backingStore, /* createIfNotExist= */ false);
+ if (index < 0) {
+ continue;
+ }
+ final int generation = backingStore.get(index);
+ pw.print(" setting: "); pw.print(
+ setting.equals(DEFAULT_MAP_KEY_FOR_UNSET_SETTINGS)
+ ? "UNSET" : setting);
+ pw.println(" generation:" + generation);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index d49627e..d3a9e91 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -120,6 +120,17 @@
dumpProtoUserSettingsLocked(proto, SettingsServiceDumpProto.USER_SETTINGS,
settingsRegistry, UserHandle.of(users.keyAt(i)));
}
+
+ // Generation registry
+ dumpProtoGenerationRegistryLocked(proto, SettingsServiceDumpProto.GENERATION_REGISTRY,
+ settingsRegistry);
+ }
+
+ private static void dumpProtoGenerationRegistryLocked(@NonNull ProtoOutputStream proto,
+ long fieldId, SettingsProvider.SettingsRegistry settingsRegistry) {
+ final long token = proto.start(fieldId);
+ settingsRegistry.getGenerationRegistry().dumpProto(proto);
+ proto.end(token);
}
/**
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 3e1b597..7a97b78 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -900,6 +900,7 @@
} finally {
Binder.restoreCallingIdentity(identity);
}
+ mSettingsRegistry.mGenerationRegistry.dump(pw);
}
}
@@ -6024,5 +6025,10 @@
return !a11yButtonTargetsSettings.isNull()
&& !TextUtils.isEmpty(a11yButtonTargetsSettings.getValue());
}
+
+ @NonNull
+ public GenerationRegistry getGenerationRegistry() {
+ return mGenerationRegistry;
+ }
}
}
diff --git a/packages/SystemUI/res/layout/notification_conversation_info.xml b/packages/SystemUI/res/layout/notification_conversation_info.xml
index 79948da..4f6e88c 100644
--- a/packages/SystemUI/res/layout/notification_conversation_info.xml
+++ b/packages/SystemUI/res/layout/notification_conversation_info.xml
@@ -170,11 +170,11 @@
android:layout_width="@dimen/notification_importance_toggle_size"
android:layout_height="@dimen/notification_importance_toggle_size"
android:layout_centerVertical="true"
- android:background="@drawable/ripple_drawable"
android:contentDescription="@string/notification_more_settings"
+ android:background="@drawable/ripple_drawable_20dp"
android:src="@drawable/ic_settings"
- android:layout_alignParentEnd="true"
- android:tint="@color/notification_guts_link_icon_tint"/>
+ android:tint="?android:attr/colorAccent"
+ android:layout_alignParentEnd="true" />
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index 4d6c2022..852db1b 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -108,11 +108,11 @@
android:layout_width="@dimen/notification_importance_toggle_size"
android:layout_height="@dimen/notification_importance_toggle_size"
android:layout_centerVertical="true"
- android:background="@android:color/transparent"
android:contentDescription="@string/notification_more_settings"
- android:src="@drawable/notif_settings_button"
- android:layout_alignParentEnd="true"
- android:tint="@color/notification_guts_link_icon_tint"/>
+ android:background="@drawable/ripple_drawable_20dp"
+ android:src="@drawable/ic_settings"
+ android:tint="?android:attr/colorAccent"
+ android:layout_alignParentEnd="true" />
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/partial_conversation_info.xml b/packages/SystemUI/res/layout/partial_conversation_info.xml
index 9ed3f92..4850b35 100644
--- a/packages/SystemUI/res/layout/partial_conversation_info.xml
+++ b/packages/SystemUI/res/layout/partial_conversation_info.xml
@@ -81,11 +81,11 @@
android:layout_width="@dimen/notification_importance_toggle_size"
android:layout_height="@dimen/notification_importance_toggle_size"
android:layout_centerVertical="true"
- android:background="@drawable/ripple_drawable"
android:contentDescription="@string/notification_more_settings"
+ android:background="@drawable/ripple_drawable_20dp"
android:src="@drawable/ic_settings"
- android:layout_alignParentEnd="true"
- android:tint="@color/notification_guts_link_icon_tint"/>
+ android:tint="?android:attr/colorAccent"
+ android:layout_alignParentEnd="true"/>
</LinearLayout>
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 8e5c76c..ac30311 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -618,9 +618,9 @@
}
logBiometricTouch(processedTouch.getEvent(), data);
- // Always pilfer pointers that are within sensor area
- if (isWithinSensorArea(mOverlay.getOverlayView(), event.getRawX(), event.getRawY(), true)) {
- Log.d("Austin", "pilferTouch invalid overlap");
+ // Always pilfer pointers that are within sensor area or when alternate bouncer is showing
+ if (isWithinSensorArea(mOverlay.getOverlayView(), event.getRawX(), event.getRawY(), true)
+ || mAlternateBouncerInteractor.isVisibleState()) {
mInputManager.pilferPointers(
mOverlay.getOverlayView().getViewRootImpl().getInputToken());
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index 414c2ec..f876aff 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -353,10 +353,19 @@
flags = flags or WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
}
- // Original sensorBounds assume portrait mode.
+ val isEnrollment = when (requestReason) {
+ REASON_ENROLL_FIND_SENSOR, REASON_ENROLL_ENROLLING -> true
+ else -> false
+ }
+
+ // Use expanded overlay unless touchExploration enabled
var rotatedBounds =
if (featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) {
- Rect(overlayParams.overlayBounds)
+ if (accessibilityManager.isTouchExplorationEnabled && isEnrollment) {
+ Rect(overlayParams.sensorBounds)
+ } else {
+ Rect(overlayParams.overlayBounds)
+ }
} else {
Rect(overlayParams.sensorBounds)
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeBrightnessHostForwarder.java b/packages/SystemUI/src/com/android/systemui/doze/DozeBrightnessHostForwarder.java
index 0aeb128..cf0dcad 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeBrightnessHostForwarder.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeBrightnessHostForwarder.java
@@ -16,6 +16,8 @@
package com.android.systemui.doze;
+import java.util.concurrent.Executor;
+
/**
* Forwards the currently used brightness to {@link DozeHost}.
*/
@@ -23,8 +25,9 @@
private final DozeHost mHost;
- public DozeBrightnessHostForwarder(DozeMachine.Service wrappedService, DozeHost host) {
- super(wrappedService);
+ public DozeBrightnessHostForwarder(DozeMachine.Service wrappedService, DozeHost host,
+ Executor bgExecutor) {
+ super(wrappedService, bgExecutor);
mHost = host;
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index f0aefb5..7f0b16b 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -39,6 +39,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -150,7 +151,6 @@
private final DockManager mDockManager;
private final Part[] mParts;
private final UserTracker mUserTracker;
-
private final ArrayList<State> mQueuedRequests = new ArrayList<>();
private State mState = State.UNINITIALIZED;
private int mPulseReason;
@@ -512,9 +512,11 @@
class Delegate implements Service {
private final Service mDelegate;
+ private final Executor mBgExecutor;
- public Delegate(Service delegate) {
+ public Delegate(Service delegate, Executor bgExecutor) {
mDelegate = delegate;
+ mBgExecutor = bgExecutor;
}
@Override
@@ -534,7 +536,9 @@
@Override
public void setDozeScreenBrightness(int brightness) {
- mDelegate.setDozeScreenBrightness(brightness);
+ mBgExecutor.execute(() -> {
+ mDelegate.setDozeScreenBrightness(brightness);
+ });
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenStatePreventingAdapter.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenStatePreventingAdapter.java
index 25c2c39..8d44472 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenStatePreventingAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenStatePreventingAdapter.java
@@ -22,14 +22,16 @@
import com.android.systemui.statusbar.phone.DozeParameters;
+import java.util.concurrent.Executor;
+
/**
* Prevents usage of doze screen states on devices that don't support them.
*/
public class DozeScreenStatePreventingAdapter extends DozeMachine.Service.Delegate {
@VisibleForTesting
- DozeScreenStatePreventingAdapter(DozeMachine.Service inner) {
- super(inner);
+ DozeScreenStatePreventingAdapter(DozeMachine.Service inner, Executor bgExecutor) {
+ super(inner, bgExecutor);
}
@Override
@@ -47,8 +49,8 @@
* return a new instance of {@link DozeScreenStatePreventingAdapter} wrapping {@code inner}.
*/
public static DozeMachine.Service wrapIfNeeded(DozeMachine.Service inner,
- DozeParameters params) {
- return isNeeded(params) ? new DozeScreenStatePreventingAdapter(inner) : inner;
+ DozeParameters params, Executor bgExecutor) {
+ return isNeeded(params) ? new DozeScreenStatePreventingAdapter(inner, bgExecutor) : inner;
}
private static boolean isNeeded(DozeParameters params) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSuspendScreenStatePreventingAdapter.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSuspendScreenStatePreventingAdapter.java
index a0c490951..f7773f1 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSuspendScreenStatePreventingAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSuspendScreenStatePreventingAdapter.java
@@ -22,14 +22,16 @@
import com.android.systemui.statusbar.phone.DozeParameters;
+import java.util.concurrent.Executor;
+
/**
* Prevents usage of doze screen states on devices that don't support them.
*/
public class DozeSuspendScreenStatePreventingAdapter extends DozeMachine.Service.Delegate {
@VisibleForTesting
- DozeSuspendScreenStatePreventingAdapter(DozeMachine.Service inner) {
- super(inner);
+ DozeSuspendScreenStatePreventingAdapter(DozeMachine.Service inner, Executor bgExecutor) {
+ super(inner, bgExecutor);
}
@Override
@@ -45,8 +47,9 @@
* return a new instance of {@link DozeSuspendScreenStatePreventingAdapter} wrapping {@code inner}.
*/
public static DozeMachine.Service wrapIfNeeded(DozeMachine.Service inner,
- DozeParameters params) {
- return isNeeded(params) ? new DozeSuspendScreenStatePreventingAdapter(inner) : inner;
+ DozeParameters params, Executor bgExecutor) {
+ return isNeeded(params) ? new DozeSuspendScreenStatePreventingAdapter(inner, bgExecutor)
+ : inner;
}
private static boolean isNeeded(DozeParameters params) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
index 069344f..d408472 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
@@ -22,6 +22,7 @@
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.doze.DozeAuthRemover;
import com.android.systemui.doze.DozeBrightnessHostForwarder;
import com.android.systemui.doze.DozeDockHandler;
@@ -45,13 +46,14 @@
import com.android.systemui.util.wakelock.DelayedWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
+import dagger.Module;
+import dagger.Provides;
+
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
-
-import dagger.Module;
-import dagger.Provides;
+import java.util.concurrent.Executor;
/** Dagger module for use with {@link com.android.systemui.doze.dagger.DozeComponent}. */
@Module
@@ -60,13 +62,13 @@
@DozeScope
@WrappedService
static DozeMachine.Service providesWrappedService(DozeMachine.Service dozeMachineService,
- DozeHost dozeHost, DozeParameters dozeParameters) {
+ DozeHost dozeHost, DozeParameters dozeParameters, @UiBackground Executor bgExecutor) {
DozeMachine.Service wrappedService = dozeMachineService;
- wrappedService = new DozeBrightnessHostForwarder(wrappedService, dozeHost);
+ wrappedService = new DozeBrightnessHostForwarder(wrappedService, dozeHost, bgExecutor);
wrappedService = DozeScreenStatePreventingAdapter.wrapIfNeeded(
- wrappedService, dozeParameters);
+ wrappedService, dozeParameters, bgExecutor);
wrappedService = DozeSuspendScreenStatePreventingAdapter.wrapIfNeeded(
- wrappedService, dozeParameters);
+ wrappedService, dozeParameters, bgExecutor);
return wrappedService;
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
index 927df55..172f922 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
@@ -110,11 +110,11 @@
viewModel.setBouncerViewDelegate(delegate)
launch {
viewModel.isShowing.collect { isShowing ->
+ view.visibility = if (isShowing) View.VISIBLE else View.INVISIBLE
if (isShowing) {
// Reset Security Container entirely.
securityContainerController.reinflateViewFlipper {
// Reset Security Container entirely.
- view.visibility = View.VISIBLE
securityContainerController.onBouncerVisibilityChanged(
/* isVisible= */ true
)
@@ -128,7 +128,6 @@
)
}
} else {
- view.visibility = View.INVISIBLE
securityContainerController.onBouncerVisibilityChanged(
/* isVisible= */ false
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index fda2277..765c93e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -869,12 +869,9 @@
// Walk down a precedence-ordered list of what indication
// should be shown based on device state
if (mDozing) {
+ boolean useMisalignmentColor = false;
mLockScreenIndicationView.setVisibility(View.GONE);
mTopIndicationView.setVisibility(VISIBLE);
- // When dozing we ignore any text color and use white instead, because
- // colors can be hard to read in low brightness.
- mTopIndicationView.setTextColor(Color.WHITE);
-
CharSequence newIndication;
if (!TextUtils.isEmpty(mBiometricMessage)) {
newIndication = mBiometricMessage; // note: doesn't show mBiometricMessageFollowUp
@@ -885,8 +882,8 @@
mIndicationArea.setVisibility(GONE);
return;
} else if (!TextUtils.isEmpty(mAlignmentIndication)) {
+ useMisalignmentColor = true;
newIndication = mAlignmentIndication;
- mTopIndicationView.setTextColor(mContext.getColor(R.color.misalignment_text_color));
} else if (mPowerPluggedIn || mEnableBatteryDefender) {
newIndication = computePowerIndication();
} else {
@@ -896,7 +893,14 @@
if (!TextUtils.equals(mTopIndicationView.getText(), newIndication)) {
mWakeLock.setAcquired(true);
- mTopIndicationView.switchIndication(newIndication, null,
+ mTopIndicationView.switchIndication(newIndication,
+ new KeyguardIndication.Builder()
+ .setMessage(newIndication)
+ .setTextColor(ColorStateList.valueOf(
+ useMisalignmentColor
+ ? mContext.getColor(R.color.misalignment_text_color)
+ : Color.WHITE))
+ .build(),
true, () -> mWakeLock.setAcquired(false));
}
return;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index bb037642..b2c2ae7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -21,6 +21,7 @@
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_OTHER
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS
+import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROLLING
import android.hardware.biometrics.BiometricOverlayConstants.ShowReason
import android.hardware.fingerprint.FingerprintManager
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback
@@ -29,6 +30,7 @@
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.Surface
+import android.view.Surface.ROTATION_0
import android.view.Surface.Rotation
import android.view.View
import android.view.WindowManager
@@ -42,6 +44,7 @@
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -159,9 +162,10 @@
private fun withRotation(@Rotation rotation: Int, block: () -> Unit) {
// Sensor that's in the top left corner of the display in natural orientation.
val sensorBounds = Rect(0, 0, SENSOR_WIDTH, SENSOR_HEIGHT)
+ val overlayBounds = Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT)
overlayParams = UdfpsOverlayParams(
sensorBounds,
- sensorBounds,
+ overlayBounds,
DISPLAY_WIDTH,
DISPLAY_HEIGHT,
scaleFactor = 1f,
@@ -314,4 +318,24 @@
assertThat(controllerOverlay.matchesRequestId(REQUEST_ID)).isTrue()
assertThat(controllerOverlay.matchesRequestId(REQUEST_ID + 1)).isFalse()
}
+
+ @Test
+ fun smallOverlayOnEnrollmentWithA11y() = withRotation(ROTATION_0) {
+ withReason(REASON_ENROLL_ENROLLING) {
+ // When a11y enabled during enrollment
+ whenever(accessibilityManager.isTouchExplorationEnabled).thenReturn(true)
+ whenever(featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true)
+
+ controllerOverlay.show(udfpsController, overlayParams)
+ verify(windowManager).addView(
+ eq(controllerOverlay.overlayView),
+ layoutParamsCaptor.capture()
+ )
+
+ // Layout params should use sensor bounds
+ val lp = layoutParamsCaptor.value
+ assertThat(lp.width).isEqualTo(overlayParams.sensorBounds.width())
+ assertThat(lp.height).isEqualTo(overlayParams.sensorBounds.height())
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 445cc87..edee3f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -1344,6 +1344,46 @@
}
@Test
+ public void onTouch_withNewTouchDetection_pilferPointerWhenAltBouncerShowing()
+ throws RemoteException {
+ final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
+ 0L);
+ final TouchProcessorResult processorResultUnchanged =
+ new TouchProcessorResult.ProcessedTouch(InteractionEvent.UNCHANGED,
+ 1 /* pointerId */, touchData);
+
+ // Enable new touch detection.
+ when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true);
+
+ // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
+ initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */);
+
+ // Configure UdfpsView to not accept the ACTION_DOWN event
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
+ when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(false);
+
+ // GIVEN that the alternate bouncer is showing and a11y touch exploration NOT enabled
+ when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
+ when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+ mFgExecutor.runAllReady();
+
+ verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+
+ // WHEN ACTION_DOWN is received and touch is not within sensor
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ processorResultUnchanged);
+ MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
+ mBiometricExecutor.runAllReady();
+ downEvent.recycle();
+
+ // THEN the touch is pilfered
+ verify(mInputManager, times(1)).pilferPointers(any());
+ }
+
+ @Test
public void onAodInterrupt_onAcquiredGood_fingerNoLongerDown() throws RemoteException {
// GIVEN UDFPS overlay is showing
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStatePreventingAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStatePreventingAdapterTest.java
index 6b3ec68..a7d7b93 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStatePreventingAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStatePreventingAdapterTest.java
@@ -28,20 +28,29 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
+import java.util.concurrent.Executor;
+
@SmallTest
public class DozeScreenStatePreventingAdapterTest extends SysuiTestCase {
+ private Executor mExecutor;
private DozeMachine.Service mInner;
private DozeScreenStatePreventingAdapter mWrapper;
@Before
public void setup() throws Exception {
+ mExecutor = new FakeExecutor(new FakeSystemClock());
mInner = mock(DozeMachine.Service.class);
- mWrapper = new DozeScreenStatePreventingAdapter(mInner);
+ mWrapper = new DozeScreenStatePreventingAdapter(
+ mInner,
+ mExecutor
+ );
}
@Test
@@ -86,7 +95,8 @@
when(params.getDisplayStateSupported()).thenReturn(false);
assertEquals(DozeScreenStatePreventingAdapter.class,
- DozeScreenStatePreventingAdapter.wrapIfNeeded(mInner, params).getClass());
+ DozeScreenStatePreventingAdapter.wrapIfNeeded(mInner, params, mExecutor)
+ .getClass());
}
@Test
@@ -94,6 +104,7 @@
DozeParameters params = mock(DozeParameters.class);
when(params.getDisplayStateSupported()).thenReturn(true);
- assertSame(mInner, DozeScreenStatePreventingAdapter.wrapIfNeeded(mInner, params));
+ assertSame(mInner, DozeScreenStatePreventingAdapter.wrapIfNeeded(mInner, params,
+ mExecutor));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuspendScreenStatePreventingAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuspendScreenStatePreventingAdapterTest.java
index 9ae7217..240d2d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuspendScreenStatePreventingAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuspendScreenStatePreventingAdapterTest.java
@@ -28,20 +28,26 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
+import java.util.concurrent.Executor;
+
@SmallTest
public class DozeSuspendScreenStatePreventingAdapterTest extends SysuiTestCase {
+ private Executor mExecutor;
private DozeMachine.Service mInner;
private DozeSuspendScreenStatePreventingAdapter mWrapper;
@Before
public void setup() throws Exception {
+ mExecutor = new FakeExecutor(new FakeSystemClock());
mInner = mock(DozeMachine.Service.class);
- mWrapper = new DozeSuspendScreenStatePreventingAdapter(mInner);
+ mWrapper = new DozeSuspendScreenStatePreventingAdapter(mInner, mExecutor);
}
@Test
@@ -92,7 +98,8 @@
when(params.getDozeSuspendDisplayStateSupported()).thenReturn(false);
assertEquals(DozeSuspendScreenStatePreventingAdapter.class,
- DozeSuspendScreenStatePreventingAdapter.wrapIfNeeded(mInner, params).getClass());
+ DozeSuspendScreenStatePreventingAdapter.wrapIfNeeded(mInner, params, mExecutor)
+ .getClass());
}
@Test
@@ -100,6 +107,7 @@
DozeParameters params = mock(DozeParameters.class);
when(params.getDozeSuspendDisplayStateSupported()).thenReturn(true);
- assertSame(mInner, DozeSuspendScreenStatePreventingAdapter.wrapIfNeeded(mInner, params));
+ assertSame(mInner, DozeSuspendScreenStatePreventingAdapter.wrapIfNeeded(mInner, params,
+ mExecutor));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt
index 39ea46a..e2aef31 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt
@@ -126,7 +126,7 @@
}
fun assertLastProgress(progress: Float) {
- waitForCondition { progress == progressHistory.last() }
+ waitForCondition { progress == progressHistory.lastOrNull() }
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index e159f18..7463061 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -271,6 +271,14 @@
*/
void onClientChangeLocked(boolean serviceInfoChanged);
+ /**
+ * Called back to notify the system the proxy client for a device has changed.
+ *
+ * Changes include if the proxy is unregistered, if its service info list has changed, or if
+ * its focus appearance has changed.
+ */
+ void onProxyChanged(int deviceId);
+
int getCurrentUserIdLocked();
Pair<float[], MagnificationSpec> getWindowTransformationMatrixAndMagnificationSpec(
@@ -315,8 +323,6 @@
void attachAccessibilityOverlayToDisplay(int displayId, SurfaceControl sc);
- void setCurrentUserFocusAppearance(int strokeWidth, int color);
-
}
public AbstractAccessibilityServiceConnection(Context context, ComponentName componentName,
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 5417009..51325e7 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -25,6 +25,9 @@
import static android.accessibilityservice.AccessibilityTrace.FLAGS_USER_BROADCAST_RECEIVER;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
+import static android.companion.virtual.VirtualDeviceManager.ACTION_VIRTUAL_DEVICE_REMOVED;
+import static android.companion.virtual.VirtualDeviceManager.EXTRA_VIRTUAL_DEVICE_ID;
+import static android.content.Context.DEVICE_ID_DEFAULT;
import static android.provider.Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED;
import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
@@ -189,7 +192,7 @@
AccessibilityUserState.ServiceInfoChangeListener,
AccessibilityWindowManager.AccessibilityEventSender,
AccessibilitySecurityPolicy.AccessibilityUserManager,
- SystemActionPerformer.SystemActionsChangedListener {
+ SystemActionPerformer.SystemActionsChangedListener, ProxyManager.SystemSupport{
private static final boolean DEBUG = false;
@@ -471,7 +474,8 @@
new MagnificationScaleProvider(mContext));
mMagnificationProcessor = new MagnificationProcessor(mMagnificationController);
mCaptioningManagerImpl = new CaptioningManagerImpl(mContext);
- mProxyManager = new ProxyManager(mLock, mA11yWindowManager, mContext);
+ mProxyManager = new ProxyManager(mLock, mA11yWindowManager, mContext, mMainHandler,
+ mUiAutomationManager, this);
mFlashNotificationsController = new FlashNotificationsController(mContext);
init();
}
@@ -862,6 +866,19 @@
};
mContext.registerReceiverAsUser(receiver, UserHandle.ALL, filter, null, mMainHandler,
Context.RECEIVER_EXPORTED);
+
+ final BroadcastReceiver virtualDeviceReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int deviceId = intent.getIntExtra(
+ EXTRA_VIRTUAL_DEVICE_ID, DEVICE_ID_DEFAULT);
+ mProxyManager.clearConnections(deviceId);
+ }
+ };
+
+ final IntentFilter virtualDeviceFilter = new IntentFilter(ACTION_VIRTUAL_DEVICE_REMOVED);
+ mContext.registerReceiver(virtualDeviceReceiver, virtualDeviceFilter,
+ Context.RECEIVER_NOT_EXPORTED);
}
/**
@@ -940,21 +957,42 @@
final int resolvedUserId = mSecurityPolicy
.resolveCallingUserIdEnforcingPermissionsLocked(userId);
+ AccessibilityUserState userState = getUserStateLocked(resolvedUserId);
+ // Support a process moving from the default device to a single virtual
+ // device.
+ final int deviceId = mProxyManager.getFirstDeviceIdForUidLocked(
+ Binder.getCallingUid());
+ Client client = new Client(callback, Binder.getCallingUid(), userState, deviceId);
// If the client is from a process that runs across users such as
// the system UI or the system we add it to the global state that
// is shared across users.
- AccessibilityUserState userState = getUserStateLocked(resolvedUserId);
- Client client = new Client(callback, Binder.getCallingUid(), userState);
if (mSecurityPolicy.isCallerInteractingAcrossUsers(userId)) {
+ if (mProxyManager.isProxyedDeviceId(deviceId)) {
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "Added global client for proxy-ed pid: "
+ + Binder.getCallingPid() + " for device id " + deviceId
+ + " with package names " + Arrays.toString(client.mPackageNames));
+ }
+ return IntPair.of(mProxyManager.getStateLocked(deviceId,
+ mUiAutomationManager.isUiAutomationRunningLocked()),
+ client.mLastSentRelevantEventTypes);
+ }
mGlobalClients.register(callback, client);
if (DEBUG) {
Slog.i(LOG_TAG, "Added global client for pid:" + Binder.getCallingPid());
}
- return IntPair.of(
- combineUserStateAndProxyState(getClientStateLocked(userState),
- mProxyManager.getStateLocked()),
- client.mLastSentRelevantEventTypes);
} else {
+ // If the display belongs to a proxy connections
+ if (mProxyManager.isProxyedDeviceId(deviceId)) {
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "Added user client for proxy-ed pid: "
+ + Binder.getCallingPid() + " for device id " + deviceId
+ + " with package names " + Arrays.toString(client.mPackageNames));
+ }
+ return IntPair.of(mProxyManager.getStateLocked(deviceId,
+ mUiAutomationManager.isUiAutomationRunningLocked()),
+ client.mLastSentRelevantEventTypes);
+ }
userState.mUserClients.register(callback, client);
// If this client is not for the current user we do not
// return a state since it is not for the foreground user.
@@ -963,12 +1001,10 @@
Slog.i(LOG_TAG, "Added user client for pid:" + Binder.getCallingPid()
+ " and userId:" + mCurrentUserId);
}
- return IntPair.of(
- (resolvedUserId == mCurrentUserId) ? combineUserStateAndProxyState(
- getClientStateLocked(userState), mProxyManager.getStateLocked())
- : 0,
- client.mLastSentRelevantEventTypes);
}
+ return IntPair.of(
+ (resolvedUserId == mCurrentUserId) ? getClientStateLocked(userState) : 0,
+ client.mLastSentRelevantEventTypes);
}
}
@@ -1094,7 +1130,7 @@
}
private void dispatchAccessibilityEventLocked(AccessibilityEvent event) {
- if (mProxyManager.isProxyed(event.getDisplayId())) {
+ if (mProxyManager.isProxyedDisplay(event.getDisplayId())) {
mProxyManager.sendAccessibilityEventLocked(event);
} else {
notifyAccessibilityServicesDelayedLocked(event, false);
@@ -1160,6 +1196,12 @@
final int resolvedUserId;
final List<AccessibilityServiceInfo> serviceInfos;
synchronized (mLock) {
+ final int deviceId = mProxyManager.getFirstDeviceIdForUidLocked(
+ Binder.getCallingUid());
+ if (mProxyManager.isProxyedDeviceId(deviceId)) {
+ return mProxyManager.getInstalledAndEnabledServiceInfosLocked(
+ AccessibilityServiceInfo.FEEDBACK_ALL_MASK, deviceId);
+ }
// We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below
// performs the current profile parent resolution.
@@ -1195,6 +1237,12 @@
}
synchronized (mLock) {
+ final int deviceId = mProxyManager.getFirstDeviceIdForUidLocked(
+ Binder.getCallingUid());
+ if (mProxyManager.isProxyedDeviceId(deviceId)) {
+ return mProxyManager.getInstalledAndEnabledServiceInfosLocked(feedbackType,
+ deviceId);
+ }
// We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below
// performs the current profile parent resolution.
@@ -1239,19 +1287,25 @@
if (resolvedUserId != mCurrentUserId) {
return;
}
- List<AccessibilityServiceConnection> services =
- getUserStateLocked(resolvedUserId).mBoundServices;
- int numServices = services.size() + mProxyManager.getNumProxysLocked();
- interfacesToInterrupt = new ArrayList<>(numServices);
- for (int i = 0; i < services.size(); i++) {
- AccessibilityServiceConnection service = services.get(i);
- IBinder a11yServiceBinder = service.mService;
- IAccessibilityServiceClient a11yServiceInterface = service.mServiceInterface;
- if ((a11yServiceBinder != null) && (a11yServiceInterface != null)) {
- interfacesToInterrupt.add(a11yServiceInterface);
+
+ final int deviceId = mProxyManager.getFirstDeviceIdForUidLocked(
+ Binder.getCallingUid());
+ if (mProxyManager.isProxyedDeviceId(deviceId)) {
+ interfacesToInterrupt = new ArrayList<>();
+ mProxyManager.addServiceInterfacesLocked(interfacesToInterrupt, deviceId);
+ } else {
+ List<AccessibilityServiceConnection> services =
+ getUserStateLocked(resolvedUserId).mBoundServices;
+ interfacesToInterrupt = new ArrayList<>(services.size());
+ for (int i = 0; i < services.size(); i++) {
+ AccessibilityServiceConnection service = services.get(i);
+ IBinder a11yServiceBinder = service.mService;
+ IAccessibilityServiceClient a11yServiceInterface = service.mServiceInterface;
+ if ((a11yServiceBinder != null) && (a11yServiceInterface != null)) {
+ interfacesToInterrupt.add(a11yServiceInterface);
+ }
}
}
- mProxyManager.addServiceInterfacesLocked(interfacesToInterrupt);
}
for (int i = 0, count = interfacesToInterrupt.size(); i < count; i++) {
try {
@@ -1827,7 +1881,8 @@
return result;
}
- private void notifyClearAccessibilityCacheLocked() {
+ @Override
+ public void notifyClearAccessibilityCacheLocked() {
AccessibilityUserState state = getCurrentUserStateLocked();
for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
AccessibilityServiceConnection service = state.mBoundServices.get(i);
@@ -2031,18 +2086,15 @@
mMainHandler.post(() -> {
broadcastToClients(userState, ignoreRemoteException(client -> {
int relevantEventTypes;
- boolean changed = false;
synchronized (mLock) {
relevantEventTypes = computeRelevantEventTypesLocked(userState, client);
-
- if (client.mLastSentRelevantEventTypes != relevantEventTypes) {
- client.mLastSentRelevantEventTypes = relevantEventTypes;
- changed = true;
+ if (!mProxyManager.isProxyedDeviceId(client.mDeviceId)) {
+ if (client.mLastSentRelevantEventTypes != relevantEventTypes) {
+ client.mLastSentRelevantEventTypes = relevantEventTypes;
+ client.mCallback.setRelevantEventTypes(relevantEventTypes);
+ }
}
}
- if (changed) {
- client.mCallback.setRelevantEventTypes(relevantEventTypes);
- }
}));
});
}
@@ -2062,7 +2114,6 @@
mUiAutomationManager.getServiceInfo(), client)
? mUiAutomationManager.getRelevantEventTypes()
: 0;
- relevantEventTypes |= mProxyManager.getRelevantEventTypesLocked();
return relevantEventTypes;
}
@@ -2116,7 +2167,7 @@
}
}
- private static boolean isClientInPackageAllowlist(
+ static boolean isClientInPackageAllowlist(
@Nullable AccessibilityServiceInfo serviceInfo, Client client) {
if (serviceInfo == null) return false;
@@ -2309,24 +2360,20 @@
updateAccessibilityEnabledSettingLocked(userState);
}
- private int combineUserStateAndProxyState(int userState, int proxyState) {
- return userState | proxyState;
+ void scheduleUpdateClientsIfNeededLocked(AccessibilityUserState userState) {
+ scheduleUpdateClientsIfNeededLocked(userState, false);
}
- void scheduleUpdateClientsIfNeededLocked(AccessibilityUserState userState) {
+ void scheduleUpdateClientsIfNeededLocked(AccessibilityUserState userState,
+ boolean forceUpdate) {
final int clientState = getClientStateLocked(userState);
- final int proxyState = mProxyManager.getStateLocked();
- if ((userState.getLastSentClientStateLocked() != clientState
- || mProxyManager.getLastSentStateLocked() != proxyState)
+ if (((userState.getLastSentClientStateLocked() != clientState || forceUpdate))
&& (mGlobalClients.getRegisteredCallbackCount() > 0
|| userState.mUserClients.getRegisteredCallbackCount() > 0)) {
userState.setLastSentClientStateLocked(clientState);
- mProxyManager.setLastStateLocked(proxyState);
- // Send both the user and proxy state to the app for now.
- // TODO(b/250929565): Send proxy state to proxy clients
mMainHandler.sendMessage(obtainMessage(
AccessibilityManagerService::sendStateToAllClients,
- this, combineUserStateAndProxyState(clientState, proxyState),
+ this, clientState,
userState.mUserId));
}
}
@@ -2346,8 +2393,13 @@
mTraceManager.logTrace(LOG_TAG + ".sendStateToClients",
FLAGS_ACCESSIBILITY_MANAGER_CLIENT, "clientState=" + clientState);
}
- clients.broadcast(ignoreRemoteException(
- client -> client.setState(clientState)));
+ clients.broadcastForEachCookie(ignoreRemoteException(
+ client -> {
+ Client managerClient = ((Client) client);
+ if (!mProxyManager.isProxyedDeviceId(managerClient.mDeviceId)) {
+ managerClient.mCallback.setState(clientState);
+ }
+ }));
}
private void scheduleNotifyClientsOfServicesStateChangeLocked(
@@ -2370,8 +2422,14 @@
mTraceManager.logTrace(LOG_TAG + ".notifyClientsOfServicesStateChange",
FLAGS_ACCESSIBILITY_MANAGER_CLIENT, "uiTimeout=" + uiTimeout);
}
- clients.broadcast(ignoreRemoteException(
- client -> client.notifyServicesStateChanged(uiTimeout)));
+
+ clients.broadcastForEachCookie(ignoreRemoteException(
+ client -> {
+ Client managerClient = ((Client) client);
+ if (!mProxyManager.isProxyedDeviceId(managerClient.mDeviceId)) {
+ managerClient.mCallback.notifyServicesStateChanged(uiTimeout);
+ }
+ }));
}
private void scheduleUpdateInputFilter(AccessibilityUserState userState) {
@@ -2444,7 +2502,6 @@
}
inputFilter = mInputFilter;
setInputFilter = true;
- mProxyManager.setAccessibilityInputFilter(mInputFilter);
}
mInputFilter.setUserAndEnabledFeatures(userState.mUserId, flags);
mInputFilter.setCombinedGenericMotionEventSources(
@@ -2477,6 +2534,7 @@
"inputFilter=" + inputFilter);
}
mWindowManagerService.setInputFilter(inputFilter);
+ mProxyManager.setAccessibilityInputFilter(inputFilter);
}
}
@@ -2541,6 +2599,20 @@
* @param userState the new user state
*/
private void onUserStateChangedLocked(AccessibilityUserState userState) {
+ onUserStateChangedLocked(userState, false);
+ }
+
+ /**
+ * Called when any property of the user state has changed.
+ *
+ * @param userState the new user state
+ * @param forceUpdate whether to force an update of the app Clients.
+ */
+ private void onUserStateChangedLocked(AccessibilityUserState userState, boolean forceUpdate) {
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "onUserStateChangedLocked for user " + userState.mUserId + " with "
+ + "forceUpdate: " + forceUpdate);
+ }
// TODO: Remove this hack
mInitialized = true;
updateLegacyCapabilitiesLocked(userState);
@@ -2553,7 +2625,7 @@
scheduleUpdateFingerprintGestureHandling(userState);
scheduleUpdateInputFilter(userState);
updateRelevantEventsLocked(userState);
- scheduleUpdateClientsIfNeededLocked(userState);
+ scheduleUpdateClientsIfNeededLocked(userState, forceUpdate);
updateAccessibilityShortcutKeyTargetsLocked(userState);
updateAccessibilityButtonTargetsLocked(userState);
// Update the capabilities before the mode because we will check the current mode is
@@ -2600,7 +2672,7 @@
if (display != null) {
if (observingWindows) {
mA11yWindowManager.startTrackingWindows(display.getDisplayId(),
- mProxyManager.isProxyed(display.getDisplayId()));
+ mProxyManager.isProxyedDisplay(display.getDisplayId()));
} else {
mA11yWindowManager.stopTrackingWindows(display.getDisplayId());
}
@@ -2867,6 +2939,8 @@
mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS, 0,
userState.mUserId);
+
+ mProxyManager.updateTimeoutsIfNeeded(nonInteractiveUiTimeout, interactiveUiTimeout);
if (nonInteractiveUiTimeout != userState.getUserNonInteractiveUiTimeoutLocked()
|| interactiveUiTimeout != userState.getUserInteractiveUiTimeoutLocked()) {
userState.setUserNonInteractiveUiTimeoutLocked(nonInteractiveUiTimeout);
@@ -3632,8 +3706,14 @@
}
synchronized(mLock) {
- final AccessibilityUserState userState = getCurrentUserStateLocked();
- return getRecommendedTimeoutMillisLocked(userState);
+ final int deviceId = mProxyManager.getFirstDeviceIdForUidLocked(
+ Binder.getCallingUid());
+ if (mProxyManager.isProxyedDeviceId(deviceId)) {
+ return mProxyManager.getRecommendedTimeoutMillisLocked(deviceId);
+ } else {
+ final AccessibilityUserState userState = getCurrentUserStateLocked();
+ return getRecommendedTimeoutMillisLocked(userState);
+ }
}
}
@@ -3712,6 +3792,11 @@
mTraceManager.logTrace(LOG_TAG + ".getFocusStrokeWidth", FLAGS_ACCESSIBILITY_MANAGER);
}
synchronized (mLock) {
+ final int deviceId = mProxyManager.getFirstDeviceIdForUidLocked(
+ Binder.getCallingUid());
+ if (mProxyManager.isProxyedDeviceId(deviceId)) {
+ return mProxyManager.getFocusStrokeWidthLocked(deviceId);
+ }
final AccessibilityUserState userState = getCurrentUserStateLocked();
return userState.getFocusStrokeWidthLocked();
@@ -3728,6 +3813,11 @@
mTraceManager.logTrace(LOG_TAG + ".getFocusColor", FLAGS_ACCESSIBILITY_MANAGER);
}
synchronized (mLock) {
+ final int deviceId = mProxyManager.getFirstDeviceIdForUidLocked(
+ Binder.getCallingUid());
+ if (mProxyManager.isProxyedDeviceId(deviceId)) {
+ return mProxyManager.getFocusColorLocked(deviceId);
+ }
final AccessibilityUserState userState = getCurrentUserStateLocked();
return userState.getFocusColorLocked();
@@ -3814,9 +3904,9 @@
throw new IllegalArgumentException("The display " + displayId + " does not exist or is"
+ " not tracked by accessibility.");
}
- if (mProxyManager.isProxyed(displayId)) {
+ if (mProxyManager.isProxyedDisplay(displayId)) {
throw new IllegalArgumentException("The display " + displayId + " is already being"
- + "proxy-ed");
+ + " proxy-ed");
}
final long identity = Binder.clearCallingIdentity();
@@ -3847,7 +3937,7 @@
}
boolean isDisplayProxyed(int displayId) {
- return mProxyManager.isProxyed(displayId);
+ return mProxyManager.isProxyedDisplay(displayId);
}
@Override
@@ -3995,13 +4085,71 @@
@Override
public void onClientChangeLocked(boolean serviceInfoChanged) {
+ onClientChangeLocked(serviceInfoChanged, false);
+ }
+
+ /**
+ * Called when the state of a service or proxy has changed
+ * @param serviceInfoChanged if the service info has changed
+ * @param forceUpdate whether to force an update of state for app clients
+ */
+ public void onClientChangeLocked(boolean serviceInfoChanged, boolean forceUpdate) {
AccessibilityUserState userState = getUserStateLocked(mCurrentUserId);
- onUserStateChangedLocked(userState);
+ onUserStateChangedLocked(userState, forceUpdate);
if (serviceInfoChanged) {
scheduleNotifyClientsOfServicesStateChangeLocked(userState);
}
}
+
+ @Override
+ public void onProxyChanged(int deviceId) {
+ mProxyManager.onProxyChanged(deviceId);
+ }
+
+ /**
+ * Removes the device from tracking. This will reset any AccessibilityManagerClients to be
+ * associated with the default user id.
+ */
+ @Override
+ public void removeDeviceIdLocked(int deviceId) {
+ resetClientsLocked(deviceId, getCurrentUserStateLocked().mUserClients);
+ resetClientsLocked(deviceId, mGlobalClients);
+ // Force an update of A11yManagers if the state was previously a proxy state and needs to be
+ // returned to the default device state.
+ onClientChangeLocked(true, true);
+ }
+
+ private void resetClientsLocked(int deviceId,
+ RemoteCallbackList<IAccessibilityManagerClient> clients) {
+ if (clients == null || clients.getRegisteredCallbackCount() == 0) {
+ return;
+ }
+ synchronized (mLock) {
+ for (int i = 0; i < clients.getRegisteredCallbackCount(); i++) {
+ final Client appClient = ((Client) clients.getRegisteredCallbackCookie(i));
+ if (appClient.mDeviceId == deviceId) {
+ appClient.mDeviceId = DEVICE_ID_DEFAULT;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void updateWindowsForAccessibilityCallbackLocked() {
+ updateWindowsForAccessibilityCallbackLocked(getUserStateLocked(mCurrentUserId));
+ }
+
+ @Override
+ public RemoteCallbackList<IAccessibilityManagerClient> getGlobalClientsLocked() {
+ return mGlobalClients;
+ }
+
+ @Override
+ public RemoteCallbackList<IAccessibilityManagerClient> getCurrentUserClientsLocked() {
+ return getCurrentUserState().mUserClients;
+ }
+
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback,
@@ -4313,13 +4461,22 @@
final IAccessibilityManagerClient mCallback;
final String[] mPackageNames;
int mLastSentRelevantEventTypes;
+ int mUid;
+ int mDeviceId = DEVICE_ID_DEFAULT;
private Client(IAccessibilityManagerClient callback, int clientUid,
- AccessibilityUserState userState) {
+ AccessibilityUserState userState, int deviceId) {
mCallback = callback;
mPackageNames = mPackageManager.getPackagesForUid(clientUid);
+ mUid = clientUid;
+ mDeviceId = deviceId;
synchronized (mLock) {
- mLastSentRelevantEventTypes = computeRelevantEventTypesLocked(userState, this);
+ if (mProxyManager.isProxyedDeviceId(deviceId)) {
+ mLastSentRelevantEventTypes =
+ mProxyManager.computeRelevantEventTypesLocked(this);
+ } else {
+ mLastSentRelevantEventTypes = computeRelevantEventTypesLocked(userState, this);
+ }
}
}
}
@@ -4805,8 +4962,10 @@
}
mMainHandler.post(() -> {
broadcastToClients(userState, ignoreRemoteException(client -> {
- client.mCallback.setFocusAppearance(userState.getFocusStrokeWidthLocked(),
- userState.getFocusColorLocked());
+ if (!mProxyManager.isProxyedDeviceId(client.mDeviceId)) {
+ client.mCallback.setFocusAppearance(userState.getFocusStrokeWidthLocked(),
+ userState.getFocusColorLocked());
+ }
}));
});
@@ -5041,11 +5200,4 @@
transaction.apply();
transaction.close();
}
-
- @Override
- public void setCurrentUserFocusAppearance(int strokeWidth, int color) {
- synchronized (mLock) {
- getCurrentUserStateLocked().setFocusAppearanceLocked(strokeWidth, color);
- }
- }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java
index b19a502..ab01fc3 100644
--- a/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java
@@ -67,6 +67,7 @@
public class ProxyAccessibilityServiceConnection extends AccessibilityServiceConnection {
private static final String LOG_TAG = "ProxyAccessibilityServiceConnection";
+ private int mDeviceId;
private int mDisplayId;
private List<AccessibilityServiceInfo> mInstalledAndEnabledServices;
@@ -75,6 +76,9 @@
/** The color of the focus rectangle */
private int mFocusColor;
+ private int mInteractiveTimeout;
+ private int mNonInteractiveTimeout;
+
ProxyAccessibilityServiceConnection(
Context context,
ComponentName componentName,
@@ -83,7 +87,7 @@
AccessibilitySecurityPolicy securityPolicy,
SystemSupport systemSupport, AccessibilityTrace trace,
WindowManagerInternal windowManagerInternal,
- AccessibilityWindowManager awm, int displayId) {
+ AccessibilityWindowManager awm, int displayId, int deviceId) {
super(/* userState= */null, context, componentName, accessibilityServiceInfo, id,
mainHandler, lock, securityPolicy, systemSupport, trace, windowManagerInternal,
/* systemActionPerformer= */ null, awm, /* activityTaskManagerService= */ null);
@@ -93,6 +97,14 @@
R.dimen.accessibility_focus_highlight_stroke_width);
mFocusColor = mContext.getResources().getColor(
R.color.accessibility_focus_highlight_color);
+ mDeviceId = deviceId;
+ }
+
+ int getDisplayId() {
+ return mDisplayId;
+ }
+ int getDeviceId() {
+ return mDeviceId;
}
/**
@@ -155,6 +167,8 @@
proxyInfo.setAccessibilityTool(isAccessibilityTool);
proxyInfo.setInteractiveUiTimeoutMillis(interactiveUiTimeout);
proxyInfo.setNonInteractiveUiTimeoutMillis(nonInteractiveUiTimeout);
+ mInteractiveTimeout = interactiveUiTimeout;
+ mNonInteractiveTimeout = nonInteractiveUiTimeout;
// If any one service info doesn't set package names, i.e. if it's interested in all
// apps, the proxy shouldn't filter by package name even if some infos specify this.
@@ -167,7 +181,7 @@
// Update connection with mAccessibilityServiceInfo values.
setDynamicallyConfigurableProperties(proxyInfo);
// Notify manager service.
- mSystemSupport.onClientChangeLocked(true);
+ mSystemSupport.onProxyChanged(mDeviceId);
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -235,12 +249,7 @@
mFocusStrokeWidth = strokeWidth;
mFocusColor = color;
- // Sets the appearance data in the A11yUserState for now, since the A11yManagers are not
- // separated.
- // TODO(254545943): Separate proxy and non-proxy states so the focus appearance on the
- // phone is not affected by the appearance of a proxy-ed app.
- mSystemSupport.setCurrentUserFocusAppearance(mFocusStrokeWidth, mFocusColor);
- mSystemSupport.onClientChangeLocked(false);
+ mSystemSupport.onProxyChanged(mDeviceId);
}
}
@@ -572,17 +581,51 @@
throw new UnsupportedOperationException("setAnimationScale is not supported");
}
+ public int getInteractiveTimeout() {
+ return mInteractiveTimeout;
+ }
+
+ public int getNonInteractiveTimeout() {
+ return mNonInteractiveTimeout;
+ }
+
+ /**
+ * Returns true if a timeout was updated.
+ */
+ public boolean updateTimeouts(int nonInteractiveUiTimeout, int interactiveUiTimeout) {
+ final int newInteractiveUiTimeout = interactiveUiTimeout != 0
+ ? interactiveUiTimeout
+ : mAccessibilityServiceInfo.getInteractiveUiTimeoutMillis();
+ final int newNonInteractiveUiTimeout = nonInteractiveUiTimeout != 0
+ ? nonInteractiveUiTimeout
+ : mAccessibilityServiceInfo.getNonInteractiveUiTimeoutMillis();
+ boolean updated = false;
+
+ if (mInteractiveTimeout != newInteractiveUiTimeout) {
+ mInteractiveTimeout = newInteractiveUiTimeout;
+ updated = true;
+ }
+ if (mNonInteractiveTimeout != newNonInteractiveUiTimeout) {
+ mNonInteractiveTimeout = newNonInteractiveUiTimeout;
+ updated = true;
+ }
+ return updated;
+ }
+
@Override
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
synchronized (mLock) {
pw.append("Proxy[displayId=" + mDisplayId);
+ pw.append(", deviceId=" + mDeviceId);
pw.append(", feedbackType"
+ AccessibilityServiceInfo.feedbackTypeToString(mFeedbackType));
pw.append(", capabilities=" + mAccessibilityServiceInfo.getCapabilities());
pw.append(", eventTypes="
+ AccessibilityEvent.eventTypeToString(mEventTypes));
pw.append(", notificationTimeout=" + mNotificationTimeout);
+ pw.append(", nonInteractiveUiTimeout=").append(String.valueOf(mNonInteractiveTimeout));
+ pw.append(", interactiveUiTimeout=").append(String.valueOf(mInteractiveTimeout));
pw.append(", focusStrokeWidth=").append(String.valueOf(mFocusStrokeWidth));
pw.append(", focusColor=").append(String.valueOf(mFocusColor));
pw.append(", installedAndEnabledServiceCount=").append(String.valueOf(
diff --git a/services/accessibility/java/com/android/server/accessibility/ProxyManager.java b/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
index e258de1..d417197 100644
--- a/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
@@ -14,26 +14,45 @@
* limitations under the License.
*/
package com.android.server.accessibility;
+
+import static android.content.Context.DEVICE_ID_DEFAULT;
+import static android.content.Context.DEVICE_ID_INVALID;
+
+import static com.android.internal.util.FunctionalUtils.ignoreRemoteException;
+
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.IAccessibilityServiceClient;
+import android.annotation.NonNull;
+import android.companion.virtual.VirtualDeviceManager;
import android.content.ComponentName;
import android.content.Context;
import android.hardware.display.DisplayManager;
import android.os.Handler;
import android.os.IBinder;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import android.view.Display;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IAccessibilityManagerClient;
+import com.android.internal.util.IntPair;
+import com.android.server.LocalServices;
+import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.wm.WindowManagerInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
+import java.util.Set;
+import java.util.function.Consumer;
/**
* Manages proxy connections.
@@ -53,26 +72,72 @@
static final String PROXY_COMPONENT_PACKAGE_NAME = "ProxyPackage";
static final String PROXY_COMPONENT_CLASS_NAME = "ProxyClass";
+ // AMS#mLock
private final Object mLock;
private final Context mContext;
+ private final Handler mMainHandler;
- // Used to determine if we should notify AccessibilityManager clients of updates.
- // TODO(254545943): Separate this so each display id has its own state. Currently there is no
- // way to identify from AccessibilityManager which proxy state should be returned.
- private int mLastState = -1;
+ private final UiAutomationManager mUiAutomationManager;
- private SparseArray<ProxyAccessibilityServiceConnection> mProxyA11yServiceConnections =
+ // Device Id -> state. Used to determine if we should notify AccessibilityManager clients of
+ // updates.
+ private final SparseIntArray mLastStates = new SparseIntArray();
+
+ // Each display id entry in a SparseArray represents a proxy a11y user.
+ private final SparseArray<ProxyAccessibilityServiceConnection> mProxyA11yServiceConnections =
new SparseArray<>();
- private AccessibilityWindowManager mA11yWindowManager;
+ private final AccessibilityWindowManager mA11yWindowManager;
private AccessibilityInputFilter mA11yInputFilter;
- ProxyManager(Object lock, AccessibilityWindowManager awm, Context context) {
+ private VirtualDeviceManagerInternal mLocalVdm;
+
+ private final SystemSupport mSystemSupport;
+
+ /**
+ * Callbacks into AccessibilityManagerService.
+ */
+ public interface SystemSupport {
+ /**
+ * Removes the device id from tracking.
+ */
+ void removeDeviceIdLocked(int deviceId);
+
+ /**
+ * Updates the windows tracking for the current user.
+ */
+ void updateWindowsForAccessibilityCallbackLocked();
+
+ /**
+ * Clears all caches.
+ */
+ void notifyClearAccessibilityCacheLocked();
+
+ /**
+ * Gets the clients for all users.
+ */
+ @NonNull
+ RemoteCallbackList<IAccessibilityManagerClient> getGlobalClientsLocked();
+
+ /**
+ * Gets the clients for the current user.
+ */
+ @NonNull
+ RemoteCallbackList<IAccessibilityManagerClient> getCurrentUserClientsLocked();
+ }
+
+ ProxyManager(Object lock, AccessibilityWindowManager awm,
+ Context context, Handler mainHandler, UiAutomationManager uiAutomationManager,
+ SystemSupport systemSupport) {
mLock = lock;
mA11yWindowManager = awm;
mContext = context;
+ mMainHandler = mainHandler;
+ mUiAutomationManager = uiAutomationManager;
+ mSystemSupport = systemSupport;
+ mLocalVdm = LocalServices.getService(VirtualDeviceManagerInternal.class);
}
/**
@@ -89,6 +154,12 @@
Slog.v(LOG_TAG, "Register proxy for display id: " + displayId);
}
+ VirtualDeviceManager vdm = mContext.getSystemService(VirtualDeviceManager.class);
+ if (vdm == null) {
+ return;
+ }
+ final int deviceId = vdm.getDeviceIdForDisplayId(displayId);
+
// Set a default AccessibilityServiceInfo that is used before the proxy's info is
// populated. A proxy has the touch exploration and window capabilities.
AccessibilityServiceInfo info = new AccessibilityServiceInfo();
@@ -101,7 +172,7 @@
new ProxyAccessibilityServiceConnection(context, info.getComponentName(), info,
id, mainHandler, mLock, securityPolicy, systemSupport, trace,
windowManagerInternal,
- mA11yWindowManager, displayId);
+ mA11yWindowManager, displayId, deviceId);
synchronized (mLock) {
mProxyA11yServiceConnections.put(displayId, connection);
@@ -113,20 +184,16 @@
@Override
public void binderDied() {
client.asBinder().unlinkToDeath(this, 0);
- clearConnection(displayId);
+ clearConnectionAndUpdateState(displayId);
}
};
client.asBinder().linkToDeath(deathRecipient, 0);
- // Notify apps that the service state has changed.
- // A11yManager#A11yServicesStateChangeListener
- synchronized (mLock) {
- connection.mSystemSupport.onClientChangeLocked(true);
- }
-
- if (mA11yInputFilter != null) {
- mA11yInputFilter.disableFeaturesForDisplayIfInstalled(displayId);
- }
+ mMainHandler.post(() -> {
+ if (mA11yInputFilter != null) {
+ mA11yInputFilter.disableFeaturesForDisplayIfInstalled(displayId);
+ }
+ });
connection.initializeServiceInterface(client);
}
@@ -134,38 +201,101 @@
* Unregister the proxy based on display id.
*/
public boolean unregisterProxy(int displayId) {
- return clearConnection(displayId);
- }
-
- private boolean clearConnection(int displayId) {
- boolean removed = false;
- synchronized (mLock) {
- if (mProxyA11yServiceConnections.contains(displayId)) {
- mProxyA11yServiceConnections.remove(displayId);
- removed = true;
- if (DEBUG) {
- Slog.v(LOG_TAG, "Unregister proxy for display id " + displayId);
- }
- }
- }
- if (removed) {
- mA11yWindowManager.stopTrackingDisplayProxy(displayId);
- if (mA11yInputFilter != null) {
- final DisplayManager displayManager = (DisplayManager)
- mContext.getSystemService(Context.DISPLAY_SERVICE);
- final Display proxyDisplay = displayManager.getDisplay(displayId);
- if (proxyDisplay != null) {
- mA11yInputFilter.enableFeaturesForDisplayIfInstalled(proxyDisplay);
- }
- }
- }
- return removed;
+ return clearConnectionAndUpdateState(displayId);
}
/**
- * Checks if a display id is being proxy-ed.
+ * Clears all proxy connections belonging to {@code deviceId}.
*/
- public boolean isProxyed(int displayId) {
+ public void clearConnections(int deviceId) {
+ final IntArray displaysToClear = new IntArray();
+ synchronized (mLock) {
+ for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) {
+ final ProxyAccessibilityServiceConnection proxy =
+ mProxyA11yServiceConnections.valueAt(i);
+ if (proxy != null && proxy.getDeviceId() == deviceId) {
+ displaysToClear.add(proxy.getDisplayId());
+ }
+ }
+ }
+ for (int i = 0; i < displaysToClear.size(); i++) {
+ clearConnectionAndUpdateState(displaysToClear.get(i));
+ }
+ }
+
+ /**
+ * Removes the system connection of an AccessibilityDisplayProxy.
+ *
+ * This will:
+ * <ul>
+ * <li> Reset Clients to belong to the default device if appropriate.
+ * <li> Stop identifying the display's a11y windows as belonging to a proxy.
+ * <li> Re-enable any input filters for the display.
+ * <li> Notify AMS that a proxy has been removed.
+ * </ul>
+ *
+ * @param displayId the display id of the connection to be cleared.
+ * @return whether the proxy was removed.
+ */
+ private boolean clearConnectionAndUpdateState(int displayId) {
+ boolean removedFromConnections = false;
+ int deviceId = DEVICE_ID_INVALID;
+ synchronized (mLock) {
+ if (mProxyA11yServiceConnections.contains(displayId)) {
+ deviceId = mProxyA11yServiceConnections.get(displayId).getDeviceId();
+ mProxyA11yServiceConnections.remove(displayId);
+ removedFromConnections = true;
+ }
+ }
+
+ if (removedFromConnections) {
+ updateStateForRemovedDisplay(displayId, deviceId);
+ }
+
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "Unregistered proxy for display id " + displayId + ": "
+ + removedFromConnections);
+ }
+ return removedFromConnections;
+ }
+
+ /**
+ * When the connection is removed from tracking in ProxyManager, propagate changes to other a11y
+ * system components like the input filter and IAccessibilityManagerClients.
+ */
+ public void updateStateForRemovedDisplay(int displayId, int deviceId) {
+ mA11yWindowManager.stopTrackingDisplayProxy(displayId);
+ // A11yInputFilter isn't thread-safe, so post on the system thread.
+ mMainHandler.post(
+ () -> {
+ if (mA11yInputFilter != null) {
+ final DisplayManager displayManager = (DisplayManager)
+ mContext.getSystemService(Context.DISPLAY_SERVICE);
+ final Display proxyDisplay = displayManager.getDisplay(displayId);
+ if (proxyDisplay != null) {
+ // A11yInputFilter isn't thread-safe, so post on the system thread.
+ mA11yInputFilter.enableFeaturesForDisplayIfInstalled(proxyDisplay);
+ }
+ }
+ });
+ // If there isn't an existing proxy for the device id, reset clients. Resetting
+ // will usually happen, since in most cases there will only be one proxy for a
+ // device.
+ if (!isProxyedDeviceId(deviceId)) {
+ synchronized (mLock) {
+ mSystemSupport.removeDeviceIdLocked(deviceId);
+ mLastStates.delete(deviceId);
+ }
+ } else {
+ // Update with the states of the remaining proxies.
+ onProxyChanged(deviceId);
+ }
+ }
+
+ /**
+ * Returns {@code true} if {@code displayId} is being proxy-ed.
+ */
+ public boolean isProxyedDisplay(int displayId) {
synchronized (mLock) {
final boolean tracked = mProxyA11yServiceConnections.contains(displayId);
if (DEBUG) {
@@ -176,6 +306,23 @@
}
/**
+ * Returns {@code true} if {@code deviceId} is being proxy-ed.
+ */
+ public boolean isProxyedDeviceId(int deviceId) {
+ if (deviceId == DEVICE_ID_DEFAULT && deviceId == DEVICE_ID_INVALID) {
+ return false;
+ }
+ boolean isTrackingDeviceId;
+ synchronized (mLock) {
+ isTrackingDeviceId = getFirstProxyForDeviceIdLocked(deviceId) != null;
+ }
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "Tracking device " + deviceId + " : " + isTrackingDeviceId);
+ }
+ return isTrackingDeviceId;
+ }
+
+ /**
* Sends AccessibilityEvents to a proxy given the event's displayId.
*/
public void sendAccessibilityEventLocked(AccessibilityEvent event) {
@@ -213,15 +360,37 @@
/**
* If there is at least one proxy, accessibility is enabled.
*/
- public int getStateLocked() {
+ public int getStateLocked(int deviceId, boolean automationRunning) {
int clientState = 0;
- final boolean a11yEnabled = mProxyA11yServiceConnections.size() > 0;
- if (a11yEnabled) {
+ if (automationRunning) {
clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
}
for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) {
final ProxyAccessibilityServiceConnection proxy =
mProxyA11yServiceConnections.valueAt(i);
+ if (proxy != null && proxy.getDeviceId() == deviceId) {
+ // Combine proxy states.
+ clientState |= getStateForDisplayIdLocked(proxy);
+ }
+ }
+
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "For device id " + deviceId + " a11y is enabled: "
+ + ((clientState & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0));
+ Slog.v(LOG_TAG, "For device id " + deviceId + " touch exploration is enabled: "
+ + ((clientState & AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED)
+ != 0));
+ }
+ return clientState;
+ }
+
+ /**
+ * If there is at least one proxy, accessibility is enabled.
+ */
+ public int getStateForDisplayIdLocked(ProxyAccessibilityServiceConnection proxy) {
+ int clientState = 0;
+ if (proxy != null) {
+ clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
if (proxy.mRequestTouchExplorationMode) {
clientState |= AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED;
}
@@ -235,61 +404,396 @@
!= 0));
}
return clientState;
- // TODO(b/254545943): When A11yManager is separated, include support for other properties.
}
/**
- * Gets the last state.
+ * Gets the last state for a device.
*/
- public int getLastSentStateLocked() {
- return mLastState;
+ public int getLastSentStateLocked(int deviceId) {
+ return mLastStates.get(deviceId, 0);
}
/**
- * Sets the last state.
+ * Sets the last state for a device.
*/
- public void setLastStateLocked(int proxyState) {
- mLastState = proxyState;
+ public void setLastStateLocked(int deviceId, int proxyState) {
+ mLastStates.put(deviceId, proxyState);
}
/**
- * Returns the relevant event types of every proxy.
- * TODO(254545943): When A11yManager is separated, return based on the A11yManager display.
+ * Updates the relevant event types of the app clients that are shown on a display owned by the
+ * specified device.
+ *
+ * A client belongs to a device id, so event types (and other state) is determined by the device
+ * id. In most cases, a device owns a single display. But if multiple displays may belong to one
+ * Virtual Device, the app clients will get the aggregated event types for all proxy-ed displays
+ * belonging to a VirtualDevice.
*/
- public int getRelevantEventTypesLocked() {
+ public void updateRelevantEventTypesLocked(int deviceId) {
+ if (!isProxyedDeviceId(deviceId)) {
+ return;
+ }
+ mMainHandler.post(() -> {
+ synchronized (mLock) {
+ broadcastToClientsLocked(ignoreRemoteException(client -> {
+ int relevantEventTypes;
+ if (client.mDeviceId == deviceId) {
+ relevantEventTypes = computeRelevantEventTypesLocked(client);
+ if (client.mLastSentRelevantEventTypes != relevantEventTypes) {
+ client.mLastSentRelevantEventTypes = relevantEventTypes;
+ client.mCallback.setRelevantEventTypes(relevantEventTypes);
+ }
+ }
+ }));
+ }
+ });
+ }
+
+ /**
+ * Returns the relevant event types for a Client.
+ */
+ int computeRelevantEventTypesLocked(AccessibilityManagerService.Client client) {
int relevantEventTypes = 0;
for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) {
- ProxyAccessibilityServiceConnection proxy =
+ final ProxyAccessibilityServiceConnection proxy =
mProxyA11yServiceConnections.valueAt(i);
- relevantEventTypes |= proxy.getRelevantEventTypes();
+ if (proxy != null && proxy.getDeviceId() == client.mDeviceId) {
+ relevantEventTypes |= proxy.getRelevantEventTypes();
+ relevantEventTypes |= AccessibilityManagerService.isClientInPackageAllowlist(
+ mUiAutomationManager.getServiceInfo(), client)
+ ? mUiAutomationManager.getRelevantEventTypes()
+ : 0;
+ }
}
if (DEBUG) {
- Slog.v(LOG_TAG, "Relevant event types for all proxies: "
- + AccessibilityEvent.eventTypeToString(relevantEventTypes));
+ Slog.v(LOG_TAG, "Relevant event types for device id " + client.mDeviceId
+ + ": " + AccessibilityEvent.eventTypeToString(relevantEventTypes));
}
return relevantEventTypes;
}
/**
- * Gets the number of current proxy connections.
- * @return
- */
- public int getNumProxysLocked() {
- return mProxyA11yServiceConnections.size();
- }
-
- /**
* Adds the service interfaces to a list.
- * @param interfaces
+ * @param interfaces the list to add to.
+ * @param deviceId the device id of the interested app client.
*/
- public void addServiceInterfacesLocked(List<IAccessibilityServiceClient> interfaces) {
+ public void addServiceInterfacesLocked(@NonNull List<IAccessibilityServiceClient> interfaces,
+ int deviceId) {
for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) {
final ProxyAccessibilityServiceConnection proxy =
mProxyA11yServiceConnections.valueAt(i);
- final IBinder proxyBinder = proxy.mService;
- final IAccessibilityServiceClient proxyInterface = proxy.mServiceInterface;
- if ((proxyBinder != null) && (proxyInterface != null)) {
- interfaces.add(proxyInterface);
+ if (proxy != null && proxy.getDeviceId() == deviceId) {
+ final IBinder proxyBinder = proxy.mService;
+ final IAccessibilityServiceClient proxyInterface = proxy.mServiceInterface;
+ if ((proxyBinder != null) && (proxyInterface != null)) {
+ interfaces.add(proxyInterface);
+ }
+ }
+ }
+ }
+
+ /**
+ * Gets the list of installed and enabled services for a device id.
+ *
+ * Note: Multiple display proxies may belong to the same device.
+ */
+ public List<AccessibilityServiceInfo> getInstalledAndEnabledServiceInfosLocked(int feedbackType,
+ int deviceId) {
+ List<AccessibilityServiceInfo> serviceInfos = new ArrayList<>();
+ for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) {
+ final ProxyAccessibilityServiceConnection proxy =
+ mProxyA11yServiceConnections.valueAt(i);
+ if (proxy != null && proxy.getDeviceId() == deviceId) {
+ // Return all proxy infos for ALL mask.
+ if (feedbackType == AccessibilityServiceInfo.FEEDBACK_ALL_MASK) {
+ serviceInfos.addAll(proxy.getInstalledAndEnabledServices());
+ } else if ((proxy.mFeedbackType & feedbackType) != 0) {
+ List<AccessibilityServiceInfo> proxyInfos =
+ proxy.getInstalledAndEnabledServices();
+ // Iterate through each info in the proxy.
+ for (AccessibilityServiceInfo info : proxyInfos) {
+ if ((info.feedbackType & feedbackType) != 0) {
+ serviceInfos.add(info);
+ }
+ }
+ }
+ }
+ }
+ return serviceInfos;
+ }
+
+ /**
+ * Handles proxy changes.
+ *
+ * <p>
+ * Changes include if the proxy is unregistered, its service info list has
+ * changed, or its focus appearance has changed.
+ * <p>
+ * Some responses may include updating app clients. A client belongs to a device id, so state is
+ * determined by the device id. In most cases, a device owns a single display. But if multiple
+ * displays belong to one Virtual Device, the app clients will get a difference in
+ * behavior depending on what is being updated.
+ *
+ * The following state methods are updated for AccessibilityManager clients belonging to a
+ * proxied device:
+ * <ul>
+ * <li> A11yManager#setRelevantEventTypes - The combined event types of all proxies belonging to
+ * a device id.
+ * <li> A11yManager#setState - The combined states of all proxies belonging to a device id.
+ * <li> A11yManager#notifyServicesStateChanged(timeout) - The highest of all proxies belonging
+ * to a device id.
+ * <li> A11yManager#setFocusAppearance - The appearance of the most recently updated display id
+ * belonging to the device.
+ * </ul>
+ * This is similar to onUserStateChangeLocked and onClientChangeLocked, but does not require an
+ * A11yUserState and only checks proxy-relevant settings.
+ */
+ public void onProxyChanged(int deviceId) {
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "onProxyChanged called for deviceId: " + deviceId);
+ }
+ //The following state updates are excluded:
+ // - Input-related state
+ // - Primary-device / hardware-specific state
+ synchronized (mLock) {
+ // A proxy may be registered after the client has been initialized in #addClient.
+ // For example, a user does not turn on accessibility until after the app has launched.
+ // Or the process was started with a default id context and should shift to a device.
+ // Update device ids of the clients if necessary.
+ updateDeviceIdsIfNeededLocked(deviceId);
+ // Start tracking of all displays if necessary.
+ mSystemSupport.updateWindowsForAccessibilityCallbackLocked();
+ // Calls A11yManager#setRelevantEventTypes (test these)
+ updateRelevantEventTypesLocked(deviceId);
+ // Calls A11yManager#setState
+ scheduleUpdateProxyClientsIfNeededLocked(deviceId);
+ //Calls A11yManager#notifyServicesStateChanged(timeout)
+ scheduleNotifyProxyClientsOfServicesStateChangeLocked(deviceId);
+ // Calls A11yManager#setFocusAppearance
+ updateFocusAppearanceLocked(deviceId);
+ mSystemSupport.notifyClearAccessibilityCacheLocked();
+ }
+ }
+
+ /**
+ * Updates the states of the app AccessibilityManagers.
+ */
+ public void scheduleUpdateProxyClientsIfNeededLocked(int deviceId) {
+ final int proxyState = getStateLocked(deviceId,
+ mUiAutomationManager.isUiAutomationRunningLocked());
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "State for device id " + deviceId + " is " + proxyState);
+ Slog.v(LOG_TAG, "Last state for device id " + deviceId + " is "
+ + getLastSentStateLocked(deviceId));
+ }
+ if ((getLastSentStateLocked(deviceId)) != proxyState) {
+ setLastStateLocked(deviceId, proxyState);
+ mMainHandler.post(() -> {
+ synchronized (mLock) {
+ broadcastToClientsLocked(ignoreRemoteException(client -> {
+ if (client.mDeviceId == deviceId) {
+ client.mCallback.setState(proxyState);
+ }
+ }));
+ }
+ });
+ }
+ }
+
+ /**
+ * Notifies AccessibilityManager of services state changes, which includes changes to the
+ * list of service infos and timeouts.
+ *
+ * @see AccessibilityManager.AccessibilityServicesStateChangeListener
+ */
+ public void scheduleNotifyProxyClientsOfServicesStateChangeLocked(int deviceId) {
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "Notify services state change at device id " + deviceId);
+ }
+ mMainHandler.post(()-> {
+ broadcastToClientsLocked(ignoreRemoteException(client -> {
+ if (client.mDeviceId == deviceId) {
+ synchronized (mLock) {
+ client.mCallback.notifyServicesStateChanged(
+ getRecommendedTimeoutMillisLocked(deviceId));
+ }
+ }
+ }));
+ });
+ }
+
+ /**
+ * Updates the focus appearance of AccessibilityManagerClients.
+ */
+ public void updateFocusAppearanceLocked(int deviceId) {
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "Update proxy focus appearance at device id " + deviceId);
+ }
+ // Reasonably assume that all proxies belonging to a virtual device should have the
+ // same focus appearance, and if they should be different these should belong to different
+ // virtual devices.
+ final ProxyAccessibilityServiceConnection proxy = getFirstProxyForDeviceIdLocked(deviceId);
+ if (proxy != null) {
+ mMainHandler.post(()-> {
+ broadcastToClientsLocked(ignoreRemoteException(client -> {
+ if (client.mDeviceId == proxy.getDeviceId()) {
+ client.mCallback.setFocusAppearance(
+ proxy.getFocusStrokeWidthLocked(),
+ proxy.getFocusColorLocked());
+ }
+ }));
+ });
+ }
+ }
+
+ private ProxyAccessibilityServiceConnection getFirstProxyForDeviceIdLocked(int deviceId) {
+ for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) {
+ final ProxyAccessibilityServiceConnection proxy =
+ mProxyA11yServiceConnections.valueAt(i);
+ if (proxy != null && proxy.getDeviceId() == deviceId) {
+ return proxy;
+ }
+ }
+ return null;
+ }
+
+ private void broadcastToClientsLocked(
+ @NonNull Consumer<AccessibilityManagerService.Client> clientAction) {
+ final RemoteCallbackList<IAccessibilityManagerClient> userClients =
+ mSystemSupport.getCurrentUserClientsLocked();
+ final RemoteCallbackList<IAccessibilityManagerClient> globalClients =
+ mSystemSupport.getGlobalClientsLocked();
+ userClients.broadcastForEachCookie(clientAction);
+ globalClients.broadcastForEachCookie(clientAction);
+ }
+
+ /**
+ * Updates the timeout and notifies app clients.
+ *
+ * For real users, timeouts are tracked in A11yUserState. For proxies, timeouts are in the
+ * service connection. The value in user state is preferred, but if this value is 0 the service
+ * info value is used.
+ *
+ * This follows the pattern in readUserRecommendedUiTimeoutSettingsLocked.
+ *
+ * TODO(b/250929565): ProxyUserState or similar should hold the timeouts
+ */
+ public void updateTimeoutsIfNeeded(int nonInteractiveUiTimeout, int interactiveUiTimeout) {
+ synchronized (mLock) {
+ for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) {
+ final ProxyAccessibilityServiceConnection proxy =
+ mProxyA11yServiceConnections.valueAt(i);
+ if (proxy != null) {
+ if (proxy.updateTimeouts(nonInteractiveUiTimeout, interactiveUiTimeout)) {
+ scheduleNotifyProxyClientsOfServicesStateChangeLocked(proxy.getDeviceId());
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Gets the recommended timeout belonging to a Virtual Device.
+ *
+ * This is the highest of all display proxies belonging to the virtual device.
+ */
+ public long getRecommendedTimeoutMillisLocked(int deviceId) {
+ int combinedInteractiveTimeout = 0;
+ int combinedNonInteractiveTimeout = 0;
+ for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) {
+ final ProxyAccessibilityServiceConnection proxy =
+ mProxyA11yServiceConnections.valueAt(i);
+ if (proxy != null && proxy.getDeviceId() == deviceId) {
+ final int proxyInteractiveUiTimeout =
+ (proxy != null) ? proxy.getInteractiveTimeout() : 0;
+ final int nonInteractiveUiTimeout =
+ (proxy != null) ? proxy.getNonInteractiveTimeout() : 0;
+ combinedInteractiveTimeout = Math.max(proxyInteractiveUiTimeout,
+ combinedInteractiveTimeout);
+ combinedNonInteractiveTimeout = Math.max(nonInteractiveUiTimeout,
+ combinedNonInteractiveTimeout);
+ }
+ }
+ return IntPair.of(combinedInteractiveTimeout, combinedNonInteractiveTimeout);
+ }
+
+ /**
+ * Gets the first focus stroke width belonging to the device.
+ */
+ public int getFocusStrokeWidthLocked(int deviceId) {
+ final ProxyAccessibilityServiceConnection proxy = getFirstProxyForDeviceIdLocked(deviceId);
+ if (proxy != null) {
+ return proxy.getFocusStrokeWidthLocked();
+ }
+ return 0;
+
+ }
+
+ /**
+ * Gets the first focus color belonging to the device.
+ */
+ public int getFocusColorLocked(int deviceId) {
+ final ProxyAccessibilityServiceConnection proxy = getFirstProxyForDeviceIdLocked(deviceId);
+ if (proxy != null) {
+ return proxy.getFocusColorLocked();
+ }
+ return 0;
+ }
+
+ /**
+ * Returns the first device id given a UID.
+ * @param callingUid the UID to check.
+ * @return the first matching device id, or DEVICE_ID_INVALID.
+ */
+ public int getFirstDeviceIdForUidLocked(int callingUid) {
+ int firstDeviceId = DEVICE_ID_INVALID;
+ final VirtualDeviceManagerInternal localVdm = getLocalVdm();
+ if (localVdm == null) {
+ return firstDeviceId;
+ }
+ final Set<Integer> deviceIds = localVdm.getDeviceIdsForUid(callingUid);
+ for (Integer uidDeviceId : deviceIds) {
+ if (uidDeviceId != DEVICE_ID_DEFAULT && uidDeviceId != DEVICE_ID_INVALID) {
+ firstDeviceId = uidDeviceId;
+ break;
+ }
+ }
+ return firstDeviceId;
+ }
+
+ /**
+ * Sets a Client device id if the app uid belongs to the virtual device.
+ */
+ public void updateDeviceIdsIfNeededLocked(int deviceId) {
+ final RemoteCallbackList<IAccessibilityManagerClient> userClients =
+ mSystemSupport.getCurrentUserClientsLocked();
+ final RemoteCallbackList<IAccessibilityManagerClient> globalClients =
+ mSystemSupport.getGlobalClientsLocked();
+
+ updateDeviceIdsIfNeededLocked(deviceId, userClients);
+ updateDeviceIdsIfNeededLocked(deviceId, globalClients);
+ }
+
+ /**
+ * Updates the device ids of IAccessibilityManagerClients if needed.
+ */
+ public void updateDeviceIdsIfNeededLocked(int deviceId,
+ @NonNull RemoteCallbackList<IAccessibilityManagerClient> clients) {
+ final VirtualDeviceManagerInternal localVdm = getLocalVdm();
+ if (localVdm == null) {
+ return;
+ }
+
+ for (int i = 0; i < clients.getRegisteredCallbackCount(); i++) {
+ final AccessibilityManagerService.Client client =
+ ((AccessibilityManagerService.Client) clients.getRegisteredCallbackCookie(i));
+ if (deviceId != DEVICE_ID_DEFAULT && deviceId != DEVICE_ID_INVALID
+ && localVdm.getDeviceIdsForUid(client.mUid).contains(deviceId)) {
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "Packages moved to device id " + deviceId + " are "
+ + Arrays.toString(client.mPackageNames));
+ }
+ client.mDeviceId = deviceId;
}
}
}
@@ -306,9 +810,18 @@
}
void setAccessibilityInputFilter(AccessibilityInputFilter filter) {
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "Set proxy input filter to " + filter);
+ }
mA11yInputFilter = filter;
}
+ VirtualDeviceManagerInternal getLocalVdm() {
+ if (mLocalVdm == null) {
+ mLocalVdm = LocalServices.getService(VirtualDeviceManagerInternal.class);
+ }
+ return mLocalVdm;
+ }
/**
* Prints information belonging to each display that is controlled by an
@@ -320,13 +833,38 @@
pw.println("Proxy manager state:");
pw.println(" Number of proxy connections: " + mProxyA11yServiceConnections.size());
pw.println(" Registered proxy connections:");
+ final RemoteCallbackList<IAccessibilityManagerClient> userClients =
+ mSystemSupport.getCurrentUserClientsLocked();
+ final RemoteCallbackList<IAccessibilityManagerClient> globalClients =
+ mSystemSupport.getGlobalClientsLocked();
for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) {
final ProxyAccessibilityServiceConnection proxy =
mProxyA11yServiceConnections.valueAt(i);
if (proxy != null) {
proxy.dump(fd, pw, args);
}
+ pw.println();
+ pw.println(" User clients for proxy's virtual device id");
+ printClientsForDeviceId(pw, userClients, proxy.getDeviceId());
+ pw.println();
+ pw.println(" Global clients for proxy's virtual device id");
+ printClientsForDeviceId(pw, globalClients, proxy.getDeviceId());
+
}
}
}
-}
\ No newline at end of file
+
+ private void printClientsForDeviceId(PrintWriter pw,
+ RemoteCallbackList<IAccessibilityManagerClient> clients, int deviceId) {
+ if (clients != null) {
+ for (int j = 0; j < clients.getRegisteredCallbackCount(); j++) {
+ final AccessibilityManagerService.Client client =
+ (AccessibilityManagerService.Client)
+ clients.getRegisteredCallbackCookie(j);
+ if (client.mDeviceId == deviceId) {
+ pw.println(" " + Arrays.toString(client.mPackageNames) + "\n");
+ }
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 0cd5577..ef7d5ae 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -13649,8 +13649,9 @@
|| action.startsWith("android.intent.action.UID_")
|| action.startsWith("android.intent.action.EXTERNAL_")) {
if (DEBUG_BROADCAST) {
- Slog.wtf(TAG, "System internals registering for " + filter
- + " with app priority; this will race with apps!",
+ Slog.wtf(TAG,
+ "System internals registering for " + filter.toLongString()
+ + " with app priority; this will race with apps!",
new Throwable());
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 6c6ac1e..350ac3b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -1095,7 +1095,7 @@
synchronized (mInternal.mProcLock) {
mInternal.mOomAdjuster.mCachedAppOptimizer.compactApp(app,
CachedAppOptimizer.CompactProfile.FULL,
- CachedAppOptimizer.CompactSource.APP, true);
+ CachedAppOptimizer.CompactSource.SHELL, true);
}
pw.println("Finished full compaction for " + app.mPid);
} else if (isSomeCompact) {
@@ -1103,7 +1103,7 @@
synchronized (mInternal.mProcLock) {
mInternal.mOomAdjuster.mCachedAppOptimizer.compactApp(app,
CachedAppOptimizer.CompactProfile.SOME,
- CachedAppOptimizer.CompactSource.APP, true);
+ CachedAppOptimizer.CompactSource.SHELL, true);
}
pw.println("Finished some compaction for " + app.mPid);
}
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 192a2ba..9c15463 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -226,8 +226,8 @@
FULL // File+anon compaction
}
- // This indicates the process OOM memory state that initiated the compaction request
- public enum CompactSource { APP, PERSISTENT, BFGS }
+ // This indicates who initiated the compaction request
+ public enum CompactSource { APP, SHELL }
public enum CancelCompactReason {
SCREEN_ON, // screen was turned on which cancels all compactions.
@@ -373,10 +373,6 @@
@GuardedBy("mPhenotypeFlagLock")
@VisibleForTesting volatile long mCompactThrottleFullFull = DEFAULT_COMPACT_THROTTLE_4;
@GuardedBy("mPhenotypeFlagLock")
- @VisibleForTesting volatile long mCompactThrottleBFGS = DEFAULT_COMPACT_THROTTLE_5;
- @GuardedBy("mPhenotypeFlagLock")
- @VisibleForTesting volatile long mCompactThrottlePersistent = DEFAULT_COMPACT_THROTTLE_6;
- @GuardedBy("mPhenotypeFlagLock")
@VisibleForTesting volatile long mCompactThrottleMinOomAdj =
DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ;
@GuardedBy("mPhenotypeFlagLock")
@@ -635,8 +631,6 @@
pw.println(" " + KEY_COMPACT_THROTTLE_2 + "=" + mCompactThrottleSomeFull);
pw.println(" " + KEY_COMPACT_THROTTLE_3 + "=" + mCompactThrottleFullSome);
pw.println(" " + KEY_COMPACT_THROTTLE_4 + "=" + mCompactThrottleFullFull);
- pw.println(" " + KEY_COMPACT_THROTTLE_5 + "=" + mCompactThrottleBFGS);
- pw.println(" " + KEY_COMPACT_THROTTLE_6 + "=" + mCompactThrottlePersistent);
pw.println(" " + KEY_COMPACT_THROTTLE_MIN_OOM_ADJ + "=" + mCompactThrottleMinOomAdj);
pw.println(" " + KEY_COMPACT_THROTTLE_MAX_OOM_ADJ + "=" + mCompactThrottleMaxOomAdj);
pw.println(" " + KEY_COMPACT_STATSD_SAMPLE_RATE + "=" + mCompactStatsdSampleRate);
@@ -728,32 +722,6 @@
}
}
- // This method returns true only if requirements are met. Note, that requirements are different
- // from throttles applied at the time a compaction is trying to be executed in the sense that
- // these are not subject to change dependent on time or memory as throttles usually do.
- @GuardedBy("mProcLock")
- boolean meetsCompactionRequirements(ProcessRecord proc) {
- if (mAm.mInternal.isPendingTopUid(proc.uid)) {
- // In case the OOM Adjust has not yet been propagated we see if this is
- // pending on becoming top app in which case we should not compact.
- if (DEBUG_COMPACTION) {
- Slog.d(TAG_AM, "Skip compaction since UID is active for " + proc.processName);
- }
- return false;
- }
-
- if (proc.mState.hasForegroundActivities()) {
- if (DEBUG_COMPACTION) {
- Slog.e(TAG_AM,
- "Skip compaction as process " + proc.processName
- + " has foreground activities");
- }
- return false;
- }
-
- return true;
- }
-
@GuardedBy("mProcLock")
boolean compactApp(
ProcessRecord app, CompactProfile compactProfile, CompactSource source, boolean force) {
@@ -777,7 +745,7 @@
return false;
}
- if (!app.mOptRecord.hasPendingCompact() && (meetsCompactionRequirements(app) || force)) {
+ if (!app.mOptRecord.hasPendingCompact()) {
final String processName = (app.processName != null ? app.processName : "");
if (DEBUG_COMPACTION) {
Slog.d(TAG_AM,
@@ -795,8 +763,7 @@
if (DEBUG_COMPACTION) {
Slog.d(TAG_AM,
" compactApp Skipped for " + app.processName + " pendingCompact= "
- + app.mOptRecord.hasPendingCompact() + " meetsCompactionRequirements="
- + meetsCompactionRequirements(app) + ". Requested compact profile: "
+ + app.mOptRecord.hasPendingCompact() + ". Requested compact profile: "
+ app.mOptRecord.getReqCompactProfile().name() + ". Compact source "
+ app.mOptRecord.getReqCompactSource().name());
}
@@ -831,18 +798,6 @@
return stats;
}
- @GuardedBy("mProcLock")
- boolean shouldCompactPersistent(ProcessRecord app, long now) {
- return (app.mOptRecord.getLastCompactTime() == 0
- || (now - app.mOptRecord.getLastCompactTime()) > mCompactThrottlePersistent);
- }
-
- @GuardedBy("mProcLock")
- boolean shouldCompactBFGS(ProcessRecord app, long now) {
- return (app.mOptRecord.getLastCompactTime() == 0
- || (now - app.mOptRecord.getLastCompactTime()) > mCompactThrottleBFGS);
- }
-
void compactAllSystem() {
if (useCompaction()) {
if (DEBUG_COMPACTION) {
@@ -1130,8 +1085,6 @@
mCompactThrottleSomeFull = Integer.parseInt(throttleSomeFullFlag);
mCompactThrottleFullSome = Integer.parseInt(throttleFullSomeFlag);
mCompactThrottleFullFull = Integer.parseInt(throttleFullFullFlag);
- mCompactThrottleBFGS = Integer.parseInt(throttleBFGSFlag);
- mCompactThrottlePersistent = Integer.parseInt(throttlePersistentFlag);
mCompactThrottleMinOomAdj = Long.parseLong(throttleMinOomAdjFlag);
mCompactThrottleMaxOomAdj = Long.parseLong(throttleMaxOomAdjFlag);
} catch (NumberFormatException e) {
@@ -1144,8 +1097,6 @@
mCompactThrottleSomeFull = DEFAULT_COMPACT_THROTTLE_2;
mCompactThrottleFullSome = DEFAULT_COMPACT_THROTTLE_3;
mCompactThrottleFullFull = DEFAULT_COMPACT_THROTTLE_4;
- mCompactThrottleBFGS = DEFAULT_COMPACT_THROTTLE_5;
- mCompactThrottlePersistent = DEFAULT_COMPACT_THROTTLE_6;
mCompactThrottleMinOomAdj = DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ;
mCompactThrottleMaxOomAdj = DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ;
}
@@ -1497,23 +1448,23 @@
@GuardedBy({"mService", "mProcLock"})
void onOomAdjustChanged(int oldAdj, int newAdj, ProcessRecord app) {
- // Cancel any currently executing compactions
- // if the process moved out of cached state
- if (newAdj < oldAdj && newAdj < ProcessList.CACHED_APP_MIN_ADJ) {
- cancelCompactionForProcess(app, CancelCompactReason.OOM_IMPROVEMENT);
- }
-
- if (oldAdj <= ProcessList.PERCEPTIBLE_APP_ADJ
- && (newAdj == ProcessList.PREVIOUS_APP_ADJ || newAdj == ProcessList.HOME_APP_ADJ)) {
- if (ENABLE_FILE_COMPACT) {
- // Perform a minor compaction when a perceptible app becomes the prev/home app
- compactApp(app, CompactProfile.SOME, CompactSource.APP, false);
+ if (useCompaction()) {
+ // Cancel any currently executing compactions
+ // if the process moved out of cached state
+ if (newAdj < oldAdj && newAdj < ProcessList.CACHED_APP_MIN_ADJ) {
+ cancelCompactionForProcess(app, CancelCompactReason.OOM_IMPROVEMENT);
}
- } else if (oldAdj < ProcessList.CACHED_APP_MIN_ADJ
- && newAdj >= ProcessList.CACHED_APP_MIN_ADJ
- && newAdj <= ProcessList.CACHED_APP_MAX_ADJ) {
- // Perform a major compaction when any app enters cached
- compactApp(app, CompactProfile.FULL, CompactSource.APP, false);
+ }
+ }
+
+ /**
+ * Callback received after a process has been frozen.
+ */
+ void onProcessFrozen(ProcessRecord frozenProc) {
+ if (useCompaction()) {
+ synchronized (mProcLock) {
+ compactApp(frozenProc, CompactProfile.FULL, CompactSource.APP, false);
+ }
}
}
@@ -1687,26 +1638,6 @@
return true;
}
}
- } else if (source == CompactSource.PERSISTENT) {
- if (start - lastCompactTime < mCompactThrottlePersistent) {
- if (DEBUG_COMPACTION) {
- Slog.d(TAG_AM,
- "Skipping persistent compaction for " + name
- + ": too soon. throttle=" + mCompactThrottlePersistent
- + " last=" + (start - lastCompactTime) + "ms ago");
- }
- return true;
- }
- } else if (source == CompactSource.BFGS) {
- if (start - lastCompactTime < mCompactThrottleBFGS) {
- if (DEBUG_COMPACTION) {
- Slog.d(TAG_AM,
- "Skipping bfgs compaction for " + name
- + ": too soon. throttle=" + mCompactThrottleBFGS
- + " last=" + (start - lastCompactTime) + "ms ago");
- }
- return true;
- }
}
}
@@ -2021,6 +1952,9 @@
}
}
}
+ if (proc.mOptRecord.isFrozen()) {
+ onProcessFrozen(proc);
+ }
}
break;
case REPORT_UNFREEZE_MSG:
@@ -2050,6 +1984,10 @@
freezeAppAsyncLSP(proc);
}
+ /**
+ * Freeze a process.
+ * @param proc process to be frozen
+ */
@GuardedBy({"mAm"})
private void freezeProcess(final ProcessRecord proc) {
int pid = proc.getPid(); // Unlocked intentionally
@@ -2083,6 +2021,10 @@
if (pid == 0 || opt.isFrozen()) {
// Already frozen or not a real process, either one being
// launched or one being killed
+ if (DEBUG_FREEZER) {
+ Slog.d(TAG_AM, "Skipping freeze for process " + pid
+ + " " + name + ". Already frozen or not a real process");
+ }
return;
}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index a86c02d..a98571b 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -2937,30 +2937,8 @@
int changes = 0;
- // don't compact during bootup
- if (mCachedAppOptimizer.useCompaction() && mService.mBooted) {
- // Cached and prev/home compaction
- // reminder: here, setAdj is previous state, curAdj is upcoming state
- if (state.getCurAdj() != state.getSetAdj()) {
- mCachedAppOptimizer.onOomAdjustChanged(state.getSetAdj(), state.getCurAdj(), app);
- } else if (mService.mWakefulness.get() != PowerManagerInternal.WAKEFULNESS_AWAKE) {
- // See if we can compact persistent and bfgs services now that screen is off
- if (state.getSetAdj() < FOREGROUND_APP_ADJ
- && !state.isRunningRemoteAnimation()
- // Because these can fire independent of oom_adj/procstate changes, we need
- // to throttle the actual dispatch of these requests in addition to the
- // processing of the requests. As a result, there is throttling both here
- // and in CachedAppOptimizer.
- && mCachedAppOptimizer.shouldCompactPersistent(app, now)) {
- mCachedAppOptimizer.compactApp(app, CachedAppOptimizer.CompactProfile.FULL,
- CachedAppOptimizer.CompactSource.PERSISTENT, false);
- } else if (state.getCurProcState()
- == ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
- && mCachedAppOptimizer.shouldCompactBFGS(app, now)) {
- mCachedAppOptimizer.compactApp(app, CachedAppOptimizer.CompactProfile.FULL,
- CachedAppOptimizer.CompactSource.BFGS, false);
- }
- }
+ if (state.getCurAdj() != state.getSetAdj()) {
+ mCachedAppOptimizer.onOomAdjustChanged(state.getSetAdj(), state.getCurAdj(), app);
}
if (state.getCurAdj() != state.getSetAdj()) {
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index a9a1d5e..1a22b89 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -48,7 +48,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@@ -324,16 +323,7 @@
private boolean checkTidValid(int uid, int tgid, int [] tids) {
// Make sure all tids belongs to the same UID (including isolated UID),
// tids can belong to different application processes.
- List<Integer> eligiblePids = null;
- // To avoid deadlock, do not call into AMS if the call is from system.
- if (uid != Process.SYSTEM_UID) {
- eligiblePids = mAmInternal.getIsolatedProcesses(uid);
- }
- if (eligiblePids == null) {
- eligiblePids = new ArrayList<>();
- }
- eligiblePids.add(tgid);
-
+ List<Integer> isolatedPids = null;
for (int threadId : tids) {
final String[] procStatusKeys = new String[] {
"Uid:",
@@ -345,7 +335,21 @@
int pidOfThreadId = (int) output[1];
// use PID check for isolated processes, use UID check for non-isolated processes.
- if (eligiblePids.contains(pidOfThreadId) || uidOfThreadId == uid) {
+ if (pidOfThreadId == tgid || uidOfThreadId == uid) {
+ continue;
+ }
+ // Only call into AM if the tid is either isolated or invalid
+ if (isolatedPids == null) {
+ // To avoid deadlock, do not call into AMS if the call is from system.
+ if (uid == Process.SYSTEM_UID) {
+ return false;
+ }
+ isolatedPids = mAmInternal.getIsolatedProcesses(uid);
+ if (isolatedPids == null) {
+ return false;
+ }
+ }
+ if (isolatedPids.contains(pidOfThreadId)) {
continue;
}
return false;
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index b2f48d9..433d807 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -154,7 +154,6 @@
import android.security.metrics.KeystoreAtom;
import android.security.metrics.KeystoreAtomPayload;
import android.security.metrics.RkpErrorStats;
-import android.security.metrics.RkpPoolStats;
import android.security.metrics.StorageStats;
import android.stats.storage.StorageEnums;
import android.telephony.ModemActivityInfo;
@@ -730,7 +729,6 @@
return pullInstalledIncrementalPackagesLocked(atomTag, data);
}
case FrameworkStatsLog.KEYSTORE2_STORAGE_STATS:
- case FrameworkStatsLog.RKP_POOL_STATS:
case FrameworkStatsLog.KEYSTORE2_KEY_CREATION_WITH_GENERAL_INFO:
case FrameworkStatsLog.KEYSTORE2_KEY_CREATION_WITH_AUTH_INFO:
case FrameworkStatsLog.KEYSTORE2_KEY_CREATION_WITH_PURPOSE_AND_MODES_INFO:
@@ -938,7 +936,6 @@
registerSettingsStats();
registerInstalledIncrementalPackages();
registerKeystoreStorageStats();
- registerRkpPoolStats();
registerKeystoreKeyCreationWithGeneralInfo();
registerKeystoreKeyCreationWithAuthInfo();
registerKeystoreKeyCreationWithPurposeModesInfo();
@@ -4258,14 +4255,6 @@
mStatsCallbackImpl);
}
- private void registerRkpPoolStats() {
- mStatsManager.setPullAtomCallback(
- FrameworkStatsLog.RKP_POOL_STATS,
- null, // use default PullAtomMetadata values,
- DIRECT_EXECUTOR,
- mStatsCallbackImpl);
- }
-
private void registerKeystoreKeyCreationWithGeneralInfo() {
mStatsManager.setPullAtomCallback(
FrameworkStatsLog.KEYSTORE2_KEY_CREATION_WITH_GENERAL_INFO,
@@ -4373,19 +4362,6 @@
return StatsManager.PULL_SUCCESS;
}
- int parseRkpPoolStats(KeystoreAtom[] atoms, List<StatsEvent> pulledData) {
- for (KeystoreAtom atomWrapper : atoms) {
- if (atomWrapper.payload.getTag() != KeystoreAtomPayload.rkpPoolStats) {
- return StatsManager.PULL_SKIP;
- }
- RkpPoolStats atom = atomWrapper.payload.getRkpPoolStats();
- pulledData.add(FrameworkStatsLog.buildStatsEvent(
- FrameworkStatsLog.RKP_POOL_STATS, atom.security_level, atom.expiring,
- atom.unassigned, atom.attested, atom.total));
- }
- return StatsManager.PULL_SUCCESS;
- }
-
int parseKeystoreKeyCreationWithGeneralInfo(KeystoreAtom[] atoms, List<StatsEvent> pulledData) {
for (KeystoreAtom atomWrapper : atoms) {
if (atomWrapper.payload.getTag()
@@ -4518,8 +4494,6 @@
switch (atomTag) {
case FrameworkStatsLog.KEYSTORE2_STORAGE_STATS:
return parseKeystoreStorageStats(atoms, pulledData);
- case FrameworkStatsLog.RKP_POOL_STATS:
- return parseRkpPoolStats(atoms, pulledData);
case FrameworkStatsLog.KEYSTORE2_KEY_CREATION_WITH_GENERAL_INFO:
return parseKeystoreKeyCreationWithGeneralInfo(atoms, pulledData);
case FrameworkStatsLog.KEYSTORE2_KEY_CREATION_WITH_AUTH_INFO:
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SdCardEjectionTests.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SdCardEjectionTests.kt
index d813962..a849b66 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SdCardEjectionTests.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SdCardEjectionTests.kt
@@ -22,6 +22,7 @@
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
@@ -41,6 +42,7 @@
@RunWith(DeviceJUnit4Parameterized::class)
@Parameterized.UseParametersRunnerFactory(
DeviceJUnit4ClassRunnerWithParameters.RunnerFactory::class)
+@Ignore("b/275403538")
class SdCardEjectionTests : BaseHostJUnit4Test() {
companion object {
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
index 1fbb8dd..eb6efd2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
@@ -166,10 +166,6 @@
CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE);
assertThat(mCachedAppOptimizerUnderTest.mFreezerStatsdSampleRate).isEqualTo(
CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE);
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5);
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6);
assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo(
CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
assertThat(mCachedAppOptimizerUnderTest.mFullDeltaRssThrottleKb).isEqualTo(
@@ -261,10 +257,6 @@
CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3 + 1);
assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4 + 1);
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5 + 1);
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6 + 1);
assertThat(mCachedAppOptimizerUnderTest.mFullDeltaRssThrottleKb).isEqualTo(
CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + 1);
assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMinOomAdj).isEqualTo(
@@ -275,10 +267,6 @@
CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f);
assertThat(mCachedAppOptimizerUnderTest.mFreezerStatsdSampleRate).isEqualTo(
CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f);
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5 + 1);
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6 + 1);
assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo(
CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1);
assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle).containsExactly(1, 2, 3);
@@ -425,10 +413,6 @@
CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3 + 1);
assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4 + 1);
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5 + 1);
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6 + 1);
assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMinOomAdj).isEqualTo(
CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ + 1);
assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMaxOomAdj).isEqualTo(
@@ -454,10 +438,6 @@
CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3);
assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5);
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6);
// Repeat for each of the throttle keys.
mCountDown = new CountDownLatch(1);
@@ -472,10 +452,6 @@
CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3);
assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5);
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6);
mCountDown = new CountDownLatch(1);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -489,10 +465,6 @@
CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3);
assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5);
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6);
mCountDown = new CountDownLatch(1);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -506,10 +478,6 @@
CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3);
assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5);
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6);
mCountDown = new CountDownLatch(1);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -523,10 +491,6 @@
CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3);
assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5);
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6);
mCountDown = new CountDownLatch(1);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -540,10 +504,6 @@
CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3);
assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5);
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6);
}
@Test
@@ -953,15 +913,7 @@
mProcessDependencies.setRssAfterCompaction(rssAfter);
// When moving within cached state
- mCachedAppOptimizerUnderTest.onOomAdjustChanged(
- ProcessList.CACHED_APP_MIN_ADJ, ProcessList.CACHED_APP_MIN_ADJ + 1, processRecord);
- waitForHandler();
- // THEN process IS NOT compacted.
- assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNull();
-
- // When moving into cached state
- mCachedAppOptimizerUnderTest.onOomAdjustChanged(ProcessList.CACHED_APP_MIN_ADJ - 1,
- ProcessList.CACHED_APP_MIN_ADJ + 1, processRecord);
+ mCachedAppOptimizerUnderTest.onProcessFrozen(processRecord);
waitForHandler();
// THEN process IS compacted.
assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/ProxyAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/ProxyAccessibilityServiceConnectionTest.java
index b5e0e07..dd44a79 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/ProxyAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/ProxyAccessibilityServiceConnectionTest.java
@@ -50,6 +50,7 @@
public class ProxyAccessibilityServiceConnectionTest {
private static final int DISPLAY_ID = 1000;
+ private static final int DEVICE_ID = 2000;
private static final int CONNECTION_ID = 1000;
private static final ComponentName COMPONENT_NAME = new ComponentName(
"com.android.server.accessibility", ".ProxyAccessibilityServiceConnectionTest");
@@ -90,7 +91,7 @@
mAccessibilityServiceInfo, CONNECTION_ID , new Handler(
getInstrumentation().getContext().getMainLooper()),
mMockLock, mMockSecurityPolicy, mMockSystemSupport, mMockA11yTrace,
- mMockWindowManagerInternal, mMockA11yWindowManager, DISPLAY_ID);
+ mMockWindowManagerInternal, mMockA11yWindowManager, DISPLAY_ID, DEVICE_ID);
}
@Test
@@ -101,7 +102,7 @@
mProxyConnection.setInstalledAndEnabledServices(infos);
- verify(mMockSystemSupport).onClientChangeLocked(true);
+ verify(mMockSystemSupport).onProxyChanged(DEVICE_ID);
}
@Test