Merge "Add a flag for deferring outgoing broadcasts" into main
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index 1f5b83c..c1add03 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -458,13 +458,21 @@
libs: ["stub-annotations"],
}
+java_defaults {
+ name: "android-non-updatable_everything_from_text_defaults",
+ defaults: [
+ "android-non-updatable_from_text_defaults",
+ ],
+ stubs_type: "everything",
+}
+
java_api_library {
name: "android-non-updatable.stubs.from-text",
api_surface: "public",
api_contributions: [
"api-stubs-docs-non-updatable.api.contribution",
],
- defaults: ["android-non-updatable_from_text_defaults"],
+ defaults: ["android-non-updatable_everything_from_text_defaults"],
full_api_surface_stub: "android_stubs_current.from-text",
}
@@ -475,7 +483,7 @@
"api-stubs-docs-non-updatable.api.contribution",
"system-api-stubs-docs-non-updatable.api.contribution",
],
- defaults: ["android-non-updatable_from_text_defaults"],
+ defaults: ["android-non-updatable_everything_from_text_defaults"],
full_api_surface_stub: "android_system_stubs_current.from-text",
}
@@ -487,7 +495,7 @@
"system-api-stubs-docs-non-updatable.api.contribution",
"test-api-stubs-docs-non-updatable.api.contribution",
],
- defaults: ["android-non-updatable_from_text_defaults"],
+ defaults: ["android-non-updatable_everything_from_text_defaults"],
full_api_surface_stub: "android_test_stubs_current.from-text",
}
@@ -499,7 +507,7 @@
"system-api-stubs-docs-non-updatable.api.contribution",
"module-lib-api-stubs-docs-non-updatable.api.contribution",
],
- defaults: ["android-non-updatable_from_text_defaults"],
+ defaults: ["android-non-updatable_everything_from_text_defaults"],
full_api_surface_stub: "android_module_lib_stubs_current_full.from-text",
}
@@ -515,7 +523,7 @@
"test-api-stubs-docs-non-updatable.api.contribution",
"module-lib-api-stubs-docs-non-updatable.api.contribution",
],
- defaults: ["android-non-updatable_from_text_defaults"],
+ defaults: ["android-non-updatable_everything_from_text_defaults"],
full_api_surface_stub: "android_test_module_lib_stubs_current.from-text",
// This module is only used for hiddenapi, and other modules should not
@@ -836,6 +844,7 @@
],
visibility: ["//visibility:public"],
enable_validation: false,
+ stubs_type: "everything",
}
java_api_library {
@@ -852,6 +861,7 @@
],
visibility: ["//visibility:public"],
enable_validation: false,
+ stubs_type: "everything",
}
java_api_library {
@@ -870,6 +880,7 @@
],
visibility: ["//visibility:public"],
enable_validation: false,
+ stubs_type: "everything",
}
java_api_library {
@@ -888,6 +899,7 @@
"system-api-stubs-docs-non-updatable.api.contribution",
],
enable_validation: false,
+ stubs_type: "everything",
}
java_api_library {
@@ -908,6 +920,7 @@
],
visibility: ["//visibility:public"],
enable_validation: false,
+ stubs_type: "everything",
}
java_api_library {
@@ -922,6 +935,7 @@
],
visibility: ["//visibility:public"],
enable_validation: false,
+ stubs_type: "everything",
}
java_api_library {
@@ -947,6 +961,7 @@
"//visibility:private",
],
enable_validation: false,
+ stubs_type: "everything",
}
java_api_library {
@@ -964,6 +979,7 @@
],
visibility: ["//visibility:public"],
enable_validation: false,
+ stubs_type: "everything",
}
////////////////////////////////////////////////////////////////////////
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index ecffe9e..faa2c70 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -392,8 +392,6 @@
return;
}
- Log.i(TAG, walFile.getAbsolutePath() + " " + size + " bytes: Bigger than "
- + threshold + "; truncating");
try {
executeForString("PRAGMA wal_checkpoint(TRUNCATE)", null, null);
mConfiguration.shouldTruncateWalFile = false;
diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java
index 5e523c0..78c8954 100644
--- a/core/java/android/database/sqlite/SQLiteOpenHelper.java
+++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java
@@ -377,8 +377,7 @@
if (writable) {
throw ex;
}
- Log.e(TAG, "Couldn't open " + mName
- + " for writing (will try read-only):", ex);
+ Log.e(TAG, "Couldn't open database for writing (will try read-only):", ex);
params = params.toBuilder().addOpenFlags(SQLiteDatabase.OPEN_READONLY).build();
db = SQLiteDatabase.openDatabase(filePath, params);
}
@@ -425,11 +424,6 @@
}
onOpen(db);
-
- if (db.isReadOnly()) {
- Log.w(TAG, "Opened " + mName + " in read-only mode");
- }
-
mDatabase = db;
return db;
} finally {
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 95526a8..8aacd5e 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -1379,7 +1379,6 @@
mSurfaceType != other.mSurfaceType ||
mIsDeferredConfig != other.mIsDeferredConfig ||
mIsShared != other.mIsShared ||
- mConfiguredFormat != other.mConfiguredFormat ||
mConfiguredDataspace != other.mConfiguredDataspace ||
mConfiguredGenerationId != other.mConfiguredGenerationId ||
!Objects.equals(mPhysicalCameraId, other.mPhysicalCameraId) ||
diff --git a/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig b/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig
index 63ae28f..2a11835 100644
--- a/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig
+++ b/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig
@@ -20,3 +20,10 @@
description: "Enable reporting USB data compliance warnings from HAL when set"
bug: "296119135"
}
+
+flag {
+ name: "enable_usb_data_signal_staking"
+ namespace: "preload_safety"
+ description: "Enables signal API with staking"
+ bug: "296119135"
+}
\ No newline at end of file
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index 3950c25..2fe115f 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -623,14 +623,14 @@
// Message is to be inserted at tail or middle of queue. Usually we don't have to
// wake up the event queue unless there is a barrier at the head of the queue and
// the message is the earliest asynchronous message in the queue.
- //
+ needWake = mBlocked && p.target == null && msg.isAsynchronous();
+
// For readability, we split this portion of the function into two blocks based on
// whether tail tracking is enabled. This has a minor implication for the case
// where tail tracking is disabled. See the comment below.
if (Flags.messageQueueTailTracking()) {
- needWake = mBlocked && p.target == null && msg.isAsynchronous()
- && mAsyncMessageCount == 0;
if (when >= mLast.when) {
+ needWake = needWake && mAsyncMessageCount == 0;
msg.next = null;
mLast.next = msg;
mLast = msg;
@@ -643,6 +643,9 @@
if (p == null || when < p.when) {
break;
}
+ if (needWake && p.isAsynchronous()) {
+ needWake = false;
+ }
}
if (p == null) {
/* Inserting at tail of queue */
@@ -652,7 +655,6 @@
prev.next = msg;
}
} else {
- needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 8495f37..fa9f03d 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -247,8 +247,6 @@
private final LegacyPermissionManager mLegacyPermissionManager;
- private final VirtualDeviceManager mVirtualDeviceManager;
-
private final ArrayMap<PackageManager.OnPermissionsChangedListener,
IOnPermissionsChangeListener> mPermissionListeners = new ArrayMap<>();
private PermissionUsageHelper mUsageHelper;
@@ -269,7 +267,6 @@
mPermissionManager = IPermissionManager.Stub.asInterface(ServiceManager.getServiceOrThrow(
"permissionmgr"));
mLegacyPermissionManager = context.getSystemService(LegacyPermissionManager.class);
- mVirtualDeviceManager = context.getSystemService(VirtualDeviceManager.class);
}
/**
@@ -1918,14 +1915,18 @@
if (deviceId == Context.DEVICE_ID_DEFAULT) {
persistentDeviceId = VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
} else if (android.companion.virtual.flags.Flags.vdmPublicApis()) {
- VirtualDevice virtualDevice = mVirtualDeviceManager.getVirtualDevice(deviceId);
- if (virtualDevice == null) {
- Slog.e(LOG_TAG, "Virtual device is not found with device Id " + deviceId);
- return null;
- }
- persistentDeviceId = virtualDevice.getPersistentDeviceId();
- if (persistentDeviceId == null) {
- Slog.e(LOG_TAG, "Cannot find persistent device Id for " + deviceId);
+ VirtualDeviceManager virtualDeviceManager = mContext.getSystemService(
+ VirtualDeviceManager.class);
+ if (virtualDeviceManager != null) {
+ VirtualDevice virtualDevice = virtualDeviceManager.getVirtualDevice(deviceId);
+ if (virtualDevice == null) {
+ Slog.e(LOG_TAG, "Virtual device is not found with device Id " + deviceId);
+ return null;
+ }
+ persistentDeviceId = virtualDevice.getPersistentDeviceId();
+ if (persistentDeviceId == null) {
+ Slog.e(LOG_TAG, "Cannot find persistent device Id for " + deviceId);
+ }
}
} else {
Slog.e(LOG_TAG, "vdmPublicApis flag is not enabled when device Id " + deviceId
diff --git a/core/java/android/security/ConfirmationPrompt.java b/core/java/android/security/ConfirmationPrompt.java
index d8c44ad..f626149 100644
--- a/core/java/android/security/ConfirmationPrompt.java
+++ b/core/java/android/security/ConfirmationPrompt.java
@@ -92,7 +92,6 @@
private Executor mExecutor;
private Context mContext;
- private final KeyStore mKeyStore = KeyStore.getInstance();
private AndroidProtectedConfirmation mProtectedConfirmation;
private AndroidProtectedConfirmation getService() {
diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java
index f1054ec..c171c1b 100644
--- a/core/java/android/security/keystore/recovery/RecoveryController.java
+++ b/core/java/android/security/keystore/recovery/RecoveryController.java
@@ -26,7 +26,6 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
-import android.security.KeyStore;
import android.security.KeyStore2;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore2.AndroidKeyStoreProvider;
@@ -272,11 +271,9 @@
public static final int ERROR_KEY_NOT_FOUND = 30;
private final ILockSettings mBinder;
- private final KeyStore mKeyStore;
- private RecoveryController(ILockSettings binder, KeyStore keystore) {
+ private RecoveryController(ILockSettings binder) {
mBinder = binder;
- mKeyStore = keystore;
}
/**
@@ -296,7 +293,7 @@
// lockSettings may be null.
ILockSettings lockSettings =
ILockSettings.Stub.asInterface(ServiceManager.getService("lock_settings"));
- return new RecoveryController(lockSettings, KeyStore.getInstance());
+ return new RecoveryController(lockSettings);
}
/**
diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java
index 00657ea..66655fc 100644
--- a/core/java/android/view/HandwritingInitiator.java
+++ b/core/java/android/view/HandwritingInitiator.java
@@ -188,8 +188,7 @@
// check whether the stylus we are tracking goes up.
if (mState != null) {
mState.mShouldInitHandwriting = false;
- if (!mState.mHasInitiatedHandwriting
- && !mState.mHasPreparedHandwritingDelegation) {
+ if (!mState.mHandled) {
// The user just did a click, long click or another stylus gesture,
// show hover icon again for the connected view.
mShowHoverIconForConnectedView = true;
@@ -204,16 +203,14 @@
// Either we've already tried to initiate handwriting, or the ongoing MotionEvent
// sequence is considered to be tap, long-click or other gestures.
if (!mState.mShouldInitHandwriting || mState.mExceedHandwritingSlop) {
- return mState.mHasInitiatedHandwriting
- || mState.mHasPreparedHandwritingDelegation;
+ return mState.mHandled;
}
final long timeElapsed =
motionEvent.getEventTime() - mState.mStylusDownTimeInMillis;
if (timeElapsed > mHandwritingTimeoutInMillis) {
mState.mShouldInitHandwriting = false;
- return mState.mHasInitiatedHandwriting
- || mState.mHasPreparedHandwritingDelegation;
+ return mState.mHandled;
}
final int pointerIndex = motionEvent.findPointerIndex(mState.mStylusPointerId);
@@ -223,7 +220,7 @@
mState.mExceedHandwritingSlop = true;
View candidateView = findBestCandidateView(mState.mStylusDownX,
mState.mStylusDownY, /* isHover */ false);
- if (candidateView != null) {
+ if (candidateView != null && candidateView.isEnabled()) {
if (candidateView == getConnectedOrFocusedView()) {
if (!mInitiateWithoutConnection && !candidateView.hasFocus()) {
requestFocusWithoutReveal(candidateView);
@@ -246,7 +243,7 @@
}
}
}
- return mState.mHasInitiatedHandwriting || mState.mHasPreparedHandwritingDelegation;
+ return mState.mHandled;
}
return false;
}
@@ -382,7 +379,7 @@
@VisibleForTesting
public void startHandwriting(@NonNull View view) {
mImm.startStylusHandwriting(view);
- mState.mHasInitiatedHandwriting = true;
+ mState.mHandled = true;
mState.mShouldInitHandwriting = false;
mShowHoverIconForConnectedView = false;
if (view instanceof TextView) {
@@ -402,13 +399,12 @@
mImm.startConnectionlessStylusHandwritingForDelegation(
view, getCursorAnchorInfoForConnectionless(view), delegatePackageName,
view::post, new DelegationCallback(view, delegatePackageName));
- mState.mHasInitiatedHandwriting = true;
mState.mShouldInitHandwriting = false;
} else {
mImm.prepareStylusHandwritingDelegation(view, delegatePackageName);
view.getHandwritingDelegatorCallback().run();
- mState.mHasPreparedHandwritingDelegation = true;
}
+ mState.mHandled = true;
}
/**
@@ -455,7 +451,7 @@
private void onDelegationAccepted(View view) {
if (mState != null) {
- mState.mHasInitiatedHandwriting = true;
+ mState.mHandled = true;
mState.mShouldInitHandwriting = false;
}
if (view instanceof TextView) {
@@ -795,12 +791,12 @@
* This boolean will be set to false, and it won't request to start handwriting.
*/
private boolean mShouldInitHandwriting;
- /**
- * Whether handwriting mode has already been initiated for the current MotionEvent sequence.
- */
- private boolean mHasInitiatedHandwriting;
- private boolean mHasPreparedHandwritingDelegation;
+ /**
+ * Whether the current MotionEvent sequence has been handled by the handwriting initiator,
+ * either by initiating handwriting mode, or by preparing handwriting delegation.
+ */
+ private boolean mHandled;
/**
* Whether the current ongoing stylus MotionEvent sequence already exceeds the
@@ -838,8 +834,7 @@
mStylusDownY = motionEvent.getY(actionIndex);
mShouldInitHandwriting = true;
- mHasInitiatedHandwriting = false;
- mHasPreparedHandwritingDelegation = false;
+ mHandled = false;
mExceedHandwritingSlop = false;
}
}
@@ -1052,8 +1047,6 @@
// Fall back to the old delegation flow
mImm.prepareStylusHandwritingDelegation(mView, mDelegatePackageName);
mView.getHandwritingDelegatorCallback().run();
- mState.mHasInitiatedHandwriting = false;
- mState.mHasPreparedHandwritingDelegation = true;
break;
}
}
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java
index f5f4fd9..9099f98 100644
--- a/core/java/android/view/PointerIcon.java
+++ b/core/java/android/view/PointerIcon.java
@@ -428,13 +428,11 @@
private BitmapDrawable getBitmapDrawableFromVectorDrawable(Resources resources,
VectorDrawable vectorDrawable) {
- Bitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(),
- vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
- // BitmapDrawables and Bitmap have a default density of DisplayMetrics.DENSITY_DEVICE,
- // (which is deprecated in favor of DENSITY_DEVICE_STABLE/resources.densityDpi). In
- // rare cases when device density differs from the resource density, the bitmap will
- // scale as the BitmapDrawable is created. Avoid by explicitly setting density here.
- bitmap.setDensity(resources.getDisplayMetrics().densityDpi);
+ // Ensure we pass the display metrics into the Bitmap constructor so that it is initialized
+ // with the correct density.
+ Bitmap bitmap = Bitmap.createBitmap(resources.getDisplayMetrics(),
+ vectorDrawable.getIntrinsicWidth(),
+ vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888, true /* hasAlpha */);
Canvas canvas = new Canvas(bitmap);
vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
vectorDrawable.draw(canvas);
diff --git a/core/java/android/window/flags/responsible_apis.aconfig b/core/java/android/window/flags/responsible_apis.aconfig
index 51890ec..94c72c6 100644
--- a/core/java/android/window/flags/responsible_apis.aconfig
+++ b/core/java/android/window/flags/responsible_apis.aconfig
@@ -46,5 +46,5 @@
name: "bal_respect_app_switch_state_when_check_bound_by_foreground_uid"
namespace: "responsible_apis"
description: "Prevent BAL based on it is bound by foreground Uid but the app switch is stopped."
- bug: "171459802"
+ bug: "283801068"
}
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index c87b7cd..5e3e1b0 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -84,7 +84,7 @@
CarrierConfigManager#KEY_AUTO_DATA_SWITCH_RAT_SIGNAL_SCORE_STRING_ARRAY.
If 0, the device always switch to the higher score SIM.
If < 0, the network type and signal strength based auto switch is disabled. -->
- <integer name="auto_data_switch_score_tolerance">4000</integer>
+ <integer name="auto_data_switch_score_tolerance">-1</integer>
<java-symbol type="integer" name="auto_data_switch_score_tolerance" />
<!-- Boolean indicating whether the Iwlan data service supports persistence of iwlan ipsec
diff --git a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
index 51eb41c..b60b806f 100644
--- a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
+++ b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
@@ -472,6 +472,26 @@
}
@Test
+ public void onTouchEvent_doesNothing_viewDisabled() {
+ mTestView1.setEnabled(false);
+
+ final int x1 = (sHwArea1.left + sHwArea1.right) / 2;
+ final int y1 = (sHwArea1.top + sHwArea1.bottom) / 2;
+ MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0);
+ mHandwritingInitiator.onTouchEvent(stylusEvent1);
+
+ final int x2 = x1 + mHandwritingSlop * 2;
+ final int y2 = y1;
+
+ MotionEvent stylusEvent2 = createStylusEvent(ACTION_MOVE, x2, y2, 0);
+ mHandwritingInitiator.onTouchEvent(stylusEvent2);
+
+ // HandwritingInitiator will not request focus if it is disabled.
+ verify(mTestView1, never()).requestFocus();
+ verify(mHandwritingInitiator, never()).startHandwriting(mTestView1);
+ }
+
+ @Test
public void onTouchEvent_focusView_inputConnectionAlreadyBuilt_stylusMoveOnce_withinHWArea() {
if (!mInitiateWithoutConnection) {
mHandwritingInitiator.onInputConnectionCreated(mTestView1);
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
index 62fe54f..ef03d3a 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
@@ -19,9 +19,9 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
-import android.security.KeyStore;
import java.io.IOException;
+import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
@@ -47,13 +47,13 @@
}
/**
- * Gets the {@link KeyStore} operation handle corresponding to the provided JCA crypto
+ * Gets the Android KeyStore operation handle corresponding to the provided JCA crypto
* primitive.
*
* <p>The following primitives are supported: {@link Cipher} and {@link Mac}.
*
- * @return KeyStore operation handle or {@code 0} if the provided primitive's KeyStore operation
- * is not in progress.
+ * @return Android KeyStore operation handle or {@code 0} if the provided primitive's Android
+ * KeyStore operation is not in progress.
*
* @throws IllegalArgumentException if the provided primitive is not supported or is not backed
* by AndroidKeyStore provider.
@@ -67,10 +67,10 @@
}
/**
- * Returns an {@code AndroidKeyStore} {@link java.security.KeyStore}} of the specified UID.
- * The {@code KeyStore} contains keys and certificates owned by that UID. Such cross-UID
- * access is permitted to a few system UIDs and only to a few other UIDs (e.g., Wi-Fi, VPN)
- * all of which are system.
+ * Returns an {@code AndroidKeyStore} {@link KeyStore} of the specified UID. The {@code
+ * KeyStore} contains keys and certificates owned by that UID. Such cross-UID access is
+ * permitted to a few system UIDs and only to a few other UIDs (e.g., Wi-Fi, VPN) all of which
+ * are system.
*
* <p>Note: the returned {@code KeyStore} is already initialized/loaded. Thus, there is
* no need to invoke {@code load} on it.
@@ -84,12 +84,12 @@
*/
@SystemApi
@NonNull
- public static java.security.KeyStore getKeyStoreForUid(int uid)
+ public static KeyStore getKeyStoreForUid(int uid)
throws KeyStoreException, NoSuchProviderException {
- final java.security.KeyStore.LoadStoreParameter loadParameter =
+ final KeyStore.LoadStoreParameter loadParameter =
new android.security.keystore2.AndroidKeyStoreLoadStoreParameter(
KeyProperties.legacyUidToNamespace(uid));
- java.security.KeyStore result = java.security.KeyStore.getInstance(PROVIDER_NAME);
+ KeyStore result = KeyStore.getInstance(PROVIDER_NAME);
try {
result.load(loadParameter);
} catch (NoSuchAlgorithmException | CertificateException | IOException e) {
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 244fe30..7aecfd8 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -910,7 +910,7 @@
/**
* Returns whether this key is critical to the device encryption flow.
*
- * @see android.security.KeyStore#FLAG_CRITICAL_TO_DEVICE_ENCRYPTION
+ * @see Builder#setCriticalToDeviceEncryption(boolean)
* @hide
*/
public boolean isCriticalToDeviceEncryption() {
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index 2495d1a..31b4a5e 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -569,7 +569,7 @@
/**
* Return whether this key is critical to the device encryption flow.
*
- * @see android.security.KeyStore#FLAG_CRITICAL_TO_DEVICE_ENCRYPTION
+ * @see Builder#setCriticalToDeviceEncryption(boolean)
* @hide
*/
public boolean isCriticalToDeviceEncryption() {
@@ -1105,9 +1105,10 @@
* Set whether this key is critical to the device encryption flow
*
* This is a special flag only available to system servers to indicate the current key
- * is part of the device encryption flow.
+ * is part of the device encryption flow. Setting this flag causes the key to not
+ * be cryptographically bound to the LSKF even if the key is otherwise authentication
+ * bound.
*
- * @see android.security.KeyStore#FLAG_CRITICAL_TO_DEVICE_ENCRYPTION
* @hide
*/
public Builder setCriticalToDeviceEncryption(boolean critical) {
diff --git a/keystore/java/android/security/keystore/KeyStoreCryptoOperation.java b/keystore/java/android/security/keystore/KeyStoreCryptoOperation.java
index 2c709ae..c42c9e4 100644
--- a/keystore/java/android/security/keystore/KeyStoreCryptoOperation.java
+++ b/keystore/java/android/security/keystore/KeyStoreCryptoOperation.java
@@ -16,18 +16,16 @@
package android.security.keystore;
-import android.security.KeyStore;
-
/**
- * Cryptographic operation backed by {@link KeyStore}.
+ * Cryptographic operation backed by Android KeyStore.
*
* @hide
*/
public interface KeyStoreCryptoOperation {
/**
- * Gets the KeyStore operation handle of this crypto operation.
+ * Gets the Android KeyStore operation handle of this crypto operation.
*
- * @return handle or {@code 0} if the KeyStore operation is not in progress.
+ * @return handle or {@code 0} if the Android KeyStore operation is not in progress.
*/
long getOperationHandle();
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyFactorySpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyFactorySpi.java
index a8dd7f3..8eca67f 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyFactorySpi.java
@@ -16,7 +16,6 @@
package android.security.keystore2;
-import android.security.KeyStore;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyInfo;
@@ -39,8 +38,6 @@
*/
public class AndroidKeyStoreKeyFactorySpi extends KeyFactorySpi {
- private final KeyStore mKeyStore = KeyStore.getInstance();
-
@Override
protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpecClass)
throws InvalidKeySpecException {
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index d204f13..99100de 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -17,7 +17,6 @@
package android.security.keystore2;
import android.annotation.NonNull;
-import android.security.KeyStore;
import android.security.KeyStore2;
import android.security.KeyStoreSecurityLevel;
import android.security.keymaster.KeymasterDefs;
@@ -161,13 +160,13 @@
}
/**
- * Gets the {@link KeyStore} operation handle corresponding to the provided JCA crypto
+ * Gets the Android KeyStore operation handle corresponding to the provided JCA crypto
* primitive.
*
* <p>The following primitives are supported: {@link Cipher}, {@link Signature} and {@link Mac}.
*
- * @return KeyStore operation handle or {@code 0} if the provided primitive's KeyStore operation
- * is not in progress.
+ * @return Android KeyStore operation handle or {@code 0} if the provided primitive's Android
+ * KeyStore operation is not in progress.
*
* @throws IllegalArgumentException if the provided primitive is not supported or is not backed
* by AndroidKeyStore provider.
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
index 2682eb6..2223091 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.security.GateKeeper;
-import android.security.KeyStore;
import android.security.keymaster.KeymasterArguments;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.KeyGenParameterSpec;
@@ -46,8 +45,6 @@
*/
public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
- private final KeyStore mKeyStore = KeyStore.getInstance();
-
@Override
protected KeySpec engineGetKeySpec(SecretKey key,
@SuppressWarnings("rawtypes") Class keySpecClass) throws InvalidKeySpecException {
diff --git a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationStreamer.java b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationStreamer.java
index 07d6a69..5bd98bc 100644
--- a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationStreamer.java
+++ b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationStreamer.java
@@ -16,12 +16,11 @@
package android.security.keystore2;
-import android.security.KeyStore;
import android.security.KeyStoreException;
/**
- * Helper for streaming a crypto operation's input and output via {@link KeyStore} service's
- * {@code update} and {@code finish} operations.
+ * Helper for streaming a crypto operation's input and output via KeyStore service's {@code update}
+ * and {@code finish} operations.
*
* <p>The helper abstracts away to issues that need to be solved in most code that uses KeyStore's
* update and finish operations. Firstly, KeyStore's update operation can consume only a limited
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
index f801b0d..a87116e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -75,7 +75,6 @@
private SurfaceControlViewHost mViewHost;
private DividerHandleView mHandle;
private DividerRoundedCorner mCorners;
- private View mBackground;
private int mTouchElevation;
private VelocityTracker mVelocityTracker;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
index 2b10377..194eb47 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
@@ -277,7 +277,7 @@
}
@Override
- public void onAnimationEnd(@androidx.annotation.NonNull Animator animation) {
+ public void onAnimationEnd(@NonNull Animator animation) {
mRunningAnimationCount--;
animT.remove(mScreenshot);
animT.apply();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index e421356..1c54754 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -163,14 +163,14 @@
/**
* Adds a split pair. This call does not validate the taskIds, only that they are not the same.
*/
- public void addSplitPair(int taskId1, int taskId2, SplitBounds splitBounds) {
+ public boolean addSplitPair(int taskId1, int taskId2, SplitBounds splitBounds) {
if (taskId1 == taskId2) {
- return;
+ return false;
}
if (mSplitTasks.get(taskId1, INVALID_TASK_ID) == taskId2
&& mTaskSplitBoundsMap.get(taskId1).equals(splitBounds)) {
// If the two tasks are already paired and the bounds are the same, then skip updating
- return;
+ return false;
}
// Remove any previous pairs
removeSplitPair(taskId1);
@@ -185,6 +185,7 @@
notifyRecentTasksChanged();
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENT_TASKS, "Add split pair: %d, %d, %s",
taskId1, taskId2, splitBounds);
+ return true;
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
index e52235f..64e26db 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
@@ -16,11 +16,14 @@
package com.android.wm.shell.splitscreen;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
+
import android.content.Context;
import android.view.SurfaceSession;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -50,6 +53,8 @@
void activate(WindowContainerTransaction wct, boolean includingTopTask) {
if (mIsActive) return;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "activate: main stage includingTopTask=%b",
+ includingTopTask);
if (includingTopTask) {
reparentTopTask(wct);
@@ -64,6 +69,8 @@
void deactivate(WindowContainerTransaction wct, boolean toTop) {
if (!mIsActive) return;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "deactivate: main stage toTop=%b rootTaskInfo=%s",
+ toTop, mRootTaskInfo);
mIsActive = false;
if (mRootTaskInfo == null) return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
index 9903113..f5fbae5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
@@ -16,12 +16,15 @@
package com.android.wm.shell.splitscreen;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
+
import android.app.ActivityManager;
import android.content.Context;
import android.view.SurfaceSession;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -47,6 +50,8 @@
}
boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove all side stage tasks: childCount=%d toTop=%b",
+ mChildrenTaskInfo.size(), toTop);
if (mChildrenTaskInfo.size() == 0) return false;
wct.reparentTasks(
mRootTaskInfo.token,
@@ -59,6 +64,8 @@
boolean removeTask(int taskId, WindowContainerToken newParent, WindowContainerTransaction wct) {
final ActivityManager.RunningTaskInfo task = mChildrenTaskInfo.get(taskId);
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove side stage task: task=%d exists=%b", taskId,
+ task != null);
if (task == null) return false;
wct.reparent(task.token, newParent, false /* onTop */);
return true;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index b60e361..1d9fdeb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -25,6 +25,8 @@
import static com.android.wm.shell.animation.Interpolators.ALPHA_OUT;
import static com.android.wm.shell.common.split.SplitScreenConstants.FADE_DURATION;
import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS;
import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString;
@@ -101,6 +103,7 @@
@NonNull Transitions.TransitionFinishCallback finishCallback,
@NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot,
@NonNull WindowContainerToken topRoot) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "playAnimation: transition=%d", info.getDebugId());
initTransition(transition, finishTransaction, finishCallback);
final TransitSession pendingTransition = getPendingTransition(transition);
@@ -123,10 +126,12 @@
playInternalAnimation(transition, info, startTransaction, mainRoot, sideRoot, topRoot);
}
- /** Internal funcation of playAnimation. */
+ /** Internal function of playAnimation. */
private void playInternalAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull WindowContainerToken mainRoot,
@NonNull WindowContainerToken sideRoot, @NonNull WindowContainerToken topRoot) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "playInternalAnimation: transition=%d",
+ info.getDebugId());
// Play some place-holder fade animations
final boolean isEnter = isPendingEnter(transition);
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
@@ -220,6 +225,8 @@
@NonNull Transitions.TransitionFinishCallback finishCallback,
@NonNull WindowContainerToken toTopRoot, @NonNull SplitDecorManager toTopDecor,
@NonNull WindowContainerToken topRoot) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "playDragDismissAnimation: transition=%d",
+ info.getDebugId());
initTransition(transition, finishTransaction, finishCallback);
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
@@ -259,6 +266,7 @@
@NonNull Transitions.TransitionFinishCallback finishCallback,
@NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot,
@NonNull SplitDecorManager mainDecor, @NonNull SplitDecorManager sideDecor) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "playResizeAnimation: transition=%d", info.getDebugId());
initTransition(transition, finishTransaction, finishCallback);
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
@@ -312,13 +320,15 @@
@Nullable
private TransitSession getPendingTransition(IBinder transition) {
if (isPendingEnter(transition)) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "\tresolved enter transition");
return mPendingEnter;
} else if (isPendingDismiss(transition)) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "\tresolved dismiss transition");
return mPendingDismiss;
} else if (isPendingResize(transition)) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "\tresolved resize transition");
return mPendingResize;
}
-
return null;
}
@@ -339,7 +349,7 @@
Transitions.TransitionHandler handler,
int extraTransitType, boolean resizeAnim) {
if (mPendingEnter != null) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
+ ProtoLog.v(WM_SHELL_TRANSITIONS, " splitTransition "
+ " skip to start enter split transition since it already exist. ");
return null;
}
@@ -355,8 +365,10 @@
mPendingEnter = new EnterSession(
transition, remoteTransition, extraTransitType, resizeAnim);
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
+ ProtoLog.v(WM_SHELL_TRANSITIONS, " splitTransition "
+ " deduced Enter split screen");
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setEnterTransition: transitType=%d resize=%b",
+ extraTransitType, resizeAnim);
}
/** Starts a transition to dismiss split. */
@@ -364,7 +376,7 @@
Transitions.TransitionHandler handler, @SplitScreen.StageType int dismissTop,
@SplitScreenController.ExitReason int reason) {
if (mPendingDismiss != null) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
+ ProtoLog.v(WM_SHELL_TRANSITIONS, " splitTransition "
+ " skip to start dismiss split transition since it already exist. reason to "
+ " dismiss = %s", exitReasonToString(reason));
return null;
@@ -381,9 +393,11 @@
@SplitScreenController.ExitReason int reason) {
mPendingDismiss = new DismissSession(transition, reason, dismissTop);
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
+ ProtoLog.v(WM_SHELL_TRANSITIONS, " splitTransition "
+ " deduced Dismiss due to %s. toTop=%s",
exitReasonToString(reason), stageTypeToString(dismissTop));
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setDismissTransition: reason=%s dismissTop=%s",
+ exitReasonToString(reason), stageTypeToString(dismissTop));
}
IBinder startResizeTransition(WindowContainerTransaction wct,
@@ -405,8 +419,9 @@
@Nullable TransitionConsumedCallback consumedCallback,
@Nullable TransitionFinishedCallback finishCallback) {
mPendingResize = new TransitSession(transition, consumedCallback, finishCallback);
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
+ ProtoLog.v(WM_SHELL_TRANSITIONS, " splitTransition "
+ " deduced Resize split screen");
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setResizeTransition");
}
void mergeAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t,
@@ -444,12 +459,15 @@
mPendingEnter.onConsumed(aborted);
mPendingEnter = null;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed for enter transition");
} else if (isPendingDismiss(transition)) {
mPendingDismiss.onConsumed(aborted);
mPendingDismiss = null;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed for dismiss transition");
} else if (isPendingResize(transition)) {
mPendingResize.onConsumed(aborted);
mPendingResize = null;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed for resize transition");
}
// TODO: handle transition consumed for active remote handler
@@ -462,12 +480,15 @@
if (isPendingEnter(mAnimatingTransition)) {
mPendingEnter.onFinished(wct, mFinishTransaction);
mPendingEnter = null;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFinish for enter transition");
} else if (isPendingDismiss(mAnimatingTransition)) {
mPendingDismiss.onFinished(wct, mFinishTransaction);
mPendingDismiss = null;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFinish for dismiss transition");
} else if (isPendingResize(mAnimatingTransition)) {
mPendingResize.onFinished(wct, mFinishTransaction);
mPendingResize = null;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFinish for resize transition");
}
mActiveRemoteHandler = null;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 2933cf4..7a1595f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -44,6 +44,7 @@
import static com.android.wm.shell.common.split.SplitScreenConstants.splitPositionToString;
import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition;
import static com.android.wm.shell.common.split.SplitScreenUtils.splitFailureMessage;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
import static com.android.wm.shell.shared.TransitionUtil.isClosingType;
import static com.android.wm.shell.shared.TransitionUtil.isOpeningType;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
@@ -138,6 +139,7 @@
import com.android.wm.shell.shared.TransitionUtil;
import com.android.wm.shell.splitscreen.SplitScreen.StageType;
import com.android.wm.shell.splitscreen.SplitScreenController.ExitReason;
+import com.android.wm.shell.splitscreen.SplitScreenController.SplitEnterReason;
import com.android.wm.shell.transition.DefaultMixedHandler;
import com.android.wm.shell.transition.LegacyTransitions;
import com.android.wm.shell.transition.Transitions;
@@ -313,6 +315,7 @@
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_FULLSCREEN, this /* listener */);
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Creating main/side root task");
mMainStage = new MainStage(
mContext,
mTaskOrganizer,
@@ -454,6 +457,8 @@
boolean moveToStage(ActivityManager.RunningTaskInfo task, @SplitPosition int stagePosition,
WindowContainerTransaction wct) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "moveToStage: task=%d position=%d", task.taskId,
+ stagePosition);
prepareEnterSplitScreen(wct, task, stagePosition, false /* resizeAnim */);
if (ENABLE_SHELL_TRANSITIONS) {
mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct,
@@ -474,6 +479,7 @@
}
boolean removeFromSideStage(int taskId) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "removeFromSideStage: task=%d", taskId);
final WindowContainerTransaction wct = new WindowContainerTransaction();
/**
@@ -498,11 +504,15 @@
enteredSplitSelect |= listener.onRequestEnterSplitSelect(taskInfo, splitPosition,
taskBounds);
}
- if (enteredSplitSelect) mTaskOrganizer.applyTransaction(wct);
+ if (enteredSplitSelect) {
+ mTaskOrganizer.applyTransaction(wct);
+ }
}
void startShortcut(String packageName, String shortcutId, @SplitPosition int position,
Bundle options, UserHandle user) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startShortcut: pkg=%s id=%s position=%d user=%d",
+ packageName, shortcutId, position, user.getIdentifier());
final boolean isEnteringSplit = !isSplitActive();
IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
@@ -564,6 +574,7 @@
/** Use this method to launch an existing Task via a taskId */
void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startTask: task=%d position=%d", taskId, position);
mSplitRequest = new SplitRequest(taskId, position);
final WindowContainerTransaction wct = new WindowContainerTransaction();
options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */);
@@ -595,6 +606,8 @@
/** Launches an activity into split. */
void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position,
@Nullable Bundle options) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startIntent: intent=%s position=%d", intent.getIntent(),
+ position);
mSplitRequest = new SplitRequest(intent.getIntent(), position);
if (!ENABLE_SHELL_TRANSITIONS) {
startIntentLegacy(intent, fillInIntent, position, options);
@@ -690,6 +703,9 @@
void startTasks(int taskId1, @Nullable Bundle options1, int taskId2, @Nullable Bundle options2,
@SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition,
@Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "startTasks: task1=%d task2=%d position=%d snapPosition=%d",
+ taskId1, taskId2, splitPosition, snapPosition);
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (taskId2 == INVALID_TASK_ID) {
if (mMainStage.containsTask(taskId1) || mSideStage.containsTask(taskId1)) {
@@ -718,6 +734,9 @@
@Nullable Bundle options1, int taskId, @Nullable Bundle options2,
@SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition,
@Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "startIntentAndTask: intent=%s task1=%d position=%d snapPosition=%d",
+ pendingIntent.getIntent(), taskId, splitPosition, snapPosition);
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (taskId == INVALID_TASK_ID) {
options1 = options1 != null ? options1 : new Bundle();
@@ -740,6 +759,9 @@
int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
@PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition,
InstanceId instanceId) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "startShortcutAndTask: shortcut=%s task1=%d position=%d snapPosition=%d",
+ shortcutInfo, taskId, splitPosition, snapPosition);
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (taskId == INVALID_TASK_ID) {
options1 = options1 != null ? options1 : new Bundle();
@@ -801,6 +823,10 @@
@Nullable ShortcutInfo shortcutInfo2, @Nullable Bundle options2,
@SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition,
@Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "startIntents: intent1=%s intent2=%s position=%d snapPosition=%d",
+ pendingIntent1.getIntent(), pendingIntent2.getIntent(), splitPosition,
+ snapPosition);
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (pendingIntent2 == null) {
options1 = options1 != null ? options1 : new Bundle();
@@ -1302,6 +1328,7 @@
}
void switchSplitPosition(String reason) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "switchSplitPosition");
final SurfaceControl.Transaction t = mTransactionPool.acquire();
mTempRect1.setEmpty();
final StageTaskListener topLeftStage =
@@ -1343,7 +1370,7 @@
});
});
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Switch split position: %s", reason);
+ ProtoLog.v(WM_SHELL_SPLIT_SCREEN, "Switch split position: %s", reason);
mLogger.logSwap(getMainStagePosition(), mMainStage.getTopChildTaskUid(),
getSideStagePosition(), mSideStage.getTopChildTaskUid(),
mSplitLayout.isLeftRightSplit());
@@ -1376,11 +1403,12 @@
if (!mMainStage.isActive()) {
return;
}
-
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onKeyguardVisibilityChanged: showing=%b", showing);
setDividerVisibility(!mKeyguardShowing, null);
}
void onFinishedWakingUp() {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFinishedWakingUp");
if (!mMainStage.isActive()) {
return;
}
@@ -1421,6 +1449,8 @@
}
void exitSplitScreen(int toTopTaskId, @ExitReason int exitReason) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "exitSplitScreen: topTaskId=%d reason=%s active=%b",
+ toTopTaskId, exitReasonToString(exitReason), mMainStage.isActive());
if (!mMainStage.isActive()) return;
StageTaskListener childrenToTop = null;
@@ -1439,6 +1469,8 @@
private void exitSplitScreen(@Nullable StageTaskListener childrenToTop,
@ExitReason int exitReason) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "exitSplitScreen: mainStageToTop=%b reason=%s active=%b",
+ childrenToTop == mMainStage, exitReasonToString(exitReason), mMainStage.isActive());
if (!mMainStage.isActive()) return;
final WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -1447,6 +1479,8 @@
private void applyExitSplitScreen(@Nullable StageTaskListener childrenToTop,
WindowContainerTransaction wct, @ExitReason int exitReason) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "applyExitSplitScreen: reason=%s",
+ exitReasonToString(exitReason));
if (!mMainStage.isActive() || mIsExiting) return;
onSplitScreenExit();
@@ -1502,7 +1536,6 @@
}
});
- Slog.i(TAG, "applyExitSplitScreen, reason = " + exitReasonToString(exitReason));
// Log the exit
if (childrenToTop != null) {
logExitToStage(exitReason, childrenToTop == mMainStage);
@@ -1527,6 +1560,7 @@
* Exits the split screen by finishing one of the tasks.
*/
protected void exitStage(@SplitPosition int stageToClose) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "exitStage: stageToClose=%d", stageToClose);
mSplitLayout.flingDividerToDismiss(stageToClose == SPLIT_POSITION_BOTTOM_OR_RIGHT,
EXIT_REASON_APP_FINISHED);
}
@@ -1540,12 +1574,13 @@
try {
activityTaskManagerService.setFocusedTask(getTaskId(stageToFocus));
} catch (RemoteException | NullPointerException e) {
- ProtoLog.e(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+ ProtoLog.e(WM_SHELL_SPLIT_SCREEN,
"Unable to update focus on the chosen stage: %s", e.getMessage());
}
}
private void clearRequestIfPresented() {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "clearRequestIfPresented");
if (mSideStageListener.mVisible && mSideStageListener.mHasChildren
&& mMainStageListener.mVisible && mSideStageListener.mHasChildren) {
mSplitRequest = null;
@@ -1581,6 +1616,8 @@
void clearSplitPairedInRecents(@ExitReason int exitReason) {
if (!shouldBreakPairedTaskInRecents(exitReason) || !mShouldUpdateRecents) return;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "clearSplitPairedInRecents: reason=%s",
+ exitReasonToString(exitReason));
mRecentTasks.ifPresent(recentTasks -> {
// Notify recents if we are exiting in a way that breaks the pair, and disable further
// updates to splits in the recents until we enter split again
@@ -1597,11 +1634,13 @@
void prepareExitSplitScreen(@StageType int stageToTop,
@NonNull WindowContainerTransaction wct) {
if (!mMainStage.isActive()) return;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareExitSplitScreen: stageToTop=%d", stageToTop);
mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE);
mMainStage.deactivate(wct, stageToTop == STAGE_TYPE_MAIN);
}
private void prepareEnterSplitScreen(WindowContainerTransaction wct) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareEnterSplitScreen");
prepareEnterSplitScreen(wct, null /* taskInfo */, SPLIT_POSITION_UNDEFINED,
!mIsDropEntering);
}
@@ -1613,6 +1652,8 @@
void prepareEnterSplitScreen(WindowContainerTransaction wct,
@Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition,
boolean resizeAnim) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareEnterSplitScreen: position=%d resize=%b",
+ startPosition, resizeAnim);
onSplitScreenEnter();
// Preemptively reset the reparenting behavior if we know that we are entering, as starting
// split tasks with activity trampolines can inadvertently trigger the task to be
@@ -1629,6 +1670,8 @@
private void prepareBringSplit(WindowContainerTransaction wct,
@Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition,
boolean resizeAnim) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareBringSplit: task=%d isSplitVisible=%b",
+ taskInfo != null ? taskInfo.taskId : -1, isSplitScreenVisible());
if (taskInfo != null) {
wct.startTask(taskInfo.taskId,
resolveStartStage(STAGE_TYPE_UNDEFINED, startPosition, null, wct));
@@ -1649,6 +1692,8 @@
private void prepareActiveSplit(WindowContainerTransaction wct,
@Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition,
boolean resizeAnim) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareActiveSplit: task=%d isSplitVisible=%b",
+ taskInfo != null ? taskInfo.taskId : -1, isSplitScreenVisible());
if (!ENABLE_SHELL_TRANSITIONS) {
// Legacy transition we need to create divider here, shell transition case we will
// create it on #finishEnterSplitScreen
@@ -1667,6 +1712,7 @@
}
private void prepareSplitLayout(WindowContainerTransaction wct, boolean resizeAnim) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareSplitLayout: resize=%b", resizeAnim);
if (resizeAnim) {
mSplitLayout.setDividerAtBorder(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT);
} else {
@@ -1686,6 +1732,7 @@
}
void finishEnterSplitScreen(SurfaceControl.Transaction finishT) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "finishEnterSplitScreen");
mSplitLayout.update(finishT, true /* resetImePosition */);
mMainStage.getSplitDecorManager().inflate(mContext, mMainStage.mRootLeash,
getMainStageBounds());
@@ -1835,12 +1882,20 @@
leftTopTaskId, rightBottomTaskId, mSplitLayout.calculateCurrentSnapPosition());
if (mainStageTopTaskId != INVALID_TASK_ID && sideStageTopTaskId != INVALID_TASK_ID) {
// Update the pair for the top tasks
- recentTasks.addSplitPair(mainStageTopTaskId, sideStageTopTaskId, splitBounds);
+ boolean added = recentTasks.addSplitPair(mainStageTopTaskId, sideStageTopTaskId,
+ splitBounds);
+ if (added) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "updateRecentTasksSplitPair: adding split pair ltTask=%d rbTask=%d",
+ leftTopTaskId, rightBottomTaskId);
+ }
}
});
}
private void sendSplitVisibilityChanged() {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "sendSplitVisibilityChanged: dividerVisible=%b",
+ mDividerVisible);
for (int i = mListeners.size() - 1; i >= 0; --i) {
final SplitScreen.SplitScreenListener l = mListeners.get(i);
l.onSplitVisibilityChanged(mDividerVisible);
@@ -1855,6 +1910,7 @@
throw new IllegalArgumentException(this + "\n Unknown task appeared: " + taskInfo);
}
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskAppeared: task=%s", taskInfo);
mRootTaskInfo = taskInfo;
mRootTaskLeash = leash;
@@ -1880,6 +1936,8 @@
if (mSplitLayout != null
&& mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)
&& mMainStage.isActive()) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskInfoChanged: task=%d updating",
+ taskInfo.taskId);
// Clear the divider remote animating flag as the divider will be re-rendered to apply
// the new rotation config. Don't reset the IME state since those updates are not in
// sync with task info changes.
@@ -1892,6 +1950,7 @@
@Override
@CallSuper
public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskVanished: task=%s", taskInfo);
if (mRootTaskInfo == null) {
throw new IllegalArgumentException(this + "\n Unknown task vanished: " + taskInfo);
}
@@ -1911,6 +1970,8 @@
@VisibleForTesting
void onRootTaskAppeared() {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRootTaskAppeared: rootTask=%s mainRoot=%b sideRoot=%b",
+ mRootTaskInfo, mMainStageListener.mHasRootTask, mSideStageListener.mHasRootTask);
// Wait unit all root tasks appeared.
if (mRootTaskInfo == null
|| !mMainStageListener.mHasRootTask
@@ -1937,6 +1998,8 @@
* #onStageHasChildrenChanged because this would be called every time child task appeared.
* NOTICE: This only be called on legacy transition. */
private void onChildTaskAppeared(StageListenerImpl stageListener, int taskId) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onChildTaskAppeared: isMainStage=%b task=%d",
+ stageListener == mMainStageListener, taskId);
// Handle entering split screen while there is a split pair running in the background.
if (stageListener == mSideStageListener && !isSplitScreenVisible() && isSplitActive()
&& mSplitRequest == null) {
@@ -1960,6 +2023,7 @@
}
private void onRootTaskVanished() {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRootTaskVanished");
final WindowContainerTransaction wct = new WindowContainerTransaction();
mLaunchAdjacentController.clearLaunchAdjacentRoot();
applyExitSplitScreen(null /* childrenToTop */, wct, EXIT_REASON_ROOT_TASK_VANISHED);
@@ -1990,6 +2054,8 @@
return;
}
+ // TODO Protolog
+
// Check if it needs to dismiss split screen when both stage invisible.
if (!mainStageVisible && mExitSplitScreenOnHide) {
exitSplitScreen(null /* childrenToTop */, EXIT_REASON_RETURN_HOME);
@@ -2020,14 +2086,14 @@
return;
}
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
- "Request to %s divider bar from %s.",
- (visible ? "show" : "hide"), Debug.getCaller());
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "setDividerVisibility: visible=%b keyguardShowing=%b dividerAnimating=%b caller=%s",
+ visible, mKeyguardShowing, mIsDividerRemoteAnimating, Debug.getCaller());
// Defer showing divider bar after keyguard dismissed, so it won't interfere with keyguard
// dismissing animation.
if (visible && mKeyguardShowing) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
" Defer showing divider bar due to keyguard showing.");
return;
}
@@ -2036,7 +2102,7 @@
sendSplitVisibilityChanged();
if (mIsDividerRemoteAnimating) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
" Skip animating divider bar due to it's remote animating.");
return;
}
@@ -2050,12 +2116,12 @@
private void applyDividerVisibility(@Nullable SurfaceControl.Transaction t) {
final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
if (dividerLeash == null) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
" Skip animating divider bar due to divider leash not ready.");
return;
}
if (mIsDividerRemoteAnimating) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
" Skip animating divider bar due to it's remote animating.");
return;
}
@@ -2119,6 +2185,8 @@
/** Callback when split roots have child or haven't under it.
* NOTICE: This only be called on legacy transition. */
private void onStageHasChildrenChanged(StageListenerImpl stageListener) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onStageHasChildrenChanged: isMainStage=%b",
+ stageListener == mMainStageListener);
final boolean hasChildren = stageListener.mHasChildren;
final boolean isSideStage = stageListener == mSideStageListener;
if (!hasChildren && !mIsExiting && mMainStage.isActive()) {
@@ -2170,13 +2238,15 @@
}
@Override
- public void onSnappedToDismiss(boolean bottomOrRight, int reason) {
+ public void onSnappedToDismiss(boolean bottomOrRight, @ExitReason int exitReason) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onSnappedToDismiss: bottomOrRight=%b reason=%s",
+ bottomOrRight, exitReasonToString(exitReason));
final boolean mainStageToTop =
bottomOrRight ? mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
: mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT;
final StageTaskListener toTopStage = mainStageToTop ? mMainStage : mSideStage;
if (!ENABLE_SHELL_TRANSITIONS) {
- exitSplitScreen(toTopStage, reason);
+ exitSplitScreen(toTopStage, exitReason);
return;
}
@@ -2219,6 +2289,7 @@
@Override
public void onLayoutSizeChanged(SplitLayout layout) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onLayoutSizeChanged");
// Reset this flag every time onLayoutSizeChanged.
mShowDecorImmediately = false;
@@ -2278,8 +2349,11 @@
mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
final StageTaskListener bottomRightStage =
mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
- return layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo,
+ boolean updated = layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo,
bottomRightStage.mRootTaskInfo);
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "updateWindowBounds: topLeftStage=%s bottomRightStage=%s",
+ layout.getBounds1(), layout.getBounds2());
+ return updated;
}
void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t,
@@ -2291,6 +2365,9 @@
(layout != null ? layout : mSplitLayout).applySurfaceChanges(t, topLeftStage.mRootLeash,
bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer,
applyResizingOffset);
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "updateSurfaceBounds: topLeftStage=%s bottomRightStage=%s",
+ layout.getBounds1(), layout.getBounds2());
}
@Override
@@ -2329,6 +2406,8 @@
@Override
public void setLayoutOffsetTarget(int offsetX, int offsetY, SplitLayout layout) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setLayoutOffsetTarget: x=%d y=%d",
+ offsetX, offsetY);
final StageTaskListener topLeftStage =
mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
final StageTaskListener bottomRightStage =
@@ -2343,6 +2422,7 @@
if (displayId != DEFAULT_DISPLAY) {
return;
}
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onDisplayAdded: display=%d", displayId);
mDisplayController.addDisplayChangingController(this::onDisplayChange);
}
@@ -2357,8 +2437,14 @@
private void onDisplayChange(int displayId, int fromRotation, int toRotation,
@Nullable DisplayAreaInfo newDisplayAreaInfo, WindowContainerTransaction wct) {
- if (displayId != DEFAULT_DISPLAY || !mMainStage.isActive()) return;
+ if (displayId != DEFAULT_DISPLAY || !mMainStage.isActive()) {
+ return;
+ }
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "onDisplayChange: display=%d fromRot=%d toRot=%d config=%s",
+ displayId, fromRotation, toRotation,
+ newDisplayAreaInfo != null ? newDisplayAreaInfo.configuration : null);
mSplitLayout.rotateTo(toRotation);
if (newDisplayAreaInfo != null) {
mSplitLayout.updateConfiguration(newDisplayAreaInfo.configuration);
@@ -2369,6 +2455,7 @@
@VisibleForTesting
void onFoldedStateChanged(boolean folded) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFoldedStateChanged: folded=%b", folded);
mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
if (!folded) return;
@@ -2439,6 +2526,8 @@
final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
if (triggerTask == null) {
if (isSplitActive()) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "handleRequest: transition=%d display rotation",
+ request.getDebugId());
// Check if the display is rotating.
final TransitionRequestInfo.DisplayChange displayChange =
request.getDisplayChange();
@@ -2467,6 +2556,8 @@
}
if (isSplitActive()) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "handleRequest: transition=%d split active",
+ request.getDebugId());
// Try to handle everything while in split-screen, so return a WCT even if it's empty.
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " split is active so using split"
+ "Transition to handle request. triggerTask=%d type=%s mainChildren=%d"
@@ -2541,6 +2632,8 @@
return null;
} else {
if (isOpening && getStageOfTask(triggerTask) != null) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "handleRequest: transition=%d enter split",
+ request.getDebugId());
// One task is appearing into split, prepare to enter split screen.
out = new WindowContainerTransaction();
prepareEnterSplitScreen(out);
@@ -2557,6 +2650,8 @@
*/
public void addEnterOrExitIfNeeded(@Nullable TransitionRequestInfo request,
@NonNull WindowContainerTransaction outWCT) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "addEnterOrExitIfNeeded: transition=%d",
+ request.getDebugId());
final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
if (triggerTask != null && triggerTask.displayId != mDisplayId) {
// Skip handling task on the other display.
@@ -2591,6 +2686,7 @@
public void mergeAnimation(IBinder transition, TransitionInfo info,
SurfaceControl.Transaction t, IBinder mergeTarget,
Transitions.TransitionFinishCallback finishCallback) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "mergeAnimation: transition=%d", info.getDebugId());
mSplitTransitions.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
}
@@ -2602,6 +2698,7 @@
@Override
public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
@Nullable SurfaceControl.Transaction finishT) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed");
mSplitTransitions.onTransitionConsumed(transition, aborted, finishT);
}
@@ -2617,6 +2714,7 @@
// If we're not in split-mode, just abort so something else can handle it.
if (!mMainStage.isActive()) return false;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startAnimation: transition=%d", info.getDebugId());
mSplitLayout.setFreezeDividerWindow(false);
final StageChangeRecord record = new StageChangeRecord();
final int transitType = info.getType();
@@ -2727,6 +2825,8 @@
if (mMixedHandler.animatePendingSplitWithDisplayChange(transition, info,
startTransaction, finishTransaction, finishCallback)) {
if (mSplitTransitions.isPendingResize(transition)) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "startAnimation: transition=%d display change", info.getDebugId());
// Only need to update in resize because divider exist before transition.
mSplitLayout.update(startTransaction, true /* resetImePosition */);
startTransaction.apply();
@@ -2797,6 +2897,8 @@
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startPendingAnimation: transition=%d",
+ info.getDebugId());
boolean shouldAnimate = true;
if (mSplitTransitions.isPendingEnter(transition)) {
shouldAnimate = startPendingEnterAnimation(transition,
@@ -2830,6 +2932,7 @@
/** Called to clean-up state and do house-keeping after the animation is done. */
public void onTransitionAnimationComplete() {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionAnimationComplete");
// If still playing, let it finish.
if (!mMainStage.isActive() && !mIsExiting) {
// Update divider state after animation so that it is still around and positioned
@@ -2842,6 +2945,8 @@
@NonNull SplitScreenTransitions.EnterSession enterTransition,
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t,
@NonNull SurfaceControl.Transaction finishT) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startPendingEnterAnimation: enterTransition=%s",
+ enterTransition);
// First, verify that we actually have opened apps in both splits.
TransitionInfo.Change mainChild = null;
TransitionInfo.Change sideChild = null;
@@ -2959,6 +3064,7 @@
}
public void goToFullscreenFromSplit() {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "goToFullscreenFromSplit");
// If main stage is focused, toEnd = true if
// mSideStagePosition = SPLIT_POSITION_BOTTOM_OR_RIGHT. Otherwise toEnd = false
// If side stage is focused, toEnd = true if
@@ -2974,6 +3080,7 @@
/** Move the specified task to fullscreen, regardless of focus state. */
public void moveTaskToFullscreen(int taskId, int exitReason) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "moveTaskToFullscreen");
boolean leftOrTop;
if (mMainStage.containsTask(taskId)) {
leftOrTop = (mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT);
@@ -2994,6 +3101,7 @@
*/
public void onPipExpandToSplit(WindowContainerTransaction wct,
ActivityManager.RunningTaskInfo taskInfo) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onPipExpandToSplit: task=%s", taskInfo);
prepareEnterSplitScreen(wct, taskInfo, getActivateSplitPosition(taskInfo),
false /*resizeAnim*/);
@@ -3040,6 +3148,9 @@
public void prepareDismissAnimation(@StageType int toStage, @ExitReason int dismissReason,
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t,
@NonNull SurfaceControl.Transaction finishT) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "prepareDismissAnimation: transition=%d toStage=%d reason=%s",
+ info.getDebugId(), toStage, exitReasonToString(dismissReason));
// Make some noise if things aren't totally expected. These states shouldn't effect
// transitions locally, but remotes (like Launcher) may get confused if they were
// depending on listener callbacks. This can happen because task-organizer callbacks
@@ -3126,6 +3237,9 @@
@NonNull SplitScreenTransitions.DismissSession dismissTransition,
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t,
@NonNull SurfaceControl.Transaction finishT) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "startPendingDismissAnimation: transition=%d dismissTransition=%s",
+ info.getDebugId(), dismissTransition);
prepareDismissAnimation(dismissTransition.mDismissTop, dismissTransition.mReason, info,
t, finishT);
if (dismissTransition.mDismissTop == STAGE_TYPE_UNDEFINED) {
@@ -3146,6 +3260,8 @@
/** Call this when starting the open-recents animation while split-screen is active. */
public void onRecentsInSplitAnimationStart(TransitionInfo info) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRecentsInSplitAnimationStart: transition=%d",
+ info.getDebugId());
if (isSplitScreenVisible()) {
// Cache tasks on live tile.
for (int i = 0; i < info.getChanges().size(); ++i) {
@@ -3178,6 +3294,7 @@
/** Call this when the recents animation during split-screen finishes. */
public void onRecentsInSplitAnimationFinish(WindowContainerTransaction finishWct,
SurfaceControl.Transaction finishT) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRecentsInSplitAnimationFinish");
mPausingTasks.clear();
// Check if the recent transition is finished by returning to the current
// split, so we can restore the divider bar.
@@ -3203,6 +3320,7 @@
/** Call this when the recents animation finishes by doing pair-to-pair switch. */
public void onRecentsPairToPairAnimationFinish(WindowContainerTransaction finishWct) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRecentsPairToPairAnimationFinish");
// Pair-to-pair switch happened so here should evict the live tile from its stage.
// Otherwise, the task will remain in stage, and occluding the new task when next time
// user entering recents.
@@ -3284,6 +3402,7 @@
* handled.
*/
private void setSplitsVisible(boolean visible) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setSplitsVisible: visible=%b", visible);
mMainStageListener.mVisible = mSideStageListener.mVisible = visible;
mMainStageListener.mHasChildren = mSideStageListener.mHasChildren = visible;
}
@@ -3292,6 +3411,7 @@
* Sets drag info to be logged when splitscreen is next entered.
*/
public void onDroppedToSplit(@SplitPosition int position, InstanceId dragSessionId) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onDroppedToSplit: position=%d", position);
if (!isSplitScreenVisible()) {
mIsDropEntering = true;
mSkipEvictingMainStageChildren = true;
@@ -3308,7 +3428,8 @@
/**
* Sets info to be logged when splitscreen is next entered.
*/
- public void onRequestToSplit(InstanceId sessionId, int enterReason) {
+ public void onRequestToSplit(InstanceId sessionId, @SplitEnterReason int enterReason) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRequestToSplit: reason=%d", enterReason);
if (!isSplitScreenVisible() && !ENABLE_SHELL_TRANSITIONS) {
// If split running background, exit split first.
// Skip this on shell transition due to we could evict existing tasks on transition
@@ -3384,6 +3505,7 @@
@Override
public void onNoLongerSupportMultiWindow(ActivityManager.RunningTaskInfo taskInfo) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onNoLongerSupportMultiWindow: task=%s", taskInfo);
if (mMainStage.isActive()) {
final boolean isMainStage = mMainStageListener == this;
if (!ENABLE_SHELL_TRANSITIONS) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index af7bf36..f33ab33 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -25,6 +25,7 @@
import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES;
import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
import android.annotation.CallSuper;
@@ -44,6 +45,7 @@
import androidx.annotation.NonNull;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ArrayUtils;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -175,6 +177,9 @@
@Override
@CallSuper
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskAppeared: task=%d taskParent=%d rootTask=%d",
+ taskInfo.taskId, taskInfo.parentTaskId,
+ mRootTaskInfo != null ? mRootTaskInfo.taskId : -1);
if (mRootTaskInfo == null) {
mRootLeash = leash;
mRootTaskInfo = taskInfo;
@@ -225,6 +230,9 @@
|| !ArrayUtils.contains(CONTROLLED_ACTIVITY_TYPES, taskInfo.getActivityType())
|| !ArrayUtils.contains(CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE,
taskInfo.getWindowingMode())) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "onTaskInfoChanged: task=%d no longer supports multiwindow",
+ taskInfo.taskId);
// Leave split screen if the task no longer supports multi window or have
// uncontrolled task.
mCallbacks.onNoLongerSupportMultiWindow(taskInfo);
@@ -251,6 +259,7 @@
@Override
@CallSuper
public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskVanished: task=%d", taskInfo.taskId);
final int taskId = taskInfo.taskId;
if (mRootTaskInfo.taskId == taskId) {
mCallbacks.onRootTaskVanished();
@@ -333,6 +342,7 @@
}
void addTask(ActivityManager.RunningTaskInfo task, WindowContainerTransaction wct) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "addTask: task=%d", task.taskId);
// Clear overridden bounds and windowing mode to make sure the child task can inherit
// windowing mode and bounds from split root.
wct.setWindowingMode(task.token, WINDOWING_MODE_UNDEFINED)
@@ -342,6 +352,7 @@
}
void reorderChild(int taskId, boolean onTop, WindowContainerTransaction wct) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "reorderChild: task=%d onTop=%b", taskId, onTop);
if (!containsTask(taskId)) {
return;
}
@@ -357,6 +368,7 @@
/** Collects all the current child tasks and prepares transaction to evict them to display. */
void evictAllChildren(WindowContainerTransaction wct) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Evicting all children");
for (int i = mChildrenTaskInfo.size() - 1; i >= 0; i--) {
final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i);
wct.reparent(taskInfo.token, null /* parent */, false /* onTop */);
@@ -367,11 +379,13 @@
for (int i = mChildrenTaskInfo.size() - 1; i >= 0; i--) {
final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i);
if (taskId == taskInfo.taskId) continue;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Evict other child: task=%d", taskId);
wct.reparent(taskInfo.token, null /* parent */, false /* onTop */);
}
}
void evictNonOpeningChildren(RemoteAnimationTarget[] apps, WindowContainerTransaction wct) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "evictNonOpeningChildren");
final SparseArray<ActivityManager.RunningTaskInfo> toBeEvict = mChildrenTaskInfo.clone();
for (int i = 0; i < apps.length; i++) {
if (apps[i].mode == MODE_OPENING) {
@@ -380,6 +394,7 @@
}
for (int i = toBeEvict.size() - 1; i >= 0; i--) {
final ActivityManager.RunningTaskInfo taskInfo = toBeEvict.valueAt(i);
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Evict non-opening child: task=%d", taskInfo.taskId);
wct.reparent(taskInfo.token, null /* parent */, false /* onTop */);
}
}
@@ -388,12 +403,15 @@
for (int i = mChildrenTaskInfo.size() - 1; i >= 0; i--) {
final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i);
if (!taskInfo.isVisible) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Evict invisible child: task=%d",
+ taskInfo.taskId);
wct.reparent(taskInfo.token, null /* parent */, false /* onTop */);
}
}
}
void evictChildren(WindowContainerTransaction wct, int taskId) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Evict child: task=%d", taskId);
final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.get(taskId);
if (taskInfo != null) {
wct.reparent(taskInfo.token, null /* parent */, false /* onTop */);
diff --git a/packages/CredentialManager/res/values/strings.xml b/packages/CredentialManager/res/values/strings.xml
index 82b47a9..527701c 100644
--- a/packages/CredentialManager/res/values/strings.xml
+++ b/packages/CredentialManager/res/values/strings.xml
@@ -63,9 +63,9 @@
<!-- This appears as the description body of the modal bottom sheet which provides all available providers for users to choose. [CHAR LIMIT=200] -->
<string name="choose_provider_body">Select a password manager to save your info and sign in faster next time</string>
<!-- This appears as the title of the modal bottom sheet for users to choose the create option inside a provider when the credential type is passkey. [CHAR LIMIT=200] -->
- <string name="choose_create_option_passkey_title">Create passkey for <xliff:g id="appName" example="Tribank">%1$s</xliff:g>?</string>
+ <string name="choose_create_option_passkey_title">Create passkey to sign in to <xliff:g id="appName" example="Tribank">%1$s</xliff:g>?</string>
<!-- This appears as the title of the modal bottom sheet for users to choose the create option inside a provider when the credential type is password. [CHAR LIMIT=200] -->
- <string name="choose_create_option_password_title">Save password for <xliff:g id="appName" example="Tribank">%1$s</xliff:g>?</string>
+ <string name="choose_create_option_password_title">Save password to sign in to <xliff:g id="appName" example="Tribank">%1$s</xliff:g>?</string>
<!-- This appears as the title of the modal bottom sheet for users to choose the create option inside a provider when the credential type is others. [CHAR LIMIT=200] -->
<string name="choose_create_option_sign_in_title">Save sign-in info for <xliff:g id="appName" example="Tribank">%1$s</xliff:g>?</string>
<!-- Types which are inserted as a placeholder as credentialTypes for other strings. [CHAR LIMIT=200] -->
diff --git a/packages/PackageInstaller/res/layout/install_content_view.xml b/packages/PackageInstaller/res/layout/install_content_view.xml
index 2ecd2d5..524a88a 100644
--- a/packages/PackageInstaller/res/layout/install_content_view.xml
+++ b/packages/PackageInstaller/res/layout/install_content_view.xml
@@ -24,114 +24,116 @@
android:paddingLeft="?android:attr/dialogPreferredPadding"
android:paddingRight="?android:attr/dialogPreferredPadding">
- <LinearLayout
- android:id="@+id/staging"
+ <LinearLayout
+ android:id="@+id/staging"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:visibility="invisible">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.Material.Subhead"
+ android:text="@string/message_staging" />
+
+ <ProgressBar
+ android:id="@+id/progress_indeterminate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
- android:visibility="invisible">
+ android:paddingTop="8dp"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:indeterminate="true" />
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
- android:text="@string/message_staging" />
+ </LinearLayout>
- <ProgressBar
- android:id="@+id/progress_indeterminate"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="8dp"
- style="?android:attr/progressBarStyleHorizontal"
- android:indeterminate="true" />
+ <LinearLayout
+ android:id="@+id/installing"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:visibility="invisible">
- </LinearLayout>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.Material.Subhead"
+ android:text="@string/installing" />
- <LinearLayout
- android:id="@+id/installing"
+ <ProgressBar
+ android:id="@+id/progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
- android:visibility="invisible">
+ android:paddingTop="8dp"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:indeterminate="true" />
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
- android:text="@string/installing" />
+ </LinearLayout>
- <ProgressBar
- android:id="@+id/progress"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="8dp"
- style="?android:attr/progressBarStyleHorizontal"
- android:indeterminate="true" />
+ <TextView
+ android:id="@+id/install_confirm_question"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.Material.Subhead"
+ android:text="@string/install_confirm_question"
+ android:visibility="invisible"
+ android:scrollbars="vertical" />
- </LinearLayout>
+ <TextView
+ android:id="@+id/install_confirm_question_update"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.Material.Subhead"
+ android:text="@string/install_confirm_question_update"
+ android:visibility="invisible"
+ android:scrollbars="vertical" />
- <TextView
- android:id="@+id/install_confirm_question"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
- android:text="@string/install_confirm_question"
- android:visibility="invisible" />
+ <TextView
+ android:id="@+id/install_success"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.Material.Subhead"
+ android:text="@string/install_done"
+ android:visibility="invisible" />
- <TextView
- android:id="@+id/install_confirm_question_update"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
- android:text="@string/install_confirm_question_update"
- android:visibility="invisible" />
+ <TextView
+ android:id="@+id/install_failed"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.Material.Subhead"
+ android:text="@string/install_failed"
+ android:visibility="invisible" />
- <TextView
- android:id="@+id/install_success"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
- android:text="@string/install_done"
- android:visibility="invisible" />
+ <TextView
+ android:id="@+id/install_failed_blocked"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.Material.Subhead"
+ android:text="@string/install_failed_blocked"
+ android:visibility="invisible" />
- <TextView
- android:id="@+id/install_failed"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
- android:text="@string/install_failed"
- android:visibility="invisible" />
+ <TextView
+ android:id="@+id/install_failed_conflict"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.Material.Subhead"
+ android:text="@string/install_failed_conflict"
+ android:visibility="invisible" />
- <TextView
- android:id="@+id/install_failed_blocked"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
- android:text="@string/install_failed_blocked"
- android:visibility="invisible" />
+ <TextView
+ android:id="@+id/install_failed_incompatible"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.Material.Subhead"
+ android:text="@string/install_failed_incompatible"
+ android:visibility="invisible" />
- <TextView
- android:id="@+id/install_failed_conflict"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
- android:text="@string/install_failed_conflict"
- android:visibility="invisible" />
+ <TextView
+ android:id="@+id/install_failed_invalid_apk"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.Material.Subhead"
+ android:text="@string/install_failed_invalid_apk"
+ android:visibility="invisible" />
- <TextView
- android:id="@+id/install_failed_incompatible"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
- android:text="@string/install_failed_incompatible"
- android:visibility="invisible" />
-
- <TextView
- android:id="@+id/install_failed_invalid_apk"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
- android:text="@string/install_failed_invalid_apk"
- android:visibility="invisible" />
-
-</FrameLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/PackageInstaller/res/layout/uninstall_content_view.xml b/packages/PackageInstaller/res/layout/uninstall_content_view.xml
index 5666c0e..434e333 100644
--- a/packages/PackageInstaller/res/layout/uninstall_content_view.xml
+++ b/packages/PackageInstaller/res/layout/uninstall_content_view.xml
@@ -18,31 +18,36 @@
<!-- Check box that is displayed in the activity resolver UI for the user
to make their selection the preferred activity. -->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:theme="?android:attr/alertDialogTheme"
- android:orientation="vertical"
- android:paddingTop="8dp"
- android:paddingStart="?android:attr/dialogPreferredPadding"
- android:paddingEnd="?android:attr/dialogPreferredPadding"
- android:clipToPadding="false">
+ android:layout_height="wrap_content">
- <TextView
- android:id="@+id/message"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead" />
+ <LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:theme="?android:attr/alertDialogTheme"
+ android:orientation="vertical"
+ android:paddingTop="8dp"
+ android:paddingStart="?android:attr/dialogPreferredPadding"
+ android:paddingEnd="?android:attr/dialogPreferredPadding"
+ android:clipToPadding="false">
- <CheckBox
- android:id="@+id/keepData"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:layout_marginStart="-8dp"
- android:paddingLeft="8sp"
- android:visibility="gone"
- style="@android:style/TextAppearance.Material.Subhead" />
+ <TextView
+ android:id="@+id/message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.Material.Subhead" />
-</LinearLayout>
\ No newline at end of file
+ <CheckBox
+ android:id="@+id/keepData"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:layout_marginStart="-8dp"
+ android:paddingLeft="8sp"
+ android:visibility="gone"
+ style="@android:style/TextAppearance.Material.Subhead" />
+
+ </LinearLayout>
+</ScrollView>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index cf6aab6..e95a8e6 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -51,6 +51,7 @@
import android.text.Html;
import android.text.Spanned;
import android.text.TextUtils;
+import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.View;
import android.widget.Button;
@@ -174,6 +175,7 @@
}
viewToEnable.setVisibility(View.VISIBLE);
+ viewToEnable.setMovementMethod(new ScrollingMovementMethod());
mEnableOk = true;
mOk.setEnabled(true);
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java
index dbe32cc..0a4aa48 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java
@@ -22,6 +22,7 @@
import android.content.DialogInterface;
import android.os.Bundle;
import android.text.Html;
+import android.text.method.ScrollingMovementMethod;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
@@ -94,6 +95,7 @@
viewToEnable = dialogView.requireViewById(R.id.install_confirm_question);
}
viewToEnable.setVisibility(View.VISIBLE);
+ viewToEnable.setMovementMethod(new ScrollingMovementMethod());
return mDialog;
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
index 452dc03..d23cd0c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
@@ -35,11 +35,13 @@
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
import com.android.systemui.keyguard.ui.composable.section.DefaultClockSection
import com.android.systemui.keyguard.ui.composable.section.LockSection
+import com.android.systemui.keyguard.ui.composable.section.MediaCarouselSection
import com.android.systemui.keyguard.ui.composable.section.NotificationSection
import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection
import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
+import com.android.systemui.media.controls.ui.composable.MediaCarousel
import com.android.systemui.res.R
import dagger.Binds
import dagger.Module
@@ -63,6 +65,7 @@
private val ambientIndicationSectionOptional: Optional<AmbientIndicationSection>,
private val bottomAreaSection: BottomAreaSection,
private val settingsMenuSection: SettingsMenuSection,
+ private val mediaCarouselSection: MediaCarouselSection,
private val clockInteractor: KeyguardClockInteractor,
) : ComposableLockscreenSceneBlueprint {
@@ -112,10 +115,16 @@
if (viewModel.isLargeClockVisible) {
Spacer(modifier = Modifier.weight(weight = 1f))
- with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
+ with(clockSection) {
+ LargeClock(
+ modifier = Modifier.fillMaxWidth(),
+ )
+ }
}
- if (viewModel.areNotificationsVisible) {
+ with(mediaCarouselSection) { MediaCarousel() }
+
+ if (viewModel.areNotificationsVisible(resources = resources)) {
with(notificationSection) {
Notifications(
modifier = Modifier.fillMaxWidth().weight(weight = 1f)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
index 71c60c7..c422c4b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
@@ -35,6 +35,7 @@
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
import com.android.systemui.keyguard.ui.composable.section.DefaultClockSection
import com.android.systemui.keyguard.ui.composable.section.LockSection
+import com.android.systemui.keyguard.ui.composable.section.MediaCarouselSection
import com.android.systemui.keyguard.ui.composable.section.NotificationSection
import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection
@@ -63,6 +64,7 @@
private val ambientIndicationSectionOptional: Optional<AmbientIndicationSection>,
private val bottomAreaSection: BottomAreaSection,
private val settingsMenuSection: SettingsMenuSection,
+ private val mediaCarouselSection: MediaCarouselSection,
private val clockInteractor: KeyguardClockInteractor,
) : ComposableLockscreenSceneBlueprint {
@@ -115,7 +117,9 @@
with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
}
- if (viewModel.areNotificationsVisible) {
+ with(mediaCarouselSection) { MediaCarousel() }
+
+ if (viewModel.areNotificationsVisible(resources = resources)) {
with(notificationSection) {
Notifications(
modifier = Modifier.fillMaxWidth().weight(weight = 1f)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
index af836b6..d218425 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
@@ -41,6 +41,7 @@
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
import com.android.systemui.keyguard.ui.composable.section.DefaultClockSection
import com.android.systemui.keyguard.ui.composable.section.LockSection
+import com.android.systemui.keyguard.ui.composable.section.MediaCarouselSection
import com.android.systemui.keyguard.ui.composable.section.NotificationSection
import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection
@@ -70,6 +71,7 @@
private val ambientIndicationSectionOptional: Optional<AmbientIndicationSection>,
private val bottomAreaSection: BottomAreaSection,
private val settingsMenuSection: SettingsMenuSection,
+ private val mediaCarouselSection: MediaCarouselSection,
private val clockInteractor: KeyguardClockInteractor,
private val largeScreenHeaderHelper: LargeScreenHeaderHelper,
) : ComposableLockscreenSceneBlueprint {
@@ -100,6 +102,14 @@
modifier = Modifier.fillMaxHeight().weight(weight = 1f),
horizontalAlignment = Alignment.CenterHorizontally,
) {
+ with(clockSection) {
+ SmallClock(
+ burnInParams = burnIn.parameters,
+ onTopChanged = burnIn.onSmallClockTopChanged,
+ modifier = Modifier.fillMaxWidth(),
+ )
+ }
+
with(smartSpaceSection) {
SmartSpace(
burnInParams = burnIn.parameters,
@@ -121,9 +131,13 @@
)
}
- Spacer(modifier = Modifier.weight(weight = 1f))
- with(clockSection) { LargeClock() }
- Spacer(modifier = Modifier.weight(weight = 1f))
+ if (viewModel.isLargeClockVisible) {
+ Spacer(modifier = Modifier.weight(weight = 1f))
+ with(clockSection) { LargeClock() }
+ Spacer(modifier = Modifier.weight(weight = 1f))
+ }
+
+ with(mediaCarouselSection) { MediaCarousel() }
}
with(notificationSection) {
val splitShadeTopMargin: Dp =
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt
index e2e7a95..f86623f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt
@@ -41,12 +41,14 @@
import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
import com.android.systemui.keyguard.ui.composable.section.LockSection
+import com.android.systemui.keyguard.ui.composable.section.MediaCarouselSection
import com.android.systemui.keyguard.ui.composable.section.NotificationSection
import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection
import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
import com.android.systemui.keyguard.ui.composable.section.WeatherClockSection
import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
+import com.android.systemui.media.controls.ui.composable.MediaCarousel
import com.android.systemui.res.R
import com.android.systemui.shade.LargeScreenHeaderHelper
import dagger.Binds
@@ -68,6 +70,7 @@
private val bottomAreaSection: BottomAreaSection,
private val settingsMenuSection: SettingsMenuSection,
private val clockInteractor: KeyguardClockInteractor,
+ private val mediaCarouselSection: MediaCarouselSection,
) : ComposableLockscreenSceneBlueprint {
override val id: String = WEATHER_CLOCK_BLUEPRINT_ID
@@ -107,7 +110,9 @@
)
}
- if (viewModel.areNotificationsVisible) {
+ with(mediaCarouselSection) { MediaCarousel() }
+
+ if (viewModel.areNotificationsVisible(resources = resources)) {
with(notificationSection) {
Notifications(
modifier = Modifier.fillMaxWidth().weight(weight = 1f)
@@ -228,6 +233,7 @@
private val clockInteractor: KeyguardClockInteractor,
private val largeScreenHeaderHelper: LargeScreenHeaderHelper,
private val weatherClockSection: WeatherClockSection,
+ private val mediaCarouselSection: MediaCarouselSection,
) : ComposableLockscreenSceneBlueprint {
override val id: String = SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID
@@ -276,6 +282,8 @@
),
)
}
+
+ with(mediaCarouselSection) { MediaCarousel() }
}
with(notificationSection) {
val splitShadeTopMargin: Dp =
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
index 335c915..152cc67 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
@@ -39,7 +39,6 @@
import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
-import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
import javax.inject.Inject
/** Provides small clock and large clock composables for the default clock face. */
@@ -49,7 +48,6 @@
private val viewModel: KeyguardClockViewModel,
private val clockInteractor: KeyguardClockInteractor,
private val aodBurnInViewModel: AodBurnInViewModel,
- private val lockscreenSmartspaceController: LockscreenSmartspaceController,
) {
@Composable
@@ -62,15 +60,11 @@
val currentClock by viewModel.currentClock.collectAsState()
viewModel.clock = currentClock
- if (clockSize != KeyguardClockSwitch.SMALL) {
+ if (clockSize != KeyguardClockSwitch.SMALL || currentClock?.smallClock?.view == null) {
onTopChanged(null)
return
}
- if (currentClock?.smallClock?.view == null) {
- return
- }
-
val view = LocalView.current
DisposableEffect(view) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt
new file mode 100644
index 0000000..dae120c
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.composable.section
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.onSizeChanged
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.res.dimensionResource
+import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.keyguard.ui.viewmodel.MediaCarouselViewModel
+import com.android.systemui.media.controls.ui.composable.MediaCarousel
+import com.android.systemui.media.controls.ui.controller.MediaCarouselController
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.media.dagger.MediaModule
+import com.android.systemui.res.R
+import com.android.systemui.util.animation.MeasurementInput
+import javax.inject.Inject
+import javax.inject.Named
+
+class MediaCarouselSection
+@Inject
+constructor(
+ private val mediaCarouselController: MediaCarouselController,
+ @param:Named(MediaModule.KEYGUARD) private val mediaHost: MediaHost,
+ private val mediaCarouselViewModel: MediaCarouselViewModel,
+) {
+
+ @Composable
+ fun SceneScope.MediaCarousel(modifier: Modifier = Modifier) {
+ if (!mediaCarouselViewModel.isMediaVisible) {
+ return
+ }
+
+ if (mediaCarouselController.mediaFrame == null) {
+ return
+ }
+
+ val mediaHeight = dimensionResource(R.dimen.qs_media_session_height_expanded)
+ // TODO(b/312714128): MediaPlayer background size is not as expected.
+ MediaCarousel(
+ modifier =
+ modifier.height(mediaHeight).fillMaxWidth().onSizeChanged { size ->
+ // Notify controller to size the carousel for the
+ // current space
+ mediaHost.measurementInput = MeasurementInput(size.width, size.height)
+ mediaCarouselController.setSceneContainerSize(size.width, size.height)
+ },
+ mediaHost = mediaHost,
+ layoutWidth = 0, // Layout width is not used.
+ layoutHeight = with(LocalDensity.current) { mediaHeight.toPx() }.toInt(),
+ carouselController = mediaCarouselController,
+ )
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
index 61b2d4e..d3e4553 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
@@ -16,9 +16,12 @@
package com.android.systemui.media.controls.ui.composable
+import android.view.ViewGroup
+import android.widget.FrameLayout
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView
+import androidx.core.view.contains
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.media.controls.ui.controller.MediaCarouselController
@@ -45,6 +48,20 @@
AndroidView(
modifier = modifier.element(MediaCarousel.Elements.Content),
- factory = { _ -> carouselController.mediaFrame },
+ factory = { context ->
+ FrameLayout(context).apply {
+ val mediaFrame = carouselController.mediaFrame
+ (mediaFrame.parent as? ViewGroup)?.removeView(mediaFrame)
+ addView(mediaFrame)
+ }
+ },
+ update = {
+ if (it.contains(carouselController.mediaFrame)) {
+ return@AndroidView
+ }
+ val mediaFrame = carouselController.mediaFrame
+ (mediaFrame.parent as? ViewGroup)?.removeView(mediaFrame)
+ it.addView(mediaFrame)
+ },
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index b611e0a..36919d0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -1052,32 +1052,6 @@
biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(true)
faceAuthenticateIsCalled()
}
- @Test
- fun authFailedCallAfterAuthLockedOutErrorShouldBeIgnored() =
- testScope.runTest {
- initCollectors()
- allPreconditionsToRunFaceAuthAreTrue()
- runCurrent()
- assertThat(canFaceAuthRun()).isTrue()
-
- underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED, false)
- runCurrent()
-
- faceAuthenticateIsCalled()
- authenticationCallback.value.onAuthenticationError(
- FACE_ERROR_LOCKOUT_PERMANENT,
- "Too many attempts, face not available"
- )
-
- val lockoutError = authStatus() as ErrorFaceAuthenticationStatus
- assertThat(lockedOut()).isTrue()
- assertThat(lockoutError.isLockoutError()).isTrue()
-
- authenticationCallback.value.onAuthenticationFailed()
- runCurrent()
-
- assertThat(authStatus()).isEqualTo(lockoutError)
- }
private suspend fun TestScope.testGatingCheckForFaceAuth(
gatingCheckModifier: suspend () -> Unit
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
index d4dd2ac..ad1cef1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
@@ -26,6 +26,7 @@
import com.android.systemui.keyguard.data.repository.fakeKeyguardClockRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
+import com.android.systemui.res.R
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -46,6 +47,7 @@
fun setup() {
with(kosmos) {
fakeFeatureFlagsClassic.set(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED, true)
+ overrideResource(R.bool.config_use_split_notification_shade, false)
underTest = lockscreenContentViewModel
}
}
@@ -87,11 +89,21 @@
}
@Test
+ fun areNotificationsVisible_splitShadeTrue_true() =
+ with(kosmos) {
+ testScope.runTest {
+ overrideResource(R.bool.config_use_split_notification_shade, true)
+ kosmos.fakeKeyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
+
+ assertThat(underTest.areNotificationsVisible(context.resources)).isTrue()
+ }
+ }
+ @Test
fun areNotificationsVisible_withSmallClock_true() =
with(kosmos) {
testScope.runTest {
kosmos.fakeKeyguardClockRepository.setClockSize(KeyguardClockSwitch.SMALL)
- assertThat(underTest.areNotificationsVisible).isTrue()
+ assertThat(underTest.areNotificationsVisible(context.resources)).isTrue()
}
}
@@ -100,7 +112,7 @@
with(kosmos) {
testScope.runTest {
kosmos.fakeKeyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
- assertThat(underTest.areNotificationsVisible).isFalse()
+ assertThat(underTest.areNotificationsVisible(context.resources)).isFalse()
}
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
index 6390e82..db4d42f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
@@ -53,6 +53,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.settings.FakeGlobalSettings;
import com.android.systemui.util.time.FakeSystemClock;
@@ -142,6 +143,12 @@
}
}
+ @Override
+ public void SysuiSetup() throws Exception {
+ super.SysuiSetup();
+ mSetFlagsRule.disableFlags(NotificationThrottleHun.FLAG_NAME);
+ }
+
@Test
public void testShowNotification_addsEntry() {
final BaseHeadsUpManager alm = createHeadsUpManager();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.java
index ec23f76..c032d7c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.java
@@ -39,6 +39,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
+import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun;
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -136,6 +137,8 @@
@Before
public void setUp() {
+ mSetFlagsRule.disableFlags(NotificationThrottleHun.FLAG_NAME);
+
when(mShadeInteractor.isAnyExpanded()).thenReturn(StateFlowKt.MutableStateFlow(false));
final AccessibilityManagerWrapper accessibilityMgr =
mDependency.injectMockDependency(AccessibilityManagerWrapper.class);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
index 363dd01..f528ec8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
@@ -18,11 +18,16 @@
import android.content.res.ColorStateList;
import android.content.res.Configuration;
+import android.hardware.biometrics.BiometricSourceType;
+import android.os.SystemClock;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
+import android.util.Log;
+import android.util.Pair;
import android.view.View;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -40,6 +45,16 @@
public class KeyguardMessageAreaController<T extends KeyguardMessageArea>
extends ViewController<T> {
/**
+ * Pair representing:
+ * first - BiometricSource the currently displayed message is associated with.
+ * second - Timestamp the biometric message came in uptimeMillis.
+ * This Pair can be null if the message is not associated with a biometric.
+ */
+ @Nullable
+ private Pair<BiometricSourceType, Long> mMessageBiometricSource = null;
+ private static final Long SKIP_SHOWING_FACE_MESSAGE_AFTER_FP_MESSAGE_MS = 3500L;
+
+ /**
* Delay before speaking an accessibility announcement. Used to prevent
* lift-to-type from interrupting itself.
*/
@@ -149,12 +164,42 @@
* Sets a message to the underlying text view.
*/
public void setMessage(CharSequence s, boolean animate) {
+ setMessage(s, animate, null);
+ }
+
+ /**
+ * Sets a message to the underlying text view.
+ */
+ public void setMessage(CharSequence s, BiometricSourceType biometricSourceType) {
+ setMessage(s, true, biometricSourceType);
+ }
+
+ private void setMessage(
+ CharSequence s,
+ boolean animate,
+ BiometricSourceType biometricSourceType) {
+ final long uptimeMillis = SystemClock.uptimeMillis();
+ if (skipShowingFaceMessage(biometricSourceType, uptimeMillis)) {
+ Log.d("KeyguardMessageAreaController", "Skip showing face message \"" + s + "\"");
+ return;
+ }
+ mMessageBiometricSource = new Pair<>(biometricSourceType, uptimeMillis);
if (mView.isDisabled()) {
return;
}
mView.setMessage(s, animate);
}
+ private boolean skipShowingFaceMessage(
+ BiometricSourceType biometricSourceType, Long currentUptimeMillis
+ ) {
+ return mMessageBiometricSource != null
+ && biometricSourceType == BiometricSourceType.FACE
+ && mMessageBiometricSource.first == BiometricSourceType.FINGERPRINT
+ && (currentUptimeMillis - mMessageBiometricSource.second)
+ < SKIP_SHOWING_FACE_MESSAGE_AFTER_FP_MESSAGE_MS;
+ }
+
public void setMessage(int resId) {
String message = resId != 0 ? mView.getResources().getString(resId) : null;
setMessage(message);
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
index d2ad096..ce4032a 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
@@ -16,6 +16,7 @@
package com.android.keyguard.logging
+import android.hardware.biometrics.BiometricSourceType
import com.android.systemui.biometrics.AuthRippleController
import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController
import com.android.systemui.log.LogBuffer
@@ -117,6 +118,26 @@
)
}
+ fun logDropNonFingerprintMessage(
+ message: CharSequence,
+ followUpMessage: CharSequence?,
+ biometricSourceType: BiometricSourceType?,
+ ) {
+ buffer.log(
+ KeyguardIndicationController.TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = message.toString()
+ str2 = followUpMessage?.toString()
+ str3 = biometricSourceType?.name
+ },
+ {
+ "droppingNonFingerprintMessage message=$str1 " +
+ "followUpMessage:$str2 biometricSourceType:$str3"
+ }
+ )
+ }
+
fun logUpdateBatteryIndication(
powerIndication: String,
pluggedIn: Boolean,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 0bd44f0..f4cd5b9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -17,7 +17,6 @@
package com.android.systemui.biometrics;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
-import static android.hardware.biometrics.Flags.customBiometricPrompt;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_BIOMETRIC_PROMPT_TRANSITION;
@@ -33,6 +32,7 @@
import android.hardware.biometrics.BiometricAuthenticator.Modality;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager.Authenticators;
+import android.hardware.biometrics.Flags;
import android.hardware.biometrics.PromptInfo;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -402,7 +402,12 @@
@Nullable FaceSensorPropertiesInternal faceProps,
@NonNull VibratorHelper vibratorHelper
) {
- if (Utils.isBiometricAllowed(config.mPromptInfo) || customBiometricPrompt()) {
+ // Set this value before showing either of the prompt.
+ mPromptSelectorInteractorProvider.get().setShouldShowBpWithoutIconForCredential(
+ config.mPromptInfo);
+
+ if (Utils.isBiometricAllowed(config.mPromptInfo)
+ || mPromptViewModel.getShowBpWithoutIconForCredential().getValue()) {
addBiometricView(config, layoutInflater, viewModel, fpProps, faceProps, vibratorHelper);
} else if (constraintBp() && Utils.isDeviceCredentialAllowed(mConfig.mPromptInfo)) {
addCredentialView(true, false);
@@ -411,7 +416,6 @@
}
}
-
private void addBiometricView(@NonNull Config config, @NonNull LayoutInflater layoutInflater,
@NonNull PromptViewModel viewModel,
@Nullable FingerprintSensorPropertiesInternal fpProps,
@@ -534,7 +538,8 @@
() -> animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED));
if (constraintBp()) {
// Do nothing on attachment with constraintLayout
- } else if (Utils.isBiometricAllowed(mConfig.mPromptInfo) || customBiometricPrompt()) {
+ } else if (Utils.isBiometricAllowed(mConfig.mPromptInfo)
+ || mPromptViewModel.getShowBpWithoutIconForCredential().getValue()) {
mBiometricScrollView.addView(mBiometricView.asView());
} else if (Utils.isDeviceCredentialAllowed(mConfig.mPromptInfo)) {
addCredentialView(true /* animatePanel */, false /* animateContents */);
@@ -819,6 +824,9 @@
final Runnable endActionRunnable = () -> {
setVisibility(View.INVISIBLE);
+ if (Flags.customBiometricPrompt()) {
+ mPromptSelectorInteractorProvider.get().resetPrompt();
+ }
removeWindowIfAttached();
};
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt
index ad7bb0e..b87fadf 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt
@@ -16,8 +16,11 @@
package com.android.systemui.biometrics.data.repository
+import android.hardware.biometrics.Flags
import android.hardware.biometrics.PromptInfo
import com.android.systemui.biometrics.AuthController
+import com.android.systemui.biometrics.Utils
+import com.android.systemui.biometrics.Utils.isDeviceCredentialAllowed
import com.android.systemui.biometrics.shared.model.PromptKind
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
@@ -65,6 +68,18 @@
*/
val isConfirmationRequired: Flow<Boolean>
+ /**
+ * If biometric prompt without icon needs to show for displaying content prior to credential
+ * view.
+ */
+ val showBpWithoutIconForCredential: StateFlow<Boolean>
+
+ /**
+ * Update whether biometric prompt without icon needs to show for displaying content prior to
+ * credential view, which should be set before [setPrompt].
+ */
+ fun setShouldShowBpWithoutIconForCredential(promptInfo: PromptInfo)
+
/** Update the prompt configuration, which should be set before [isShowing]. */
fun setPrompt(
promptInfo: PromptInfo,
@@ -129,6 +144,19 @@
}
.distinctUntilChanged()
+ private val _showBpWithoutIconForCredential: MutableStateFlow<Boolean> = MutableStateFlow(false)
+ override val showBpWithoutIconForCredential = _showBpWithoutIconForCredential.asStateFlow()
+
+ override fun setShouldShowBpWithoutIconForCredential(promptInfo: PromptInfo) {
+ val hasCredentialViewShown = kind.value !is PromptKind.Biometric
+ val showBpForCredential =
+ Flags.customBiometricPrompt() &&
+ !Utils.isBiometricAllowed(promptInfo) &&
+ isDeviceCredentialAllowed(promptInfo) &&
+ promptInfo.contentView != null
+ _showBpWithoutIconForCredential.value = showBpForCredential && !hasCredentialViewShown
+ }
+
override fun setPrompt(
promptInfo: PromptInfo,
userId: Int,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt
index e3facff..94cea57 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt
@@ -30,6 +30,7 @@
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -59,6 +60,13 @@
/** If the prompt is currently showing. */
val isShowing: Flow<Boolean> = biometricPromptRepository.isShowing
+ /**
+ * If biometric prompt without icon needs to show for displaying content prior to credential
+ * view.
+ */
+ val showBpWithoutIconForCredential: StateFlow<Boolean> =
+ biometricPromptRepository.showBpWithoutIconForCredential
+
/** Metadata about the current credential prompt, including app-supplied preferences. */
val prompt: Flow<BiometricPromptRequest.Credential?> =
combine(
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
index b3f9574..45816c1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
@@ -32,6 +32,7 @@
import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
@@ -70,6 +71,18 @@
/** Fingerprint sensor type */
val sensorType: Flow<FingerprintSensorType>
+ /**
+ * If biometric prompt without icon needs to show for displaying content prior to credential
+ * view.
+ */
+ val showBpWithoutIconForCredential: StateFlow<Boolean>
+
+ /**
+ * Update whether biometric prompt without icon needs to show for displaying content prior to
+ * credential view, which should be set before [PromptRepository.setPrompt].
+ */
+ fun setShouldShowBpWithoutIconForCredential(promptInfo: PromptInfo)
+
/** Use biometrics for authentication. */
fun useBiometricsForAuthentication(
promptInfo: PromptInfo,
@@ -154,6 +167,12 @@
override val sensorType: Flow<FingerprintSensorType> = fingerprintPropertyRepository.sensorType
+ override val showBpWithoutIconForCredential = promptRepository.showBpWithoutIconForCredential
+
+ override fun setShouldShowBpWithoutIconForCredential(promptInfo: PromptInfo) {
+ promptRepository.setShouldShowBpWithoutIconForCredential(promptInfo)
+ }
+
override fun useBiometricsForAuthentication(
promptInfo: PromptInfo,
userId: Int,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
index 6133a51c..6f079e2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
@@ -17,6 +17,7 @@
val title: String,
val subtitle: String,
val description: String,
+ val contentView: PromptContentView?,
val userInfo: BiometricUserInfo,
val operationInfo: BiometricOperationInfo,
val showEmergencyCallButton: Boolean,
@@ -33,11 +34,11 @@
title = info.title?.toString() ?: "",
subtitle = info.subtitle?.toString() ?: "",
description = info.description?.toString() ?: "",
+ contentView = info.contentView,
userInfo = userInfo,
operationInfo = operationInfo,
showEmergencyCallButton = info.isShowEmergencyCallButton
) {
- val contentView: PromptContentView? = info.contentView
val logoRes: Int = info.logoRes
val logoBitmap: Bitmap? = info.logoBitmap
val logoDescription: String? = info.logoDescription
@@ -54,6 +55,7 @@
title = (info.deviceCredentialTitle ?: info.title)?.toString() ?: "",
subtitle = (info.deviceCredentialSubtitle ?: info.subtitle)?.toString() ?: "",
description = (info.deviceCredentialDescription ?: info.description)?.toString() ?: "",
+ contentView = info.contentView,
userInfo = userInfo,
operationInfo = operationInfo,
showEmergencyCallButton = info.isShowEmergencyCallButton
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index cd5b124..ea39247 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -22,7 +22,6 @@
import android.hardware.biometrics.BiometricAuthenticator
import android.hardware.biometrics.BiometricConstants
import android.hardware.biometrics.BiometricPrompt
-import android.hardware.biometrics.Flags.customBiometricPrompt
import android.hardware.face.FaceManager
import android.text.method.ScrollingMovementMethod
import android.util.Log
@@ -151,17 +150,6 @@
// these do not change and need to be set before any size transitions
val modalities = viewModel.modalities.first()
- // If there is no biometrics available, biometric prompt is showing just for displaying
- // content, no authentication needed.
- if (!(customBiometricPrompt() && modalities.isEmpty)) {
- PromptIconViewBinder.bind(
- iconView,
- iconOverlayView,
- iconSizeOverride,
- viewModel,
- )
- }
-
if (modalities.hasFingerprint) {
/**
* Load the given [rawResources] immediately so they are cached for use in the
@@ -233,6 +221,19 @@
)
}
+ lifecycleScope.launch {
+ viewModel.showBpWithoutIconForCredential.collect {
+ if (!it) {
+ PromptIconViewBinder.bind(
+ iconView,
+ iconOverlayView,
+ iconSizeOverride,
+ viewModel,
+ )
+ }
+ }
+ }
+
// TODO(b/251476085): migrate legacy icon controllers and remove
// The fingerprint sensor is started by the legacy
// AuthContainerView#onDialogAnimatedIn in all cases but the implicit coex flow
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
index a37d916..8336d5e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
@@ -21,7 +21,6 @@
import android.animation.ValueAnimator
import android.graphics.Outline
import android.graphics.Rect
-import android.hardware.biometrics.Flags
import android.transition.AutoTransition
import android.transition.TransitionManager
import android.view.Surface
@@ -55,6 +54,7 @@
import com.android.systemui.biometrics.ui.viewmodel.isRight
import com.android.systemui.biometrics.ui.viewmodel.isSmall
import com.android.systemui.biometrics.ui.viewmodel.isTop
+import com.android.systemui.keyguard.ui.view.layout.sections.setVisibility
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
import kotlin.math.abs
@@ -111,14 +111,9 @@
val smallConstraintSet = ConstraintSet()
smallConstraintSet.clone(mediumConstraintSet)
- viewsToHideWhenSmall.forEach { smallConstraintSet.setVisibility(it.id, View.GONE) }
val largeConstraintSet = ConstraintSet()
largeConstraintSet.clone(mediumConstraintSet)
- viewsToHideWhenSmall.forEach { largeConstraintSet.setVisibility(it.id, View.GONE) }
- largeConstraintSet.setVisibility(iconHolderView.id, View.GONE)
- largeConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
- largeConstraintSet.setVisibility(R.id.indicator, View.GONE)
largeConstraintSet.setGuidelineBegin(leftGuideline.id, 0)
largeConstraintSet.setGuidelineEnd(rightGuideline.id, 0)
largeConstraintSet.setGuidelineEnd(bottomGuideline.id, 0)
@@ -219,13 +214,18 @@
}
}
- view.repeatWhenAttached {
- var currentSize: PromptSize? = null
- val modalities = viewModel.modalities.first()
- // TODO(b/288175072): Move all visibility settings together.
- // If there is no biometrics available, biometric prompt is showing just for
- // displaying content, no authentication needed.
- if (Flags.customBiometricPrompt() && modalities.isEmpty) {
+ fun setConstraintSetVisibility() {
+ viewsToHideWhenSmall.forEach {
+ mediumConstraintSet.setVisibility(it.id, it.showContentOrHide())
+ largeConstraintSet.setVisibility(it.id, View.GONE)
+ smallConstraintSet.setVisibility(it.id, View.GONE)
+ }
+
+ largeConstraintSet.setVisibility(iconHolderView.id, View.GONE)
+ largeConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
+ largeConstraintSet.setVisibility(R.id.indicator, View.GONE)
+
+ if (viewModel.showBpWithoutIconForCredential.value) {
smallConstraintSet.setVisibility(iconHolderView.id, View.GONE)
smallConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
smallConstraintSet.setVisibility(R.id.indicator, View.GONE)
@@ -233,12 +233,17 @@
mediumConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
mediumConstraintSet.setVisibility(R.id.indicator, View.GONE)
}
+ }
+
+ view.repeatWhenAttached {
+ var currentSize: PromptSize? = null
+
lifecycleScope.launch {
combine(viewModel.position, viewModel.size, ::Pair).collect {
(position, size) ->
view.doOnAttach {
measureBounds(position)
-
+ setConstraintSetVisibility()
when {
size.isSmall -> {
val ratio =
@@ -313,7 +318,6 @@
// TODO(b/251476085): migrate the legacy panel controller and simplify this
view.repeatWhenAttached {
var currentSize: PromptSize? = null
- val modalities = viewModel.modalities.first()
lifecycleScope.launch {
/**
* View is only set visible in BiometricViewSizeBinder once PromptSize is
@@ -331,11 +335,13 @@
// prepare for animated size transitions
for (v in viewsToHideWhenSmall) {
- v.showContentOrHide(forceHide = size.isSmall)
+ v.visibility = v.showContentOrHide(forceHide = size.isSmall)
}
- if (Flags.customBiometricPrompt() && modalities.isEmpty) {
+
+ if (viewModel.showBpWithoutIconForCredential.value) {
iconHolderView.visibility = View.GONE
}
+
if (currentSize == null && size.isSmall) {
iconHolderView.alpha = 0f
}
@@ -344,9 +350,9 @@
}
// TODO(b/302735104): Fix wrong height due to the delay of
- // PromptContentView. addOnLayoutChangeListener() will cause crash when
- // showing credential view, since |PromptIconViewModel| won't release
- // the flow.
+ // PromptContentView. addOnLayoutChangeListener() will cause crash
+ // when showing credential view, since |PromptIconViewModel| won't
+ // release the flow.
// propagate size changes to legacy panel controller and animate
// transitions
view.doOnLayout {
@@ -460,15 +466,14 @@
return r == Surface.ROTATION_90 || r == Surface.ROTATION_270
}
-private fun View.showContentOrHide(forceHide: Boolean = false) {
+private fun View.showContentOrHide(forceHide: Boolean = false): Int {
val isTextViewWithBlankText = this is TextView && this.text.isBlank()
val isImageViewWithoutImage = this is ImageView && this.drawable == null
- visibility =
- if (forceHide || isTextViewWithBlankText || isImageViewWithoutImage) {
- View.GONE
- } else {
- View.VISIBLE
- }
+ return if (forceHide || isTextViewWithBlankText || isImageViewWithoutImage) {
+ View.GONE
+ } else {
+ View.VISIBLE
+ }
}
private fun View.centerX(): Int {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
index 03c5c53..46be8c7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
@@ -4,13 +4,13 @@
import android.graphics.drawable.Drawable
import android.text.InputType
import com.android.internal.widget.LockPatternView
-import com.android.systemui.res.R
import com.android.systemui.biometrics.Utils
import com.android.systemui.biometrics.domain.interactor.CredentialStatus
import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor
import com.android.systemui.biometrics.domain.model.BiometricPromptRequest
import com.android.systemui.biometrics.shared.model.BiometricUserInfo
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.res.R
import javax.inject.Inject
import kotlin.reflect.KClass
import kotlinx.coroutines.flow.Flow
@@ -32,14 +32,16 @@
/** Top level information about the prompt. */
val header: Flow<CredentialHeaderViewModel> =
- credentialInteractor.prompt.filterIsInstance<BiometricPromptRequest.Credential>().map {
- request ->
+ combine(
+ credentialInteractor.prompt.filterIsInstance<BiometricPromptRequest.Credential>(),
+ credentialInteractor.showBpWithoutIconForCredential
+ ) { request, showBpWithoutIconForCredential ->
BiometricPromptHeaderViewModelImpl(
request,
user = request.userInfo,
title = request.title,
- subtitle = request.subtitle,
- description = request.description,
+ subtitle = if (showBpWithoutIconForCredential) "" else request.subtitle,
+ description = if (showBpWithoutIconForCredential) "" else request.description,
icon = applicationContext.asLockIcon(request.userInfo.deviceCredentialOwnerId),
showEmergencyCallButton = request.showEmergencyCallButton
)
@@ -145,7 +147,7 @@
.createLaunchEmergencyDialerIntent(null)
.setFlags(
android.content.Intent.FLAG_ACTIVITY_NEW_TASK or
- android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP
+ android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP
)
context.startActivity(intent)
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index c933e0e..2c749ba 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -47,6 +47,7 @@
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
@@ -123,6 +124,9 @@
/** The kind of credential the user has. */
val credentialKind: Flow<PromptKind> = promptSelectorInteractor.credentialKind
+ val showBpWithoutIconForCredential: StateFlow<Boolean> =
+ promptSelectorInteractor.showBpWithoutIconForCredential
+
/** The label to use for the cancel button. */
val negativeButtonText: Flow<String> =
promptSelectorInteractor.prompt.map { it?.negativeButtonText ?: "" }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 1157d97..19af371 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -51,6 +51,7 @@
import com.android.systemui.controls.dagger.ControlsModule;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.SystemUser;
+import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.demomode.dagger.DemoModeModule;
import com.android.systemui.deviceentry.DeviceEntryModule;
import com.android.systemui.display.DisplayModule;
@@ -365,7 +366,8 @@
SysUiState sysUiState,
FeatureFlags featureFlags,
NotifPipelineFlags notifPipelineFlags,
- @Main Executor sysuiMainExecutor) {
+ @Main Executor sysuiMainExecutor,
+ @UiBackground Executor sysuiUiBgExecutor) {
return Optional.ofNullable(BubblesManager.create(context,
bubblesOptional,
notificationShadeWindowController,
@@ -384,7 +386,8 @@
sysUiState,
featureFlags,
notifPipelineFlags,
- sysuiMainExecutor));
+ sysuiMainExecutor,
+ sysuiUiBgExecutor));
}
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
index cf7d601..baae986 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -62,6 +62,10 @@
import com.android.systemui.user.data.model.SelectionStatus
import com.android.systemui.user.data.repository.UserRepository
import com.google.errorprone.annotations.CompileTimeConstant
+import java.io.PrintWriter
+import java.util.Arrays
+import java.util.stream.Collectors
+import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
@@ -83,10 +87,6 @@
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
-import java.io.PrintWriter
-import java.util.Arrays
-import java.util.stream.Collectors
-import javax.inject.Inject
/**
* API to run face authentication and detection for device entry / on keyguard (as opposed to the
@@ -431,11 +431,11 @@
override fun onAuthenticationFailed() {
_isAuthenticated.value = false
faceAuthLogger.authenticationFailed()
+ _authenticationStatus.value = FailedFaceAuthenticationStatus()
if (!_isLockedOut.value) {
// onAuthenticationError gets invoked before onAuthenticationFailed when the
// last auth attempt locks out face authentication.
- // Skip updating the authentication status in such a scenario.
- _authenticationStatus.value = FailedFaceAuthenticationStatus()
+ // Skip onFaceAuthRequestCompleted in such a scenario.
onFaceAuthRequestCompleted()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 7263ae9..cb1571e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -44,6 +44,7 @@
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
@@ -287,7 +288,7 @@
if (KeyguardWmStateRefactor.isEnabled) {
// When the refactor is enabled, we no longer use isKeyguardGoingAway.
scope.launch {
- swipeToDismissInteractor.dismissFling.collect { _ ->
+ swipeToDismissInteractor.dismissFling.filterNotNull().collect { _ ->
startTransitionTo(KeyguardState.GONE)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
index d792889..23320be 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
@@ -23,6 +23,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.res.R
+import com.android.systemui.statusbar.policy.SplitShadeStateController
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
@@ -39,6 +40,7 @@
private val interactor: KeyguardBlueprintInteractor,
private val authController: AuthController,
val longPress: KeyguardLongPressViewModel,
+ val splitShadeStateController: SplitShadeStateController,
) {
private val clockSize = clockInteractor.clockSize
@@ -46,8 +48,10 @@
get() = authController.isUdfpsSupported
val isLargeClockVisible: Boolean
get() = clockSize.value == KeyguardClockSwitch.LARGE
- val areNotificationsVisible: Boolean
- get() = !isLargeClockVisible
+ fun areNotificationsVisible(resources: Resources): Boolean {
+ return !isLargeClockVisible ||
+ splitShadeStateController.shouldUseSplitNotificationShade(resources)
+ }
fun getSmartSpacePaddingTop(resources: Resources): Int {
return if (isLargeClockVisible) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/MediaCarouselViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/MediaCarouselViewModel.kt
new file mode 100644
index 0000000..027a739
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/MediaCarouselViewModel.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import javax.inject.Inject
+
+class MediaCarouselViewModel @Inject constructor(mediaDataManager: MediaDataManager) {
+ val isMediaVisible: Boolean = mediaDataManager.hasActiveMediaOrRecommendation()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 19fe60a..1ec86ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -71,6 +71,7 @@
import android.provider.DeviceConfig;
import android.text.TextUtils;
import android.text.format.Formatter;
+import android.util.Pair;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
@@ -187,6 +188,7 @@
private CharSequence mTransientIndication;
private CharSequence mBiometricMessage;
private CharSequence mBiometricMessageFollowUp;
+ private BiometricSourceType mBiometricMessageSource;
protected ColorStateList mInitialTextColorState;
private boolean mVisible;
private boolean mOrganizationOwnedDevice;
@@ -206,7 +208,7 @@
private int mBatteryLevel;
private boolean mBatteryPresent = true;
private long mChargingTimeRemaining;
- private String mBiometricErrorMessageToShowOnScreenOn;
+ private Pair<String, BiometricSourceType> mBiometricErrorMessageToShowOnScreenOn;
private final Set<Integer> mCoExFaceAcquisitionMsgIdsToShow;
private final FaceHelpMessageDeferral mFaceAcquiredMessageDeferral;
private boolean mInited;
@@ -225,15 +227,18 @@
mIsActiveDreamLockscreenHosted = isLockscreenHosted;
updateDeviceEntryIndication(false);
};
- private final ScreenLifecycle.Observer mScreenObserver =
- new ScreenLifecycle.Observer() {
+ private final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
@Override
public void onScreenTurnedOn() {
mHandler.removeMessages(MSG_RESET_ERROR_MESSAGE_ON_SCREEN_ON);
if (mBiometricErrorMessageToShowOnScreenOn != null) {
String followUpMessage = mFaceLockedOutThisAuthSession
? faceLockedOutFollowupMessage() : null;
- showBiometricMessage(mBiometricErrorMessageToShowOnScreenOn, followUpMessage);
+ showBiometricMessage(
+ mBiometricErrorMessageToShowOnScreenOn.first,
+ followUpMessage,
+ mBiometricErrorMessageToShowOnScreenOn.second
+ );
// We want to keep this message around in case the screen was off
hideBiometricMessageDelayed(DEFAULT_HIDE_DELAY_MS);
mBiometricErrorMessageToShowOnScreenOn = null;
@@ -879,8 +884,35 @@
updateTransient();
}
- private void showBiometricMessage(CharSequence biometricMessage) {
- showBiometricMessage(biometricMessage, null);
+ private void showSuccessBiometricMessage(
+ CharSequence biometricMessage,
+ @Nullable CharSequence biometricMessageFollowUp,
+ BiometricSourceType biometricSourceType
+ ) {
+ showBiometricMessage(biometricMessage, biometricMessageFollowUp, biometricSourceType, true);
+ }
+
+ private void showSuccessBiometricMessage(CharSequence biometricMessage,
+ BiometricSourceType biometricSourceType) {
+ showSuccessBiometricMessage(biometricMessage, null, biometricSourceType);
+ }
+
+ private void showBiometricMessage(CharSequence biometricMessage,
+ BiometricSourceType biometricSourceType) {
+ showBiometricMessage(biometricMessage, null, biometricSourceType, false);
+ }
+
+ private void showBiometricMessage(
+ CharSequence biometricMessage,
+ @Nullable CharSequence biometricMessageFollowUp,
+ BiometricSourceType biometricSourceType
+ ) {
+ showBiometricMessage(
+ biometricMessage,
+ biometricMessageFollowUp,
+ biometricSourceType,
+ false
+ );
}
/**
@@ -889,15 +921,33 @@
* by {@link KeyguardIndicationRotateTextViewController}, see class for rotating message
* logic.
*/
- private void showBiometricMessage(CharSequence biometricMessage,
- @Nullable CharSequence biometricMessageFollowUp) {
+ private void showBiometricMessage(
+ CharSequence biometricMessage,
+ @Nullable CharSequence biometricMessageFollowUp,
+ BiometricSourceType biometricSourceType,
+ boolean isSuccessMessage
+ ) {
if (TextUtils.equals(biometricMessage, mBiometricMessage)
+ && biometricSourceType == mBiometricMessageSource
&& TextUtils.equals(biometricMessageFollowUp, mBiometricMessageFollowUp)) {
return;
}
+ if (!isSuccessMessage
+ && mBiometricMessageSource == FINGERPRINT
+ && biometricSourceType != FINGERPRINT) {
+ // drop all non-fingerprint biometric messages if there's a fingerprint message showing
+ mKeyguardLogger.logDropNonFingerprintMessage(
+ biometricMessage,
+ biometricMessageFollowUp,
+ biometricSourceType
+ );
+ return;
+ }
+
mBiometricMessage = biometricMessage;
mBiometricMessageFollowUp = biometricMessageFollowUp;
+ mBiometricMessageSource = biometricSourceType;
mHandler.removeMessages(MSG_SHOW_ACTION_TO_UNLOCK);
hideBiometricMessageDelayed(
@@ -914,6 +964,7 @@
if (mBiometricMessage != null || mBiometricMessageFollowUp != null) {
mBiometricMessage = null;
mBiometricMessageFollowUp = null;
+ mBiometricMessageSource = null;
mHideBiometricMessageHandler.cancel();
updateBiometricMessage();
}
@@ -1085,7 +1136,8 @@
} else {
message = mContext.getString(R.string.keyguard_retry);
}
- mStatusBarKeyguardViewManager.setKeyguardMessage(message, mInitialTextColorState);
+ mStatusBarKeyguardViewManager.setKeyguardMessage(message, mInitialTextColorState,
+ null);
}
} else {
final boolean canSkipBouncer = mKeyguardUpdateMonitor.getUserCanSkipBouncer(
@@ -1097,34 +1149,40 @@
|| mAccessibilityManager.isTouchExplorationEnabled();
if (udfpsSupported && faceAuthenticated) { // co-ex
if (a11yEnabled) {
- showBiometricMessage(
+ showSuccessBiometricMessage(
mContext.getString(R.string.keyguard_face_successful_unlock),
- mContext.getString(R.string.keyguard_unlock)
+ mContext.getString(R.string.keyguard_unlock),
+ FACE
);
} else {
- showBiometricMessage(
+ showSuccessBiometricMessage(
mContext.getString(R.string.keyguard_face_successful_unlock),
- mContext.getString(R.string.keyguard_unlock_press)
+ mContext.getString(R.string.keyguard_unlock_press),
+ FACE
);
}
} else if (faceAuthenticated) { // face-only
- showBiometricMessage(
+ showSuccessBiometricMessage(
mContext.getString(R.string.keyguard_face_successful_unlock),
- mContext.getString(R.string.keyguard_unlock)
+ mContext.getString(R.string.keyguard_unlock),
+ FACE
);
} else if (udfpsSupported) { // udfps-only
if (a11yEnabled) {
- showBiometricMessage(mContext.getString(R.string.keyguard_unlock));
+ showSuccessBiometricMessage(
+ mContext.getString(R.string.keyguard_unlock),
+ null
+ );
} else {
- showBiometricMessage(mContext.getString(
- R.string.keyguard_unlock_press));
+ showSuccessBiometricMessage(mContext.getString(
+ R.string.keyguard_unlock_press), null);
}
} else { // no security or unlocked by a trust agent
- showBiometricMessage(mContext.getString(R.string.keyguard_unlock));
+ showSuccessBiometricMessage(mContext.getString(R.string.keyguard_unlock), null);
}
} else {
// suggest swiping up for the primary authentication bouncer
- showBiometricMessage(mContext.getString(R.string.keyguard_unlock));
+ showBiometricMessage(mContext.getString(R.string.keyguard_unlock), null);
}
}
}
@@ -1228,6 +1286,13 @@
&& msgId != BIOMETRIC_HELP_FACE_NOT_AVAILABLE;
final boolean faceAuthFailed = biometricSourceType == FACE
&& msgId == BIOMETRIC_HELP_FACE_NOT_RECOGNIZED; // ran through matcher & failed
+ if (faceAuthFailed && mFaceLockedOutThisAuthSession) {
+ mKeyguardLogger.logBiometricMessage(
+ "skipped showing faceAuthFailed message due to lockout",
+ msgId,
+ helpString);
+ return;
+ }
final boolean fpAuthFailed = biometricSourceType == FINGERPRINT
&& msgId == BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED; // ran matcher & failed
final boolean isUnlockWithFingerprintPossible = canUnlockWithFingerprint();
@@ -1245,49 +1310,55 @@
mBouncerMessageInteractor.setFaceAcquisitionMessage(helpString);
}
mStatusBarKeyguardViewManager.setKeyguardMessage(helpString,
- mInitialTextColorState);
+ mInitialTextColorState, biometricSourceType);
} else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
if (isCoExFaceAcquisitionMessage && msgId == FACE_ACQUIRED_TOO_DARK) {
showBiometricMessage(
helpString,
- mContext.getString(R.string.keyguard_suggest_fingerprint)
+ mContext.getString(R.string.keyguard_suggest_fingerprint),
+ biometricSourceType
);
} else if (faceAuthFailed && isUnlockWithFingerprintPossible) {
showBiometricMessage(
mContext.getString(R.string.keyguard_face_failed),
- mContext.getString(R.string.keyguard_suggest_fingerprint)
+ mContext.getString(R.string.keyguard_suggest_fingerprint),
+ biometricSourceType
);
} else if (fpAuthFailed
&& mKeyguardUpdateMonitor.isCurrentUserUnlockedWithFace()) {
// face had already previously unlocked the device, so instead of showing a
// fingerprint error, tell them they have already unlocked with face auth
// and how to enter their device
- showBiometricMessage(
+ showSuccessBiometricMessage(
mContext.getString(R.string.keyguard_face_successful_unlock),
- mContext.getString(R.string.keyguard_unlock)
+ mContext.getString(R.string.keyguard_unlock),
+ null
);
} else if (fpAuthFailed
&& mKeyguardUpdateMonitor.getUserHasTrust(getCurrentUser())) {
- showBiometricMessage(
+ showSuccessBiometricMessage(
getTrustGrantedIndication(),
- mContext.getString(R.string.keyguard_unlock)
+ mContext.getString(R.string.keyguard_unlock),
+ null
);
} else if (faceAuthUnavailable) {
showBiometricMessage(
helpString,
isUnlockWithFingerprintPossible
? mContext.getString(R.string.keyguard_suggest_fingerprint)
- : mContext.getString(R.string.keyguard_unlock)
+ : mContext.getString(R.string.keyguard_unlock),
+ biometricSourceType
);
} else {
- showBiometricMessage(helpString);
+ showBiometricMessage(helpString, biometricSourceType);
}
} else if (faceAuthFailed) {
// show action to unlock
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SHOW_ACTION_TO_UNLOCK),
TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
} else {
- mBiometricErrorMessageToShowOnScreenOn = helpString;
+ mBiometricErrorMessageToShowOnScreenOn =
+ new Pair<>(helpString, biometricSourceType);
mHandler.sendMessageDelayed(
mHandler.obtainMessage(MSG_RESET_ERROR_MESSAGE_ON_SCREEN_ON),
1000);
@@ -1333,7 +1404,7 @@
} else if (mIndicationHelper.isFaceLockoutErrorMsg(msgId)) {
handleFaceLockoutError(errString);
} else {
- showErrorMessageNowOrLater(errString, null);
+ showErrorMessageNowOrLater(errString, null, FACE);
}
}
@@ -1343,7 +1414,7 @@
msgId,
errString);
} else {
- showErrorMessageNowOrLater(errString, null);
+ showErrorMessageNowOrLater(errString, null, FINGERPRINT);
}
}
@@ -1371,7 +1442,7 @@
@Override
public void onTrustAgentErrorMessage(CharSequence message) {
- showBiometricMessage(message);
+ showBiometricMessage(message, null);
}
@Override
@@ -1459,12 +1530,13 @@
// had too many unsuccessful attempts.
if (!mFaceLockedOutThisAuthSession) {
mFaceLockedOutThisAuthSession = true;
- showErrorMessageNowOrLater(errString, followupMessage);
+ showErrorMessageNowOrLater(errString, followupMessage, FACE);
} else if (!mAuthController.isUdfpsFingerDown()) {
// On subsequent lockouts, we show a more generic locked out message.
showErrorMessageNowOrLater(
mContext.getString(R.string.keyguard_face_unlock_unavailable),
- followupMessage);
+ followupMessage,
+ FACE);
}
}
@@ -1484,7 +1556,8 @@
&& !mStatusBarKeyguardViewManager.isBouncerShowing()) {
showBiometricMessage(
deferredFaceMessage,
- mContext.getString(R.string.keyguard_suggest_fingerprint)
+ mContext.getString(R.string.keyguard_suggest_fingerprint),
+ FACE
);
} else {
// otherwise, don't show any message
@@ -1496,7 +1569,8 @@
// user to manually retry.
showBiometricMessage(
deferredFaceMessage,
- mContext.getString(R.string.keyguard_unlock)
+ mContext.getString(R.string.keyguard_unlock),
+ FACE
);
} else {
// Face-only
@@ -1510,13 +1584,15 @@
getCurrentUser()) && mKeyguardUpdateMonitor.isUnlockingWithFingerprintAllowed();
}
- private void showErrorMessageNowOrLater(String errString, @Nullable String followUpMsg) {
+ private void showErrorMessageNowOrLater(String errString, @Nullable String followUpMsg,
+ BiometricSourceType biometricSourceType) {
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
- mStatusBarKeyguardViewManager.setKeyguardMessage(errString, mInitialTextColorState);
+ mStatusBarKeyguardViewManager.setKeyguardMessage(errString, mInitialTextColorState,
+ biometricSourceType);
} else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
- showBiometricMessage(errString, followUpMsg);
+ showBiometricMessage(errString, followUpMsg, biometricSourceType);
} else {
- mBiometricErrorMessageToShowOnScreenOn = errString;
+ mBiometricErrorMessageToShowOnScreenOn = new Pair<>(errString, biometricSourceType);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 29fd225..d1055c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -23,7 +23,6 @@
import static com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
-import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
import static com.android.systemui.util.kotlin.JavaAdapterKt.combineFlows;
import android.content.Context;
@@ -98,6 +97,7 @@
import com.android.systemui.unfold.FoldAodAnimationController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.android.systemui.util.kotlin.JavaAdapter;
import dagger.Lazy;
@@ -348,6 +348,8 @@
private Lazy<KeyguardSurfaceBehindInteractor> mSurfaceBehindInteractor;
private Lazy<KeyguardDismissActionInteractor> mKeyguardDismissActionInteractor;
+ private final JavaAdapter mJavaAdapter;
+
@Inject
public StatusBarKeyguardViewManager(
Context context,
@@ -378,7 +380,8 @@
Lazy<WindowManagerLockscreenVisibilityInteractor> wmLockscreenVisibilityInteractor,
Lazy<KeyguardDismissActionInteractor> keyguardDismissActionInteractorLazy,
SelectedUserInteractor selectedUserInteractor,
- Lazy<KeyguardSurfaceBehindInteractor> surfaceBehindInteractor
+ Lazy<KeyguardSurfaceBehindInteractor> surfaceBehindInteractor,
+ JavaAdapter javaAdapter
) {
mContext = context;
mViewMediatorCallback = callback;
@@ -411,6 +414,7 @@
mKeyguardDismissActionInteractor = keyguardDismissActionInteractorLazy;
mSelectedUserInteractor = selectedUserInteractor;
mSurfaceBehindInteractor = surfaceBehindInteractor;
+ mJavaAdapter = javaAdapter;
}
KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@@ -481,8 +485,7 @@
if (KeyguardWmStateRefactor.isEnabled()) {
// Show the keyguard views whenever we've told WM that the lockscreen is visible.
- collectFlow(
- getViewRootImpl().getView(),
+ mJavaAdapter.alwaysCollectFlow(
combineFlows(
mWmLockscreenVisibilityInteractor.get().getLockscreenVisibility(),
mSurfaceBehindInteractor.get().isAnimatingSurface(),
@@ -781,7 +784,7 @@
}
updateAlternateBouncerShowing(mAlternateBouncerInteractor.show());
- setKeyguardMessage(message, null);
+ setKeyguardMessage(message, null, null);
return;
}
@@ -1444,11 +1447,12 @@
}
/** Display security message to relevant KeyguardMessageArea. */
- public void setKeyguardMessage(String message, ColorStateList colorState) {
+ public void setKeyguardMessage(String message, ColorStateList colorState,
+ BiometricSourceType biometricSourceType) {
if (mAlternateBouncerInteractor.isVisibleState()) {
if (mKeyguardMessageAreaController != null) {
DeviceEntryUdfpsRefactor.assertInLegacyMode();
- mKeyguardMessageAreaController.setMessage(message);
+ mKeyguardMessageAreaController.setMessage(message, biometricSourceType);
}
} else {
mPrimaryBouncerInteractor.showMessage(message, colorState);
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index 65dede8..cb61534 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -28,6 +28,7 @@
import static com.android.server.notification.Flags.screenshareNotificationHiding;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES;
import android.app.INotificationManager;
import android.app.Notification;
@@ -50,6 +51,7 @@
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.flags.FeatureFlags;
@@ -97,6 +99,7 @@
private final Context mContext;
private final Bubbles mBubbles;
private final NotificationShadeWindowController mNotificationShadeWindowController;
+ private final KeyguardStateController mKeyguardStateController;
private final ShadeController mShadeController;
private final IStatusBarService mBarService;
private final INotificationManager mNotificationManager;
@@ -109,6 +112,7 @@
private final NotifPipeline mNotifPipeline;
private final NotifPipelineFlags mNotifPipelineFlags;
private final Executor mSysuiMainExecutor;
+ private final Executor mSysuiUiBgExecutor;
private final Bubbles.SysuiProxy mSysuiProxy;
// TODO (b/145659174): allow for multiple callbacks to support the "shadow" new notif pipeline
@@ -116,6 +120,8 @@
private final StatusBarWindowCallback mStatusBarWindowCallback;
private final Runnable mSensitiveStateChangedListener;
private boolean mPanelExpanded;
+ private boolean mKeyguardShowing;
+ private boolean mDreamingOrInPreview;
/**
* Creates {@link BubblesManager}, returns {@code null} if Optional {@link Bubbles} not present
@@ -140,7 +146,8 @@
SysUiState sysUiState,
FeatureFlags featureFlags,
NotifPipelineFlags notifPipelineFlags,
- Executor sysuiMainExecutor) {
+ Executor sysuiMainExecutor,
+ Executor sysuiUiBgExecutor) {
if (bubblesOptional.isPresent()) {
return new BubblesManager(context,
bubblesOptional.get(),
@@ -160,7 +167,8 @@
sysUiState,
featureFlags,
notifPipelineFlags,
- sysuiMainExecutor);
+ sysuiMainExecutor,
+ sysuiUiBgExecutor);
} else {
return null;
}
@@ -185,10 +193,12 @@
SysUiState sysUiState,
FeatureFlags featureFlags,
NotifPipelineFlags notifPipelineFlags,
- Executor sysuiMainExecutor) {
+ Executor sysuiMainExecutor,
+ Executor sysuiUiBgExecutor) {
mContext = context;
mBubbles = bubbles;
mNotificationShadeWindowController = notificationShadeWindowController;
+ mKeyguardStateController = keyguardStateController;
mShadeController = shadeController;
mNotificationManager = notificationManager;
mDreamManager = dreamManager;
@@ -200,6 +210,7 @@
mNotifPipeline = notifPipeline;
mNotifPipelineFlags = notifPipelineFlags;
mSysuiMainExecutor = sysuiMainExecutor;
+ mSysuiUiBgExecutor = sysuiUiBgExecutor;
mBarService = statusBarService == null
? IStatusBarService.Stub.asInterface(
@@ -208,12 +219,11 @@
setupNotifPipeline();
- keyguardStateController.addCallback(new KeyguardStateController.Callback() {
+ // TODO(b/327410864): use KeyguardTransitionInteractor to listen for keyguard changes
+ mKeyguardStateController.addCallback(new KeyguardStateController.Callback() {
@Override
public void onKeyguardShowingChanged() {
- boolean isUnlockedShade = !keyguardStateController.isShowing()
- && !isDreamingOrInPreview();
- bubbles.onStatusBarStateChanged(isUnlockedShade);
+ updateKeyguardAndDreamingState();
}
});
@@ -256,6 +266,14 @@
mPanelExpanded = panelExpanded;
mBubbles.onNotificationPanelExpandedChanged(panelExpanded);
}
+ if (!mKeyguardShowing && mDreamingOrInPreview && !isDreaming) {
+ // We check for dreaming state changes when keyguard status changes.
+ // This causes us to miss events if dreaming state changes after keyguard.
+ // Add a check here for the case where keyguard is dismissed before
+ // dreaming state changes. Otherwise bubbles remain invisible.
+ // TODO(b/327410864): use KeyguardTransitionInteractor for dreaming changes
+ updateKeyguardAndDreamingState();
+ }
};
notificationShadeWindowController.registerCallback(mStatusBarWindowCallback);
@@ -395,6 +413,19 @@
mBubbles.setSysuiProxy(mSysuiProxy);
}
+ private void updateKeyguardAndDreamingState() {
+ mSysuiUiBgExecutor.execute(() -> {
+ mKeyguardShowing = mKeyguardStateController.isShowing();
+ mDreamingOrInPreview = isDreamingOrInPreview();
+ boolean isUnlockedShade = !mKeyguardShowing && !mDreamingOrInPreview;
+ ProtoLog.d(WM_SHELL_BUBBLES,
+ "handleKeyguardOrDreamChange isUnlockedShade=%b keyguardShowing=%b "
+ + "dreamingOrInPreview=%b",
+ isUnlockedShade, mKeyguardShowing, mDreamingOrInPreview);
+ mBubbles.onStatusBarStateChanged(isUnlockedShade);
+ });
+ }
+
private boolean isDreamingOrInPreview() {
try {
return mDreamManager.isDreamingOrInPreview();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
index d4522d0..93e7602 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
@@ -19,11 +19,14 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.hardware.biometrics.BiometricSourceType;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -119,4 +122,51 @@
when(mKeyguardMessageArea.getText()).thenReturn(msg);
assertThat(mMessageAreaController.getMessage()).isEqualTo(msg);
}
+
+ @Test
+ public void testFingerprintMessageUpdate() {
+ String msg = "fpMessage";
+ mMessageAreaController.setMessage(
+ msg, BiometricSourceType.FINGERPRINT
+ );
+ verify(mKeyguardMessageArea).setMessage(msg, /* animate= */ true);
+
+ String msg2 = "fpMessage2";
+ mMessageAreaController.setMessage(
+ msg2, BiometricSourceType.FINGERPRINT
+ );
+ verify(mKeyguardMessageArea).setMessage(msg2, /* animate= */ true);
+ }
+
+ @Test
+ public void testFaceMessageDroppedWhileFingerprintMessageShowing() {
+ String fpMsg = "fpMessage";
+ mMessageAreaController.setMessage(
+ fpMsg, BiometricSourceType.FINGERPRINT
+ );
+ verify(mKeyguardMessageArea).setMessage(eq(fpMsg), /* animate= */ anyBoolean());
+
+ String faceMessage = "faceMessage";
+ mMessageAreaController.setMessage(
+ faceMessage, BiometricSourceType.FACE
+ );
+ verify(mKeyguardMessageArea, never())
+ .setMessage(eq(faceMessage), /* animate= */ anyBoolean());
+ }
+
+ @Test
+ public void testGenericMessageShowsAfterFingerprintMessageShowing() {
+ String fpMsg = "fpMessage";
+ mMessageAreaController.setMessage(
+ fpMsg, BiometricSourceType.FINGERPRINT
+ );
+ verify(mKeyguardMessageArea).setMessage(eq(fpMsg), /* animate= */ anyBoolean());
+
+ String genericMessage = "genericMessage";
+ mMessageAreaController.setMessage(
+ genericMessage, null
+ );
+ verify(mKeyguardMessageArea)
+ .setMessage(eq(genericMessage), /* animate= */ anyBoolean());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 2c1a87d..10b86ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -21,8 +21,8 @@
import android.hardware.biometrics.BiometricConstants
import android.hardware.biometrics.BiometricManager
import android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT
-import android.hardware.biometrics.Flags.customBiometricPrompt
import android.hardware.biometrics.PromptInfo
+import android.hardware.biometrics.PromptVerticalListContentView
import android.hardware.face.FaceSensorPropertiesInternal
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import android.os.Handler
@@ -40,7 +40,6 @@
import androidx.test.filters.SmallTest
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.widget.LockPatternUtils
-import com.android.systemui.Flags.FLAG_CONSTRAINT_BP
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
@@ -148,8 +147,6 @@
@Before
fun setup() {
- mSetFlagsRule.disableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
- mSetFlagsRule.disableFlags(FLAG_CONSTRAINT_BP)
displayRepository = FakeDisplayRepository()
displayStateInteractor =
@@ -388,9 +385,10 @@
}
@Test
- fun testShowCredentialUI() {
+ fun testShowCredentialUI_withDescription() {
+ mSetFlagsRule.disableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
val container = initializeFingerprintContainer(
- authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
+ authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
)
waitForIdleSync()
@@ -399,16 +397,12 @@
}
@Test
- fun testShowBiometricUIWhenCustomBpEnabledAndNoSensors() {
- mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
+ fun testShowCredentialUI_withCustomBp() {
val container = initializeFingerprintContainer(
- authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
+ authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL,
+ isUsingContentView = true
)
- waitForIdleSync()
-
- assertThat(customBiometricPrompt()).isTrue()
- assertThat(container.hasBiometricPrompt()).isTrue()
- assertThat(container.hasCredentialView()).isFalse()
+ checkBpShowsForCredentialAndGoToCredential(container)
}
@Test
@@ -512,11 +506,13 @@
private fun initializeFingerprintContainer(
authenticators: Int = BiometricManager.Authenticators.BIOMETRIC_WEAK,
- addToView: Boolean = true
+ addToView: Boolean = true,
+ isUsingContentView: Boolean = false,
) = initializeContainer(
TestAuthContainerView(
authenticators = authenticators,
- fingerprintProps = fingerprintSensorPropertiesInternal()
+ fingerprintProps = fingerprintSensorPropertiesInternal(),
+ isUsingContentView = isUsingContentView,
),
addToView
)
@@ -549,7 +545,8 @@
private inner class TestAuthContainerView(
authenticators: Int = BiometricManager.Authenticators.BIOMETRIC_WEAK,
fingerprintProps: List<FingerprintSensorPropertiesInternal> = listOf(),
- faceProps: List<FaceSensorPropertiesInternal> = listOf()
+ faceProps: List<FaceSensorPropertiesInternal> = listOf(),
+ isUsingContentView: Boolean = false,
) : AuthContainerView(
Config().apply {
mContext = this@AuthContainerViewTest.context
@@ -559,6 +556,9 @@
mSkipAnimation = true
mPromptInfo = PromptInfo().apply {
this.authenticators = authenticators
+ if (isUsingContentView) {
+ this.contentView = PromptVerticalListContentView.Builder().build()
+ }
}
mOpPackageName = OP_PACKAGE_NAME
},
@@ -614,6 +614,17 @@
val layoutParams = AuthContainerView.getLayoutParams(windowToken, "")
assertThat((layoutParams.fitInsetsTypes and WindowInsets.Type.systemBars()) == 0).isTrue()
}
+
+ private fun checkBpShowsForCredentialAndGoToCredential(container: TestAuthContainerView) {
+ waitForIdleSync()
+ assertThat(container.hasBiometricPrompt()).isTrue()
+ assertThat(container.hasCredentialView()).isFalse()
+
+ container.animateToCredentialUI(false)
+ waitForIdleSync()
+ assertThat(container.hasBiometricPrompt()).isFalse()
+ assertThat(container.hasCredentialView()).isTrue()
+ }
}
private fun AuthContainerView.hasBiometricPrompt() =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt
index b39e09d..7b972d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt
@@ -16,7 +16,9 @@
package com.android.systemui.biometrics.data.repository
+import android.hardware.biometrics.BiometricManager
import android.hardware.biometrics.PromptInfo
+import android.hardware.biometrics.PromptVerticalListContentView
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
@@ -131,6 +133,52 @@
}
@Test
+ fun showBpWithoutIconForCredential_withCustomBp() =
+ testScope.runTest {
+ for (case in
+ listOf(
+ PromptKind.Biometric(),
+ PromptKind.Pin,
+ PromptKind.Password,
+ PromptKind.Pattern
+ )) {
+ val hasCredentialViewShown = case !is PromptKind.Biometric
+ val promptInfo =
+ PromptInfo().apply {
+ authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
+ contentView = PromptVerticalListContentView.Builder().build()
+ }
+ repository.setPrompt(promptInfo, USER_ID, CHALLENGE, case, OP_PACKAGE_NAME)
+ repository.setShouldShowBpWithoutIconForCredential(promptInfo)
+
+ assertThat(repository.showBpWithoutIconForCredential.value)
+ .isEqualTo(!hasCredentialViewShown)
+ }
+ }
+
+ @Test
+ fun showBpWithoutIconForCredential_withDescription() =
+ testScope.runTest {
+ for (case in
+ listOf(
+ PromptKind.Biometric(),
+ PromptKind.Pin,
+ PromptKind.Password,
+ PromptKind.Pattern
+ )) {
+ val promptInfo =
+ PromptInfo().apply {
+ authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
+ description = "description"
+ }
+ repository.setPrompt(promptInfo, USER_ID, CHALLENGE, case, OP_PACKAGE_NAME)
+ repository.setShouldShowBpWithoutIconForCredential(promptInfo)
+
+ assertThat(repository.showBpWithoutIconForCredential.value).isFalse()
+ }
+ }
+
+ @Test
fun setsAndUnsetsPrompt() =
testScope.runTest {
val kind = PromptKind.Pin
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
index 52b4275..2817780 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
@@ -147,7 +147,7 @@
testScope.runTest { useCredentialAndReset(Utils.CREDENTIAL_PIN) }
@Test
- fun usePattermCredentialAndReset() =
+ fun usePatternCredentialAndReset() =
testScope.runTest { useCredentialAndReset(Utils.CREDENTIAL_PATTERN) }
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
index 6d8e7aa..6aebe36 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
@@ -126,7 +126,7 @@
keyguardRepository.setKeyguardDismissible(true)
runCurrent()
shadeRepository.setCurrentFling(
- FlingInfo(expand = true) // Not a dismiss fling (expand = true).
+ FlingInfo(expand = false) // Is a dismiss fling upward (expand = false).
)
runCurrent()
@@ -153,4 +153,22 @@
assertThatRepository(transitionRepository).noTransitionsStarted()
}
+
+ @Test
+ @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testDoesNotTransitionToGone_whenDismissFling_emitsNull() =
+ testScope.runTest {
+ underTest.start()
+ verify(transitionRepository, never()).startTransition(any())
+
+ keyguardRepository.setKeyguardDismissible(true)
+ runCurrent()
+
+ // The fling is null when it a) initializes b) ends and in either case we should not
+ // swipe to unlock.
+ shadeRepository.setCurrentFling(null)
+ runCurrent()
+
+ assertThatRepository(transitionRepository).noTransitionsStarted()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index aa53558..1504d4c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -92,6 +92,21 @@
@TestableLooper.RunWithLooper
public class KeyguardIndicationControllerTest extends KeyguardIndicationControllerBaseTest {
@Test
+ public void afterFaceLockout_skipShowingFaceNotRecognized() {
+ createController();
+ onFaceLockoutError("lockout");
+ verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE, "lockout");
+ clearInvocations(mRotateTextViewController);
+
+ // WHEN face sends an onBiometricHelp BIOMETRIC_HELP_FACE_NOT_RECOGNIZED (face fail)
+ mKeyguardUpdateMonitorCallback.onBiometricHelp(
+ BIOMETRIC_HELP_FACE_NOT_RECOGNIZED,
+ "Face not recognized",
+ BiometricSourceType.FACE);
+ verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE); // no updated message
+ }
+
+ @Test
public void createController_setIndicationAreaAgain_destroysPreviousRotateTextViewController() {
// GIVEN a controller with a mocked rotate text view controlller
final KeyguardIndicationRotateTextViewController mockedRotateTextViewController =
@@ -593,7 +608,7 @@
mController.getKeyguardCallback().onBiometricError(FACE_ERROR_TIMEOUT,
"A message", BiometricSourceType.FACE);
- verify(mStatusBarKeyguardViewManager).setKeyguardMessage(eq(message), any());
+ verify(mStatusBarKeyguardViewManager).setKeyguardMessage(eq(message), any(), any());
}
@Test
@@ -608,7 +623,8 @@
mController.getKeyguardCallback().onBiometricError(FACE_ERROR_TIMEOUT,
"A message", BiometricSourceType.FACE);
- verify(mStatusBarKeyguardViewManager, never()).setKeyguardMessage(eq(message), any());
+ verify(mStatusBarKeyguardViewManager, never()).setKeyguardMessage(
+ eq(message), any(), any());
}
@Test
@@ -1242,7 +1258,7 @@
public void onBiometricFailed_resetFaceHelpMessageDeferral() {
createController();
- // WHEN face sends an onBiometricHelp BIOMETRIC_HELP_FACE_NOT_RECOGNIZED
+ // WHEN face sends an onBiometricAuthFailed
mKeyguardUpdateMonitorCallback.onBiometricAuthFailed(BiometricSourceType.FACE);
// THEN face help message deferral is reset
@@ -1331,7 +1347,9 @@
verify(mStatusBarKeyguardViewManager)
.setKeyguardMessage(
eq(mContext.getString(R.string.keyguard_face_unlock_unavailable)),
- any());
+ any(),
+ any()
+ );
}
@Test
@@ -1471,6 +1489,71 @@
mContext.getString(R.string.keyguard_suggest_fingerprint));
}
+ @Test
+ public void faceErrorMessageDroppedBecauseFingerprintMessageShowing() {
+ createController();
+ mController.setVisible(true);
+ mController.getKeyguardCallback().onBiometricHelp(BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED,
+ "fp not recognized", BiometricSourceType.FINGERPRINT);
+ clearInvocations(mRotateTextViewController);
+
+ onFaceLockoutError("lockout");
+ verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE);
+ }
+
+ @Test
+ public void faceUnlockedMessageShowsEvenWhenFingerprintMessageShowing() {
+ createController();
+ mController.setVisible(true);
+ mController.getKeyguardCallback().onBiometricHelp(BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED,
+ "fp not recognized", BiometricSourceType.FINGERPRINT);
+ clearInvocations(mRotateTextViewController);
+
+ when(mKeyguardUpdateMonitor.getIsFaceAuthenticated()).thenReturn(true);
+ when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(getCurrentUser()))
+ .thenReturn(true);
+ mController.getKeyguardCallback().onBiometricAuthenticated(0,
+ BiometricSourceType.FACE, false);
+ verifyIndicationMessage(
+ INDICATION_TYPE_BIOMETRIC_MESSAGE,
+ mContext.getString(R.string.keyguard_face_successful_unlock));
+ }
+
+ @Test
+ public void onTrustAgentErrorMessageDroppedBecauseFingerprintMessageShowing() {
+ createController();
+ mController.setVisible(true);
+ mController.getKeyguardCallback().onBiometricHelp(BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED,
+ "fp not recognized", BiometricSourceType.FINGERPRINT);
+ clearInvocations(mRotateTextViewController);
+
+ mKeyguardUpdateMonitorCallback.onTrustAgentErrorMessage("testMessage");
+ verifyNoMessage(INDICATION_TYPE_TRUST);
+ verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE);
+ }
+
+ @Test
+ public void trustGrantedMessageShowsEvenWhenFingerprintMessageShowing() {
+ createController();
+ mController.setVisible(true);
+ mController.getKeyguardCallback().onBiometricHelp(BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED,
+ "fp not recognized", BiometricSourceType.FINGERPRINT);
+ clearInvocations(mRotateTextViewController);
+
+ // GIVEN trust is granted
+ when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
+
+ // WHEN the showTrustGranted method is called
+ final String trustGrantedMsg = "testing trust granted message after fp message";
+ mController.getKeyguardCallback().onTrustGrantedForCurrentUser(
+ false, false, new TrustGrantFlags(0), trustGrantedMsg);
+
+ // THEN verify the trust granted message shows
+ verifyIndicationMessage(
+ INDICATION_TYPE_TRUST,
+ trustGrantedMsg);
+ }
+
private void screenIsTurningOn() {
when(mScreenLifecycle.getScreenState()).thenReturn(SCREEN_TURNING_ON);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index bd7406a..3666248 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -97,6 +97,7 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.android.systemui.util.kotlin.JavaAdapter;
import com.google.common.truth.Truth;
@@ -222,7 +223,8 @@
() -> mock(WindowManagerLockscreenVisibilityInteractor.class),
() -> mock(KeyguardDismissActionInteractor.class),
mSelectedUserInteractor,
- () -> mock(KeyguardSurfaceBehindInteractor.class)) {
+ () -> mock(KeyguardSurfaceBehindInteractor.class),
+ mock(JavaAdapter.class)) {
@Override
public ViewRootImpl getViewRootImpl() {
return mViewRootImpl;
@@ -730,7 +732,8 @@
() -> mock(WindowManagerLockscreenVisibilityInteractor.class),
() -> mock(KeyguardDismissActionInteractor.class),
mSelectedUserInteractor,
- () -> mock(KeyguardSurfaceBehindInteractor.class)) {
+ () -> mock(KeyguardSurfaceBehindInteractor.class),
+ mock(JavaAdapter.class)) {
@Override
public ViewRootImpl getViewRootImpl() {
return mViewRootImpl;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index a930860..fbefb0e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -609,6 +609,7 @@
mSysUiState,
mFeatureFlags,
mNotifPipelineFlags,
+ syncExecutor,
syncExecutor);
mBubblesManager.addNotifCallback(mNotifCallback);
@@ -651,7 +652,7 @@
}
@Test
- public void dreamingHidesBubbles() throws RemoteException {
+ public void bubblesHiddenWhileDreaming() throws RemoteException {
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
assertThat(mBubbleController.getStackView().getVisibility()).isEqualTo(View.VISIBLE);
@@ -662,7 +663,17 @@
mKeyguardStateControllerCallbackCaptor.getValue();
callback.onKeyguardShowingChanged();
+ // Dreaming should hide bubbles
assertThat(mBubbleController.getStackView().getVisibility()).isEqualTo(View.INVISIBLE);
+
+ // Finish dreaming should show bubbles
+ mNotificationShadeWindowController.setDreaming(false);
+ when(mIDreamManager.isDreamingOrInPreview()).thenReturn(false); // dreaming finished
+
+ // Dreaming updates come through mNotificationShadeWindowController
+ mNotificationShadeWindowController.notifyStateChangedCallbacks();
+
+ assertThat(mBubbleController.getStackView().getVisibility()).isEqualTo(View.VISIBLE);
}
@Test
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt
index f192de2..c3af437 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt
@@ -1,6 +1,8 @@
package com.android.systemui.biometrics.data.repository
+import android.hardware.biometrics.Flags
import android.hardware.biometrics.PromptInfo
+import com.android.systemui.biometrics.Utils
import com.android.systemui.biometrics.shared.model.PromptKind
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -26,6 +28,9 @@
private val _isConfirmationRequired = MutableStateFlow(false)
override val isConfirmationRequired = _isConfirmationRequired.asStateFlow()
+ private val _showBpWithoutIconForCredential = MutableStateFlow(false)
+ override val showBpWithoutIconForCredential = _showBpWithoutIconForCredential.asStateFlow()
+
private val _opPackageName: MutableStateFlow<String?> = MutableStateFlow(null)
override val opPackageName = _opPackageName.asStateFlow()
@@ -69,6 +74,16 @@
_isConfirmationRequired.value = false
}
+ override fun setShouldShowBpWithoutIconForCredential(promptInfo: PromptInfo) {
+ val hasCredentialViewShown = kind.value !is PromptKind.Biometric
+ val showBpForCredential =
+ Flags.customBiometricPrompt() &&
+ !Utils.isBiometricAllowed(promptInfo) &&
+ Utils.isDeviceCredentialAllowed(promptInfo) &&
+ promptInfo.contentView != null
+ _showBpWithoutIconForCredential.value = showBpForCredential && !hasCredentialViewShown
+ }
+
fun setIsShowing(showing: Boolean) {
_isShowing.value = showing
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt
index 96de4ba..8da5dd4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt
@@ -20,6 +20,7 @@
import com.android.systemui.keyguard.domain.interactor.keyguardBlueprintInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardClockInteractor
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.policy.splitShadeStateController
val Kosmos.lockscreenContentViewModel by
Kosmos.Fixture {
@@ -28,5 +29,6 @@
interactor = keyguardBlueprintInteractor,
authController = authController,
longPress = keyguardLongPressViewModel,
+ splitShadeStateController = splitShadeStateController,
)
}
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 007b746..fb826c8 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
@@ -24,6 +24,7 @@
import android.app.TaskStackListener;
import android.content.Context;
import android.content.pm.UserInfo;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.ComponentInfoInternal;
@@ -39,6 +40,7 @@
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.face.IFaceServiceReceiver;
import android.os.Binder;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -64,6 +66,7 @@
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.BiometricStateCallback;
+import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
@@ -359,6 +362,17 @@
null /* callback */);
}
+ if (Build.isDebuggable()) {
+ BiometricUtils<Face> utils = FaceUtils.getInstance(
+ mFaceSensors.keyAt(0));
+ for (UserInfo user : UserManager.get(mContext).getAliveUsers()) {
+ List<Face> enrollments = utils.getBiometricsForUser(mContext, user.id);
+ Slog.d(getTag(), "Expecting enrollments for user " + user.id + ": "
+ + enrollments.stream().map(
+ BiometricAuthenticator.Identifier::getBiometricId).toList());
+ }
+ }
+
return mDaemon;
}
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 a104cf4..c04c47e 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
@@ -27,6 +27,7 @@
import android.content.Context;
import android.content.pm.UserInfo;
import android.content.res.TypedArray;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.IInvalidationCallback;
@@ -46,6 +47,7 @@
import android.hardware.fingerprint.ISidefpsController;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.Binder;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -71,6 +73,7 @@
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.BiometricStateCallback;
+import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
@@ -382,6 +385,17 @@
null /* callback */);
}
+ if (Build.isDebuggable()) {
+ BiometricUtils<Fingerprint> utils = FingerprintUtils.getInstance(
+ mFingerprintSensors.keyAt(0));
+ for (UserInfo user : UserManager.get(mContext).getAliveUsers()) {
+ List<Fingerprint> enrollments = utils.getBiometricsForUser(mContext, user.id);
+ Slog.d(getTag(), "Expecting enrollments for user " + user.id + ": "
+ + enrollments.stream().map(
+ BiometricAuthenticator.Identifier::getBiometricId).toList());
+ }
+ }
+
return mDaemon;
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 9a76ebd..29ea071 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -108,7 +108,6 @@
import android.provider.Settings;
import android.security.AndroidKeyStoreMaintenance;
import android.security.Authorization;
-import android.security.KeyStore;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
import android.security.keystore.recovery.KeyChainProtectionParams;
@@ -169,6 +168,7 @@
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
+import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
@@ -292,7 +292,7 @@
private final IActivityManager mActivityManager;
private final SyntheticPasswordManager mSpManager;
- private final java.security.KeyStore mJavaKeyStore;
+ private final KeyStore mKeyStore;
private final RecoverableKeyStoreManager mRecoverableKeyStoreManager;
private final UnifiedProfilePasswordCache mUnifiedProfilePasswordCache;
@@ -564,10 +564,6 @@
return DeviceStateCache.getInstance();
}
- public KeyStore getKeyStore() {
- return KeyStore.getInstance();
- }
-
public RecoverableKeyStoreManager getRecoverableKeyStoreManager() {
return RecoverableKeyStoreManager.getInstance(mContext);
}
@@ -619,9 +615,9 @@
return (BiometricManager) mContext.getSystemService(Context.BIOMETRIC_SERVICE);
}
- public java.security.KeyStore getJavaKeyStore() {
+ public KeyStore getKeyStore() {
try {
- java.security.KeyStore ks = java.security.KeyStore.getInstance(
+ KeyStore ks = KeyStore.getInstance(
SyntheticPasswordCrypto.androidKeystoreProviderName());
ks.load(new AndroidKeyStoreLoadStoreParameter(
SyntheticPasswordCrypto.keyNamespace()));
@@ -631,8 +627,7 @@
}
}
- public @NonNull UnifiedProfilePasswordCache getUnifiedProfilePasswordCache(
- java.security.KeyStore ks) {
+ public @NonNull UnifiedProfilePasswordCache getUnifiedProfilePasswordCache(KeyStore ks) {
return new UnifiedProfilePasswordCache(ks);
}
@@ -654,7 +649,7 @@
protected LockSettingsService(Injector injector) {
mInjector = injector;
mContext = injector.getContext();
- mJavaKeyStore = injector.getJavaKeyStore();
+ mKeyStore = injector.getKeyStore();
mRecoverableKeyStoreManager = injector.getRecoverableKeyStoreManager();
mHandler = injector.getHandler(injector.getServiceThread());
mStrongAuth = injector.getStrongAuth();
@@ -676,7 +671,7 @@
mGatekeeperPasswords = new LongSparseArray<>();
mSpManager = injector.getSyntheticPasswordManager(mStorage);
- mUnifiedProfilePasswordCache = injector.getUnifiedProfilePasswordCache(mJavaKeyStore);
+ mUnifiedProfilePasswordCache = injector.getUnifiedProfilePasswordCache(mKeyStore);
mBiometricDeferredQueue = new BiometricDeferredQueue(mSpManager, mHandler);
mRebootEscrowManager = injector.getRebootEscrowManager(new RebootEscrowCallbacks(),
@@ -1482,7 +1477,7 @@
byte[] encryptedPassword = Arrays.copyOfRange(storedData, PROFILE_KEY_IV_SIZE,
storedData.length);
byte[] decryptionResult;
- SecretKey decryptionKey = (SecretKey) mJavaKeyStore.getKey(
+ SecretKey decryptionKey = (SecretKey) mKeyStore.getKey(
PROFILE_KEY_NAME_DECRYPT + userId, null);
Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
@@ -1875,9 +1870,10 @@
}
}
- private void onPostPasswordChanged(LockscreenCredential newCredential, int userHandle) {
- updatePasswordHistory(newCredential, userHandle);
- mContext.getSystemService(TrustManager.class).reportEnabledTrustAgentsChanged(userHandle);
+ private void onPostPasswordChanged(LockscreenCredential newCredential, int userId) {
+ updatePasswordHistory(newCredential, userId);
+ mContext.getSystemService(TrustManager.class).reportEnabledTrustAgentsChanged(userId);
+ sendMainUserCredentialChangedNotificationIfNeeded(userId);
}
/**
@@ -2076,16 +2072,16 @@
keyGenerator.init(new SecureRandom());
SecretKey secretKey = keyGenerator.generateKey();
try {
- mJavaKeyStore.setEntry(
+ mKeyStore.setEntry(
PROFILE_KEY_NAME_ENCRYPT + profileUserId,
- new java.security.KeyStore.SecretKeyEntry(secretKey),
+ new KeyStore.SecretKeyEntry(secretKey),
new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build());
- mJavaKeyStore.setEntry(
+ mKeyStore.setEntry(
PROFILE_KEY_NAME_DECRYPT + profileUserId,
- new java.security.KeyStore.SecretKeyEntry(secretKey),
+ new KeyStore.SecretKeyEntry(secretKey),
new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
@@ -2094,7 +2090,7 @@
.setUserAuthenticationValidityDurationSeconds(30)
.build());
// Key imported, obtain a reference to it.
- SecretKey keyStoreEncryptionKey = (SecretKey) mJavaKeyStore.getKey(
+ SecretKey keyStoreEncryptionKey = (SecretKey) mKeyStore.getKey(
PROFILE_KEY_NAME_ENCRYPT + profileUserId, null);
Cipher cipher = Cipher.getInstance(
KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_GCM + "/"
@@ -2104,7 +2100,7 @@
iv = cipher.getIV();
} finally {
// The original key can now be discarded.
- mJavaKeyStore.deleteEntry(PROFILE_KEY_NAME_ENCRYPT + profileUserId);
+ mKeyStore.deleteEntry(PROFILE_KEY_NAME_ENCRYPT + profileUserId);
}
} catch (UnrecoverableKeyException
| BadPaddingException | IllegalBlockSizeException | KeyStoreException
@@ -2556,11 +2552,10 @@
final String encryptAlias = PROFILE_KEY_NAME_ENCRYPT + targetUserId;
final String decryptAlias = PROFILE_KEY_NAME_DECRYPT + targetUserId;
try {
- if (mJavaKeyStore.containsAlias(encryptAlias) ||
- mJavaKeyStore.containsAlias(decryptAlias)) {
+ if (mKeyStore.containsAlias(encryptAlias) || mKeyStore.containsAlias(decryptAlias)) {
Slogf.i(TAG, "Removing keystore profile key for user %d", targetUserId);
- mJavaKeyStore.deleteEntry(encryptAlias);
- mJavaKeyStore.deleteEntry(decryptAlias);
+ mKeyStore.deleteEntry(encryptAlias);
+ mKeyStore.deleteEntry(decryptAlias);
}
} catch (KeyStoreException e) {
// We have tried our best to remove the key.
@@ -3062,7 +3057,6 @@
setCurrentLskfBasedProtectorId(newProtectorId, userId);
LockPatternUtils.invalidateCredentialTypeCache();
synchronizeUnifiedChallengeForProfiles(userId, profilePasswords);
- sendMainUserCredentialChangedNotificationIfNeeded(userId);
setUserPasswordMetrics(credential, userId);
mUnifiedProfilePasswordCache.removePassword(userId);
@@ -3457,7 +3451,7 @@
private void dumpKeystoreKeys(IndentingPrintWriter pw) {
try {
- final Enumeration<String> aliases = mJavaKeyStore.aliases();
+ final Enumeration<String> aliases = mKeyStore.aliases();
while (aliases.hasMoreElements()) {
pw.println(aliases.nextElement());
}
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt
index b9d89c2..28889de 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt
@@ -347,7 +347,8 @@
}
fun isAppOpGranted(flags: Int): Boolean =
- isPermissionGranted(flags) && !flags.hasBits(APP_OP_REVOKED)
+ isPermissionGranted(flags) && !flags.hasBits(RESTRICTION_REVOKED) &&
+ !flags.hasBits(APP_OP_REVOKED)
fun toApiFlags(flags: Int): Int {
var apiFlags = 0
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index e59b5ea..2ba3969 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -48,7 +48,6 @@
import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
import android.provider.Settings;
-import android.security.KeyStore;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -102,7 +101,6 @@
IActivityManager mActivityManager;
DevicePolicyManager mDevicePolicyManager;
DevicePolicyManagerInternal mDevicePolicyManagerInternal;
- KeyStore mKeyStore;
MockSyntheticPasswordManager mSpManager;
IAuthSecret mAuthSecretService;
WindowManagerInternal mMockWindowManager;
@@ -165,7 +163,6 @@
new LockSettingsServiceTestable.MockInjector(
mContext,
mStorage,
- mKeyStore,
mActivityManager,
setUpStorageManagerMock(),
mSpManager,
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
index 296d2cb..f9077c4 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
@@ -30,7 +30,6 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.storage.IStorageManager;
-import android.security.KeyStore;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.service.gatekeeper.IGateKeeperService;
@@ -41,6 +40,7 @@
import com.android.server.pm.UserManagerInternal;
import java.io.FileNotFoundException;
+import java.security.KeyStore;
public class LockSettingsServiceTestable extends LockSettingsService {
private Intent mSavedFrpNotificationIntent = null;
@@ -50,7 +50,6 @@
public static class MockInjector extends LockSettingsService.Injector {
private LockSettingsStorage mLockSettingsStorage;
- private KeyStore mKeyStore;
private IActivityManager mActivityManager;
private IStorageManager mStorageManager;
private SyntheticPasswordManager mSpManager;
@@ -62,14 +61,13 @@
public boolean mIsHeadlessSystemUserMode = false;
public boolean mIsMainUserPermanentAdmin = false;
- public MockInjector(Context context, LockSettingsStorage storage, KeyStore keyStore,
- IActivityManager activityManager,
- IStorageManager storageManager, SyntheticPasswordManager spManager,
- FakeGsiService gsiService, RecoverableKeyStoreManager recoverableKeyStoreManager,
+ public MockInjector(Context context, LockSettingsStorage storage,
+ IActivityManager activityManager, IStorageManager storageManager,
+ SyntheticPasswordManager spManager, FakeGsiService gsiService,
+ RecoverableKeyStoreManager recoverableKeyStoreManager,
UserManagerInternal userManagerInternal, DeviceStateCache deviceStateCache) {
super(context);
mLockSettingsStorage = storage;
- mKeyStore = keyStore;
mActivityManager = activityManager;
mStorageManager = storageManager;
mSpManager = spManager;
@@ -110,11 +108,6 @@
}
@Override
- public KeyStore getKeyStore() {
- return mKeyStore;
- }
-
- @Override
public IStorageManager getStorageManager() {
return mStorageManager;
}
@@ -145,8 +138,7 @@
}
@Override
- public UnifiedProfilePasswordCache getUnifiedProfilePasswordCache(
- java.security.KeyStore ks) {
+ public UnifiedProfilePasswordCache getUnifiedProfilePasswordCache(KeyStore ks) {
return mock(UnifiedProfilePasswordCache.class);
}
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index 530a39e..0f2c62d 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -53,11 +53,14 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.service.usb.UsbServiceDumpProto;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
@@ -147,6 +150,7 @@
private final UsbSettingsManager mSettingsManager;
private final UsbPermissionManager mPermissionManager;
+ static final int PACKAGE_MONITOR_OPERATION_ID = 1;
/**
* The user id of the current user. There might be several profiles (with separate user ids)
* per user.
@@ -156,6 +160,10 @@
private final Object mLock = new Object();
+ // Key: USB port id
+ // Value: A set of UIDs of requesters who request disabling usb data
+ private final ArrayMap<String, ArraySet<Integer>> mUsbDisableRequesters = new ArrayMap<>();
+
/**
* @return the {@link UsbUserSettingsManager} for the given userId
*/
@@ -261,6 +269,10 @@
if (mDeviceManager != null) {
mDeviceManager.bootCompleted();
}
+ if (android.hardware.usb.flags.Flags.enableUsbDataSignalStaking()) {
+ new PackageUninstallMonitor()
+ .register(mContext, UserHandle.ALL, BackgroundThread.getHandler());
+ }
}
/** Called when a user is unlocked. */
@@ -873,6 +885,11 @@
Objects.requireNonNull(callback, "enableUsbData: callback must not be null. opId:"
+ operationId);
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+
+ if (android.hardware.usb.flags.Flags.enableUsbDataSignalStaking()) {
+ if (!shouldUpdateUsbSignaling(portId, enable, Binder.getCallingUid())) return false;
+ }
+
final long ident = Binder.clearCallingIdentity();
boolean wait;
try {
@@ -892,6 +909,31 @@
return wait;
}
+ /**
+ * If enable = true, exclude UID from update list.
+ * If enable = false, include UID in update list.
+ * Return false if enable = true and the list is empty (no updates).
+ * Return true otherwise (let downstream decide on updates).
+ */
+ private boolean shouldUpdateUsbSignaling(String portId, boolean enable, int uid) {
+ synchronized (mUsbDisableRequesters) {
+ if (!mUsbDisableRequesters.containsKey(portId)) {
+ mUsbDisableRequesters.put(portId, new ArraySet<>());
+ }
+
+ ArraySet<Integer> uidsOfDisableRequesters = mUsbDisableRequesters.get(portId);
+
+ if (enable) {
+ uidsOfDisableRequesters.remove(uid);
+ // re-enable USB port (return true) if there are no other disable requesters
+ return uidsOfDisableRequesters.isEmpty();
+ } else {
+ uidsOfDisableRequesters.add(uid);
+ }
+ }
+ return true;
+ }
+
@Override
public void enableUsbDataWhileDocked(String portId, int operationId,
IUsbOperationInternal callback) {
@@ -1344,4 +1386,26 @@
private static String removeLastChar(String value) {
return value.substring(0, value.length() - 1);
}
+
+ /**
+ * Upon app removal, clear associated UIDs from the mUsbDisableRequesters list
+ * and re-enable USB data signaling if no remaining apps require USB disabling.
+ */
+ private class PackageUninstallMonitor extends PackageMonitor {
+ @Override
+ public void onUidRemoved(int uid) {
+ synchronized (mUsbDisableRequesters) {
+ for (String portId : mUsbDisableRequesters.keySet()) {
+ ArraySet<Integer> disabledUid = mUsbDisableRequesters.get(portId);
+ if (disabledUid != null) {
+ disabledUid.remove(uid);
+ if (disabledUid.isEmpty()) {
+ enableUsbData(portId, true, PACKAGE_MONITOR_OPERATION_ID,
+ new IUsbOperationInternal.Default());
+ }
+ }
+ }
+ }
+ }
+ }
}