Merge changes I9ee8bf3a,If924197c into main
* changes:
Add shell/freeformat to ktfmt directories
Apply ktfmt on shell/freeform/
diff --git a/core/java/android/app/appfunctions/AppFunctionManager.java b/core/java/android/app/appfunctions/AppFunctionManager.java
index 5b478d09..8944bb9 100644
--- a/core/java/android/app/appfunctions/AppFunctionManager.java
+++ b/core/java/android/app/appfunctions/AppFunctionManager.java
@@ -47,10 +47,10 @@
* <p>An app function is a piece of functionality that apps expose to the system for cross-app
* orchestration.
*
- * <p>**Developer Workflow:**
+ * <p>**Building App Functions:**
*
- * <p>Most developers should interact with app functions through the AppFunctions SDK. This SDK
- * library offers a more convenient and type-safe way to represent the inputs and outputs of an app
+ * <p>Most developers should build app functions through the AppFunctions SDK. This SDK library
+ * offers a more convenient and type-safe way to represent the inputs and outputs of an app
* function, using custom data classes called "AppFunction Schemas".
*
* <p>The suggested way to build an app function is to use the AppFunctions SDK. The SDK provides
diff --git a/core/java/android/content/pm/parsing/ApkLite.java b/core/java/android/content/pm/parsing/ApkLite.java
index 4220590..c83cf96 100644
--- a/core/java/android/content/pm/parsing/ApkLite.java
+++ b/core/java/android/content/pm/parsing/ApkLite.java
@@ -156,6 +156,12 @@
*/
private final @Nullable String[][] mUsesSdkLibrariesCertDigests;
+ private final @NonNull List<String> mUsesStaticLibraries;
+
+ private final @Nullable long[] mUsesStaticLibrariesVersions;
+
+ private final @Nullable String[][] mUsesStaticLibrariesCertDigests;
+
/**
* Indicates if this system app can be updated.
*/
@@ -185,7 +191,10 @@
Set<String> requiredSplitTypes, Set<String> splitTypes,
boolean hasDeviceAdminReceiver, boolean isSdkLibrary,
List<String> usesSdkLibraries, long[] usesSdkLibrariesVersionsMajor,
- String[][] usesSdkLibrariesCertDigests, boolean updatableSystem,
+ String[][] usesSdkLibrariesCertDigests,
+ List<String> usesStaticLibraries, long[] usesStaticLibrariesVersionsMajor,
+ String[][] usesStaticLibrariesCertDigests,
+ boolean updatableSystem,
String emergencyInstaller, List<SharedLibraryInfo> declaredLibraries) {
mPath = path;
mPackageName = packageName;
@@ -223,6 +232,9 @@
mUsesSdkLibraries = usesSdkLibraries;
mUsesSdkLibrariesVersionsMajor = usesSdkLibrariesVersionsMajor;
mUsesSdkLibrariesCertDigests = usesSdkLibrariesCertDigests;
+ mUsesStaticLibraries = usesStaticLibraries;
+ mUsesStaticLibrariesVersions = usesStaticLibrariesVersionsMajor;
+ mUsesStaticLibrariesCertDigests = usesStaticLibrariesCertDigests;
mUpdatableSystem = updatableSystem;
mEmergencyInstaller = emergencyInstaller;
mArchivedPackage = null;
@@ -266,6 +278,9 @@
mUsesSdkLibraries = Collections.emptyList();
mUsesSdkLibrariesVersionsMajor = null;
mUsesSdkLibrariesCertDigests = null;
+ mUsesStaticLibraries = Collections.emptyList();
+ mUsesStaticLibrariesVersions = null;
+ mUsesStaticLibrariesCertDigests = null;
mUpdatableSystem = true;
mEmergencyInstaller = null;
mArchivedPackage = archivedPackage;
@@ -602,6 +617,21 @@
return mUsesSdkLibrariesCertDigests;
}
+ @DataClass.Generated.Member
+ public @NonNull List<String> getUsesStaticLibraries() {
+ return mUsesStaticLibraries;
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable long[] getUsesStaticLibrariesVersions() {
+ return mUsesStaticLibrariesVersions;
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable String[][] getUsesStaticLibrariesCertDigests() {
+ return mUsesStaticLibrariesCertDigests;
+ }
+
/**
* Indicates if this system app can be updated.
*/
@@ -632,10 +662,10 @@
}
@DataClass.Generated(
- time = 1729247366948L,
+ time = 1730202160705L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/content/pm/parsing/ApkLite.java",
- inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mRevisionCode\nprivate final int mInstallLocation\nprivate final int mMinSdkVersion\nprivate final int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final boolean mFeatureSplit\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mProfileableByShell\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final boolean mOverlayIsStatic\nprivate final int mOverlayPriority\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyName\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyValue\nprivate final int mRollbackDataPolicy\nprivate final boolean mHasDeviceAdminReceiver\nprivate final boolean mIsSdkLibrary\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mUsesSdkLibraries\nprivate final @android.annotation.Nullable long[] mUsesSdkLibrariesVersionsMajor\nprivate final @android.annotation.Nullable java.lang.String[][] mUsesSdkLibrariesCertDigests\nprivate final boolean mUpdatableSystem\nprivate final @android.annotation.Nullable java.lang.String mEmergencyInstaller\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mDeclaredLibraries\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
+ inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mRevisionCode\nprivate final int mInstallLocation\nprivate final int mMinSdkVersion\nprivate final int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final boolean mFeatureSplit\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mProfileableByShell\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final boolean mOverlayIsStatic\nprivate final int mOverlayPriority\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyName\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyValue\nprivate final int mRollbackDataPolicy\nprivate final boolean mHasDeviceAdminReceiver\nprivate final boolean mIsSdkLibrary\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mUsesSdkLibraries\nprivate final @android.annotation.Nullable long[] mUsesSdkLibrariesVersionsMajor\nprivate final @android.annotation.Nullable java.lang.String[][] mUsesSdkLibrariesCertDigests\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mUsesStaticLibraries\nprivate final @android.annotation.Nullable long[] mUsesStaticLibrariesVersions\nprivate final @android.annotation.Nullable java.lang.String[][] mUsesStaticLibrariesCertDigests\nprivate final boolean mUpdatableSystem\nprivate final @android.annotation.Nullable java.lang.String mEmergencyInstaller\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mDeclaredLibraries\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 50d8758..34c3f57 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -91,6 +91,7 @@
private static final String TAG_USES_SPLIT = "uses-split";
private static final String TAG_MANIFEST = "manifest";
private static final String TAG_USES_SDK_LIBRARY = "uses-sdk-library";
+ private static final String TAG_USES_STATIC_LIBRARY = "uses-static-library";
private static final String TAG_SDK_LIBRARY = "sdk-library";
private static final int SDK_VERSION = Build.VERSION.SDK_INT;
private static final String[] SDK_CODENAMES = Build.VERSION.ACTIVE_CODENAMES;
@@ -466,6 +467,11 @@
List<String> usesSdkLibraries = new ArrayList<>();
long[] usesSdkLibrariesVersionsMajor = new long[0];
String[][] usesSdkLibrariesCertDigests = new String[0][0];
+
+ List<String> usesStaticLibraries = new ArrayList<>();
+ long[] usesStaticLibrariesVersions = new long[0];
+ String[][] usesStaticLibrariesCertDigests = new String[0][0];
+
List<SharedLibraryInfo> declaredLibraries = new ArrayList<>();
// Only search the tree when the tag is the direct child of <manifest> tag
@@ -580,6 +586,51 @@
usesSdkLibrariesCertDigests, new String[]{usesSdkCertDigest},
/*allowDuplicates=*/ true);
break;
+ case TAG_USES_STATIC_LIBRARY:
+ String usesStaticLibName = parser.getAttributeValue(
+ ANDROID_RES_NAMESPACE, "name");
+ long usesStaticLibVersion = parser.getAttributeIntValue(
+ ANDROID_RES_NAMESPACE, "version", -1);
+ String usesStaticLibCertDigest = parser.getAttributeValue(
+ ANDROID_RES_NAMESPACE, "certDigest");
+
+ if (usesStaticLibName == null || usesStaticLibName.isBlank()
+ || usesStaticLibVersion < 0
+ || usesStaticLibCertDigest == null) {
+ return input.error(
+ PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+ "Bad uses-static-library declaration name: "
+ + usesStaticLibName
+ + " version: " + usesStaticLibVersion
+ + " certDigest: " + usesStaticLibCertDigest);
+ }
+
+ if (usesStaticLibraries.contains(usesStaticLibName)) {
+ return input.error(
+ PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+ "Bad uses-sdk-library declaration. Depending on"
+ + " multiple versions of static library: "
+ + usesStaticLibName);
+ }
+
+ usesStaticLibraries.add(usesStaticLibName);
+ usesStaticLibrariesVersions = ArrayUtils.appendLong(
+ usesStaticLibrariesVersions, usesStaticLibVersion,
+ /*allowDuplicates=*/ true);
+
+ // We allow ":" delimiters in the SHA declaration as this is the format
+ // emitted by the certtool making it easy for developers to copy/paste.
+ // TODO(372862145): Add test for this replacement
+ usesStaticLibCertDigest =
+ usesStaticLibCertDigest.replace(":", "").toLowerCase();
+
+ // TODO(372862145): Add support for multiple signer for app targeting
+ // O-MR1
+ usesStaticLibrariesCertDigests = ArrayUtils.appendElement(
+ String[].class, usesStaticLibrariesCertDigests,
+ new String[]{usesStaticLibCertDigest},
+ /*allowDuplicates=*/ true);
+ break;
case TAG_SDK_LIBRARY:
isSdkLibrary = true;
// Mirrors ParsingPackageUtils#parseSdkLibrary until lite and full
@@ -753,7 +804,9 @@
rollbackDataPolicy, requiredSplitTypes.first, requiredSplitTypes.second,
hasDeviceAdminReceiver, isSdkLibrary, usesSdkLibraries,
usesSdkLibrariesVersionsMajor, usesSdkLibrariesCertDigests,
- updatableSystem, emergencyInstaller, declaredLibraries));
+ usesStaticLibraries, usesStaticLibrariesVersions,
+ usesStaticLibrariesCertDigests, updatableSystem, emergencyInstaller,
+ declaredLibraries));
}
private static boolean isDeviceAdminReceiver(
diff --git a/core/java/android/content/pm/parsing/PackageLite.java b/core/java/android/content/pm/parsing/PackageLite.java
index 79c5973..76b25fe 100644
--- a/core/java/android/content/pm/parsing/PackageLite.java
+++ b/core/java/android/content/pm/parsing/PackageLite.java
@@ -121,6 +121,12 @@
private final @Nullable String[][] mUsesSdkLibrariesCertDigests;
+ private final @NonNull List<String> mUsesStaticLibraries;
+
+ private final @Nullable long[] mUsesStaticLibrariesVersions;
+
+ private final @Nullable String[][] mUsesStaticLibrariesCertDigests;
+
private final @NonNull List<SharedLibraryInfo> mDeclaredLibraries;
/**
@@ -158,6 +164,9 @@
mUsesSdkLibraries = baseApk.getUsesSdkLibraries();
mUsesSdkLibrariesVersionsMajor = baseApk.getUsesSdkLibrariesVersionsMajor();
mUsesSdkLibrariesCertDigests = baseApk.getUsesSdkLibrariesCertDigests();
+ mUsesStaticLibraries = baseApk.getUsesStaticLibraries();
+ mUsesStaticLibrariesVersions = baseApk.getUsesStaticLibrariesVersions();
+ mUsesStaticLibrariesCertDigests = baseApk.getUsesStaticLibrariesCertDigests();
mSplitNames = splitNames;
mSplitTypes = splitTypes;
mIsFeatureSplits = isFeatureSplits;
@@ -462,6 +471,21 @@
}
@DataClass.Generated.Member
+ public @NonNull List<String> getUsesStaticLibraries() {
+ return mUsesStaticLibraries;
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable long[] getUsesStaticLibrariesVersions() {
+ return mUsesStaticLibrariesVersions;
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable String[][] getUsesStaticLibrariesCertDigests() {
+ return mUsesStaticLibrariesCertDigests;
+ }
+
+ @DataClass.Generated.Member
public @NonNull List<SharedLibraryInfo> getDeclaredLibraries() {
return mDeclaredLibraries;
}
@@ -475,10 +499,10 @@
}
@DataClass.Generated(
- time = 1729248757933L,
+ time = 1730203707341L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/content/pm/parsing/PackageLite.java",
- inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.NonNull java.lang.String mBaseApkPath\nprivate final @android.annotation.Nullable java.lang.String[] mSplitApkPaths\nprivate final @android.annotation.Nullable java.lang.String[] mSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mUsesSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mBaseRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mTargetSdk\nprivate final int mBaseRevisionCode\nprivate final @android.annotation.Nullable int[] mSplitRevisionCodes\nprivate final int mInstallLocation\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final @android.annotation.Nullable boolean[] mIsFeatureSplits\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mProfileableByShell\nprivate final boolean mUseEmbeddedDex\nprivate final boolean mIsSdkLibrary\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mUsesSdkLibraries\nprivate final @android.annotation.Nullable long[] mUsesSdkLibrariesVersionsMajor\nprivate final @android.annotation.Nullable java.lang.String[][] mUsesSdkLibrariesCertDigests\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mDeclaredLibraries\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic java.util.List<java.lang.String> getAllApkPaths()\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass PackageLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
+ inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.NonNull java.lang.String mBaseApkPath\nprivate final @android.annotation.Nullable java.lang.String[] mSplitApkPaths\nprivate final @android.annotation.Nullable java.lang.String[] mSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mUsesSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mBaseRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mTargetSdk\nprivate final int mBaseRevisionCode\nprivate final @android.annotation.Nullable int[] mSplitRevisionCodes\nprivate final int mInstallLocation\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final @android.annotation.Nullable boolean[] mIsFeatureSplits\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mProfileableByShell\nprivate final boolean mUseEmbeddedDex\nprivate final boolean mIsSdkLibrary\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mUsesSdkLibraries\nprivate final @android.annotation.Nullable long[] mUsesSdkLibrariesVersionsMajor\nprivate final @android.annotation.Nullable java.lang.String[][] mUsesSdkLibrariesCertDigests\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mUsesStaticLibraries\nprivate final @android.annotation.Nullable long[] mUsesStaticLibrariesVersions\nprivate final @android.annotation.Nullable java.lang.String[][] mUsesStaticLibrariesCertDigests\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mDeclaredLibraries\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic java.util.List<java.lang.String> getAllApkPaths()\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass PackageLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/hardware/input/KeyGlyphMap.java b/core/java/android/hardware/input/KeyGlyphMap.java
index b517a63..6a16502 100644
--- a/core/java/android/hardware/input/KeyGlyphMap.java
+++ b/core/java/android/hardware/input/KeyGlyphMap.java
@@ -158,6 +158,15 @@
return getDrawable(context, mModifierGlyphs.get(modifier, 0));
}
+ /**
+ * Provides the drawable resource for the glyph for a modifier state (e.g. META_META_ON).
+ * Returns null if not available.
+ */
+ @Nullable
+ public Drawable getDrawableForModifierState(Context context, int modifierState) {
+ return getDrawable(context, mModifierGlyphs.get(modifierState, 0));
+ }
+
@Nullable
private Drawable getDrawable(Context context, @DrawableRes int drawableRes) {
PackageManager pm = context.getPackageManager();
diff --git a/core/java/com/android/internal/widget/NotificationProgressDrawable.java b/core/java/com/android/internal/widget/NotificationProgressDrawable.java
index 2be7273..30deb49 100644
--- a/core/java/com/android/internal/widget/NotificationProgressDrawable.java
+++ b/core/java/com/android/internal/widget/NotificationProgressDrawable.java
@@ -190,12 +190,12 @@
final float segWidth = segment.mFraction * totalWidth;
// Advance the start position to account for a point immediately prior.
final float startOffset = getSegStartOffset(prevPart, pointRadius,
- mState.mSegPointGap);
+ mState.mSegPointGap, x);
final float start = x + startOffset;
// Retract the end position to account for the padding and a point immediately
// after.
final float endOffset = getSegEndOffset(nextPart, pointRadius, mState.mSegPointGap,
- mState.mSegSegGap);
+ mState.mSegSegGap, x + segWidth, totalWidth);
final float end = x + segWidth - endOffset;
// Transparent is not allowed (and also is the default in the data), so use that
@@ -215,8 +215,17 @@
// width (ignoring offset and padding)
x += segWidth;
} else if (part instanceof Point point) {
- mPointRect.set((int) (x - pointRadius), (int) (centerY - pointRadius),
- (int) (x + pointRadius), (int) (centerY + pointRadius));
+ final float pointWidth = 2 * pointRadius;
+ float start = x - pointRadius;
+ if (start < 0) start = 0;
+ float end = start + pointWidth;
+ if (end > totalWidth) {
+ end = totalWidth;
+ if (totalWidth > pointWidth) start = totalWidth - pointWidth;
+ }
+ mPointRect.set((int) start, (int) (centerY - pointRadius), (int) end,
+ (int) (centerY + pointRadius));
+
if (point.mIcon != null) {
point.mIcon.setBounds(mPointRect);
point.mIcon.draw(canvas);
@@ -238,14 +247,22 @@
}
}
- private static float getSegStartOffset(Part prevPart, float pointRadius, float segPointGap) {
- return (prevPart instanceof Point) ? pointRadius + segPointGap : 0F;
+ private static float getSegStartOffset(Part prevPart, float pointRadius, float segPointGap,
+ float startX) {
+ if (!(prevPart instanceof Point)) return 0F;
+ final float pointOffset = (startX < pointRadius) ? (pointRadius - startX) : 0;
+ return pointOffset + pointRadius + segPointGap;
}
private static float getSegEndOffset(Part nextPart, float pointRadius, float segPointGap,
- float segSegGap) {
+ float segSegGap, float endX, float totalWidth) {
if (nextPart == null) return 0F;
- return (nextPart instanceof Point) ? segPointGap + pointRadius : segSegGap;
+ if (!(nextPart instanceof Point)) return segSegGap;
+
+ final float pointWidth = 2 * pointRadius;
+ final float pointOffset = (endX + pointRadius > totalWidth && totalWidth > pointWidth)
+ ? (endX + pointRadius - totalWidth) : 0;
+ return segPointGap + pointRadius + pointOffset;
}
@Override
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 54262e8..a382d79 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -150,6 +150,7 @@
":HelloWorldUsingSdk1AndSdk1",
":HelloWorldUsingSdk1And2",
":HelloWorldUsingSdkMalformedNegativeVersion",
+ ":CtsStaticSharedLibConsumerApp1",
],
}
diff --git a/core/tests/coretests/AndroidTest.xml b/core/tests/coretests/AndroidTest.xml
index 3bc8172..3f7c83a 100644
--- a/core/tests/coretests/AndroidTest.xml
+++ b/core/tests/coretests/AndroidTest.xml
@@ -39,6 +39,8 @@
value="/data/local/tmp/tests/coretests/pm/HelloWorldUsingSdkMalformedNegativeVersion.apk"/>
<option name="push-file" key="HelloWorldSdk1.apk"
value="/data/local/tmp/tests/coretests/pm/HelloWorldSdk1.apk"/>
+ <option name="push-file" key="CtsStaticSharedLibConsumerApp1.apk"
+ value="/data/local/tmp/tests/coretests/pm/CtsStaticSharedLibConsumerApp1.apk"/>
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
diff --git a/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java b/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java
index f9b481f..d4618d7 100644
--- a/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java
+++ b/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java
@@ -71,6 +71,7 @@
private static final String TEST_APP_USING_SDK1_AND_SDK1 = "HelloWorldUsingSdk1AndSdk1.apk";
private static final String TEST_APP_USING_SDK_MALFORMED_VERSION =
"HelloWorldUsingSdkMalformedNegativeVersion.apk";
+ private static final String TEST_APP_USING_STATIC_LIB = "CtsStaticSharedLibConsumerApp1.apk";
private static final String TEST_SDK1 = "HelloWorldSdk1.apk";
private static final String TEST_SDK1_PACKAGE = "com.test.sdk1_1";
private static final String TEST_SDK1_NAME = "com.test.sdk1";
@@ -166,6 +167,30 @@
@SuppressLint("CheckResult")
@Test
+ public void testParseApkLite_getUsesStaticLibrary_sameAsPackageParser() throws Exception {
+ File apkFile = copyApkToTmpDir(TEST_APP_USING_STATIC_LIB);
+ ParseResult<ApkLite> result = ApkLiteParseUtils
+ .parseApkLite(ParseTypeImpl.forDefaultParsing().reset(), apkFile, 0);
+ assertThat(result.isError()).isFalse();
+ ApkLite baseApk = result.getResult();
+
+ AndroidPackage pkg = mPackageParser2.parsePackage(apkFile, 0, true).hideAsFinal();
+ assertThat(baseApk.getUsesStaticLibraries())
+ .containsExactlyElementsIn(pkg.getUsesStaticLibraries());
+ List<Long> versionsBoxed = Arrays.stream(pkg.getUsesStaticLibrariesVersions()).boxed()
+ .toList();
+ assertThat(baseApk.getUsesStaticLibrariesVersions()).asList()
+ .containsExactlyElementsIn(versionsBoxed);
+
+ String[][] liteCerts = baseApk.getUsesStaticLibrariesCertDigests();
+ String[][] pkgCerts = pkg.getUsesStaticLibrariesCertDigests();
+ for (int i = 0; i < liteCerts.length; i++) {
+ assertThat(liteCerts[i]).isEqualTo(pkgCerts[i]);
+ }
+ }
+
+ @SuppressLint("CheckResult")
+ @Test
public void testParseApkLite_malformedUsesSdkLibrary_duplicate() throws Exception {
File apkFile = copyApkToTmpDir(TEST_APP_USING_SDK1_AND_SDK1);
ParseResult<ApkLite> result = ApkLiteParseUtils
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
index b4e2a4a..5ce73b9 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
@@ -169,21 +169,7 @@
|| containsConsumer(consumer)) {
return;
}
- final IllegalArgumentException exception = new IllegalArgumentException(
- "Context must be a UI Context with display association, which should be"
- + " an Activity, WindowContext or InputMethodService");
- if (!context.isUiContext()) {
- throw exception;
- }
- if (context.getAssociatedDisplayId() == INVALID_DISPLAY) {
- // This is to identify if #isUiContext of a non-UI Context is overridden.
- // #isUiContext is more likely to be overridden than #getAssociatedDisplayId
- // since #isUiContext is a public API.
- StrictMode.onIncorrectContextUsed("The registered Context is a UI Context "
- + "but not associated with any display. "
- + "This Context may not receive any WindowLayoutInfo update. "
- + dumpAllBaseContextToString(context), exception);
- }
+ assertUiContext(context);
Log.d(TAG, "Register WindowLayoutInfoListener on "
+ dumpAllBaseContextToString(context));
mFoldingFeatureProducer.getData((features) -> {
@@ -363,6 +349,7 @@
@Override
@NonNull
public WindowLayoutInfo getCurrentWindowLayoutInfo(@NonNull @UiContext Context context) {
+ assertUiContext(context);
synchronized (mLock) {
return getWindowLayoutInfo(context, mLastReportedFoldingFeatures);
}
@@ -377,6 +364,25 @@
return mSupportedWindowFeatures;
}
+ private void assertUiContext(@NonNull Context context) {
+ final IllegalArgumentException exception = new IllegalArgumentException(
+ "Context must be a UI Context with display association, which should be "
+ + "an Activity, WindowContext or InputMethodService");
+ if (!context.isUiContext()) {
+ throw exception;
+ }
+ if (context.getAssociatedDisplayId() == INVALID_DISPLAY) {
+ // This is to identify if #isUiContext of a non-UI Context is overridden.
+ // #isUiContext is more likely to be overridden than #getAssociatedDisplayId
+ // since #isUiContext is a public API.
+ StrictMode.onIncorrectContextUsed("The given context is a UI context, "
+ + "but it is not associated with any display. "
+ + "This context may not receive WindowLayoutInfo updates and "
+ + "may get an empty WindowLayoutInfo return value. "
+ + dumpAllBaseContextToString(context), exception);
+ }
+ }
+
/** @see #getWindowLayoutInfo(Context, List) */
private WindowLayoutInfo getWindowLayoutInfo(int displayId,
@NonNull WindowConfiguration windowConfiguration,
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/layout/WindowLayoutComponentImplTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/layout/WindowLayoutComponentImplTest.java
index 4c75f2e..0643feb 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/layout/WindowLayoutComponentImplTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/layout/WindowLayoutComponentImplTest.java
@@ -74,6 +74,11 @@
mWindowLayoutComponent.onDisplayFeaturesChanged(Collections.emptyList());
}
+ @Test(expected = IllegalArgumentException.class)
+ public void testAddWindowLayoutListener_nonUiContext_throwsError() {
+ mWindowLayoutComponent.addWindowLayoutInfoListener(mAppContext, info -> {});
+ }
+
@Test
public void testGetCurrentWindowLayoutInfo_noFoldingFeature_returnsEmptyList() {
final Context testUiContext = new TestUiContext(mAppContext);
@@ -127,12 +132,9 @@
featureRect, FoldingFeature.TYPE_HINGE, FoldingFeature.STATE_FLAT));
}
- @Test
- public void testGetCurrentWindowLayoutInfo_nonUiContext_returnsEmptyList() {
- final WindowLayoutInfo layoutInfo =
- mWindowLayoutComponent.getCurrentWindowLayoutInfo(mAppContext);
-
- assertThat(layoutInfo.getDisplayFeatures()).isEmpty();
+ @Test(expected = IllegalArgumentException.class)
+ public void testGetCurrentWindowLayoutInfo_nonUiContext_throwsError() {
+ mWindowLayoutComponent.getCurrentWindowLayoutInfo(mAppContext);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index e53e134..ec58292 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -561,9 +561,9 @@
final TransitionInfo.AnimationOptions options;
if (Flags.moveAnimationOptionsToChange()) {
- options = info.getAnimationOptions();
- } else {
options = change.getAnimationOptions();
+ } else {
+ options = info.getAnimationOptions();
}
if (options != null) {
attachThumbnail(animations, onAnimFinish, change, options, cornerRadius);
diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java
index 43377d8..6870446 100644
--- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java
+++ b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java
@@ -16,7 +16,6 @@
package com.google.android.appfunctions.sidecar;
-import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -41,8 +40,6 @@
* <p>This class wraps {@link android.app.appfunctions.AppFunctionManager} functionalities and
* exposes it here as a sidecar library (avoiding direct dependency on the platform API).
*/
-// TODO(b/357551503): Implement get and set enabled app function APIs.
-// TODO(b/367329899): Add sidecar library to Android B builds.
public final class AppFunctionManager {
/**
* The default state of the app function. Call {@link #setAppFunctionEnabled} with this to reset
@@ -70,9 +67,9 @@
@IntDef(
prefix = {"APP_FUNCTION_STATE_"},
value = {
- APP_FUNCTION_STATE_DEFAULT,
- APP_FUNCTION_STATE_ENABLED,
- APP_FUNCTION_STATE_DISABLED
+ APP_FUNCTION_STATE_DEFAULT,
+ APP_FUNCTION_STATE_ENABLED,
+ APP_FUNCTION_STATE_DISABLED
})
@Retention(RetentionPolicy.SOURCE)
public @interface EnabledState {}
@@ -102,6 +99,9 @@
* <p>Proxies request and response to the underlying {@link
* android.app.appfunctions.AppFunctionManager#executeAppFunction}, converting the request and
* response in the appropriate type required by the function.
+ *
+ * <p>See {@link android.app.appfunctions.AppFunctionManager#executeAppFunction} for the
+ * documented behaviour of this method.
*/
public void executeAppFunction(
@NonNull ExecuteAppFunctionRequest sidecarRequest,
@@ -128,24 +128,8 @@
/**
* Returns a boolean through a callback, indicating whether the app function is enabled.
*
- * <p>* This method can only check app functions owned by the caller, or those where the caller
- * has visibility to the owner package and holds either the {@link
- * Manifest.permission#EXECUTE_APP_FUNCTIONS} or {@link
- * Manifest.permission#EXECUTE_APP_FUNCTIONS_TRUSTED} permission.
- *
- * <p>If operation fails, the callback's {@link OutcomeReceiver#onError} is called with errors:
- *
- * <ul>
- * <li>{@link IllegalArgumentException}, if the function is not found or the caller does not
- * have access to it.
- * </ul>
- *
- * @param functionIdentifier the identifier of the app function to check (unique within the
- * target package) and in most cases, these are automatically generated by the AppFunctions
- * SDK
- * @param targetPackage the package name of the app function's owner
- * @param executor the executor to run the request
- * @param callback the callback to receive the function enabled check result
+ * <p>See {@link android.app.appfunctions.AppFunctionManager#isAppFunctionEnabled} for the
+ * documented behaviour of this method.
*/
public void isAppFunctionEnabled(
@NonNull String functionIdentifier,
@@ -158,20 +142,8 @@
/**
* Sets the enabled state of the app function owned by the calling package.
*
- * <p>If operation fails, the callback's {@link OutcomeReceiver#onError} is called with errors:
- *
- * <ul>
- * <li>{@link IllegalArgumentException}, if the function is not found or the caller does not
- * have access to it.
- * </ul>
- *
- * @param functionIdentifier the identifier of the app function to enable (unique within the
- * calling package). In most cases, identifiers are automatically generated by the
- * AppFunctions SDK
- * @param newEnabledState the new state of the app function
- * @param executor the executor to run the callback
- * @param callback the callback to receive the result of the function enablement. The call was
- * successful if no exception was thrown.
+ * <p>See {@link android.app.appfunctions.AppFunctionManager#setAppFunctionEnabled} for the
+ * documented behavoir of this method.
*/
// Constants in @EnabledState should always mirror those in
// android.app.appfunctions.AppFunctionManager.
diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionRequest.java b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionRequest.java
index fa6d2ff..593c521 100644
--- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionRequest.java
+++ b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionRequest.java
@@ -34,8 +34,8 @@
@NonNull private final String mTargetPackageName;
/**
- * Returns the unique string identifier of the app function to be executed. TODO(b/357551503):
- * Document how callers can get the available function identifiers.
+ * The unique string identifier of the app function to be executed. This identifier is used to
+ * execute a specific app function.
*/
@NonNull private final String mFunctionIdentifier;
@@ -49,8 +49,6 @@
*
* <p>The document may have missing parameters. Developers are advised to implement defensive
* handling measures.
- *
- * <p>TODO(b/357551503): Document how function parameters can be obtained for function execution
*/
@NonNull private final GenericDocument mParameters;
@@ -71,7 +69,19 @@
return mTargetPackageName;
}
- /** Returns the unique string identifier of the app function to be executed. */
+ /**
+ * Returns the unique string identifier of the app function to be executed.
+ *
+ * <p>When there is a package change or the device starts up, the metadata of available
+ * functions is indexed by AppSearch. AppSearch stores the indexed information as {@code
+ * AppFunctionStaticMetadata} document.
+ *
+ * <p>The ID can be obtained by querying the {@code AppFunctionStaticMetadata} documents from
+ * AppSearch.
+ *
+ * <p>If the {@code functionId} provided is invalid, the caller will get an invalid argument
+ * response.
+ */
@NonNull
public String getFunctionIdentifier() {
return mFunctionIdentifier;
@@ -83,6 +93,12 @@
*
* <p>The bundle may have missing parameters. Developers are advised to implement defensive
* handling measures.
+ *
+ * <p>Similar to {@link #getFunctionIdentifier()} the parameters required by a function can be
+ * obtained by querying AppSearch for the corresponding {@code AppFunctionStaticMetadata}. This
+ * metadata will contain enough information for the caller to resolve the required parameters
+ * either using information from the metadata itself or using the AppFunction SDK for function
+ * callers.
*/
@NonNull
public GenericDocument getParameters() {
@@ -128,10 +144,7 @@
@NonNull
public ExecuteAppFunctionRequest build() {
return new ExecuteAppFunctionRequest(
- mTargetPackageName,
- mFunctionIdentifier,
- mExtras,
- mParameters);
+ mTargetPackageName, mFunctionIdentifier, mExtras, mParameters);
}
}
}
diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java
index 969e5d5..d5dfaeb 100644
--- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java
+++ b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java
@@ -56,14 +56,15 @@
/** The caller does not have the permission to execute an app function. */
public static final int RESULT_DENIED = 1;
- /** An unknown error occurred while processing the call in the AppFunctionService. */
+ /**
+ * An unknown error occurred while processing the call in the AppFunctionService.
+ *
+ * <p>This error is thrown when the service is connected in the remote application but an
+ * unexpected error is thrown from the bound application.
+ */
public static final int RESULT_APP_UNKNOWN_ERROR = 2;
- /**
- * An internal error occurred within AppFunctionManagerService.
- *
- * <p>This error may be considered similar to {@link IllegalStateException}
- */
+ /** An internal unexpected error coming from the system. */
public static final int RESULT_INTERNAL_ERROR = 3;
/**
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 8845d2e..8641f70 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -322,18 +322,27 @@
}
private void updatePreferredTransport() {
- if (mProfiles.stream().noneMatch(p -> p instanceof LeAudioProfile)
- || mProfiles.stream().noneMatch(p -> p instanceof HidProfile)) {
+ LeAudioProfile leAudioProfile =
+ (LeAudioProfile)
+ mProfiles.stream()
+ .filter(p -> p instanceof LeAudioProfile)
+ .findFirst()
+ .orElse(null);
+ HidProfile hidProfile =
+ (HidProfile)
+ mProfiles.stream()
+ .filter(p -> p instanceof HidProfile)
+ .findFirst()
+ .orElse(null);
+ if (leAudioProfile == null || hidProfile == null) {
return;
}
// Both LeAudioProfile and HidProfile are connectable.
- if (!mProfileManager
- .getHidProfile()
- .setPreferredTransport(
- mDevice,
- mProfileManager.getLeAudioProfile().isEnabled(mDevice)
- ? BluetoothDevice.TRANSPORT_LE
- : BluetoothDevice.TRANSPORT_BREDR)) {
+ if (!hidProfile.setPreferredTransport(
+ mDevice,
+ leAudioProfile.isEnabled(mDevice)
+ ? BluetoothDevice.TRANSPORT_LE
+ : BluetoothDevice.TRANSPORT_BREDR)) {
Log.w(TAG, "Fail to set preferred transport");
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 70cb2ef..30f8a79 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -1952,9 +1952,6 @@
@Test
public void leAudioHidDevice_leAudioEnabled_setPreferredTransportToLE() {
-
- when(mProfileManager.getHidProfile()).thenReturn(mHidProfile);
- when(mProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile);
when(mLeAudioProfile.isEnabled(mDevice)).thenReturn(true);
updateProfileStatus(mHidProfile, BluetoothProfile.STATE_CONNECTED);
@@ -1965,8 +1962,6 @@
@Test
public void leAudioHidDevice_leAudioDisabled_setPreferredTransportToBredr() {
- when(mProfileManager.getHidProfile()).thenReturn(mHidProfile);
- when(mProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile);
when(mLeAudioProfile.isEnabled(mDevice)).thenReturn(false);
updateProfileStatus(mLeAudioProfile, BluetoothProfile.STATE_CONNECTED);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
index beba0f0..1914867 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
@@ -172,7 +172,7 @@
mVolumeController.setDeviceInteractive(false);
when(mWakefullnessLifcycle.getWakefulness()).thenReturn(
WakefulnessLifecycle.WAKEFULNESS_AWAKE);
- mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI);
+ mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI, true);
verify(mCallback, never()).onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED, false,
LOCK_TASK_MODE_NONE);
}
@@ -182,7 +182,7 @@
mVolumeController.setDeviceInteractive(true);
when(mWakefullnessLifcycle.getWakefulness()).thenReturn(
WakefulnessLifecycle.WAKEFULNESS_AWAKE);
- mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI);
+ mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI, true);
verify(mCallback, times(1)).onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED, false,
LOCK_TASK_MODE_NONE);
}
@@ -192,11 +192,11 @@
mVolumeController.setDeviceInteractive(true);
when(mWakefullnessLifcycle.getWakefulness()).thenReturn(
WakefulnessLifecycle.WAKEFULNESS_AWAKE);
- mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI);
+ mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI, true);
mVolumeController.setDeviceInteractive(false);
when(mWakefullnessLifcycle.getWakefulness()).thenReturn(
WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP);
- mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI);
+ mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI, true);
verify(mCallback, times(1)).onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED, false,
LOCK_TASK_MODE_NONE);
}
@@ -210,7 +210,7 @@
AudioManager.DEVICE_OUT_BLE_HEADSET);
mVolumeController.onVolumeChangedW(
- AudioManager.STREAM_VOICE_CALL, AudioManager.FLAG_SHOW_UI);
+ AudioManager.STREAM_VOICE_CALL, AudioManager.FLAG_SHOW_UI, true);
verify(mCallback, times(1)).onStateChanged(any());
}
@@ -224,7 +224,7 @@
AudioManager.DEVICE_OUT_BLUETOOTH_A2DP);
mVolumeController.onVolumeChangedW(
- AudioManager.STREAM_VOICE_CALL, AudioManager.FLAG_SHOW_UI);
+ AudioManager.STREAM_VOICE_CALL, AudioManager.FLAG_SHOW_UI, true);
verify(mCallback, never()).onStateChanged(any());
}
@@ -241,14 +241,16 @@
.thenReturn(AudioManager.DEVICE_NONE);
mVolumeController.mInAudioSharing = true;
- mVolumeController.onVolumeChangedW(AudioManager.STREAM_MUSIC, AudioManager.FLAG_SHOW_UI);
+ mVolumeController.onVolumeChangedW(
+ AudioManager.STREAM_MUSIC, AudioManager.FLAG_SHOW_UI, true);
verify(mCallback).onStateChanged(stateCaptor.capture());
assertThat(stateCaptor.getValue().states.contains(AudioManager.STREAM_MUSIC)).isTrue();
assertThat(stateCaptor.getValue().states.get(AudioManager.STREAM_MUSIC).routedToBluetooth)
.isTrue();
mVolumeController.mInAudioSharing = false;
- mVolumeController.onVolumeChangedW(AudioManager.STREAM_MUSIC, AudioManager.FLAG_SHOW_UI);
+ mVolumeController.onVolumeChangedW(
+ AudioManager.STREAM_MUSIC, AudioManager.FLAG_SHOW_UI, true);
verify(mCallback, times(2)).onStateChanged(stateCaptor.capture());
assertThat(stateCaptor.getValue().states.contains(AudioManager.STREAM_MUSIC)).isTrue();
assertThat(stateCaptor.getValue().states.get(AudioManager.STREAM_MUSIC).routedToBluetooth)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/VolumeDialogControllerImplTestKt.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/VolumeDialogControllerImplTestKt.kt
index 98cea9d..76b7b8f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/VolumeDialogControllerImplTestKt.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/VolumeDialogControllerImplTestKt.kt
@@ -18,12 +18,15 @@
import android.app.activityManager
import android.app.keyguardManager
+import android.content.Intent
import android.content.applicationContext
import android.content.packageManager
+import android.content.testableContext
import android.media.AudioManager
import android.media.IVolumeController
import android.os.Handler
import android.os.looper
+import android.os.testableLooper
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
@@ -34,6 +37,8 @@
import com.android.settingslib.volume.data.model.VolumeControllerEvent
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.broadcast.broadcastDispatcherContext
import com.android.systemui.dump.dumpManager
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.wakefulnessLifecycle
@@ -81,10 +86,11 @@
audioRepository.init()
threadFactory =
FakeThreadFactory(FakeExecutor(fakeSystemClock)).apply { setLooper(looper) }
+ broadcastDispatcherContext = testableContext
underTest =
VolumeDialogControllerImpl(
applicationContext,
- mock {},
+ broadcastDispatcher,
mock {
on { ringerMode }.thenReturn(mock<RingerModeLiveData> {})
on { ringerModeInternal }.thenReturn(mock<RingerModeLiveData> {})
@@ -112,6 +118,23 @@
}
@Test
+ fun broadcastEvent_sendsChangesOnce() =
+ with(kosmos) {
+ testScope.runTest {
+ whenever(audioManager.getLastAudibleStreamVolume(any())).thenReturn(1)
+ broadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ applicationContext,
+ Intent(AudioManager.ACTION_VOLUME_CHANGED).apply {
+ putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, AudioManager.STREAM_SYSTEM)
+ },
+ )
+ testableLooper.processAllMessages()
+
+ verify(callbacks) { 1 * { onStateChanged(any()) } }
+ }
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_USE_VOLUME_CONTROLLER)
fun useVolumeControllerEnabled_listensToVolumeController() =
testVolumeController { stream: Int, flags: Int ->
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index bbd8f3dc..b705872 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -552,7 +552,7 @@
&& mShowVolumeDialog;
}
- boolean onVolumeChangedW(int stream, int flags) {
+ boolean onVolumeChangedW(int stream, int flags, boolean sendChanges) {
final boolean showUI = shouldShowUI(flags);
final boolean fromKey = (flags & AudioManager.FLAG_FROM_KEY) != 0;
final boolean showVibrateHint = (flags & AudioManager.FLAG_SHOW_VIBRATE_HINT) != 0;
@@ -564,7 +564,7 @@
int lastAudibleStreamVolume = getAudioManagerStreamVolume(stream);
changed |= updateStreamLevelW(stream, lastAudibleStreamVolume);
changed |= checkRoutedToBluetoothW(showUI ? AudioManager.STREAM_MUSIC : stream);
- if (changed) {
+ if (changed && sendChanges) {
mCallbacks.onStateChanged(mState);
}
if (showUI) {
@@ -950,7 +950,7 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case VOLUME_CHANGED: onVolumeChangedW(msg.arg1, msg.arg2); break;
+ case VOLUME_CHANGED: onVolumeChangedW(msg.arg1, msg.arg2, true); break;
case DISMISS_REQUESTED: onDismissRequestedW(msg.arg1); break;
case GET_STATE: onGetStateW(); break;
case SET_RINGER_MODE: onSetRingerModeW(msg.arg1, msg.arg2 != 0); break;
@@ -1307,7 +1307,7 @@
if (D.BUG) Log.d(TAG, "onReceive VOLUME_CHANGED_ACTION stream=" + stream
+ " oldLevel=" + oldLevel);
if (stream != STREAM_UNKNOWN) {
- changed |= onVolumeChangedW(stream, 0);
+ changed |= onVolumeChangedW(stream, 0, false);
}
} else if (action.equals(AudioManager.STREAM_DEVICES_CHANGED_ACTION)) {
final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
@@ -1320,7 +1320,7 @@
+ stream + " devices=" + devices + " oldDevices=" + oldDevices);
if (stream != STREAM_UNKNOWN) {
changed |= checkRoutedToBluetoothW(stream);
- changed |= onVolumeChangedW(stream, 0);
+ changed |= onVolumeChangedW(stream, 0, false);
}
} else if (action.equals(AudioManager.STREAM_MUTE_CHANGED_ACTION)) {
final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
diff --git a/packages/SystemUI/tests/utils/src/android/os/LooperKosmos.kt b/packages/SystemUI/tests/utils/src/android/os/LooperKosmos.kt
index 4303365..3f79c59 100644
--- a/packages/SystemUI/tests/utils/src/android/os/LooperKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/android/os/LooperKosmos.kt
@@ -21,11 +21,12 @@
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.testCase
-val Kosmos.looper by Fixture {
- val testableLooper = TestableLooper.get(testCase)
- checkNotNull(testableLooper) {
+val Kosmos.testableLooper: TestableLooper by Fixture {
+ checkNotNull(TestableLooper.get(testCase)) {
"TestableLooper is null, make sure the test class is annotated with RunWithLooper"
}
+}
+val Kosmos.looper: Looper by Fixture {
checkNotNull(testableLooper.looper) {
"TestableLooper.getLooper() is returning null, make sure the test class is annotated " +
"with RunWithLooper"
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/BroadcastDispatcherKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/BroadcastDispatcherKosmos.kt
index 7207948..b2289a1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/BroadcastDispatcherKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/BroadcastDispatcherKosmos.kt
@@ -16,19 +16,21 @@
package com.android.systemui.broadcast
+import com.android.systemui.SysuiTestableContext
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.util.mockito.mock
+var Kosmos.broadcastDispatcherContext: SysuiTestableContext by Kosmos.Fixture { mock() }
val Kosmos.broadcastDispatcher by
Kosmos.Fixture {
FakeBroadcastDispatcher(
- context = mock(),
+ context = broadcastDispatcherContext,
mainExecutor = mock(),
broadcastRunningLooper = mock(),
broadcastRunningExecutor = mock(),
dumpManager = mock(),
logger = mock(),
userTracker = mock(),
- shouldFailOnLeakedReceiver = false
+ shouldFailOnLeakedReceiver = false,
)
}
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java b/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java
index 93b0e66..30c7319 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java
@@ -115,7 +115,7 @@
/** {@see FrameworkStatsLog.BIOMETRIC_ENROLLED}. */
public void enroll(int statsModality, int statsAction, int statsClient,
int targetUserId, long latency, boolean enrollSuccessful, float ambientLightLux,
- int source) {
+ int source, int templateId) {
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ENROLLED,
statsModality,
targetUserId,
@@ -124,7 +124,27 @@
-1, /* sensorId */
ambientLightLux,
source,
- -1 /* templateId*/);
+ templateId);
+ }
+
+ /** {@see FrameworkStatsLog.BIOMETRIC_UNENROLLED} */
+ public void unenrolled(int statsModality, int targetUserId, int reason, int templateId) {
+ FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_UNENROLLED,
+ statsModality,
+ targetUserId,
+ reason,
+ templateId);
+ }
+
+ /** {@see FrameworkStatsLog.BIOMETRIC_ENUMERATED} */
+ public void enumerated(int statsModality, int targetUserId, int result, int[] templateIdsHal,
+ int[] templateIdsFramework) {
+ FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ENUMERATED,
+ statsModality,
+ targetUserId,
+ result,
+ templateIdsHal,
+ templateIdsFramework);
}
/** {@see FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED}. */
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricLogger.java b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
index 9351bc0..08582ca 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
@@ -32,6 +32,8 @@
import com.android.server.biometrics.AuthenticationStatsCollector;
import com.android.server.biometrics.Utils;
+import java.util.Arrays;
+
/**
* Logger for all reported Biometric framework events.
*/
@@ -254,7 +256,7 @@
/** Log enrollment outcome. */
public void logOnEnrolled(int targetUserId, long latency, boolean enrollSuccessful,
- int source) {
+ int source, int templateId) {
if (!mShouldLogMetrics) {
return;
}
@@ -265,7 +267,8 @@
+ ", Client: " + mStatsClient
+ ", Latency: " + latency
+ ", Lux: " + mALSProbe.getMostRecentLux()
- + ", Success: " + enrollSuccessful);
+ + ", Success: " + enrollSuccessful
+ + ", TemplateId: " + templateId);
} else {
Slog.v(TAG, "Enroll latency: " + latency);
}
@@ -275,7 +278,51 @@
}
mSink.enroll(mStatsModality, mStatsAction, mStatsClient,
- targetUserId, latency, enrollSuccessful, mALSProbe.getMostRecentLux(), source);
+ targetUserId, latency, enrollSuccessful, mALSProbe.getMostRecentLux(), source,
+ templateId);
+ }
+
+ /** Log un-enrollment. */
+ public void logOnUnEnrolled(int targetUserId, int reason, int templateId) {
+ if (!mShouldLogMetrics) {
+ return;
+ }
+
+ if (DEBUG) {
+ Slog.v(TAG, "UnEnrolled! Modality: " + mStatsModality
+ + ", User: " + targetUserId
+ + ", reason: " + reason
+ + ", templateId: " + templateId);
+ }
+
+ if (shouldSkipLogging()) {
+ return;
+ }
+
+ mSink.unenrolled(mStatsModality, targetUserId, reason, templateId);
+ }
+
+ /** Log enumeration. */
+ public void logOnEnumerated(int targetUserId, int result, int[] templateIdsHal,
+ int[] templateIdsFramework) {
+ if (!mShouldLogMetrics) {
+ return;
+ }
+
+ if (DEBUG) {
+ Slog.v(TAG, "Enumerated! Modality: " + mStatsModality
+ + ", User: " + targetUserId
+ + ", result: " + result
+ + ", templateIdsHal: " + Arrays.toString(templateIdsHal)
+ + ", templateIdsFramework: " + Arrays.toString(templateIdsFramework));
+ }
+
+ if (shouldSkipLogging()) {
+ return;
+ }
+
+ mSink.enumerated(mStatsModality, targetUserId, result, templateIdsHal,
+ templateIdsFramework);
}
/** Report unexpected enrollment reported by the HAL. */
diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
index 32c0bd3..38bf999 100644
--- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
@@ -95,7 +95,7 @@
mBiometricUtils.addBiometricForUser(getContext(), getTargetUserId(), identifier);
getLogger().logOnEnrolled(getTargetUserId(),
System.currentTimeMillis() - mEnrollmentStartTimeMs,
- true /* enrollSuccessful */, mEnrollReason);
+ true /* enrollSuccessful */, mEnrollReason, identifier.getBiometricId());
mCallback.onClientFinished(this, true /* success */);
}
notifyUserActivity();
@@ -123,7 +123,7 @@
public void onError(int error, int vendorCode) {
getLogger().logOnEnrolled(getTargetUserId(),
System.currentTimeMillis() - mEnrollmentStartTimeMs,
- false /* enrollSuccessful */, mEnrollReason);
+ false /* enrollSuccessful */, mEnrollReason, -1 /* templateId */);
super.onError(error, vendorCode);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
index 6c30559..03b382e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.Build;
import android.os.IBinder;
import android.util.Slog;
@@ -135,7 +136,7 @@
Supplier<T> lazyDaemon, IBinder token, int biometricId, int userId, String owner,
BiometricUtils<S> utils, int sensorId,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
- Map<Integer, Long> authenticatorIds);
+ Map<Integer, Long> authenticatorIds, int reason);
protected InternalCleanupClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
int userId, @NonNull String owner, int sensorId,
@@ -158,7 +159,8 @@
mCurrentTask = getRemovalClient(getContext(), mLazyDaemon, getToken(),
template.mIdentifier.getBiometricId(), template.mUserId,
getContext().getPackageName(), mBiometricUtils, getSensorId(),
- getLogger(), getBiometricContext(), mAuthenticatorIds);
+ getLogger(), getBiometricContext(), mAuthenticatorIds,
+ BiometricsProtoEnums.UNENROLL_REASON_DANGLING_HAL);
getLogger().logUnknownEnrollmentInHal();
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
index 2c2c685..5a47f1be 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import android.util.Slog;
@@ -46,6 +47,10 @@
// List of templates to remove from the HAL
private List<BiometricAuthenticator.Identifier> mUnknownHALTemplates = new ArrayList<>();
private final int mInitialEnrolledSize;
+ private final int[] mEnrolledIdsFrameworkArray;
+ private final List<Integer> mEnrolledIdsHalList = new ArrayList<>();
+ private boolean mIsDanglingFramework;
+ private boolean mIsDanglingHal;
protected InternalEnumerateClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
@NonNull IBinder token, int userId, @NonNull String owner,
@@ -60,14 +65,26 @@
mEnrolledList = enrolledList;
mInitialEnrolledSize = mEnrolledList.size();
mUtils = utils;
+ // Record ids from frameworks for metrics
+ mEnrolledIdsFrameworkArray = new int[mInitialEnrolledSize];
+ for (int i = 0; i < mInitialEnrolledSize; i++) {
+ mEnrolledIdsFrameworkArray[i] = mEnrolledList.get(i).getBiometricId();
+ }
}
@Override
public void onEnumerationResult(BiometricAuthenticator.Identifier identifier,
int remaining) {
+ if (identifier != null) {
+ // Record ids from hal for metrics
+ mEnrolledIdsHalList.add(identifier.getBiometricId());
+ }
handleEnumeratedTemplate(identifier);
if (remaining == 0) {
+ mIsDanglingHal = !mUnknownHALTemplates.isEmpty();
+ mIsDanglingFramework = !mEnrolledList.isEmpty();
doTemplateCleanup();
+ logEnumerationResult();
mCallback.onClientFinished(this, true /* success */);
}
}
@@ -123,7 +140,9 @@
+ identifier.getBiometricId() + " " + identifier.getName());
mUtils.removeBiometricForUser(getContext(),
getTargetUserId(), identifier.getBiometricId());
-
+ getLogger().logOnUnEnrolled(getTargetUserId(),
+ BiometricsProtoEnums.UNENROLL_REASON_DANGLING_FRAMEWORK,
+ identifier.getBiometricId());
getLogger().logUnknownEnrollmentInFramework();
}
@@ -134,6 +153,32 @@
mEnrolledList.clear();
}
+ private void logEnumerationResult() {
+ final int result;
+ if (mIsDanglingFramework && mIsDanglingHal) {
+ result = BiometricsProtoEnums.ENUMERATION_RESULT_DANGLING_BOTH;
+ } else if (mIsDanglingFramework) {
+ result = BiometricsProtoEnums.ENUMERATION_RESULT_DANGLING_FRAMEWORK;
+ } else if (mIsDanglingHal) {
+ result = BiometricsProtoEnums.ENUMERATION_RESULT_DANGLING_HAL;
+ } else {
+ result = BiometricsProtoEnums.ENUMERATION_RESULT_OK;
+ }
+
+ final int[] idsHalArray = listToArray(mEnrolledIdsHalList);
+ getLogger().logOnEnumerated(
+ getTargetUserId(), result, idsHalArray, mEnrolledIdsFrameworkArray);
+ }
+
+ private int[] listToArray(List<Integer> ids) {
+ final int size = ids.size();
+ int[] array = new int[size];
+ for (int i = 0; i < size; i++) {
+ array[i] = ids.get(i);
+ }
+ return array;
+ }
+
public List<BiometricAuthenticator.Identifier> getUnknownHALTemplates() {
return mUnknownHALTemplates;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
index ad5877a..0259906 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
@@ -42,17 +42,19 @@
private final BiometricUtils<S> mBiometricUtils;
private final Map<Integer, Long> mAuthenticatorIds;
private final boolean mHasEnrollmentsBeforeStarting;
+ private final int mReason;
public RemovalClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
int userId, @NonNull String owner, @NonNull BiometricUtils<S> utils, int sensorId,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
- @NonNull Map<Integer, Long> authenticatorIds) {
+ @NonNull Map<Integer, Long> authenticatorIds, int reason) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
logger, biometricContext, false /* isMandatoryBiometrics */);
mBiometricUtils = utils;
mAuthenticatorIds = authenticatorIds;
mHasEnrollmentsBeforeStarting = !utils.getBiometricsForUser(context, userId).isEmpty();
+ mReason = reason;
}
@Override
@@ -87,6 +89,7 @@
Slog.d(TAG, "onRemoved: " + identifier.getBiometricId() + " remaining: " + remaining);
mBiometricUtils.removeBiometricForUser(getContext(), getTargetUserId(),
identifier.getBiometricId());
+ getLogger().logOnUnEnrolled(getTargetUserId(), mReason, identifier.getBiometricId());
try {
getListener().onRemoved(identifier, remaining);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
index c27b7c4..5adc251 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
@@ -63,12 +63,12 @@
Supplier<AidlSession> lazyDaemon, IBinder token,
int biometricId, int userId, String owner, BiometricUtils<Face> utils, int sensorId,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
- Map<Integer, Long> authenticatorIds) {
+ Map<Integer, Long> authenticatorIds, int reason) {
// Internal remove does not need to send results to anyone. Cleanup (enumerate + remove)
// is all done internally.
return new FaceRemovalClient(context, lazyDaemon, token,
null /* ClientMonitorCallbackConverter */, new int[] {biometricId}, userId, owner,
- utils, sensorId, logger, biometricContext, authenticatorIds);
+ utils, sensorId, logger, biometricContext, authenticatorIds, reason);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index 5127e68..36e4a7e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -668,7 +668,8 @@
BiometricsProtoEnums.CLIENT_UNKNOWN,
mAuthenticationStatsCollector),
mBiometricContext,
- mFaceSensors.get(sensorId).getAuthenticatorIds());
+ mFaceSensors.get(sensorId).getAuthenticatorIds(),
+ BiometricsProtoEnums.UNENROLL_REASON_USER_REQUEST);
scheduleForSensor(sensorId, client, mBiometricStateCallback);
});
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java
index 0793888..971cfbb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java
@@ -46,9 +46,9 @@
int[] biometricIds, int userId, @NonNull String owner,
@NonNull BiometricUtils<Face> utils, int sensorId,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
- @NonNull Map<Integer, Long> authenticatorIds) {
+ @NonNull Map<Integer, Long> authenticatorIds, int reason) {
super(context, lazyDaemon, token, listener, userId, owner, utils, sensorId,
- logger, biometricContext, authenticatorIds);
+ logger, biometricContext, authenticatorIds, reason);
mBiometricIds = biometricIds;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
index 40b8a45..77670ec 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
@@ -71,11 +71,11 @@
Supplier<AidlSession> lazyDaemon, IBinder token, int biometricId, int userId,
String owner, BiometricUtils<Fingerprint> utils, int sensorId,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
- Map<Integer, Long> authenticatorIds) {
+ Map<Integer, Long> authenticatorIds, int reason) {
return new FingerprintRemovalClient(context, lazyDaemon, token,
null /* ClientMonitorCallbackConverter */, new int[] {biometricId}, userId, owner,
utils, sensorId, logger.swapAction(context, BiometricsProtoEnums.ACTION_REMOVE),
- biometricContext, authenticatorIds);
+ biometricContext, authenticatorIds, reason);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 456591c..c18d925 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -659,7 +659,8 @@
sensorId, createLogger(BiometricsProtoEnums.ACTION_REMOVE,
BiometricsProtoEnums.CLIENT_UNKNOWN,
mAuthenticationStatsCollector), mBiometricContext,
- mFingerprintSensors.get(sensorId).getAuthenticatorIds());
+ mFingerprintSensors.get(sensorId).getAuthenticatorIds(),
+ BiometricsProtoEnums.UNENROLL_REASON_USER_REQUEST);
scheduleForSensor(sensorId, client, mBiometricStateCallback);
});
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java
index 4f08f6f..c6b955a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java
@@ -47,9 +47,9 @@
@Nullable ClientMonitorCallbackConverter listener, int[] biometricIds, int userId,
@NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
- @NonNull Map<Integer, Long> authenticatorIds) {
+ @NonNull Map<Integer, Long> authenticatorIds, int reason) {
super(context, lazyDaemon, token, listener, userId, owner, utils, sensorId,
- logger, biometricContext, authenticatorIds);
+ logger, biometricContext, authenticatorIds, reason);
mBiometricIds = biometricIds;
}
diff --git a/services/core/java/com/android/server/input/KeyboardGlyphManager.java b/services/core/java/com/android/server/input/KeyboardGlyphManager.java
index f59d72b..6f19a3f 100644
--- a/services/core/java/com/android/server/input/KeyboardGlyphManager.java
+++ b/services/core/java/com/android/server/input/KeyboardGlyphManager.java
@@ -149,17 +149,17 @@
continue;
}
final ActivityInfo activityInfo = resolveInfo.activityInfo;
- KeyGlyphMapData data = getKeyboardGlyphMapsInPackage(pm, activityInfo);
- if (data == null) {
+ List<KeyGlyphMapData> data = getKeyboardGlyphMapsInPackage(pm, activityInfo);
+ if (data == null || data.isEmpty()) {
continue;
}
- glyphMaps.add(data);
+ glyphMaps.addAll(data);
}
return glyphMaps;
}
@Nullable
- private KeyGlyphMapData getKeyboardGlyphMapsInPackage(PackageManager pm,
+ private List<KeyGlyphMapData> getKeyboardGlyphMapsInPackage(PackageManager pm,
@NonNull ActivityInfo receiver) {
Bundle metaData = receiver.metaData;
if (metaData == null) {
@@ -175,6 +175,7 @@
try {
Resources resources = pm.getResourcesForApplication(receiver.applicationInfo);
+ List<KeyGlyphMapData> glyphMaps = new ArrayList<>();
try (XmlResourceParser parser = resources.getXml(configResId)) {
XmlUtils.beginDocument(parser, TAG_KEYBOARD_GLYPH_MAPS);
@@ -193,13 +194,14 @@
int vendor = a.getInt(R.styleable.KeyboardGlyphMap_vendorId, -1);
int product = a.getInt(R.styleable.KeyboardGlyphMap_productId, -1);
if (glyphMapRes != 0 && vendor != -1 && product != -1) {
- return new KeyGlyphMapData(receiver.packageName, receiver.name,
- glyphMapRes, vendor, product);
+ glyphMaps.add(new KeyGlyphMapData(receiver.packageName, receiver.name,
+ glyphMapRes, vendor, product));
}
} finally {
a.recycle();
}
}
+ return glyphMaps;
}
} catch (Exception ex) {
Slog.w(TAG, "Could not parse keyboard glyph map resource from receiver "
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index 4824c16..9f40bed 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -261,6 +261,14 @@
}
}
+ boolean hasDimState() {
+ return mDimState != null;
+ }
+
+ boolean isDimming() {
+ return mDimState != null && mDimState.isDimming();
+ }
+
@NonNull
private DimState obtainDimState(@NonNull WindowState window) {
if (mDimState == null) {
@@ -276,7 +284,6 @@
return mDimState != null ? mDimState.mDimSurface : null;
}
- @Deprecated
Rect getDimBounds() {
return mDimState != null ? mDimState.mDimBounds : null;
}
diff --git a/services/core/java/com/android/server/wm/DimmerAnimationHelper.java b/services/core/java/com/android/server/wm/DimmerAnimationHelper.java
index 3999e03..0d0e548 100644
--- a/services/core/java/com/android/server/wm/DimmerAnimationHelper.java
+++ b/services/core/java/com/android/server/wm/DimmerAnimationHelper.java
@@ -27,11 +27,13 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.graphics.Rect;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
import android.view.SurfaceControl;
import com.android.internal.protolog.ProtoLog;
+import com.android.window.flags.Flags;
import java.io.PrintWriter;
@@ -153,6 +155,9 @@
? mRequestedProperties.mGeometryParent.getSurfaceControl() : null,
mRequestedProperties.mDimmingContainer != startProperties.mDimmingContainer
? mRequestedProperties.mDimmingContainer.getSurfaceControl() : null, t);
+ if (Flags.useTasksDimOnly()) {
+ setBounds(dim, mCurrentProperties.mDimmingContainer, t);
+ }
if (!startProperties.hasSameVisualProperties(mRequestedProperties)) {
stopCurrentAnimation(dim.mDimSurface);
@@ -253,6 +258,32 @@
}
}
+ static void setBounds(@NonNull Dimmer.DimState dim, @NonNull WindowState relativeParent,
+ @NonNull SurfaceControl.Transaction t) {
+ TaskFragment taskFragment = relativeParent.getTaskFragment();
+ Rect taskFragmentBounds = taskFragment != null ? taskFragment.getBounds() : null;
+ Task task = relativeParent.getTask();
+ Rect taskBounds = task != null ? task.getBounds() : null;
+ Rect hostBounds = dim.mHostContainer.getBounds();
+ boolean isEmbedded = taskFragment != null && taskFragment.isEmbedded();
+
+ Rect relativeBounds = new Rect();
+ if (isEmbedded) {
+ // Embedded activities can be dimmed at task or fragment level
+ dim.mDimBounds.set(taskFragment.isDimmingOnParentTask()
+ ? taskBounds : taskFragmentBounds);
+ relativeBounds.set(dim.mDimBounds);
+ relativeBounds.offset(-taskBounds.left, -taskBounds.top);
+ } else {
+ dim.mDimBounds.set(hostBounds);
+ relativeBounds.set(dim.mDimBounds);
+ relativeBounds.offsetTo(0, 0);
+ }
+
+ t.setWindowCrop(dim.mDimSurface, relativeBounds.width(), relativeBounds.height());
+ t.setPosition(dim.mDimSurface, relativeBounds.left, relativeBounds.top);
+ }
+
void setCurrentAlphaBlur(@NonNull Dimmer.DimState dim, @NonNull SurfaceControl.Transaction t) {
final SurfaceControl sc = dim.mDimSurface;
try {
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 29ffda7..3b24798 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -46,6 +46,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.ProtoLog;
import com.android.server.policy.WindowManagerPolicy;
+import com.android.window.flags.Flags;
import java.io.PrintWriter;
import java.util.Comparator;
@@ -53,6 +54,7 @@
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
+
/**
* Container for grouping WindowContainer below DisplayContent.
*
@@ -831,11 +833,14 @@
void prepareSurfaces() {
mDimmer.resetDimStates();
super.prepareSurfaces();
- final Rect dimBounds = mDimmer.getDimBounds();
- if (dimBounds != null) {
- // Bounds need to be relative, as the dim layer is a child.
- getBounds(dimBounds);
- dimBounds.offsetTo(0 /* newLeft */, 0 /* newTop */);
+ Rect dimBounds = null;
+ if (!Flags.useTasksDimOnly()) {
+ dimBounds = mDimmer.getDimBounds();
+ if (dimBounds != null) {
+ // Bounds need to be relative, as the dim layer is a child.
+ getBounds(dimBounds);
+ dimBounds.offsetTo(0 /* newLeft */, 0 /* newTop */);
+ }
}
// If SystemUI is dragging for recents, we want to reset the dim state so any dim layer
@@ -845,7 +850,7 @@
mDimmer.resetDimStates();
}
- if (dimBounds != null) {
+ if (mDimmer.hasDimState()) {
if (mDimmer.updateDims(getSyncTransaction())) {
scheduleAnimation();
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 8a624b3..b14dd3f 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3297,22 +3297,25 @@
mDimmer.resetDimStates();
super.prepareSurfaces();
- final Rect dimBounds = mDimmer.getDimBounds();
- if (dimBounds != null) {
- getDimBounds(dimBounds);
+ Rect dimBounds = null;
+ if (!Flags.useTasksDimOnly()) {
+ dimBounds = mDimmer.getDimBounds();
+ if (dimBounds != null) {
+ getDimBounds(dimBounds);
- // Bounds need to be relative, as the dim layer is a child.
- if (inFreeformWindowingMode()) {
- getBounds(mTmpRect);
- dimBounds.offsetTo(dimBounds.left - mTmpRect.left, dimBounds.top - mTmpRect.top);
- } else {
- dimBounds.offsetTo(0, 0);
+ // Bounds need to be relative, as the dim layer is a child.
+ if (inFreeformWindowingMode()) {
+ getBounds(mTmpRect);
+ dimBounds.offset(-mTmpRect.left, -mTmpRect.top);
+ } else {
+ dimBounds.offsetTo(0, 0);
+ }
}
}
final SurfaceControl.Transaction t = getSyncTransaction();
- if (dimBounds != null && mDimmer.updateDims(t)) {
+ if (mDimmer.hasDimState() && mDimmer.updateDims(t)) {
scheduleAnimation();
}
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index d6ba312..606d51d 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -3157,12 +3157,16 @@
/** Bounds to be used for dimming, as well as touch related tests. */
void getDimBounds(@NonNull Rect out) {
- if (mIsEmbedded && isDimmingOnParentTask() && getDimmer().getDimBounds() != null) {
- // Return the task bounds if the dimmer is showing and should cover on the Task (not
- // just on this embedded TaskFragment).
- out.set(getTask().getBounds());
+ if (Flags.useTasksDimOnly() && mDimmer.hasDimState()) {
+ out.set(mDimmer.getDimBounds());
} else {
- out.set(getBounds());
+ if (mIsEmbedded && isDimmingOnParentTask() && getDimmer().getDimBounds() != null) {
+ // Return the task bounds if the dimmer is showing and should cover on the Task (not
+ // just on this embedded TaskFragment).
+ out.set(getTask().getBounds());
+ } else {
+ out.set(getBounds());
+ }
}
}
@@ -3193,10 +3197,16 @@
mDimmer.resetDimStates();
super.prepareSurfaces();
- final Rect dimBounds = mDimmer.getDimBounds();
- if (dimBounds != null) {
- // Bounds need to be relative, as the dim layer is a child.
- dimBounds.offsetTo(0 /* newLeft */, 0 /* newTop */);
+ if (!Flags.useTasksDimOnly()) {
+ final Rect dimBounds = mDimmer.getDimBounds();
+ if (dimBounds != null) {
+ // Bounds need to be relative, as the dim layer is a child.
+ dimBounds.offsetTo(0 /* newLeft */, 0 /* newTop */);
+ if (mDimmer.updateDims(getSyncTransaction())) {
+ scheduleAnimation();
+ }
+ }
+ } else {
if (mDimmer.updateDims(getSyncTransaction())) {
scheduleAnimation();
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
index 8f23ab9..d7bfea8 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
@@ -16,6 +16,8 @@
package com.android.server.biometrics.log;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyFloat;
@@ -45,10 +47,14 @@
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
@Presubmit
@SmallTest
public class BiometricLoggerTest {
@@ -136,12 +142,53 @@
final int targetUserId = 4;
final long latency = 44;
final boolean enrollSuccessful = true;
+ final int templateId = 4;
- mLogger.logOnEnrolled(targetUserId, latency, enrollSuccessful, -1);
+ mLogger.logOnEnrolled(targetUserId, latency, enrollSuccessful, -1, templateId);
verify(mSink).enroll(
eq(DEFAULT_MODALITY), eq(DEFAULT_ACTION), eq(DEFAULT_CLIENT),
- eq(targetUserId), eq(latency), eq(enrollSuccessful), anyFloat(), anyInt());
+ eq(targetUserId), eq(latency), eq(enrollSuccessful), anyFloat(), anyInt(),
+ eq(templateId));
+ }
+
+ @Test
+ public void testUnEnroll() {
+ mLogger = createLogger();
+
+ final int targetUserId = 4;
+ final int reason = BiometricsProtoEnums.UNENROLL_REASON_DANGLING_FRAMEWORK;
+ final int templateId = 4;
+
+ mLogger.logOnUnEnrolled(targetUserId, reason, templateId);
+
+ verify(mSink).unenrolled(
+ eq(DEFAULT_MODALITY), eq(targetUserId), eq(reason), eq(templateId));
+ }
+
+ @Test
+ public void testEnumeration() {
+ mLogger = createLogger();
+
+ final int targetUserId = 4;
+ final int result = BiometricsProtoEnums.ENUMERATION_RESULT_OK;
+ final int[] templateIdsHal = {1, 2};
+ final int[] templateIdsFw = {2, 3};
+ mLogger.logOnEnumerated(targetUserId, result, templateIdsHal, templateIdsFw);
+
+ final ArgumentCaptor<int[]> captorHalIds = ArgumentCaptor.forClass(int[].class);
+ final ArgumentCaptor<int[]> captorFwIds = ArgumentCaptor.forClass(int[].class);
+ verify(mSink).enumerated(
+ eq(DEFAULT_MODALITY), eq(targetUserId), eq(result), captorHalIds.capture(),
+ captorFwIds.capture());
+ assertThat(captorHalIds.getAllValues().stream()
+ .flatMap(x -> Arrays.stream(x).boxed())
+ .collect(Collectors.toList()))
+ .containsExactly(1, 2);
+ assertThat(captorFwIds.getAllValues().stream()
+ .flatMap(x -> Arrays.stream(x).boxed())
+ .collect(Collectors.toList()))
+ .containsExactly(2, 3);
}
@Test
@@ -193,7 +240,8 @@
mLogger.logOnEnrolled(2 /* targetUserId */,
10 /* latency */,
true /* enrollSuccessful */,
- 30 /* source */);
+ 30 /* source */,
+ 1 /* templateId */);
mLogger.logOnError(mContext, mOpContext,
4 /* error */,
0 /* vendorCode */,
@@ -207,7 +255,7 @@
anyLong(), anyInt(), anyBoolean(), anyInt(), anyFloat());
verify(mSink, never()).enroll(
anyInt(), anyInt(), anyInt(), anyInt(), anyLong(), anyBoolean(), anyFloat(),
- anyInt());
+ anyInt(), anyInt());
verify(mSink, never()).error(eq(mOpContext),
anyInt(), anyInt(), anyInt(), anyBoolean(),
anyLong(), anyInt(), anyInt(), anyInt());
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index 90c07d4..fd8c792 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -37,6 +37,7 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.mockito.kotlin.StubberKt.doReturn;
import android.content.Context;
import android.hardware.biometrics.AuthenticateOptions;
@@ -810,14 +811,16 @@
final ClientMonitorCallback callback0 = mock(ClientMonitorCallback.class);
final ClientMonitorCallback callback1 = mock(ClientMonitorCallback.class);
final ClientMonitorCallback callback2 = mock(ClientMonitorCallback.class);
+ final BiometricLogger logger = mock(BiometricLogger.class);
+ doReturn(logger).when(logger).swapAction(any(), anyInt());
final TestInternalCleanupClient client1 = new
TestInternalCleanupClient(mContext, daemon, userId,
- owner, TEST_SENSOR_ID, mock(BiometricLogger.class),
+ owner, TEST_SENSOR_ID, logger,
mBiometricContext, utils, authenticatorIds);
final TestInternalCleanupClient client2 = new
TestInternalCleanupClient(mContext, daemon, userId,
- owner, TEST_SENSOR_ID, mock(BiometricLogger.class),
+ owner, TEST_SENSOR_ID, logger,
mBiometricContext, utils, authenticatorIds);
//add initial start client to scheduler, so later clients will be on pending operation queue
@@ -1248,9 +1251,9 @@
@Nullable ClientMonitorCallbackConverter listener, int[] biometricIds, int userId,
@NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
- @NonNull Map<Integer, Long> authenticatorIds) {
+ @NonNull Map<Integer, Long> authenticatorIds, int reason) {
super(context, lazyDaemon, token, listener, userId, owner, utils, sensorId,
- logger, biometricContext, authenticatorIds);
+ logger, biometricContext, authenticatorIds, reason);
}
@Override
@@ -1289,10 +1292,10 @@
Supplier<Object> lazyDaemon, IBinder token, int biometricId, int userId,
String owner, BiometricUtils<Fingerprint> utils, int sensorId,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
- Map<Integer, Long> authenticatorIds) {
+ Map<Integer, Long> authenticatorIds, int reason) {
return new TestRemovalClient(context, lazyDaemon, token, null,
new int[]{biometricId}, userId, owner, utils, sensorId, logger,
- biometricContext, authenticatorIds);
+ biometricContext, authenticatorIds, reason);
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
index 6ac95c8..276da39 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
@@ -197,7 +197,7 @@
client.onEnrollResult(new Face("face", 1 /* faceId */, 20 /* deviceId */), 0);
verify(mBiometricLogger).logOnEnrolled(anyInt(), anyLong(), anyBoolean(),
- eq(BiometricsProtoEnums.ENROLLMENT_SOURCE_SUW));
+ eq(BiometricsProtoEnums.ENROLLMENT_SOURCE_SUW), eq(1));
}
private FaceEnrollClient createClient() throws RemoteException {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClientTest.java
index d8bdd50..734ee16 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClientTest.java
@@ -21,6 +21,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -28,6 +29,7 @@
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.ISession;
import android.hardware.face.Face;
import android.os.IBinder;
@@ -44,12 +46,15 @@
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
+import java.util.stream.Collectors;
@Presubmit
@SmallTest
@@ -104,6 +109,8 @@
mClient.onEnumerationResult(mFace, 0);
return null;
}).when(mSession).enumerateEnrollments();
+ final ArgumentCaptor<int[]> captorHalIds = ArgumentCaptor.forClass(int[].class);
+ final ArgumentCaptor<int[]> captorFwIds = ArgumentCaptor.forClass(int[].class);
mClient.start(mCallback);
@@ -111,6 +118,17 @@
assertThat(mClient.getUnknownHALTemplates().size()).isEqualTo(0);
assertThat(mNotificationSent).isFalse();
verify(mBiometricUtils, never()).removeBiometricForUser(any(), anyInt(), anyInt());
+ verify(mBiometricLogger).logOnEnumerated(eq(USER_ID),
+ eq(BiometricsProtoEnums.ENUMERATION_RESULT_OK), captorHalIds.capture(),
+ captorFwIds.capture());
+ assertThat(captorHalIds.getAllValues().stream()
+ .flatMap(x -> Arrays.stream(x).boxed())
+ .collect(Collectors.toList()))
+ .containsExactly(mBiometricId);
+ assertThat(captorFwIds.getAllValues().stream()
+ .flatMap(x -> Arrays.stream(x).boxed())
+ .collect(Collectors.toList()))
+ .containsExactly(mBiometricId);
verify(mCallback).onClientFinished(mClient, true);
}
@@ -171,6 +189,8 @@
mClient.onEnumerationResult(identifier, 0);
return null;
}).when(mSession).enumerateEnrollments();
+ final ArgumentCaptor<int[]> captorHalIds = ArgumentCaptor.forClass(int[].class);
+ final ArgumentCaptor<int[]> captorFwIds = ArgumentCaptor.forClass(int[].class);
mClient.start(mCallback);
@@ -178,6 +198,17 @@
assertThat(mClient.getUnknownHALTemplates().size()).isEqualTo(1);
assertThat(mNotificationSent).isTrue();
verify(mBiometricUtils).removeBiometricForUser(mContext, USER_ID, mBiometricId);
+ verify(mBiometricLogger).logOnEnumerated(eq(USER_ID),
+ eq(BiometricsProtoEnums.ENUMERATION_RESULT_DANGLING_BOTH), captorHalIds.capture(),
+ captorFwIds.capture());
+ assertThat(captorHalIds.getAllValues().stream()
+ .flatMap(x -> Arrays.stream(x).boxed())
+ .collect(Collectors.toList()))
+ .containsExactly(2);
+ assertThat(captorFwIds.getAllValues().stream()
+ .flatMap(x -> Arrays.stream(x).boxed())
+ .collect(Collectors.toList()))
+ .containsExactly(mBiometricId);
verify(mCallback).onClientFinished(mClient, true);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClientTest.java
index 1d9e933..c224af2 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClientTest.java
@@ -25,6 +25,7 @@
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.ISession;
import android.hardware.face.Face;
import android.os.IBinder;
@@ -119,6 +120,6 @@
return new FaceRemovalClient(mContext, () -> aidl, mToken,
mClientMonitorCallbackConverter, biometricIds, USER_ID,
"own-it", mUtils /* utils */, 5 /* sensorId */, mBiometricLogger, mBiometricContext,
- mAuthenticatorIds);
+ mAuthenticatorIds, BiometricsProtoEnums.UNENROLL_REASON_UNKNOWN);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
index 5c6513d..ea96d19 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
@@ -309,7 +309,7 @@
client.onEnrollResult(new Fingerprint("fingerprint", 1 /* faceId */, 20 /* deviceId */), 0);
verify(mBiometricLogger).logOnEnrolled(anyInt(), anyLong(), anyBoolean(),
- eq(BiometricsProtoEnums.ENROLLMENT_SOURCE_SUW));
+ eq(BiometricsProtoEnums.ENROLLMENT_SOURCE_SUW), eq(1));
}
private void showHideOverlay(
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java
index 7dcf841..a6604d1 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java
@@ -18,6 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
@@ -99,6 +100,7 @@
mAddedIds = new ArrayList<>();
when(mAidlSession.getSession()).thenReturn(mSession);
+ doReturn(mLogger).when(mLogger).swapAction(any(), anyInt());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClientTest.java
index fab1200..a809c3b 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClientTest.java
@@ -21,6 +21,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -28,6 +29,7 @@
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.fingerprint.ISession;
import android.hardware.fingerprint.Fingerprint;
import android.os.IBinder;
@@ -44,11 +46,13 @@
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -108,6 +112,8 @@
mClient.onEnumerationResult(new Fingerprint("three", 3, 1), 0);
return null;
}).when(mSession).enumerateEnrollments();
+ final ArgumentCaptor<int[]> captorHalIds = ArgumentCaptor.forClass(int[].class);
+ final ArgumentCaptor<int[]> captorFwIds = ArgumentCaptor.forClass(int[].class);
mClient.start(mCallback);
verify(mSession).enumerateEnrollments();
@@ -116,6 +122,17 @@
.collect(Collectors.toList())).containsExactly(2, 3);
assertThat(mNotificationSent).isTrue();
verify(mBiometricUtils).removeBiometricForUser(mContext, USER_ID, 1);
+ verify(mBiometricLogger).logOnEnumerated(eq(USER_ID),
+ eq(BiometricsProtoEnums.ENUMERATION_RESULT_DANGLING_BOTH), captorHalIds.capture(),
+ captorFwIds.capture());
+ assertThat(captorHalIds.getAllValues().stream()
+ .flatMap(x -> Arrays.stream(x).boxed())
+ .collect(Collectors.toList()))
+ .containsExactly(2, 3);
+ assertThat(captorFwIds.getAllValues().stream()
+ .flatMap(x -> Arrays.stream(x).boxed())
+ .collect(Collectors.toList()))
+ .containsExactly(1);
verify(mCallback).onClientFinished(mClient, true);
}
@@ -125,12 +142,25 @@
mClient.onEnumerationResult(new Fingerprint("one", 1, 1), 0);
return null;
}).when(mSession).enumerateEnrollments();
+ final ArgumentCaptor<int[]> captorHalIds = ArgumentCaptor.forClass(int[].class);
+ final ArgumentCaptor<int[]> captorFwIds = ArgumentCaptor.forClass(int[].class);
mClient.start(mCallback);
verify(mSession).enumerateEnrollments();
assertThat(mClient.getUnknownHALTemplates().size()).isEqualTo(0);
assertThat(mNotificationSent).isFalse();
verify(mBiometricUtils, never()).removeBiometricForUser(any(), anyInt(), anyInt());
+ verify(mBiometricLogger).logOnEnumerated(eq(USER_ID),
+ eq(BiometricsProtoEnums.ENUMERATION_RESULT_OK), captorHalIds.capture(),
+ captorFwIds.capture());
+ assertThat(captorHalIds.getAllValues().stream()
+ .flatMap(x -> Arrays.stream(x).boxed())
+ .collect(Collectors.toList()))
+ .containsExactly(1);
+ assertThat(captorFwIds.getAllValues().stream()
+ .flatMap(x -> Arrays.stream(x).boxed())
+ .collect(Collectors.toList()))
+ .containsExactly(1);
verify(mCallback).onClientFinished(mClient, true);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClientTest.java
index 64f07e2..ced916e 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClientTest.java
@@ -26,6 +26,7 @@
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.fingerprint.ISession;
import android.hardware.fingerprint.Fingerprint;
import android.os.IBinder;
@@ -91,7 +92,8 @@
mClient = new FingerprintRemovalClient(mContext, () -> mAidlSession, mToken, mListener,
mBiometricIds, USER_ID, TAG, mBiometricUtils, SENSOR_ID,
- mBiometricLogger, mBiometricContext, mAuthenticatorIds);
+ mBiometricLogger, mBiometricContext, mAuthenticatorIds,
+ BiometricsProtoEnums.UNENROLL_REASON_UNKNOWN);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index 1f3aa35..a30591e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -23,6 +23,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.utils.LastCallVerifier.lastCall;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -33,6 +34,7 @@
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
@@ -56,6 +58,7 @@
final SurfaceSession mSession = new SurfaceSession();
final SurfaceControl mHostControl = mock(SurfaceControl.class);
final SurfaceControl.Transaction mHostTransaction = spy(StubTransaction.class);
+ Rect mBounds = new Rect(10, 20, 30, 40);
MockSurfaceBuildingContainer(WindowManagerService wm) {
super(wm);
@@ -94,6 +97,11 @@
public SurfaceControl.Transaction getPendingTransaction() {
return mHostTransaction;
}
+
+ @Override
+ public Rect getBounds() {
+ return mBounds;
+ }
}
static class MockAnimationAdapterFactory extends DimmerAnimationHelper.AnimationAdapterFactory {
@@ -104,34 +112,63 @@
}
}
+ static class TestActivityEmbeddingMock {
+ Task mTask = mock(Task.class);
+ TaskFragment mLeft = mock(TaskFragment.class);
+ TaskFragment mRight = mock(TaskFragment.class);
+ Rect mTaskBounds = new Rect(10, 0, 50, 40);
+ Rect mLeftBounds = new Rect(10, 0, 30, 40);
+ Rect mRightBounds = new Rect(30, 0, 50, 40);
+
+ TestActivityEmbeddingMock() {
+ when(mTask.getBounds()).thenReturn(mTaskBounds);
+ when(mLeft.getBounds()).thenReturn(mLeftBounds);
+ when(mRight.getBounds()).thenReturn(mRightBounds);
+ when(mLeft.isEmbedded()).thenReturn(true);
+ when(mRight.isEmbedded()).thenReturn(true);
+ }
+
+ void pretendParentToTask(WindowState child) {
+ when(child.getTaskFragment()).thenReturn(mTask);
+ when(child.getTask()).thenReturn(mTask);
+ }
+
+ void pretendParentToRight(WindowState child) {
+ when(child.getTaskFragment()).thenReturn(mRight);
+ when(child.getTask()).thenReturn(mTask);
+ }
+ }
+
+ WindowState getMockDimmingContainer() {
+ WindowState window = mock(WindowState.class);
+ SurfaceControl surface = mock(SurfaceControl.class);
+ when(window.getSurfaceControl()).thenReturn(surface);
+ return window;
+ }
+
private Dimmer mDimmer;
private SurfaceControl.Transaction mTransaction;
+ MockSurfaceBuildingContainer mHost;
private WindowState mChild1;
private WindowState mChild2;
private static AnimationAdapter sTestAnimation;
@Before
public void setUp() throws Exception {
- MockSurfaceBuildingContainer host = new MockSurfaceBuildingContainer(mWm);
- mTransaction = host.getSyncTransaction();
-
- final SurfaceControl mControl1 = mock(SurfaceControl.class);
- final SurfaceControl mControl2 = mock(SurfaceControl.class);
+ mHost = new MockSurfaceBuildingContainer(mWm);
+ mTransaction = mHost.getSyncTransaction();
SurfaceAnimator animator = mock(SurfaceAnimator.class);
when(animator.getAnimation()).thenReturn(null);
- mChild1 = mock(WindowState.class);
- when(mChild1.getSurfaceControl()).thenReturn(mControl1);
+ mChild1 = getMockDimmingContainer();
+ mChild2 = getMockDimmingContainer();
- mChild2 = mock(WindowState.class);
- when(mChild2.getSurfaceControl()).thenReturn(mControl2);
-
- host.addChild(mChild1, 0);
- host.addChild(mChild2, 1);
+ mHost.addChild(mChild1, 0);
+ mHost.addChild(mChild2, 1);
sTestAnimation = spy(new MockAnimationAdapter());
- mDimmer = new Dimmer(host, new MockAnimationAdapterFactory());
+ mDimmer = new Dimmer(mHost, new MockAnimationAdapterFactory());
}
@Test
@@ -150,6 +187,63 @@
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_USE_TASKS_DIM_ONLY)
+ public void testBoundsInActivityEmbeddingForWholeTask() {
+ final WindowState dimmingWindow = getMockDimmingContainer();
+ TestActivityEmbeddingMock embedding = new TestActivityEmbeddingMock();
+ embedding.pretendParentToRight(dimmingWindow);
+ when(embedding.mRight.isDimmingOnParentTask()).thenReturn(true);
+
+ mDimmer.adjustAppearance(dimmingWindow, 1, 1);
+ mDimmer.adjustPosition(dimmingWindow, dimmingWindow);
+ mDimmer.updateDims(mTransaction);
+ verify(mTransaction).setWindowCrop(mDimmer.getDimLayer(),
+ embedding.mTaskBounds.width(), embedding.mTaskBounds.height());
+ verify(mTransaction).setPosition(mDimmer.getDimLayer(), 0, 0);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_USE_TASKS_DIM_ONLY)
+ public void testBoundsInActivityEmbeddingForTaskFragmentOnly() {
+ final WindowState dimmingWindow = getMockDimmingContainer();
+ TestActivityEmbeddingMock embedding = new TestActivityEmbeddingMock();
+ embedding.pretendParentToRight(dimmingWindow);
+ when(embedding.mRight.isDimmingOnParentTask()).thenReturn(false);
+
+ mDimmer.adjustAppearance(dimmingWindow, 1, 1);
+ mDimmer.adjustPosition(dimmingWindow, dimmingWindow);
+ mDimmer.updateDims(mTransaction);
+ Rect expectedAbsoluteBounds = embedding.mRightBounds;
+ Rect expectedRelativeBounds = new Rect(expectedAbsoluteBounds);
+ expectedRelativeBounds.offset(-embedding.mTaskBounds.left, -embedding.mTaskBounds.top);
+ verify(mTransaction).setWindowCrop(mDimmer.getDimLayer(),
+ embedding.mRightBounds.width(), embedding.mRightBounds.height());
+ verify(mTransaction).setPosition(mDimmer.getDimLayer(),
+ expectedRelativeBounds.left, expectedRelativeBounds.top);
+ assertEquals(expectedAbsoluteBounds, mDimmer.getDimBounds());
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_USE_TASKS_DIM_ONLY)
+ public void testDimBoundsAdaptToResizing() {
+ // First call with some generic bounds
+ mDimmer.adjustAppearance(mChild1, 0.5f, 1);
+ mDimmer.adjustPosition(mChild1, mChild1);
+ mDimmer.updateDims(mTransaction);
+ verify(mTransaction).setWindowCrop(mDimmer.getDimLayer(),
+ mHost.getBounds().width(), mHost.getBounds().height());
+
+ // Change bounds
+ mHost.getBounds().left += 5;
+ mHost.getBounds().top += 6;
+ mDimmer.adjustAppearance(mChild1, 1, 1);
+ mDimmer.adjustPosition(mChild1, mChild1);
+ mDimmer.updateDims(mTransaction);
+ verify(mTransaction).setWindowCrop(mDimmer.getDimLayer(),
+ mHost.getBounds().width(), mHost.getBounds().height());
+ }
+
+ @Test
public void testDimBelowWithChildSurfaceCreatesSurfaceBelowChild() {
final float alpha = 0.7f;
final int blur = 50;
diff --git a/tests/Input/res/xml/keyboard_glyph_maps.xml b/tests/Input/res/xml/keyboard_glyph_maps.xml
index d0616ff..42561c1 100644
--- a/tests/Input/res/xml/keyboard_glyph_maps.xml
+++ b/tests/Input/res/xml/keyboard_glyph_maps.xml
@@ -19,4 +19,8 @@
androidprv:glyphMap="@xml/test_glyph_map"
androidprv:vendorId="0x1234"
androidprv:productId="0x3456" />
+ <keyboard-glyph-map
+ androidprv:glyphMap="@xml/test_glyph_map2"
+ androidprv:vendorId="0x1235"
+ androidprv:productId="0x3457" />
</keyboard-glyph-maps>
\ No newline at end of file
diff --git a/tests/Input/res/xml/test_glyph_map2.xml b/tests/Input/res/xml/test_glyph_map2.xml
new file mode 100644
index 0000000..7a7c1ac
--- /dev/null
+++ b/tests/Input/res/xml/test_glyph_map2.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<keyboard-glyph-map xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <key-glyph
+ androidprv:keycode="KEYCODE_BACK"
+ androidprv:glyphDrawable="@drawable/test_key_drawable" />
+ <modifier-glyph
+ androidprv:modifier="META"
+ androidprv:glyphDrawable="@drawable/test_modifier_drawable" />
+ <function-row-key androidprv:keycode="KEYCODE_EMOJI_PICKER" />
+ <hardware-defined-shortcut
+ androidprv:keycode="KEYCODE_1"
+ androidprv:modifierState="FUNCTION"
+ androidprv:outKeycode="KEYCODE_BACK" />
+ <hardware-defined-shortcut
+ androidprv:keycode="KEYCODE_2"
+ androidprv:modifierState="FUNCTION|META"
+ androidprv:outKeycode="KEYCODE_HOME" />
+</keyboard-glyph-map>
\ No newline at end of file
diff --git a/tests/Input/src/com/android/server/input/KeyboardGlyphManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardGlyphManagerTests.kt
index ff8a9ba..5da0beb 100644
--- a/tests/Input/src/com/android/server/input/KeyboardGlyphManagerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardGlyphManagerTests.kt
@@ -59,6 +59,9 @@
const val DEVICE_ID = 1
const val VENDOR_ID = 0x1234
const val PRODUCT_ID = 0x3456
+ const val DEVICE_ID2 = 2
+ const val VENDOR_ID2 = 0x1235
+ const val PRODUCT_ID2 = 0x3457
const val PACKAGE_NAME = "KeyboardLayoutManagerTests"
const val RECEIVER_NAME = "DummyReceiver"
}
@@ -96,8 +99,11 @@
.thenReturn(inputManager)
keyboardDevice = createKeyboard(DEVICE_ID, VENDOR_ID, PRODUCT_ID, 0, "", "")
- Mockito.`when`(inputManagerRule.mock.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID))
+ Mockito.`when`(inputManagerRule.mock.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID, DEVICE_ID2))
Mockito.`when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)).thenReturn(keyboardDevice)
+
+ val keyboardDevice2 = createKeyboard(DEVICE_ID2, VENDOR_ID2, PRODUCT_ID2, 0, "", "")
+ Mockito.`when`(inputManagerRule.mock.getInputDevice(DEVICE_ID2)).thenReturn(keyboardDevice2)
}
private fun setupBroadcastReceiver() {
@@ -143,6 +149,10 @@
"Glyph map for test keyboard(deviceId=$DEVICE_ID) must exist",
keyboardGlyphManager.getKeyGlyphMap(DEVICE_ID)
)
+ assertNotNull(
+ "Glyph map for test keyboard(deviceId=$DEVICE_ID2) must exist",
+ keyboardGlyphManager.getKeyGlyphMap(DEVICE_ID2)
+ )
assertNull(
"Glyph map for non-existing keyboard must be null",
keyboardGlyphManager.getKeyGlyphMap(-2)
@@ -158,6 +168,7 @@
assertNotNull(glyphMap.getDrawableForModifier(context, KeyEvent.KEYCODE_META_LEFT))
assertNotNull(glyphMap.getDrawableForModifier(context, KeyEvent.KEYCODE_META_RIGHT))
+ assertNotNull(glyphMap.getDrawableForModifierState(context, KeyEvent.META_META_ON))
val functionRowKeys = glyphMap.functionRowKeys
assertEquals(1, functionRowKeys.size)