Merge "Add DREAMING -> GONE when dismissible; fix WmLsVis issue with that case and others." into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 97d28d1..2f843f9 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -345,7 +345,7 @@
name: "android.nfc.flags-aconfig",
package: "android.nfc",
container: "system",
- srcs: ["nfc/java/android/nfc/*.aconfig"],
+ srcs: ["nfc-non-updatable/flags/*.aconfig"],
}
cc_aconfig_library {
diff --git a/core/java/android/hardware/input/KeyGestureEvent.java b/core/java/android/hardware/input/KeyGestureEvent.java
index 47ef461..9f8505f 100644
--- a/core/java/android/hardware/input/KeyGestureEvent.java
+++ b/core/java/android/hardware/input/KeyGestureEvent.java
@@ -125,7 +125,10 @@
public static final int KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK = 75;
public static final int KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW = 76;
public static final int KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB = 77;
-
+ public static final int KEY_GESTURE_TYPE_MAGNIFIER_PAN_LEFT = 78;
+ public static final int KEY_GESTURE_TYPE_MAGNIFIER_PAN_RIGHT = 79;
+ public static final int KEY_GESTURE_TYPE_MAGNIFIER_PAN_UP = 80;
+ public static final int KEY_GESTURE_TYPE_MAGNIFIER_PAN_DOWN = 81;
public static final int FLAG_CANCELLED = 1;
@@ -217,7 +220,11 @@
KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION,
KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK,
KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW,
- KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB
+ KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB,
+ KEY_GESTURE_TYPE_MAGNIFIER_PAN_LEFT,
+ KEY_GESTURE_TYPE_MAGNIFIER_PAN_RIGHT,
+ KEY_GESTURE_TYPE_MAGNIFIER_PAN_UP,
+ KEY_GESTURE_TYPE_MAGNIFIER_PAN_DOWN,
})
@Retention(RetentionPolicy.SOURCE)
public @interface KeyGestureType {
@@ -792,6 +799,14 @@
return "KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW";
case KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB:
return "KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB";
+ case KEY_GESTURE_TYPE_MAGNIFIER_PAN_LEFT:
+ return "KEY_GESTURE_TYPE_MAGNIFIER_PAN_LEFT";
+ case KEY_GESTURE_TYPE_MAGNIFIER_PAN_RIGHT:
+ return "KEY_GESTURE_TYPE_MAGNIFIER_PAN_RIGHT";
+ case KEY_GESTURE_TYPE_MAGNIFIER_PAN_UP:
+ return "KEY_GESTURE_TYPE_MAGNIFIER_PAN_UP";
+ case KEY_GESTURE_TYPE_MAGNIFIER_PAN_DOWN:
+ return "KEY_GESTURE_TYPE_MAGNIFIER_PAN_DOWN";
default:
return Integer.toHexString(value);
}
diff --git a/nfc-non-updatable/Android.bp b/nfc-non-updatable/Android.bp
new file mode 100644
index 0000000..ff987bb
--- /dev/null
+++ b/nfc-non-updatable/Android.bp
@@ -0,0 +1,23 @@
+package {
+ default_team: "trendy_team_fwk_nfc",
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+ name: "framework-nfc-non-updatable-sources",
+ path: "java",
+ srcs: [
+ "java/android/nfc/NfcServiceManager.java",
+ "java/android/nfc/cardemulation/ApduServiceInfo.aidl",
+ "java/android/nfc/cardemulation/ApduServiceInfo.java",
+ "java/android/nfc/cardemulation/NfcFServiceInfo.aidl",
+ "java/android/nfc/cardemulation/NfcFServiceInfo.java",
+ "java/android/nfc/cardemulation/AidGroup.aidl",
+ "java/android/nfc/cardemulation/AidGroup.java",
+ ],
+}
diff --git a/nfc-non-updatable/OWNERS b/nfc-non-updatable/OWNERS
new file mode 100644
index 0000000..f46dccd
--- /dev/null
+++ b/nfc-non-updatable/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 48448
+include platform/packages/apps/Nfc:/OWNERS
\ No newline at end of file
diff --git a/nfc/java/android/nfc/flags.aconfig b/nfc-non-updatable/flags/flags.aconfig
similarity index 100%
rename from nfc/java/android/nfc/flags.aconfig
rename to nfc-non-updatable/flags/flags.aconfig
diff --git a/nfc/java/android/nfc/NfcServiceManager.java b/nfc-non-updatable/java/android/nfc/NfcServiceManager.java
similarity index 100%
rename from nfc/java/android/nfc/NfcServiceManager.java
rename to nfc-non-updatable/java/android/nfc/NfcServiceManager.java
diff --git a/nfc/java/android/nfc/cardemulation/AidGroup.aidl b/nfc-non-updatable/java/android/nfc/cardemulation/AidGroup.aidl
similarity index 100%
rename from nfc/java/android/nfc/cardemulation/AidGroup.aidl
rename to nfc-non-updatable/java/android/nfc/cardemulation/AidGroup.aidl
diff --git a/nfc/java/android/nfc/cardemulation/AidGroup.java b/nfc-non-updatable/java/android/nfc/cardemulation/AidGroup.java
similarity index 100%
rename from nfc/java/android/nfc/cardemulation/AidGroup.java
rename to nfc-non-updatable/java/android/nfc/cardemulation/AidGroup.java
diff --git a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.aidl b/nfc-non-updatable/java/android/nfc/cardemulation/ApduServiceInfo.aidl
similarity index 100%
rename from nfc/java/android/nfc/cardemulation/ApduServiceInfo.aidl
rename to nfc-non-updatable/java/android/nfc/cardemulation/ApduServiceInfo.aidl
diff --git a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java b/nfc-non-updatable/java/android/nfc/cardemulation/ApduServiceInfo.java
similarity index 100%
rename from nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
rename to nfc-non-updatable/java/android/nfc/cardemulation/ApduServiceInfo.java
diff --git a/nfc/java/android/nfc/cardemulation/NfcFServiceInfo.aidl b/nfc-non-updatable/java/android/nfc/cardemulation/NfcFServiceInfo.aidl
similarity index 100%
rename from nfc/java/android/nfc/cardemulation/NfcFServiceInfo.aidl
rename to nfc-non-updatable/java/android/nfc/cardemulation/NfcFServiceInfo.aidl
diff --git a/nfc/java/android/nfc/cardemulation/NfcFServiceInfo.java b/nfc-non-updatable/java/android/nfc/cardemulation/NfcFServiceInfo.java
similarity index 100%
rename from nfc/java/android/nfc/cardemulation/NfcFServiceInfo.java
rename to nfc-non-updatable/java/android/nfc/cardemulation/NfcFServiceInfo.java
diff --git a/nfc/Android.bp b/nfc/Android.bp
index 9490487..0fdb3bd 100644
--- a/nfc/Android.bp
+++ b/nfc/Android.bp
@@ -9,20 +9,6 @@
}
filegroup {
- name: "framework-nfc-non-updatable-sources",
- path: "java",
- srcs: [
- "java/android/nfc/NfcServiceManager.java",
- "java/android/nfc/cardemulation/ApduServiceInfo.aidl",
- "java/android/nfc/cardemulation/ApduServiceInfo.java",
- "java/android/nfc/cardemulation/NfcFServiceInfo.aidl",
- "java/android/nfc/cardemulation/NfcFServiceInfo.java",
- "java/android/nfc/cardemulation/AidGroup.aidl",
- "java/android/nfc/cardemulation/AidGroup.java",
- ],
-}
-
-filegroup {
name: "framework-nfc-updatable-sources",
path: "java",
srcs: [
@@ -34,9 +20,6 @@
"//packages/apps/Nfc:__subpackages__",
"//packages/modules/Nfc:__subpackages__",
],
- exclude_srcs: [
- ":framework-nfc-non-updatable-sources",
- ],
}
java_sdk_library {
diff --git a/nfc/tests/Android.bp b/nfc/tests/Android.bp
index b6090e8..17fb810 100644
--- a/nfc/tests/Android.bp
+++ b/nfc/tests/Android.bp
@@ -29,7 +29,6 @@
"androidx.test.rules",
"androidx.test.runner",
"androidx.test.ext.junit",
- "framework-nfc.impl",
"mockito-target-extended-minus-junit4",
"frameworks-base-testutils",
"truth",
@@ -40,16 +39,25 @@
"testables",
],
libs: [
+ "androidx.annotation_annotation",
+ "unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage
+ "framework-permission-s.stubs.module_lib",
+ "framework-permission.stubs.module_lib",
"android.test.base.stubs.system",
"android.test.mock.stubs.system",
"android.test.runner.stubs.system",
+ "framework-nfc.impl",
],
jni_libs: [
// Required for ExtendedMockito
"libdexmakerjvmtiagent",
"libstaticjvmtiagent",
],
- srcs: ["src/**/*.java"],
+ srcs: [
+ "src/**/*.java",
+ ":framework-nfc-updatable-sources",
+ ":framework-nfc-non-updatable-sources",
+ ],
platform_apis: true,
certificate: "platform",
test_suites: [
diff --git a/packages/SettingsLib/tests/robotests/Android.bp b/packages/SettingsLib/tests/robotests/Android.bp
index f380e7f..81358ca 100644
--- a/packages/SettingsLib/tests/robotests/Android.bp
+++ b/packages/SettingsLib/tests/robotests/Android.bp
@@ -76,7 +76,6 @@
tools: ["soong_zip"],
cmd: "mkdir -p $(genDir)/META-INF/services/ && touch $(genDir)/META-INF/services/org.robolectric.internal.ShadowProvider &&" +
"echo -e 'org.robolectric.Shadows' >> $(genDir)/META-INF/services/org.robolectric.internal.ShadowProvider && " +
- "echo -e 'org.robolectric.shadows.multidex.Shadows' >> $(genDir)/META-INF/services/org.robolectric.internal.ShadowProvider && " +
"echo -e 'org.robolectric.shadows.httpclient.Shadows' >> $(genDir)/META-INF/services/org.robolectric.internal.ShadowProvider && " +
//"echo -e 'com.android.settings.testutils.shadow.Shadows' >> $(genDir)/META-INF/services/org.robolectric.internal.ShadowProvider && " +
"echo -e 'com.android.settingslib.testutils.shadow.Shadows' >> $(genDir)/META-INF/services/org.robolectric.internal.ShadowProvider && " +
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index e1b6c9c..5133575 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -245,22 +245,45 @@
final boolean complete =
event.getAction() == KeyGestureEvent.ACTION_GESTURE_COMPLETE
&& !event.isCancelled();
+
+ // TODO(b/355499907): Receive and handle held key gestures, which can be used
+ // for continuous scaling and panning. In addition, handle multiple pan gestures
+ // at the same time (e.g. user may try to pan diagonally) reasonably, including
+ // decreasing diagonal movement by sqrt(2) to make it appear the same speed
+ // as non-diagonal movement.
+
+ if (!complete) {
+ return false;
+ }
+
final int gestureType = event.getKeyGestureType();
final int displayId = isDisplayIdValid(event.getDisplayId())
? event.getDisplayId() : Display.DEFAULT_DISPLAY;
switch (gestureType) {
case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN:
- if (complete) {
mAms.getMagnificationController().scaleMagnificationByStep(
displayId, MagnificationController.ZOOM_DIRECTION_IN);
- }
return true;
case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT:
- if (complete) {
mAms.getMagnificationController().scaleMagnificationByStep(
displayId, MagnificationController.ZOOM_DIRECTION_OUT);
- }
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_LEFT:
+ mAms.getMagnificationController().panMagnificationByStep(
+ displayId, MagnificationController.PAN_DIRECTION_LEFT);
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_RIGHT:
+ mAms.getMagnificationController().panMagnificationByStep(
+ displayId, MagnificationController.PAN_DIRECTION_RIGHT);
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_UP:
+ mAms.getMagnificationController().panMagnificationByStep(
+ displayId, MagnificationController.PAN_DIRECTION_UP);
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_DOWN:
+ mAms.getMagnificationController().panMagnificationByStep(
+ displayId, MagnificationController.PAN_DIRECTION_DOWN);
return true;
}
return false;
@@ -270,7 +293,11 @@
public boolean isKeyGestureSupported(int gestureType) {
return switch (gestureType) {
case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN,
- KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT -> true;
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_LEFT,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_RIGHT,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_UP,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_DOWN -> true;
default -> false;
};
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index 058b2be..2e131b6 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -35,14 +35,19 @@
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
+import android.hardware.display.DisplayManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
+import android.util.DisplayMetrics;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import android.util.SparseDoubleArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
+import android.util.TypedValue;
+import android.view.Display;
import android.view.accessibility.MagnificationAnimationCallback;
import com.android.internal.accessibility.util.AccessibilityStatsLogUtils;
@@ -102,6 +107,7 @@
/** Whether the platform supports window magnification feature. */
private final boolean mSupportWindowMagnification;
private final MagnificationScaleStepProvider mScaleStepProvider;
+ private final MagnificationPanStepProvider mPanStepProvider;
private final Executor mBackgroundExecutor;
@@ -132,7 +138,7 @@
.UiChangesForAccessibilityCallbacks> mAccessibilityCallbacksDelegateArray =
new SparseArray<>();
- // Direction magnifier scale can be altered.
+ // Direction magnification scale can be altered.
public static final int ZOOM_DIRECTION_IN = 0;
public static final int ZOOM_DIRECTION_OUT = 1;
@@ -140,6 +146,16 @@
public @interface ZoomDirection {
}
+ // Directions magnification center can be moved.
+ public static final int PAN_DIRECTION_LEFT = 0;
+ public static final int PAN_DIRECTION_RIGHT = 1;
+ public static final int PAN_DIRECTION_UP = 2;
+ public static final int PAN_DIRECTION_DOWN = 3;
+
+ @IntDef({PAN_DIRECTION_LEFT, PAN_DIRECTION_RIGHT, PAN_DIRECTION_UP, PAN_DIRECTION_DOWN})
+ public @interface PanDirection {
+ }
+
/**
* A callback to inform the magnification transition result on the given display.
*/
@@ -188,6 +204,87 @@
}
}
+ /**
+ * An interface to configure how much the magnification center should be affected when panning
+ * in steps.
+ */
+ public interface MagnificationPanStepProvider {
+ /**
+ * Calculate the next value based on the current scale.
+ *
+ * @param currentScale The current magnification scale value.
+ * @param displayId The displayId for the display being magnified.
+ * @return The next pan step value.
+ */
+ float nextPanStep(float currentScale, int displayId);
+ }
+
+ public static class DefaultMagnificationPanStepProvider implements
+ MagnificationPanStepProvider, DisplayManager.DisplayListener {
+ // We want panning to be 40 dip per keystroke at scale 2, and 1 dip per keystroke at scale
+ // 20. This can be defined using y = mx + b to get the slope and intercept.
+ // This works even if the device does not allow magnification up to 20x; we will still get
+ // a reasonable lineary ramp of panning movement for each scale step.
+ private static final float DEFAULT_SCALE = 2.0f;
+ private static final float PAN_STEP_AT_DEFAULT_SCALE_DIP = 40.0f;
+ private static final float SCALE_FOR_1_DIP_PAN = 20.0f;
+
+ private SparseDoubleArray mPanStepSlopes;
+ private SparseDoubleArray mPanStepIntercepts;
+
+ private final DisplayManager mDisplayManager;
+
+ DefaultMagnificationPanStepProvider(Context context) {
+ mDisplayManager = context.getSystemService(DisplayManager.class);
+ mDisplayManager.registerDisplayListener(this, /*handler=*/null);
+ mPanStepSlopes = new SparseDoubleArray();
+ mPanStepIntercepts = new SparseDoubleArray();
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ updateForDisplay(displayId);
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ updateForDisplay(displayId);
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ mPanStepSlopes.delete(displayId);
+ mPanStepIntercepts.delete(displayId);
+ }
+
+ @Override
+ public float nextPanStep(float currentScale, int displayId) {
+ if (mPanStepSlopes.indexOfKey(displayId) < 0) {
+ updateForDisplay(displayId);
+ }
+ return Math.max((float) (mPanStepSlopes.get(displayId) * currentScale
+ + mPanStepIntercepts.get(displayId)), 1);
+ }
+
+ private void updateForDisplay(int displayId) {
+ Display display = mDisplayManager.getDisplay(displayId);
+ if (display == null) {
+ return;
+ }
+ DisplayMetrics metrics = new DisplayMetrics();
+ display.getMetrics(metrics);
+ final float panStepAtDefaultScaleInPx = TypedValue.convertDimensionToPixels(
+ TypedValue.COMPLEX_UNIT_DIP, PAN_STEP_AT_DEFAULT_SCALE_DIP, metrics);
+ final float panStepAtMaxScaleInPx = TypedValue.convertDimensionToPixels(
+ TypedValue.COMPLEX_UNIT_DIP, 1.0f, metrics);
+ final float panStepSlope = (panStepAtMaxScaleInPx - panStepAtDefaultScaleInPx)
+ / (SCALE_FOR_1_DIP_PAN - DEFAULT_SCALE);
+ mPanStepSlopes.put(displayId, panStepSlope);
+ mPanStepIntercepts.put(displayId,
+ panStepAtDefaultScaleInPx - panStepSlope * DEFAULT_SCALE);
+ }
+ }
+
public MagnificationController(AccessibilityManagerService ams, Object lock,
Context context, MagnificationScaleProvider scaleProvider,
Executor backgroundExecutor) {
@@ -201,6 +298,7 @@
mSupportWindowMagnification = context.getPackageManager().hasSystemFeature(
FEATURE_WINDOW_MAGNIFICATION);
mScaleStepProvider = new DefaultMagnificationScaleStepProvider();
+ mPanStepProvider = new DefaultMagnificationPanStepProvider(mContext);
mAlwaysOnMagnificationFeatureFlag = new AlwaysOnMagnificationFeatureFlag(context);
mAlwaysOnMagnificationFeatureFlag.addOnChangedListener(
@@ -935,13 +1033,12 @@
}
/**
- * Scales the magnifier on the given display one step in/out based on the zoomIn param.
+ * Scales the magnifier on the given display one step in/out based on the direction param.
*
* @param displayId The logical display id.
* @param direction Whether the scale should be zoomed in or out.
- * @return {@code true} if the magnification scale was affected.
*/
- public boolean scaleMagnificationByStep(int displayId, @ZoomDirection int direction) {
+ public void scaleMagnificationByStep(int displayId, @ZoomDirection int direction) {
if (getFullScreenMagnificationController().isActivated(displayId)) {
final float magnificationScale = getFullScreenMagnificationController().getScale(
displayId);
@@ -950,7 +1047,6 @@
getFullScreenMagnificationController().setScaleAndCenter(displayId,
nextMagnificationScale,
Float.NaN, Float.NaN, true, MAGNIFICATION_GESTURE_HANDLER_ID);
- return nextMagnificationScale != magnificationScale;
}
if (getMagnificationConnectionManager().isWindowMagnifierEnabled(displayId)) {
@@ -959,10 +1055,51 @@
final float nextMagnificationScale = mScaleStepProvider.nextScaleStep(
magnificationScale, direction);
getMagnificationConnectionManager().setScale(displayId, nextMagnificationScale);
- return nextMagnificationScale != magnificationScale;
+ }
+ }
+
+ /**
+ * Pans the magnifier on the given display one step left/right/up/down based on the direction
+ * param.
+ *
+ * @param displayId The logical display id.
+ * @param direction Whether the direction should be left/right/up/down.
+ */
+ public void panMagnificationByStep(int displayId, @PanDirection int direction) {
+ final boolean fullscreenActivated =
+ getFullScreenMagnificationController().isActivated(displayId);
+ final boolean windowActivated =
+ getMagnificationConnectionManager().isWindowMagnifierEnabled(displayId);
+ if (!fullscreenActivated && !windowActivated) {
+ return;
}
- return false;
+ final float scale = fullscreenActivated
+ ? getFullScreenMagnificationController().getScale(displayId)
+ : getMagnificationConnectionManager().getScale(displayId);
+ final float step = mPanStepProvider.nextPanStep(scale, displayId);
+
+ float offsetX = 0;
+ float offsetY = 0;
+ if (direction == PAN_DIRECTION_LEFT) {
+ offsetX = -step;
+ } else if (direction == PAN_DIRECTION_RIGHT) {
+ offsetX = step;
+ } else if (direction == PAN_DIRECTION_UP) {
+ offsetY = -step;
+ } else if (direction == PAN_DIRECTION_DOWN) {
+ offsetY = step;
+ }
+
+ if (fullscreenActivated) {
+ final float centerX = getFullScreenMagnificationController().getCenterX(displayId);
+ final float centerY = getFullScreenMagnificationController().getCenterY(displayId);
+ getFullScreenMagnificationController().setScaleAndCenter(displayId, scale,
+ centerX + offsetX, centerY + offsetY, true, MAGNIFICATION_GESTURE_HANDLER_ID);
+ } else {
+ getMagnificationConnectionManager().moveWindowMagnification(displayId, offsetX,
+ offsetY);
+ }
}
private final class DisableMagnificationCallback implements
diff --git a/services/autofill/bugfixes.aconfig b/services/autofill/bugfixes.aconfig
index 65c446e..ec6c3b7 100644
--- a/services/autofill/bugfixes.aconfig
+++ b/services/autofill/bugfixes.aconfig
@@ -74,6 +74,16 @@
}
flag {
+ name: "multiple_fill_history"
+ namespace: "autofill"
+ description: "Allows tracking per Session FillEventHistory. As a bugfix flag to guard against DeviceConfig flag"
+ bug: "365630157"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "add_session_id_to_client_state"
namespace: "autofill"
description: "Include the session id into the FillEventHistory events as part of ClientState"
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 0fa43ac..11710c9 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -152,6 +152,15 @@
@GuardedBy("mLock")
private final SparseArray<Session> mSessions = new SparseArray<>();
+ /**
+ * Cache of FillEventHistory for active Sessions.
+ *
+ * <p>New histories are added whenever a Session is created and are kept until Sessions are
+ * removed through removeSessionLocked()
+ */
+ @GuardedBy("mLock")
+ private final SparseArray<FillEventHistory> mFillHistories = new SparseArray<>();
+
/** The last selection */
@GuardedBy("mLock")
private FillEventHistory mEventHistory;
@@ -663,6 +672,10 @@
flags, mInputMethodManagerInternal, isPrimaryCredential);
mSessions.put(newSession.id, newSession);
+ if (Flags.multipleFillHistory() && !forAugmentedAutofillOnly) {
+ mFillHistories.put(newSession.id, new FillEventHistory(sessionId, null));
+ }
+
return newSession;
}
@@ -756,6 +769,28 @@
TAG,
"removeSessionLocked(): removed " + sessionId);
}
+
+ FillEventHistory history = null;
+
+ if (Flags.multipleFillHistory() && mFillHistories != null) {
+ history = mFillHistories.get(sessionId);
+ mFillHistories.delete(sessionId);
+ }
+
+ if (mInfo == null || mInfo.getServiceInfo() == null) {
+ if (sVerbose) {
+ Slog.v(TAG, "removeSessionLocked(): early return because mInfo is null");
+ }
+ return;
+ }
+
+ if (mMaster == null) {
+ if (sVerbose) {
+ Slog.v(TAG, "removeSessionLocked(): early return because mMaster is null");
+ }
+ return;
+ }
+
RemoteFillService remoteService =
new RemoteFillService(
getContext(),
@@ -764,7 +799,8 @@
/* callbacks= */ null,
mMaster.isInstantServiceAllowed(),
/* credentialAutofillService= */ null);
- remoteService.onSessionDestroyed(null);
+
+ remoteService.onSessionDestroyed(history);
}
}
@@ -886,6 +922,10 @@
}
}
mSessions.clear();
+ if (Flags.multipleFillHistory()) {
+ mFillHistories.clear();
+ }
+
for (int i = 0; i < remoteFillServices.size(); i++) {
remoteFillServices.valueAt(i).destroy();
}
@@ -944,60 +984,132 @@
return true;
}
- /**
- * Updates the last fill selection when an authentication was selected.
- */
- void setAuthenticationSelected(int sessionId, @Nullable Bundle clientState,
- int uiType, @Nullable AutofillId focusedId) {
- synchronized (mLock) {
- if (isValidEventLocked("setAuthenticationSelected()", sessionId)) {
- mEventHistory.addEvent(
- new Event(Event.TYPE_AUTHENTICATION_SELECTED, null, clientState, null, null,
- null, null, null, null, null, null,
- NO_SAVE_UI_REASON_NONE, uiType, focusedId));
+ @GuardedBy("mLock")
+ void addEventToHistory(String eventName, int sessionId, Event event) {
+ // For the singleton filleventhistory
+ if (isValidEventLocked(eventName, sessionId)) {
+ mEventHistory.addEvent(event);
+ }
+
+ if (Flags.multipleFillHistory()) {
+ FillEventHistory history = mFillHistories.get(sessionId);
+ if (history != null) {
+ history.addEvent(event);
+ } else {
+ if (sVerbose) {
+ Slog.v(TAG, eventName
+ + " not logged because FillEventHistory is not tracked for: "
+ + sessionId);
+ }
}
}
}
/**
- * Updates the last fill selection when an dataset authentication was selected.
+ * Updates the last fill selection when an authentication was selected.
*/
- void logDatasetAuthenticationSelected(@Nullable String selectedDataset, int sessionId,
- @Nullable Bundle clientState, int uiType, @Nullable AutofillId focusedId) {
+ void setAuthenticationSelected(int sessionId, @Nullable Bundle clientState,
+ int uiType, @Nullable AutofillId focusedId, boolean shouldAdd) {
synchronized (mLock) {
- if (isValidEventLocked("logDatasetAuthenticationSelected()", sessionId)) {
- mEventHistory.addEvent(
- new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset,
- clientState, null, null, null, null, null, null, null, null,
- NO_SAVE_UI_REASON_NONE, uiType, focusedId));
+
+ String methodName = "setAuthenticationSelected()";
+
+ if (!shouldAdd) {
+ if (sVerbose) {
+ Slog.v(TAG, methodName + " not logged because shouldAdd is false");
+ }
+ return;
}
+
+ Event event =
+ new Event(
+ Event.TYPE_AUTHENTICATION_SELECTED,
+ null,
+ clientState,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ NO_SAVE_UI_REASON_NONE,
+ uiType,
+ focusedId);
+
+ addEventToHistory(methodName, sessionId, event);
+ }
+ }
+
+ /** Updates the last fill selection when a dataset authentication was selected. */
+ void logDatasetAuthenticationSelected(
+ @Nullable String selectedDataset,
+ int sessionId,
+ @Nullable Bundle clientState,
+ int uiType,
+ @Nullable AutofillId focusedId,
+ boolean shouldAdd) {
+ synchronized (mLock) {
+ String methodName = "logDatasetAuthenticationSelected()";
+
+ if (!shouldAdd) {
+ if (sVerbose) {
+ Slog.v(TAG, methodName + " not logged because shouldAdd is false");
+ }
+ return;
+ }
+
+ Event event = new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset,
+ clientState, null, null, null, null, null, null, null, null,
+ NO_SAVE_UI_REASON_NONE, uiType, focusedId);
+ addEventToHistory(methodName, sessionId, event);
}
}
/**
* Updates the last fill selection when an save Ui is shown.
*/
- void logSaveShown(int sessionId, @Nullable Bundle clientState) {
+ void logSaveShown(int sessionId, @Nullable Bundle clientState, boolean shouldAdd) {
synchronized (mLock) {
- if (isValidEventLocked("logSaveShown()", sessionId)) {
- mEventHistory.addEvent(new Event(Event.TYPE_SAVE_SHOWN, null, clientState, null,
- null, null, null, null, null, null, null, /* focusedId= */ null));
+ String methodName = "logSaveShown()";
+
+ if (!shouldAdd) {
+ if (sVerbose) {
+ Slog.v(TAG, methodName + " not logged because shouldAdd is false");
+ }
+ return;
}
+
+ Event event = new Event(Event.TYPE_SAVE_SHOWN, null, clientState, null,
+ null, null, null, null, null, null, null, /* focusedId= */ null);
+
+ addEventToHistory(methodName, sessionId, event);
}
}
- /**
- * Updates the last fill response when a dataset was selected.
- */
- void logDatasetSelected(@Nullable String selectedDataset, int sessionId,
- @Nullable Bundle clientState, int uiType, @Nullable AutofillId focusedId) {
+ /** Updates the last fill response when a dataset was selected. */
+ void logDatasetSelected(
+ @Nullable String selectedDataset,
+ int sessionId,
+ @Nullable Bundle clientState,
+ int uiType,
+ @Nullable AutofillId focusedId,
+ boolean shouldAdd) {
synchronized (mLock) {
- if (isValidEventLocked("logDatasetSelected()", sessionId)) {
- mEventHistory.addEvent(
- new Event(Event.TYPE_DATASET_SELECTED, selectedDataset, clientState, null,
- null, null, null, null, null, null, null, NO_SAVE_UI_REASON_NONE,
- uiType, focusedId));
+ String methodName = "logDatasetSelected()";
+
+ if (!shouldAdd) {
+ if (sVerbose) {
+ Slog.v(TAG, methodName + " not logged because shouldAdd is false");
+ }
+ return;
}
+
+ Event event = new Event(Event.TYPE_DATASET_SELECTED, selectedDataset, clientState, null,
+ null, null, null, null, null, null, null, NO_SAVE_UI_REASON_NONE,
+ uiType, focusedId);
+ addEventToHistory(methodName, sessionId, event);
}
}
@@ -1005,40 +1117,75 @@
* Updates the last fill response when a dataset is shown.
*/
void logDatasetShown(int sessionId, @Nullable Bundle clientState, int uiType,
- @Nullable AutofillId focusedId) {
+ @Nullable AutofillId focusedId, boolean shouldAdd) {
synchronized (mLock) {
- if (isValidEventLocked("logDatasetShown", sessionId)) {
- mEventHistory.addEvent(
- new Event(Event.TYPE_DATASETS_SHOWN, null, clientState, null, null, null,
+ String methodName = "logDatasetShown()";
+
+ if (!shouldAdd) {
+ if (sVerbose) {
+ Slog.v(TAG, methodName + " not logged because shouldAdd is false");
+ }
+ return;
+ }
+
+ Event event = new Event(Event.TYPE_DATASETS_SHOWN, null, clientState, null, null, null,
null, null, null, null, null, NO_SAVE_UI_REASON_NONE,
- uiType, focusedId));
+ uiType, focusedId);
+ addEventToHistory(methodName, sessionId, event);
+ }
+ }
+
+ void logViewEnteredForHistory(
+ int sessionId,
+ @Nullable Bundle clientState,
+ FillEventHistory history,
+ @Nullable AutofillId focusedId) {
+ if (history.getEvents() != null) {
+ // Do not log this event more than once
+ for (Event event : history.getEvents()) {
+ if (event.getType() == Event.TYPE_VIEW_REQUESTED_AUTOFILL) {
+ if (sVerbose) {
+ Slog.v(TAG, "logViewEntered: already logged TYPE_VIEW_REQUESTED_AUTOFILL");
+ }
+ return;
+ }
}
}
+
+ history.addEvent(
+ new Event(Event.TYPE_VIEW_REQUESTED_AUTOFILL, null, clientState, null,
+ null, null, null, null, null, null, null, focusedId));
}
/**
* Updates the last fill response when a view was entered.
*/
void logViewEntered(int sessionId, @Nullable Bundle clientState,
- @Nullable AutofillId focusedId) {
+ @Nullable AutofillId focusedId, boolean shouldAdd) {
synchronized (mLock) {
- if (!isValidEventLocked("logViewEntered", sessionId)) {
+ String methodName = "logViewEntered()";
+
+ if (!shouldAdd) {
+ if (sVerbose) {
+ Slog.v(TAG, methodName + " not logged because shouldAdd is false");
+ }
return;
}
- if (mEventHistory.getEvents() != null) {
- // Do not log this event more than once
- for (Event event : mEventHistory.getEvents()) {
- if (event.getType() == Event.TYPE_VIEW_REQUESTED_AUTOFILL) {
- Slog.v(TAG, "logViewEntered: already logged TYPE_VIEW_REQUESTED_AUTOFILL");
- return;
- }
- }
+ // This log does not call addEventToHistory() because each distinct FillEventHistory
+ // can only contain 1 TYPE_VIEW_REQUESTED_AUTOFILL event. Therefore, checking both
+ // the singleton FillEventHistory and the per Session FillEventHistory is necessary
+
+ if (isValidEventLocked(methodName, sessionId)) {
+ logViewEnteredForHistory(sessionId, clientState, mEventHistory, focusedId);
}
- mEventHistory.addEvent(
- new Event(Event.TYPE_VIEW_REQUESTED_AUTOFILL, null, clientState, null,
- null, null, null, null, null, null, null, focusedId));
+ if (Flags.multipleFillHistory()) {
+ FillEventHistory history = mFillHistories.get(sessionId);
+ if (history != null) {
+ logViewEnteredForHistory(sessionId, clientState, history, focusedId);
+ }
+ }
}
}
@@ -1096,12 +1243,12 @@
@Nullable ArrayList<String> changedDatasetIds,
@Nullable ArrayList<AutofillId> manuallyFilledFieldIds,
@Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
- @NonNull ComponentName appComponentName, boolean compatMode) {
+ @NonNull ComponentName appComponentName, boolean compatMode, boolean shouldAdd) {
logContextCommittedLocked(sessionId, clientState, selectedDatasets, ignoredDatasets,
changedFieldIds, changedDatasetIds, manuallyFilledFieldIds,
manuallyFilledDatasetIds, /* detectedFieldIdsList= */ null,
/* detectedFieldClassificationsList= */ null, appComponentName, compatMode,
- Event.NO_SAVE_UI_REASON_NONE);
+ Event.NO_SAVE_UI_REASON_NONE, shouldAdd);
}
@GuardedBy("mLock")
@@ -1115,9 +1262,19 @@
@Nullable ArrayList<AutofillId> detectedFieldIdsList,
@Nullable ArrayList<FieldClassification> detectedFieldClassificationsList,
@NonNull ComponentName appComponentName, boolean compatMode,
- @NoSaveReason int saveDialogNotShowReason) {
- if (isValidEventLocked("logDatasetNotSelected()", sessionId)) {
+ @NoSaveReason int saveDialogNotShowReason,
+ boolean shouldAdd) {
+
+ String methodName = "logContextCommittedLocked()";
+
+ if (!shouldAdd) {
if (sVerbose) {
+ Slog.v(TAG, methodName + " not logged because shouldAdd is false");
+ }
+ return;
+ }
+
+ if (sVerbose) {
Slog.v(TAG, "logContextCommitted() with FieldClassification: id=" + sessionId
+ ", selectedDatasets=" + selectedDatasets
+ ", ignoredDatasetIds=" + ignoredDatasets
@@ -1129,44 +1286,58 @@
+ ", appComponentName=" + appComponentName.toShortString()
+ ", compatMode=" + compatMode
+ ", saveDialogNotShowReason=" + saveDialogNotShowReason);
- }
- AutofillId[] detectedFieldsIds = null;
- FieldClassification[] detectedFieldClassifications = null;
- if (detectedFieldIdsList != null) {
- detectedFieldsIds = new AutofillId[detectedFieldIdsList.size()];
- detectedFieldIdsList.toArray(detectedFieldsIds);
- detectedFieldClassifications =
- new FieldClassification[detectedFieldClassificationsList.size()];
- detectedFieldClassificationsList.toArray(detectedFieldClassifications);
-
- final int numberFields = detectedFieldsIds.length;
- int totalSize = 0;
- float totalScore = 0;
- for (int i = 0; i < numberFields; i++) {
- final FieldClassification fc = detectedFieldClassifications[i];
- final List<Match> matches = fc.getMatches();
- final int size = matches.size();
- totalSize += size;
- for (int j = 0; j < size; j++) {
- totalScore += matches.get(j).getScore();
- }
- }
-
- final int averageScore = (int) ((totalScore * 100) / totalSize);
- mMetricsLogger.write(Helper
- .newLogMaker(MetricsEvent.AUTOFILL_FIELD_CLASSIFICATION_MATCHES,
- appComponentName, getServicePackageName(), sessionId, compatMode)
- .setCounterValue(numberFields)
- .addTaggedData(MetricsEvent.FIELD_AUTOFILL_MATCH_SCORE,
- averageScore));
- }
- mEventHistory.addEvent(new Event(Event.TYPE_CONTEXT_COMMITTED, null,
- clientState, selectedDatasets, ignoredDatasets,
- changedFieldIds, changedDatasetIds,
- manuallyFilledFieldIds, manuallyFilledDatasetIds,
- detectedFieldsIds, detectedFieldClassifications, saveDialogNotShowReason,
- /* focusedId= */ null));
}
+
+ AutofillId[] detectedFieldsIds = null;
+ FieldClassification[] detectedFieldClassifications = null;
+ if (detectedFieldIdsList != null) {
+ detectedFieldsIds = new AutofillId[detectedFieldIdsList.size()];
+ detectedFieldIdsList.toArray(detectedFieldsIds);
+ detectedFieldClassifications =
+ new FieldClassification[detectedFieldClassificationsList.size()];
+ detectedFieldClassificationsList.toArray(detectedFieldClassifications);
+
+ final int numberFields = detectedFieldsIds.length;
+ int totalSize = 0;
+ float totalScore = 0;
+ for (int i = 0; i < numberFields; i++) {
+ final FieldClassification fc = detectedFieldClassifications[i];
+ final List<Match> matches = fc.getMatches();
+ final int size = matches.size();
+ totalSize += size;
+ for (int j = 0; j < size; j++) {
+ totalScore += matches.get(j).getScore();
+ }
+ }
+
+ final int averageScore = (int) ((totalScore * 100) / totalSize);
+ mMetricsLogger.write(
+ Helper.newLogMaker(
+ MetricsEvent.AUTOFILL_FIELD_CLASSIFICATION_MATCHES,
+ appComponentName,
+ getServicePackageName(),
+ sessionId,
+ compatMode)
+ .setCounterValue(numberFields)
+ .addTaggedData(MetricsEvent.FIELD_AUTOFILL_MATCH_SCORE, averageScore));
+ }
+ Event event =
+ new Event(
+ Event.TYPE_CONTEXT_COMMITTED,
+ null,
+ clientState,
+ selectedDatasets,
+ ignoredDatasets,
+ changedFieldIds,
+ changedDatasetIds,
+ manuallyFilledFieldIds,
+ manuallyFilledDatasetIds,
+ detectedFieldsIds,
+ detectedFieldClassifications,
+ saveDialogNotShowReason,
+ /* focusedId= */ null);
+
+ addEventToHistory(methodName, sessionId, event);
}
/**
@@ -1174,7 +1345,9 @@
*
* @param callingUid The calling uid
* @return The history for the autofill or the augmented autofill events depending on the {@code
- * callingUid}, or {@code null} if there is none.
+ * callingUid}, or {@code null} if there is none.
+ * @deprecated Use {@link
+ * android.service.autofill.AutofillService#onSessionDestroyed(FillEventHistory)}
*/
FillEventHistory getFillEventHistory(int callingUid) {
synchronized (mLock) {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index ba9865d..3ecff3b 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1962,7 +1962,7 @@
if (mLogViewEntered) {
mLogViewEntered = false;
- mService.logViewEntered(id, null, mCurrentViewId);
+ mService.logViewEntered(id, null, mCurrentViewId, shouldAddEventToHistory());
}
}
@@ -2866,7 +2866,12 @@
forceRemoveFromServiceLocked();
return;
}
- mService.setAuthenticationSelected(id, mClientState, uiType, mCurrentViewId);
+ mService.setAuthenticationSelected(
+ id,
+ mClientState,
+ uiType,
+ mCurrentViewId,
+ shouldAddEventToHistory());
}
@@ -2941,7 +2946,12 @@
if (!mLoggedInlineDatasetShown) {
// Chip inflation already logged, do not log again.
// This is needed because every chip inflation will call this.
- mService.logDatasetShown(this.id, mClientState, uiType, mCurrentViewId);
+ mService.logDatasetShown(
+ this.id,
+ mClientState,
+ uiType,
+ mCurrentViewId,
+ shouldAddEventToHistory());
Slog.d(TAG, "onShown(): " + uiType + ", " + numDatasetsShown);
}
mLoggedInlineDatasetShown = true;
@@ -2949,7 +2959,12 @@
mPresentationStatsEventLogger.logWhenDatasetShown(numDatasetsShown);
// Explicitly sets maybeSetSuggestionPresentedTimestampMs
mPresentationStatsEventLogger.maybeSetSuggestionPresentedTimestampMs();
- mService.logDatasetShown(this.id, mClientState, uiType, mCurrentViewId);
+ mService.logDatasetShown(
+ this.id,
+ mClientState,
+ uiType,
+ mCurrentViewId,
+ shouldAddEventToHistory());
Slog.d(TAG, "onShown(): " + uiType + ", " + numDatasetsShown);
}
}
@@ -3943,7 +3958,8 @@
detectedFieldClassifications,
mComponentName,
mCompatMode,
- saveDialogNotShowReason);
+ saveDialogNotShowReason,
+ shouldAddEventToHistory());
mSessionCommittedEventLogger.maybeSetCommitReason(commitReason);
mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount);
mSaveEventLogger.maybeSetSaveUiNotShownReason(saveDialogNotShowReason);
@@ -4590,7 +4606,7 @@
}
private void logSaveShown() {
- mService.logSaveShown(id, mClientState);
+ mService.logSaveShown(id, mClientState, shouldAddEventToHistory());
}
@Nullable
@@ -5248,7 +5264,8 @@
// so this calling logViewEntered will be a nop.
// Calling logViewEntered() twice will only log it once
// TODO(271181979): this is broken for multiple partitions
- mService.logViewEntered(this.id, null, mCurrentViewId);
+ mService.logViewEntered(
+ this.id, null, mCurrentViewId, shouldAddEventToHistory());
}
// If this is the first time view is entered for inline, the last
@@ -6863,8 +6880,13 @@
// Autofill it directly...
if (dataset.getAuthentication() == null) {
if (generateEvent) {
- mService.logDatasetSelected(dataset.getId(), id, mClientState, uiType,
- mCurrentViewId);
+ mService.logDatasetSelected(
+ dataset.getId(),
+ id,
+ mClientState,
+ uiType,
+ mCurrentViewId,
+ shouldAddEventToHistory());
}
if (mCurrentViewId != null) {
mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId);
@@ -6875,7 +6897,7 @@
// ...or handle authentication.
mService.logDatasetAuthenticationSelected(dataset.getId(), id, mClientState, uiType,
- mCurrentViewId);
+ mCurrentViewId, shouldAddEventToHistory());
mPresentationStatsEventLogger.maybeSetAuthenticationType(
AUTHENTICATION_TYPE_DATASET_AUTHENTICATION);
// does not matter the value of isPrimary because null response won't be overridden.
@@ -8018,6 +8040,32 @@
mService.getMaster().logRequestLocked(historyItem);
}
+ /**
+ * Don't add secondary providers to FillEventHistory
+ */
+ boolean shouldAddEventToHistory() {
+
+ FillResponse lastResponse = null;
+
+ synchronized (mLock) {
+ lastResponse = getLastResponseLocked("shouldAddEventToHistory(%s)");
+ }
+
+ // There might be events (like TYPE_VIEW_REQUESTED_AUTOFILL) that are
+ // generated before FillRequest/FillResponse mechanism are started, so
+ // still need to log it
+ if (lastResponse == null) {
+ return true;
+ }
+
+ if (mRequestId.isSecondaryProvider(lastResponse.getRequestId())) {
+ // The request was to a secondary provider - don't log these events
+ return false;
+ }
+
+ return true;
+ }
+
private void wtf(@Nullable Exception e, String fmt, Object... args) {
final String message = String.format(fmt, args);
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/input/InputGestureManager.java b/services/core/java/com/android/server/input/InputGestureManager.java
index 73d5630..8681ea5 100644
--- a/services/core/java/com/android/server/input/InputGestureManager.java
+++ b/services/core/java/com/android/server/input/InputGestureManager.java
@@ -221,6 +221,18 @@
systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_EQUALS,
KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN));
+ systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_DPAD_LEFT,
+ KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_LEFT));
+ systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_DPAD_RIGHT,
+ KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_RIGHT));
+ systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_DPAD_UP,
+ KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_UP));
+ systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_DPAD_DOWN,
+ KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_DOWN));
systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_M,
KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION));
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index c878799..4ef602f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -25,6 +25,7 @@
import static com.android.server.wm.WindowManagerInternal.AccessibilityControllerInternal.UiChangesForAccessibilityCallbacks;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -34,6 +35,7 @@
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.floatThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doAnswer;
@@ -50,9 +52,11 @@
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
+import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
import android.os.Looper;
import android.os.RemoteException;
@@ -60,6 +64,7 @@
import android.provider.Settings;
import android.test.mock.MockContentResolver;
import android.testing.DexmakerShareClassLoaderRule;
+import android.util.DisplayMetrics;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.accessibility.IRemoteMagnificationAnimationCallback;
@@ -79,6 +84,8 @@
import com.android.server.input.InputManagerInternal;
import com.android.server.wm.WindowManagerInternal;
+import com.google.common.truth.Expect;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -96,6 +103,9 @@
@RunWith(AndroidJUnit4.class)
public class MagnificationControllerTest {
+ @Rule
+ public final Expect expect = Expect.create();
+
private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
private static final int TEST_SERVICE_ID = 1;
private static final Region INITIAL_SCREEN_MAGNIFICATION_REGION =
@@ -119,6 +129,8 @@
@Mock
private Context mContext;
@Mock
+ private Resources mResources;
+ @Mock
private PackageManager mPackageManager;
@Mock
@@ -156,6 +168,7 @@
@Mock
private DisplayManagerInternal mDisplayManagerInternal;
+ private Display mDisplay;
@Mock
private Scroller mMockScroller;
@@ -210,6 +223,12 @@
LocalServices.removeServiceForTest(DisplayManagerInternal.class);
LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternal);
+ DisplayManager displayManager = new DisplayManager(mContext);
+ mDisplay = displayManager.getDisplay(TEST_DISPLAY);
+ when(mContext.getSystemServiceName(DisplayManager.class)).thenReturn(
+ Context.DISPLAY_SERVICE);
+ when(mContext.getSystemService(DisplayManager.class)).thenReturn(displayManager);
+
mScreenMagnificationController =
spy(
new FullScreenMagnificationController(
@@ -686,16 +705,19 @@
float currentScale = mScreenMagnificationController.getScale(TEST_DISPLAY);
while (currentScale < SCALE_MAX_VALUE) {
- assertThat(mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY,
- MagnificationController.ZOOM_DIRECTION_IN)).isTrue();
+ mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.ZOOM_DIRECTION_IN);
final float nextScale = mScreenMagnificationController.getScale(TEST_DISPLAY);
assertThat(nextScale).isGreaterThan(currentScale);
currentScale = nextScale;
}
assertThat(currentScale).isEqualTo(SCALE_MAX_VALUE);
- assertThat(mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY,
- MagnificationController.ZOOM_DIRECTION_IN)).isFalse();
+ // Trying to scale further does not change the scale.
+ mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.ZOOM_DIRECTION_IN);
+ final float finalScale = mScreenMagnificationController.getScale(TEST_DISPLAY);
+ assertThat(finalScale).isEqualTo(currentScale);
}
@Test
@@ -706,16 +728,19 @@
float currentScale = mScreenMagnificationController.getScale(TEST_DISPLAY);
while (currentScale > SCALE_MIN_VALUE) {
- assertThat(mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY,
- MagnificationController.ZOOM_DIRECTION_OUT)).isTrue();
+ mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.ZOOM_DIRECTION_OUT);
final float nextScale = mScreenMagnificationController.getScale(TEST_DISPLAY);
assertThat(nextScale).isLessThan(currentScale);
currentScale = nextScale;
}
assertThat(currentScale).isEqualTo(SCALE_MIN_VALUE);
- assertThat(mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY,
- MagnificationController.ZOOM_DIRECTION_OUT)).isFalse();
+ // Trying to scale further does not change the scale.
+ mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.ZOOM_DIRECTION_OUT);
+ final float finalScale = mScreenMagnificationController.getScale(TEST_DISPLAY);
+ assertThat(finalScale).isEqualTo(currentScale);
}
@Test
@@ -740,6 +765,121 @@
}
@Test
+ public void panMagnificationByStep_fullscreenMode_stepSizeAtScale2() throws RemoteException {
+ setMagnificationEnabled(MODE_FULLSCREEN);
+ // At scale 2.0f, each step should be about 40 dpi.
+ mMagnificationController.onPerformScaleAction(TEST_DISPLAY, 2.0f, false);
+ reset(mScreenMagnificationController);
+
+ testFullscreenMagnificationPanWithStepSize(40.0f);
+ }
+
+ @Test
+ public void panMagnificationByStep_fullscreenMode_stepSizeAtScale8() throws RemoteException {
+ setMagnificationEnabled(MODE_FULLSCREEN);
+ // At scale 8.0f, each step should be about 27 dpi.
+ mMagnificationController.onPerformScaleAction(TEST_DISPLAY, 8.0f, false);
+ reset(mScreenMagnificationController);
+
+ testFullscreenMagnificationPanWithStepSize(27.0f);
+ }
+
+ @Test
+ public void panMagnificationByStep_windowMode_stepSizeAtScale2() throws RemoteException {
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2.0f, 100f, 200f);
+
+ testWindowMagnificationPanWithStepSize(40.0f);
+ }
+
+ @Test
+ public void panMagnificationByStep_windowMode_stepSizeAtScale8() throws RemoteException {
+ setMagnificationEnabled(MODE_WINDOW);
+ // At scale 8.0f, each step should be about 27.
+ mMagnificationController.onPerformScaleAction(TEST_DISPLAY, 8.0f, false);
+ reset(mMagnificationConnectionManager);
+
+ testWindowMagnificationPanWithStepSize(27.0f);
+ }
+
+ @Test
+ public void panMagnificationByStep_fullscreenMode_reachesRightEdgeOfScreen()
+ throws RemoteException {
+ setMagnificationEnabled(MODE_FULLSCREEN);
+ // At scale 2.0f, each step should be about 40.
+ mMagnificationController.onPerformScaleAction(TEST_DISPLAY, DEFAULT_SCALE, false);
+ reset(mScreenMagnificationController);
+
+ float currentCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+ float currentCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+
+ DisplayMetrics metrics = new DisplayMetrics();
+ mDisplay.getMetrics(metrics);
+ float expectedStep = 40.0f * metrics.density;
+
+ // Move right, eventually we should reach the edge.
+ int maxNumSteps = (int) (metrics.widthPixels / expectedStep) + 1;
+ int numSteps = 0;
+ while (numSteps < maxNumSteps) {
+ mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.PAN_DIRECTION_RIGHT);
+ float newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+ float newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+ assertThat(currentCenterY).isEqualTo(newCenterY);
+
+ assertThat(newCenterX).isAtLeast(currentCenterX);
+ if (newCenterX == currentCenterX) {
+ break;
+ }
+
+ currentCenterX = newCenterX;
+ currentCenterY = newCenterY;
+ numSteps++;
+ }
+ assertWithMessage("Still not at edge after panning right " + numSteps
+ + " steps. Current position: " + currentCenterX + "," + currentCenterY)
+ .that(numSteps).isLessThan(maxNumSteps);
+ }
+
+ @Test
+ public void panMagnificationByStep_fullscreenMode_reachesBottomEdgeOfScreen()
+ throws RemoteException {
+ setMagnificationEnabled(MODE_FULLSCREEN);
+ // At scale 2.0f, each step should be about 40.
+ mMagnificationController.onPerformScaleAction(TEST_DISPLAY, DEFAULT_SCALE, false);
+ reset(mScreenMagnificationController);
+
+ float currentCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+ float currentCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+
+ DisplayMetrics metrics = new DisplayMetrics();
+ mDisplay.getMetrics(metrics);
+ float expectedStep = 40.0f * metrics.density;
+
+ // Move down, eventually we should reach the edge.
+ int maxNumSteps = (int) (metrics.heightPixels / expectedStep) + 1;
+ int numSteps = 0;
+ while (numSteps < maxNumSteps) {
+ mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.PAN_DIRECTION_DOWN);
+ float newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+ float newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+ assertThat(currentCenterX).isEqualTo(newCenterX);
+
+ assertThat(newCenterY).isAtLeast(currentCenterY);
+ if (newCenterY == currentCenterY) {
+ break;
+ }
+
+ currentCenterX = newCenterX;
+ currentCenterY = newCenterY;
+ numSteps++;
+ }
+ assertWithMessage("Still not at edge after panning down "
+ + numSteps + " steps. Current position: " + currentCenterX + "," + currentCenterY)
+ .that(numSteps).isLessThan(maxNumSteps);
+ }
+
+ @Test
public void enableWindowMode_notifyMagnificationChanged() throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
@@ -1425,6 +1565,91 @@
return captor.getValue();
}
+ private void testFullscreenMagnificationPanWithStepSize(float expectedStep) {
+ DisplayMetrics metrics = new DisplayMetrics();
+ mDisplay.getMetrics(metrics);
+ expectedStep *= metrics.density;
+
+ float currentCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+ float currentCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+
+ // Move right.
+ mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.PAN_DIRECTION_RIGHT);
+ float newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+ float newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+ expect.that(currentCenterX).isLessThan(newCenterX);
+ expect.that(newCenterX - currentCenterX).isWithin(0.01f).of(expectedStep);
+ expect.that(currentCenterY).isEqualTo(newCenterY);
+
+ currentCenterX = newCenterX;
+ currentCenterY = newCenterY;
+
+ // Move left.
+ mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.PAN_DIRECTION_LEFT);
+ newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+ newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+ expect.that(currentCenterX).isGreaterThan(newCenterX);
+ expect.that(currentCenterX - newCenterX).isWithin(0.01f).of(expectedStep);
+ expect.that(currentCenterY).isEqualTo(newCenterY);
+
+ currentCenterX = newCenterX;
+ currentCenterY = newCenterY;
+
+ // Move down.
+ mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.PAN_DIRECTION_DOWN);
+ newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+ newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+ expect.that(currentCenterX).isEqualTo(newCenterX);
+ expect.that(currentCenterY).isLessThan(newCenterY);
+ expect.that(newCenterY - currentCenterY).isWithin(0.1f).of(expectedStep);
+
+ currentCenterX = newCenterX;
+ currentCenterY = newCenterY;
+
+ // Move up.
+ mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.PAN_DIRECTION_UP);
+ newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+ newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+ expect.that(currentCenterX).isEqualTo(newCenterX);
+ expect.that(currentCenterY).isGreaterThan(newCenterY);
+ expect.that(currentCenterY - newCenterY).isWithin(0.01f).of(expectedStep);
+ }
+
+ private void testWindowMagnificationPanWithStepSize(float expectedStepDip)
+ throws RemoteException {
+ DisplayMetrics metrics = new DisplayMetrics();
+ mDisplay.getMetrics(metrics);
+ final float expectedStep = expectedStepDip * metrics.density;
+
+ // Move right.
+ mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.PAN_DIRECTION_RIGHT);
+ verify(mMockConnection.getConnection()).moveWindowMagnifier(eq(TEST_DISPLAY),
+ floatThat(step -> Math.abs(step - expectedStep) < 0.0001), eq(0.0f));
+
+ // Move left.
+ mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.PAN_DIRECTION_LEFT);
+ verify(mMockConnection.getConnection()).moveWindowMagnifier(eq(TEST_DISPLAY),
+ floatThat(step -> Math.abs(expectedStep - step) < 0.0001), eq(0.0f));
+
+ // Move down.
+ mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.PAN_DIRECTION_DOWN);
+ verify(mMockConnection.getConnection()).moveWindowMagnifier(eq(TEST_DISPLAY),
+ eq(0.0f), floatThat(step -> Math.abs(expectedStep - step) < 0.0001));
+
+ // Move up.
+ mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.PAN_DIRECTION_UP);
+ verify(mMockConnection.getConnection()).moveWindowMagnifier(eq(TEST_DISPLAY),
+ eq(0.0f), floatThat(step -> Math.abs(expectedStep - step) < 0.0001));
+ }
+
private static class WindowMagnificationMgrCallbackDelegate implements
MagnificationConnectionManager.Callback {
private MagnificationConnectionManager.Callback mCallback;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 80e86a1..fbd53f7 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -3169,7 +3169,7 @@
doThrow(new SecurityException("no access")).when(mUgmInternal)
.checkGrantUriPermission(eq(UID_N_MR1), any(), eq(sound),
- anyInt(), eq(Process.myUserHandle().getIdentifier()));
+ anyInt(), eq(UserHandle.getUserId(UID_N_MR1)));
final NotificationChannel channel = new NotificationChannel("id2", "name2",
NotificationManager.IMPORTANCE_DEFAULT);
@@ -3189,7 +3189,7 @@
doThrow(new SecurityException("no access")).when(mUgmInternal)
.checkGrantUriPermission(eq(UID_N_MR1), any(), any(),
- anyInt(), eq(Process.myUserHandle().getIdentifier()));
+ anyInt(), eq(UserHandle.getUserId(UID_N_MR1)));
final NotificationChannel channel = new NotificationChannel("id2", "name2",
NotificationManager.IMPORTANCE_DEFAULT);
@@ -3208,7 +3208,7 @@
doThrow(new SecurityException("no access")).when(mUgmInternal)
.checkGrantUriPermission(eq(UID_N_MR1), any(), any(),
- anyInt(), eq(Process.myUserHandle().getIdentifier()));
+ anyInt(), eq(UserHandle.getUserId(UID_N_MR1)));
final NotificationChannel channel = new NotificationChannel("id2", "name2",
NotificationManager.IMPORTANCE_DEFAULT);
diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
index fafb0e0..4959cb3 100644
--- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
@@ -782,6 +782,54 @@
KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
),
+ TestData(
+ "META + ALT + 'Down' -> Magnification Pan Down",
+ intArrayOf(
+ KeyEvent.KEYCODE_CTRL_LEFT,
+ KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_DPAD_DOWN
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_DOWN,
+ intArrayOf(KeyEvent.KEYCODE_DPAD_DOWN),
+ KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "META + ALT + 'Up' -> Magnification Pan Up",
+ intArrayOf(
+ KeyEvent.KEYCODE_CTRL_LEFT,
+ KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_DPAD_UP
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_UP,
+ intArrayOf(KeyEvent.KEYCODE_DPAD_UP),
+ KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "META + ALT + 'Left' -> Magnification Pan Left",
+ intArrayOf(
+ KeyEvent.KEYCODE_CTRL_LEFT,
+ KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_DPAD_LEFT
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_LEFT,
+ intArrayOf(KeyEvent.KEYCODE_DPAD_LEFT),
+ KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "META + ALT + 'Right' -> Magnification Pan Right",
+ intArrayOf(
+ KeyEvent.KEYCODE_CTRL_LEFT,
+ KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_DPAD_RIGHT
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_RIGHT,
+ intArrayOf(KeyEvent.KEYCODE_DPAD_RIGHT),
+ KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
)
}