Merge "Properly sets the display id on Autofill UIs." into udc-dev
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
index 430a1e2..4d646de 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
@@ -292,6 +292,10 @@
return "permission";
case EXACT_ALLOW_REASON_POLICY_PERMISSION:
return "policy_permission";
+ case EXACT_ALLOW_REASON_LISTENER:
+ return "listener";
+ case EXACT_ALLOW_REASON_PRIORITIZED:
+ return "prioritized";
case EXACT_ALLOW_REASON_NOT_APPLICABLE:
return "N/A";
default:
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 7931d1a..2dbff58 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -724,7 +724,10 @@
}
getPositionListener().addSubscriber(mCursorAnchorInfoNotifier, true);
- makeBlink();
+ // Call resumeBlink here instead of makeBlink to ensure that if mBlink is not null the
+ // Blink object is uncancelled. This ensures when a view is removed and added back the
+ // cursor will resume blinking.
+ resumeBlink();
}
void onDetachedFromWindow() {
@@ -1094,8 +1097,10 @@
private void resumeBlink() {
if (mBlink != null) {
mBlink.uncancel();
- makeBlink();
}
+ // Moving makeBlink outside of the null check block ensures that mBlink object gets
+ // instantiated when the view is added to the window if mBlink is still null.
+ makeBlink();
}
void adjustInputType(boolean password, boolean passwordInputType,
@@ -2921,6 +2926,9 @@
if (shouldBlink()) {
mShowCursor = SystemClock.uptimeMillis();
if (mBlink == null) mBlink = new Blink();
+ // Call uncancel as mBlink could have previously been cancelled and cursor will not
+ // resume blinking unless uncancelled.
+ mBlink.uncancel();
mTextView.removeCallbacks(mBlink);
mTextView.postDelayed(mBlink, BLINK);
} else {
diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java
index 3a8f427..4f9fc39 100644
--- a/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java
+++ b/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java
@@ -32,6 +32,9 @@
import android.os.Build;
import android.os.UserHandle;
import android.provider.Settings;
+import android.telecom.TelecomManager;
+import android.telephony.Annotation;
+import android.telephony.TelephonyManager;
import android.text.ParcelableSpan;
import android.text.Spanned;
import android.text.TextUtils;
@@ -204,6 +207,32 @@
}
/**
+ * Intercepts the {@link AccessibilityService#GLOBAL_ACTION_KEYCODE_HEADSETHOOK} action
+ * by directly interacting with TelecomManager if a call is incoming or in progress.
+ *
+ * <p>
+ * Provided here in shared utils to be used by both the legacy and modern (SysUI)
+ * system action implementations.
+ * </p>
+ *
+ * @return True if the action was propagated to TelecomManager, otherwise false.
+ */
+ public static boolean interceptHeadsetHookForActiveCall(Context context) {
+ final TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
+ @Annotation.CallState final int callState =
+ telecomManager != null ? telecomManager.getCallState()
+ : TelephonyManager.CALL_STATE_IDLE;
+ if (callState == TelephonyManager.CALL_STATE_RINGING) {
+ telecomManager.acceptRingingCall();
+ return true;
+ } else if (callState == TelephonyManager.CALL_STATE_OFFHOOK) {
+ telecomManager.endCall();
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Indicates whether the current user has completed setup via the setup wizard.
* {@link android.provider.Settings.Secure#USER_SETUP_COMPLETE}
*
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 31220b4..dd535a1 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -8228,6 +8228,8 @@
<service android:name="com.android.server.companion.datatransfer.contextsync.CallMetadataSyncInCallService"
android:permission="android.permission.BIND_INCALL_SERVICE"
android:exported="true">
+ <meta-data android:name="android.telecom.INCLUDE_SELF_MANAGED_CALLS"
+ android:value="true" />
<intent-filter>
<action android:name="android.telecom.InCallService"/>
</intent-filter>
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 193544e..88b9643 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -934,13 +934,25 @@
}
}
levelCaps = createFromProfileLevel(mMime, profile, maxLevel);
- // remove profile from this format otherwise levelCaps.isFormatSupported will
- // get into this same conditon and loop forever.
- Map<String, Object> mapWithoutProfile = new HashMap<>(map);
- mapWithoutProfile.remove(MediaFormat.KEY_PROFILE);
- MediaFormat formatWithoutProfile = new MediaFormat(mapWithoutProfile);
- if (levelCaps != null && !levelCaps.isFormatSupported(formatWithoutProfile)) {
- return false;
+ // We must remove the profile from this format otherwise levelCaps.isFormatSupported
+ // will get into this same condition and loop forever. Furthermore, since levelCaps
+ // does not contain features and bitrate specific keys, keep only keys relevant for
+ // a level check.
+ Map<String, Object> levelCriticalFormatMap = new HashMap<>(map);
+ final Set<String> criticalKeys =
+ isVideo() ? VideoCapabilities.VIDEO_LEVEL_CRITICAL_FORMAT_KEYS :
+ isAudio() ? AudioCapabilities.AUDIO_LEVEL_CRITICAL_FORMAT_KEYS :
+ null;
+
+ // critical keys will always contain KEY_MIME, but should also contain others to be
+ // meaningful
+ if (criticalKeys != null && criticalKeys.size() > 1 && levelCaps != null) {
+ levelCriticalFormatMap.keySet().retainAll(criticalKeys);
+
+ MediaFormat levelCriticalFormat = new MediaFormat(levelCriticalFormatMap);
+ if (!levelCaps.isFormatSupported(levelCriticalFormat)) {
+ return false;
+ }
}
}
if (mAudioCaps != null && !mAudioCaps.supportsFormat(format)) {
@@ -1633,6 +1645,16 @@
}
}
+ /* package private */
+ // must not contain KEY_PROFILE
+ static final Set<String> AUDIO_LEVEL_CRITICAL_FORMAT_KEYS = Set.of(
+ // We don't set level-specific limits for audio codecs today. Key candidates would
+ // be sample rate, bit rate or channel count.
+ // MediaFormat.KEY_SAMPLE_RATE,
+ // MediaFormat.KEY_CHANNEL_COUNT,
+ // MediaFormat.KEY_BIT_RATE,
+ MediaFormat.KEY_MIME);
+
/** @hide */
public boolean supportsFormat(MediaFormat format) {
Map<String, Object> map = format.getMap();
@@ -2357,6 +2379,15 @@
return ok;
}
+ /* package private */
+ // must not contain KEY_PROFILE
+ static final Set<String> VIDEO_LEVEL_CRITICAL_FORMAT_KEYS = Set.of(
+ MediaFormat.KEY_WIDTH,
+ MediaFormat.KEY_HEIGHT,
+ MediaFormat.KEY_FRAME_RATE,
+ MediaFormat.KEY_BIT_RATE,
+ MediaFormat.KEY_MIME);
+
/**
* @hide
* @throws java.lang.ClassCastException */
diff --git a/packages/CredentialManager/res/values/strings.xml b/packages/CredentialManager/res/values/strings.xml
index 905e0ca..a3b2752 100644
--- a/packages/CredentialManager/res/values/strings.xml
+++ b/packages/CredentialManager/res/values/strings.xml
@@ -114,13 +114,13 @@
<!-- Strings for the get flow. -->
<!-- This appears as the title of the modal bottom sheet asking for user confirmation to use the single previously saved passkey to sign in to the app. [CHAR LIMIT=200] -->
<string name="get_dialog_title_use_passkey_for">Use your saved passkey for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string>
- <!-- This appears as the title of the dialog asking for user confirmation to use the single previously saved credential to sign in to the app. [CHAR LIMIT=200] -->
- <string name="get_dialog_title_use_sign_in_for">Use your saved sign-in for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string>
- <!-- This appears as the title of the dialog asking for user to make a choice from various previously saved credentials to sign in to the app. [CHAR LIMIT=200] -->
- <string name="get_dialog_title_choose_sign_in_for">Choose a saved sign-in for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string>
- <!-- This appears as the title of the dialog asking for user to make a choice from various previously saved credentials to sign in to the app. [CHAR LIMIT=200] -->
+ <!-- This appears as the title of the dialog asking for user confirmation to use the single user credential (previously saved or to be created) to sign in to the app. [CHAR LIMIT=200] -->
+ <string name="get_dialog_title_use_sign_in_for">Use your sign-in for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string>
+ <!-- This appears as the title of the dialog asking for user to make a choice from various available user credentials (previously saved or to be created) to sign in to the app. [CHAR LIMIT=200] -->
+ <string name="get_dialog_title_choose_sign_in_for">Choose a sign-in for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string>
+ <!-- This appears as the title of the dialog asking for user to make a choice from options of available user information (e.g. driver's license, vaccination status) to pass to the app. [CHAR LIMIT=200] -->
<string name="get_dialog_title_choose_option_for">Choose an option for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string>
- <!-- This appears as the title of the dialog asking user to use a previously saved credentials to sign in to the app. [CHAR LIMIT=200] -->
+ <!-- This appears as the title of the dialog asking user to send a piece of user information (e.g. driver's license, vaccination status) to the app. [CHAR LIMIT=200] -->
<string name="get_dialog_title_use_info_on">Use this info on <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string>
<!-- This is a label for a button that links the user to different sign-in methods . [CHAR LIMIT=80] -->
<string name="get_dialog_use_saved_passkey_for">Sign in another way</string>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 2c669bb..9573913 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -396,6 +396,7 @@
private int mFaceRunningState = BIOMETRIC_STATE_STOPPED;
private boolean mIsDreaming;
private boolean mLogoutEnabled;
+ private boolean mIsFaceEnrolled;
private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private int mPostureState = DEVICE_POSTURE_UNKNOWN;
private FingerprintInteractiveToAuthProvider mFingerprintInteractiveToAuthProvider;
@@ -2572,6 +2573,16 @@
}
}
+ private void updateFaceEnrolled(int userId) {
+ final Boolean isFaceEnrolled = isFaceSupported()
+ && mBiometricEnabledForUser.get(userId)
+ && mAuthController.isFaceAuthEnrolled(userId);
+ if (mIsFaceEnrolled != isFaceEnrolled) {
+ mLogger.logFaceEnrolledUpdated(mIsFaceEnrolled, isFaceEnrolled);
+ }
+ mIsFaceEnrolled = isFaceEnrolled;
+ }
+
private boolean isFaceSupported() {
return mFaceManager != null && !mFaceSensorProperties.isEmpty();
}
@@ -2611,17 +2622,10 @@
}
/**
- * @return true if there's at least one face enrolled for the given user
- */
- private boolean isFaceEnrolled(int userId) {
- return mAuthController.isFaceAuthEnrolled(userId);
- }
-
- /**
- * @return true if there's at least one face enrolled for the current user
+ * @return true if there's at least one face enrolled
*/
public boolean isFaceEnrolled() {
- return isFaceEnrolled(getCurrentUser());
+ return mIsFaceEnrolled;
}
private final UserTracker.Callback mUserChangedCallback = new UserTracker.Callback() {
@@ -3280,13 +3284,14 @@
@SuppressLint("MissingPermission")
@VisibleForTesting
boolean isUnlockWithFingerprintPossible(int userId) {
- boolean newFpPossible = isFingerprintSupported()
- && !isFingerprintDisabled(userId) && mAuthController.isFingerprintEnrolled(userId);
- Boolean oldFpPossible = mIsUnlockWithFingerprintPossible.getOrDefault(userId, false);
- if (oldFpPossible != newFpPossible) {
- mLogger.logFpPossibleUpdated(userId, oldFpPossible, newFpPossible);
+ // TODO (b/242022358), make this rely on onEnrollmentChanged event and update it only once.
+ boolean newFpEnrolled = isFingerprintSupported()
+ && !isFingerprintDisabled(userId) && mFpm.hasEnrolledTemplates(userId);
+ Boolean oldFpEnrolled = mIsUnlockWithFingerprintPossible.getOrDefault(userId, false);
+ if (oldFpEnrolled != newFpEnrolled) {
+ mLogger.logFpEnrolledUpdated(userId, oldFpEnrolled, newFpEnrolled);
}
- mIsUnlockWithFingerprintPossible.put(userId, newFpPossible);
+ mIsUnlockWithFingerprintPossible.put(userId, newFpEnrolled);
return mIsUnlockWithFingerprintPossible.get(userId);
}
@@ -3301,13 +3306,24 @@
/**
* @deprecated This is being migrated to use modern architecture.
*/
- @VisibleForTesting
@Deprecated
- public boolean isUnlockWithFacePossible(int userId) {
+ private boolean isUnlockWithFacePossible(int userId) {
if (isFaceAuthInteractorEnabled()) {
return getFaceAuthInteractor().canFaceAuthRun();
}
- return isFaceSupported() && isFaceEnrolled(userId) && !isFaceDisabled(userId);
+ return isFaceAuthEnabledForUser(userId) && !isFaceDisabled(userId);
+ }
+
+ /**
+ * If face hardware is available, user has enrolled and enabled auth via setting.
+ *
+ * @deprecated This is being migrated to use modern architecture.
+ */
+ @Deprecated
+ public boolean isFaceAuthEnabledForUser(int userId) {
+ // TODO (b/242022358), make this rely on onEnrollmentChanged event and update it only once.
+ updateFaceEnrolled(userId);
+ return mIsFaceEnrolled;
}
private void stopListeningForFingerprint() {
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index fe40145..1661806 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -630,7 +630,7 @@
)
}
- fun logFpPossibleUpdated(userId: Int, oldValue: Boolean, newValue: Boolean) {
+ fun logFpEnrolledUpdated(userId: Int, oldValue: Boolean, newValue: Boolean) {
logBuffer.log(
TAG,
DEBUG,
@@ -639,7 +639,7 @@
bool1 = oldValue
bool2 = newValue
},
- { "Fp possible state changed for userId: $int1 old: $bool1, new: $bool2" }
+ { "Fp enrolled state changed for userId: $int1 old: $bool1, new: $bool2" }
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/OWNERS b/packages/SystemUI/src/com/android/systemui/accessibility/OWNERS
new file mode 100644
index 0000000..1f66c91
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 44215
+
+include /core/java/android/view/accessibility/OWNERS
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
index f3c71da..4158390 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
@@ -45,6 +45,8 @@
import com.android.internal.R;
import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity;
+import com.android.internal.accessibility.util.AccessibilityUtils;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ScreenshotHelper;
import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
@@ -520,8 +522,11 @@
SCREENSHOT_ACCESSIBILITY_ACTIONS, new Handler(Looper.getMainLooper()), null);
}
- private void handleHeadsetHook() {
- sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HEADSETHOOK);
+ @VisibleForTesting
+ void handleHeadsetHook() {
+ if (!AccessibilityUtils.interceptHeadsetHookForActiveCall(mContext)) {
+ sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HEADSETHOOK);
+ }
}
private void handleAccessibilityButton() {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
index 7bfd84e..155c26d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
@@ -463,13 +463,17 @@
if ((configDiff & ActivityInfo.CONFIG_UI_MODE) != 0
|| (configDiff & ActivityInfo.CONFIG_ASSETS_PATHS) != 0
|| (configDiff & ActivityInfo.CONFIG_FONT_SCALE) != 0
+ || (configDiff & ActivityInfo.CONFIG_LOCALE) != 0
|| (configDiff & ActivityInfo.CONFIG_DENSITY) != 0) {
// We listen to following config changes to trigger layout inflation:
// CONFIG_UI_MODE: theme change
// CONFIG_ASSETS_PATHS: wallpaper change
// CONFIG_FONT_SCALE: font size change
+ // CONFIG_LOCALE: language change
// CONFIG_DENSITY: display size change
+ mParams.accessibilityTitle = getAccessibilityWindowTitle(mContext);
+
boolean showSettingPanelAfterThemeChange = mIsVisible;
hideSettingPanel(/* resetPosition= */ false);
inflateView();
@@ -490,11 +494,6 @@
+ mDraggableWindowBounds.top;
return;
}
-
- if ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0) {
- updateAccessibilityWindowTitle();
- return;
- }
}
private void onWindowInsetChanged() {
@@ -515,13 +514,6 @@
}
}
- private void updateAccessibilityWindowTitle() {
- mParams.accessibilityTitle = getAccessibilityWindowTitle(mContext);
- if (mIsVisible) {
- mWindowManager.updateViewLayout(mSettingView, mParams);
- }
- }
-
public void editMagnifierSizeMode(boolean enable) {
setEditMagnifierSizeMode(enable);
updateSelectedButton(MagnificationSize.NONE);
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt
index 120704c..3fc3ad6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt
@@ -154,6 +154,7 @@
get() = controller?.sessionToken
private var started = false
private var playbackType = PLAYBACK_TYPE_UNKNOWN
+ private var playbackVolumeControlId: String? = null
private var current: MediaDeviceData? = null
set(value) {
val sameWithoutIcon = value != null && value.equalsWithoutIcon(field)
@@ -181,6 +182,7 @@
localMediaManager.startScan()
muteAwaitConnectionManager?.startListening()
playbackType = controller?.playbackInfo?.playbackType ?: PLAYBACK_TYPE_UNKNOWN
+ playbackVolumeControlId = controller?.playbackInfo?.volumeControlId
controller?.registerCallback(this)
updateCurrent()
started = true
@@ -209,6 +211,8 @@
println(" current device is ${current?.name}")
val type = controller?.playbackInfo?.playbackType
println(" PlaybackType=$type (1 for local, 2 for remote) cached=$playbackType")
+ val volumeControlId = controller?.playbackInfo?.volumeControlId
+ println(" volumeControlId=$volumeControlId cached= $playbackVolumeControlId")
println(" routingSession=$routingSession")
println(" selectedRoutes=$selectedRoutes")
}
@@ -217,10 +221,15 @@
@WorkerThread
override fun onAudioInfoChanged(info: MediaController.PlaybackInfo?) {
val newPlaybackType = info?.playbackType ?: PLAYBACK_TYPE_UNKNOWN
- if (newPlaybackType == playbackType) {
+ val newPlaybackVolumeControlId = info?.volumeControlId
+ if (
+ newPlaybackType == playbackType &&
+ newPlaybackVolumeControlId == playbackVolumeControlId
+ ) {
return
}
playbackType = newPlaybackType
+ playbackVolumeControlId = newPlaybackVolumeControlId
updateCurrent()
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 9ebc8e4..53daffc 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -713,7 +713,7 @@
dividerItems.forEach((key, item) -> {
finalMediaItems.add(key, item);
});
- finalMediaItems.add(new MediaItem());
+ attachConnectNewDeviceItemIfNeeded(finalMediaItems);
mMediaItemList.clear();
mMediaItemList.addAll(finalMediaItems);
}
@@ -749,7 +749,7 @@
finalMediaItems.add(new MediaItem(device));
}
}
- finalMediaItems.add(new MediaItem());
+ attachConnectNewDeviceItemIfNeeded(finalMediaItems);
mMediaItemList.clear();
mMediaItemList.addAll(finalMediaItems);
}
@@ -760,6 +760,13 @@
new MediaItem(title, MediaItem.MediaItemType.TYPE_GROUP_DIVIDER));
}
+ private void attachConnectNewDeviceItemIfNeeded(List<MediaItem> mediaItems) {
+ // Attach "Connect a device" item only when current output is not remote and not a group
+ if (!isCurrentConnectedDeviceRemote() && getSelectedMediaDevice().size() == 1) {
+ mediaItems.add(new MediaItem());
+ }
+ }
+
private void attachRangeInfo(List<MediaDevice> devices) {
for (MediaDevice mediaDevice : devices) {
if (mNearbyDeviceInfoMap.containsKey(mediaDevice.getId())) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
index c17366a..74ab47f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
@@ -115,7 +115,9 @@
val onKeyguard = keyguardUpdateMonitor.isKeyguardVisible &&
!statusBarStateController.isDozing
- val shouldListen = (onKeyguard || bouncerVisible) && keyguardUpdateMonitor.isFaceEnrolled
+ val userId = KeyguardUpdateMonitor.getCurrentUser()
+ val isFaceEnabled = keyguardUpdateMonitor.isFaceAuthEnabledForUser(userId)
+ val shouldListen = (onKeyguard || bouncerVisible) && isFaceEnabled
if (shouldListen != isListening) {
isListening = shouldListen
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index 3d811cf..673819b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -240,7 +240,7 @@
|| (Build.IS_DEBUGGABLE && DEBUG_AUTH_WITH_ADB && mDebugUnlocked);
boolean trustManaged = mKeyguardUpdateMonitor.getUserTrustIsManaged(user);
boolean trusted = mKeyguardUpdateMonitor.getUserHasTrust(user);
- boolean faceAuthEnabled = mKeyguardUpdateMonitor.isFaceEnrolled();
+ boolean faceAuthEnabled = mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(user);
boolean changed = secure != mSecure || canDismissLockScreen != mCanDismissLockScreen
|| trustManaged != mTrustManaged || mTrusted != trusted
|| mFaceAuthEnabled != faceAuthEnabled;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 71246c9..2962c14 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -383,7 +383,6 @@
}
private void setupFingerprintAuth(boolean isClass3) throws RemoteException {
- when(mAuthController.isFingerprintEnrolled(anyInt())).thenReturn(true);
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
mFingerprintSensorProperties = List.of(
@@ -2693,42 +2692,33 @@
}
@Test
public void testFingerprintSensorProperties() throws RemoteException {
- // GIVEN no fingerprint sensor properties
- when(mAuthController.isFingerprintEnrolled(anyInt())).thenReturn(true);
mFingerprintAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(
new ArrayList<>());
- // THEN fingerprint is not possible
assertThat(mKeyguardUpdateMonitor.isUnlockWithFingerprintPossible(
KeyguardUpdateMonitor.getCurrentUser())).isFalse();
- // WHEN there are fingerprint sensor properties
mFingerprintAuthenticatorsRegisteredCallback
.onAllAuthenticatorsRegistered(mFingerprintSensorProperties);
- // THEN unlock with fp is possible & fingerprint starts listening
+ verifyFingerprintAuthenticateCall();
assertThat(mKeyguardUpdateMonitor.isUnlockWithFingerprintPossible(
KeyguardUpdateMonitor.getCurrentUser())).isTrue();
- verifyFingerprintAuthenticateCall();
}
@Test
public void testFaceSensorProperties() throws RemoteException {
- // GIVEN no face sensor properties
- when(mAuthController.isFaceAuthEnrolled(anyInt())).thenReturn(true);
mFaceAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(new ArrayList<>());
- // THEN face is not possible
- assertThat(mKeyguardUpdateMonitor.isUnlockWithFacePossible(
+ assertThat(mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(
KeyguardUpdateMonitor.getCurrentUser())).isFalse();
- // WHEN there are face sensor properties
mFaceAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(mFaceSensorProperties);
+ biometricsEnabledForCurrentUser();
- // THEN face is possible but face does NOT start listening immediately
- assertThat(mKeyguardUpdateMonitor.isUnlockWithFacePossible(
- KeyguardUpdateMonitor.getCurrentUser())).isTrue();
verifyFaceAuthenticateNeverCalled();
verifyFaceDetectNeverCalled();
+ assertThat(mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(
+ KeyguardUpdateMonitor.getCurrentUser())).isTrue();
}
@Test
@@ -2801,6 +2791,9 @@
}
private void mockCanBypassLockscreen(boolean canBypass) {
+ // force update the isFaceEnrolled cache:
+ mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(getCurrentUser());
+
mKeyguardUpdateMonitor.setKeyguardBypassController(mKeyguardBypassController);
when(mKeyguardBypassController.canBypass()).thenReturn(canBypass);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/OWNERS b/packages/SystemUI/tests/src/com/android/systemui/accessibility/OWNERS
new file mode 100644
index 0000000..a2001e6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/view/accessibility/OWNERS
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java
new file mode 100644
index 0000000..025c88c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.input.InputManager;
+import android.os.RemoteException;
+import android.telecom.TelecomManager;
+import android.telephony.TelephonyManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.KeyEvent;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.settings.FakeDisplayTracker;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.shade.ShadeController;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
+
+import dagger.Lazy;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+@TestableLooper.RunWithLooper
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class SystemActionsTest extends SysuiTestCase {
+ @Mock
+ private UserTracker mUserTracker;
+ @Mock
+ private NotificationShadeWindowController mNotificationShadeController;
+ @Mock
+ private ShadeController mShadeController;
+ @Mock
+ private Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
+ @Mock
+ private Optional<Recents> mRecentsOptional;
+ @Mock
+ private TelecomManager mTelecomManager;
+ @Mock
+ private InputManager mInputManager;
+ private final FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
+
+ private SystemActions mSystemActions;
+
+ @Before
+ public void setUp() throws RemoteException {
+ MockitoAnnotations.initMocks(this);
+ mContext.addMockSystemService(TelecomManager.class, mTelecomManager);
+ mContext.addMockSystemService(InputManager.class, mInputManager);
+ mSystemActions = new SystemActions(mContext, mUserTracker, mNotificationShadeController,
+ mShadeController, mCentralSurfacesOptionalLazy, mRecentsOptional, mDisplayTracker);
+ }
+
+ @Test
+ public void handleHeadsetHook_callStateIdle_injectsKeyEvents() {
+ when(mTelecomManager.getCallState()).thenReturn(TelephonyManager.CALL_STATE_IDLE);
+ // Use a custom doAnswer captor that copies the KeyEvent before storing it, because the
+ // method under test modifies the event object after injecting it which prevents
+ // reliably asserting on the event properties.
+ final List<KeyEvent> keyEvents = new ArrayList<>();
+ doAnswer(invocation -> {
+ keyEvents.add(new KeyEvent(invocation.getArgument(0)));
+ return null;
+ }).when(mInputManager).injectInputEvent(any(), anyInt());
+
+ mSystemActions.handleHeadsetHook();
+
+ assertThat(keyEvents.size()).isEqualTo(2);
+ assertThat(keyEvents.get(0).getKeyCode()).isEqualTo(KeyEvent.KEYCODE_HEADSETHOOK);
+ assertThat(keyEvents.get(0).getAction()).isEqualTo(KeyEvent.ACTION_DOWN);
+ assertThat(keyEvents.get(1).getKeyCode()).isEqualTo(KeyEvent.KEYCODE_HEADSETHOOK);
+ assertThat(keyEvents.get(1).getAction()).isEqualTo(KeyEvent.ACTION_UP);
+ }
+
+ @Test
+ public void handleHeadsetHook_callStateRinging_answersCall() {
+ when(mTelecomManager.getCallState()).thenReturn(TelephonyManager.CALL_STATE_RINGING);
+
+ mSystemActions.handleHeadsetHook();
+
+ verify(mTelecomManager).acceptRingingCall();
+ }
+
+ @Test
+ public void handleHeadsetHook_callStateOffhook_endsCall() {
+ when(mTelecomManager.getCallState()).thenReturn(TelephonyManager.CALL_STATE_OFFHOOK);
+
+ mSystemActions.handleHeadsetHook();
+
+ verify(mTelecomManager).endCall();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt
index a45e9d9..0c57e7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt
@@ -468,7 +468,7 @@
}
@Test
- fun audioInfoChanged() {
+ fun audioInfoPlaybackTypeChanged() {
whenever(playbackInfo.getPlaybackType()).thenReturn(PlaybackInfo.PLAYBACK_TYPE_LOCAL)
whenever(controller.getPlaybackInfo()).thenReturn(playbackInfo)
// GIVEN a controller with local playback type
@@ -486,6 +486,25 @@
}
@Test
+ fun audioInfoVolumeControlIdChanged() {
+ whenever(playbackInfo.getPlaybackType()).thenReturn(PlaybackInfo.PLAYBACK_TYPE_LOCAL)
+ whenever(playbackInfo.getVolumeControlId()).thenReturn(null)
+ whenever(controller.getPlaybackInfo()).thenReturn(playbackInfo)
+ // GIVEN a controller with local playback type
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+ reset(mr2)
+ // WHEN onAudioInfoChanged fires with a volume control id change
+ whenever(playbackInfo.getVolumeControlId()).thenReturn("placeholder id")
+ val captor = ArgumentCaptor.forClass(MediaController.Callback::class.java)
+ verify(controller).registerCallback(captor.capture())
+ captor.value.onAudioInfoChanged(playbackInfo)
+ // THEN the route is checked
+ verify(mr2).getRoutingSessionForMediaController(eq(controller))
+ }
+
+ @Test
fun audioInfoHasntChanged() {
whenever(playbackInfo.getPlaybackType()).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
whenever(controller.getPlaybackInfo()).thenReturn(playbackInfo)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index 0bdcaf4..de0c6a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -342,6 +342,30 @@
}
@Test
+ public void advanced_onDeviceListUpdateWithConnectedDeviceRemote_verifyItemSize() {
+ when(mFlags.isEnabled(Flags.OUTPUT_SWITCHER_ADVANCED_LAYOUT)).thenReturn(true);
+ when(mMediaDevice1.getFeatures()).thenReturn(
+ ImmutableList.of(MediaRoute2Info.FEATURE_REMOTE_PLAYBACK));
+ when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice1);
+ mMediaOutputController.start(mCb);
+ reset(mCb);
+
+ mMediaOutputController.onDeviceListUpdate(mMediaDevices);
+ final List<MediaDevice> devices = new ArrayList<>();
+ for (MediaItem item : mMediaOutputController.getMediaItemList()) {
+ if (item.getMediaDevice().isPresent()) {
+ devices.add(item.getMediaDevice().get());
+ }
+ }
+
+ assertThat(devices.containsAll(mMediaDevices)).isTrue();
+ assertThat(devices.size()).isEqualTo(mMediaDevices.size());
+ assertThat(mMediaOutputController.getMediaItemList().size()).isEqualTo(
+ mMediaDevices.size() + 1);
+ verify(mCb).onDeviceListChanged();
+ }
+
+ @Test
public void advanced_categorizeMediaItems_withSuggestedDevice_verifyDeviceListSize() {
when(mFlags.isEnabled(Flags.OUTPUT_SWITCHER_ADVANCED_LAYOUT)).thenReturn(true);
when(mMediaDevice1.isSuggestedDevice()).thenReturn(true);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index a3b4a0f..a510d16 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -490,7 +490,7 @@
mMainHandler, context,
new PolicyWarningUIController.NotificationController(context));
mSecurityPolicy = new AccessibilitySecurityPolicy(policyWarningUIController, mContext,
- this);
+ this, LocalServices.getService(PackageManagerInternal.class));
mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler,
mWindowManagerService, this, mSecurityPolicy, this, mTraceManager);
mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
index 8865623..65c1873 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
@@ -29,6 +29,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
@@ -99,6 +100,7 @@
private final Context mContext;
private final PackageManager mPackageManager;
+ private final PackageManagerInternal mPackageManagerInternal;
private final UserManager mUserManager;
private final AppOpsManager mAppOpsManager;
private final AccessibilityUserManager mAccessibilityUserManager;
@@ -116,10 +118,12 @@
*/
public AccessibilitySecurityPolicy(PolicyWarningUIController policyWarningUIController,
@NonNull Context context,
- @NonNull AccessibilityUserManager a11yUserManager) {
+ @NonNull AccessibilityUserManager a11yUserManager,
+ @NonNull PackageManagerInternal packageManagerInternal) {
mContext = context;
mAccessibilityUserManager = a11yUserManager;
mPackageManager = mContext.getPackageManager();
+ mPackageManagerInternal = packageManagerInternal;
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
mPolicyWarningUIController = policyWarningUIController;
@@ -513,12 +517,7 @@
private boolean isValidPackageForUid(String packageName, int uid) {
final long token = Binder.clearCallingIdentity();
try {
- // Since we treat calls from a profile as if made by its parent, using
- // MATCH_ANY_USER to query the uid of the given package name.
- return uid == mPackageManager.getPackageUidAsUser(
- packageName, PackageManager.MATCH_ANY_USER, UserHandle.getUserId(uid));
- } catch (PackageManager.NameNotFoundException e) {
- return false;
+ return mPackageManagerInternal.isSameApp(packageName, uid, UserHandle.getUserId(uid));
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
index a13df47..9747579 100644
--- a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
+++ b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
@@ -36,6 +36,7 @@
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import com.android.internal.R;
+import com.android.internal.accessibility.util.AccessibilityUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ScreenshotHelper;
@@ -302,8 +303,10 @@
case AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT:
return takeScreenshot();
case AccessibilityService.GLOBAL_ACTION_KEYCODE_HEADSETHOOK:
- sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HEADSETHOOK,
- InputDevice.SOURCE_KEYBOARD);
+ if (!AccessibilityUtils.interceptHeadsetHookForActiveCall(mContext)) {
+ sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HEADSETHOOK,
+ InputDevice.SOURCE_KEYBOARD);
+ }
return true;
case AccessibilityService.GLOBAL_ACTION_DPAD_UP:
sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_UP,
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index d140403..6360e2a 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -167,7 +167,7 @@
.onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE)
.replaceWith("?");
- private static final int MAX_LOW_POWER_STATS_SIZE = 16384;
+ private static final int MAX_LOW_POWER_STATS_SIZE = 32768;
private static final int POWER_STATS_QUERY_TIMEOUT_MILLIS = 2000;
private static final String EMPTY = "Empty";
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 51872b3..98d2d3d 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -803,6 +803,20 @@
protected WallpaperData mLastLockWallpaper;
private IWallpaperManagerCallback mKeyguardListener;
private boolean mWaitingForUnlock;
+
+ /**
+ * Flag set to true after reboot if the home wallpaper is waiting for the device to be unlocked.
+ * This happens for wallpapers that are not direct-boot aware; they can only be rendered after
+ * the user unlocks the device for the first time after a reboot. In the meantime, the default
+ * wallpaper is shown instead.
+ */
+ private boolean mHomeWallpaperWaitingForUnlock;
+
+ /**
+ * Flag set to true after reboot if the lock wallpaper is waiting for the device to be unlocked.
+ */
+ private boolean mLockWallpaperWaitingForUnlock;
+
private boolean mShuttingDown;
/**
@@ -1790,7 +1804,23 @@
public void onUnlockUser(final int userId) {
synchronized (mLock) {
if (mCurrentUserId == userId) {
- if (mWaitingForUnlock) {
+ if (mIsLockscreenLiveWallpaperEnabled) {
+ if (mHomeWallpaperWaitingForUnlock) {
+ final WallpaperData systemWallpaper =
+ getWallpaperSafeLocked(userId, FLAG_SYSTEM);
+ switchWallpaper(systemWallpaper, null);
+ // TODO(b/278261563): call notifyCallbacksLocked inside switchWallpaper
+ notifyCallbacksLocked(systemWallpaper);
+ }
+ if (mLockWallpaperWaitingForUnlock) {
+ final WallpaperData lockWallpaper =
+ getWallpaperSafeLocked(userId, FLAG_LOCK);
+ switchWallpaper(lockWallpaper, null);
+ notifyCallbacksLocked(lockWallpaper);
+ }
+ }
+
+ if (mWaitingForUnlock && !mIsLockscreenLiveWallpaperEnabled) {
// the desired wallpaper is not direct-boot aware, load it now
final WallpaperData systemWallpaper =
getWallpaperSafeLocked(userId, FLAG_SYSTEM);
@@ -1845,13 +1875,23 @@
}
mCurrentUserId = userId;
systemWallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
- final WallpaperData tmpLockWallpaper = mLockWallpaperMap.get(userId);
- lockWallpaper = tmpLockWallpaper == null ? systemWallpaper : tmpLockWallpaper;
+
+ if (mIsLockscreenLiveWallpaperEnabled) {
+ lockWallpaper = systemWallpaper.mWhich == (FLAG_LOCK | FLAG_SYSTEM)
+ ? systemWallpaper : getWallpaperSafeLocked(userId, FLAG_LOCK);
+ } else {
+ final WallpaperData tmpLockWallpaper = mLockWallpaperMap.get(userId);
+ lockWallpaper = tmpLockWallpaper == null ? systemWallpaper : tmpLockWallpaper;
+ }
+
// Not started watching yet, in case wallpaper data was loaded for other reasons.
if (systemWallpaper.wallpaperObserver == null) {
systemWallpaper.wallpaperObserver = new WallpaperObserver(systemWallpaper);
systemWallpaper.wallpaperObserver.startWatching();
}
+ if (mIsLockscreenLiveWallpaperEnabled && lockWallpaper != systemWallpaper) {
+ switchWallpaper(lockWallpaper, null);
+ }
switchWallpaper(systemWallpaper, reply);
}
@@ -1870,6 +1910,11 @@
void switchWallpaper(WallpaperData wallpaper, IRemoteCallback reply) {
synchronized (mLock) {
mWaitingForUnlock = false;
+ if (mIsLockscreenLiveWallpaperEnabled) {
+ if ((wallpaper.mWhich & FLAG_SYSTEM) != 0) mHomeWallpaperWaitingForUnlock = false;
+ if ((wallpaper.mWhich & FLAG_LOCK) != 0) mLockWallpaperWaitingForUnlock = false;
+ }
+
final ComponentName cname = wallpaper.wallpaperComponent != null ?
wallpaper.wallpaperComponent : wallpaper.nextWallpaperComponent;
if (!bindWallpaperComponentLocked(cname, true, false, wallpaper, reply)) {
@@ -1882,6 +1927,11 @@
} catch (RemoteException ignored) {
}
+ if (mIsLockscreenLiveWallpaperEnabled) {
+ onSwitchWallpaperFailLocked(wallpaper, reply, si);
+ return;
+ }
+
if (si == null) {
Slog.w(TAG, "Failure starting previous wallpaper; clearing");
clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, reply);
@@ -1899,6 +1949,43 @@
}
}
+ /**
+ * Fallback method if a wallpaper fails to load on boot or after a user switch.
+ * Only called if mIsLockscreenLiveWallpaperEnabled is true.
+ */
+ private void onSwitchWallpaperFailLocked(
+ WallpaperData wallpaper, IRemoteCallback reply, ServiceInfo serviceInfo) {
+
+ if (serviceInfo == null) {
+ Slog.w(TAG, "Failure starting previous wallpaper; clearing");
+
+ if (wallpaper.mWhich == (FLAG_LOCK | FLAG_SYSTEM)) {
+ clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
+ clearWallpaperLocked(false, FLAG_LOCK, wallpaper.userId, reply);
+ } else {
+ clearWallpaperLocked(false, wallpaper.mWhich, wallpaper.userId, reply);
+ }
+ return;
+ }
+ Slog.w(TAG, "Wallpaper isn't direct boot aware; using fallback until unlocked");
+ // We might end up persisting the current wallpaper data
+ // while locked, so pretend like the component was actually
+ // bound into place
+ wallpaper.wallpaperComponent = wallpaper.nextWallpaperComponent;
+ final WallpaperData fallback = new WallpaperData(wallpaper.userId, wallpaper.mWhich);
+
+ // files from the previous static wallpaper may still be stored in memory.
+ // delete them in order to show the default wallpaper.
+ if (wallpaper.wallpaperFile.exists()) {
+ wallpaper.wallpaperFile.delete();
+ wallpaper.cropFile.delete();
+ }
+
+ bindWallpaperComponentLocked(mImageWallpaper, true, false, fallback, reply);
+ if ((wallpaper.mWhich & FLAG_SYSTEM) != 0) mHomeWallpaperWaitingForUnlock = true;
+ if ((wallpaper.mWhich & FLAG_LOCK) != 0) mLockWallpaperWaitingForUnlock = true;
+ }
+
@Override
public void clearWallpaper(String callingPackage, int which, int userId) {
if (DEBUG) Slog.v(TAG, "clearWallpaper");
diff --git a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java
index 32f7b96..5c929a9 100644
--- a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java
+++ b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java
@@ -239,7 +239,17 @@
}
return null;
}
- source.getBounds(mTmpRect);
+ mTmpRect.setEmpty();
+ if (source.mTransitionController.inFinishingTransition(source)) {
+ final Transition.ChangeInfo changeInfo = source.mTransitionController
+ .mFinishingTransition.mChanges.get(source);
+ if (changeInfo != null) {
+ mTmpRect.set(changeInfo.mAbsoluteBounds);
+ }
+ }
+ if (mTmpRect.isEmpty()) {
+ source.getBounds(mTmpRect);
+ }
mTmpRect.offsetTo(0, 0);
SurfaceControl[] excludeLayers;
final WindowState imeWindow = source.getDisplayContent().mInputMethodWindow;
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index a5adf3f..f1d4de9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -54,6 +54,7 @@
import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_HIGH;
import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_ALLOW_LIST;
import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_COMPAT;
+import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_LISTENER;
import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_NOT_APPLICABLE;
import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_PERMISSION;
import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_POLICY_PERMISSION;
@@ -2728,6 +2729,66 @@
}
@Test
+ public void exactListenerBinderCallWithoutPermissionWithoutAllowlist() throws RemoteException {
+ mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
+ mockChangeEnabled(AlarmManager.ENABLE_USE_EXACT_ALARM, true);
+
+ mockScheduleExactAlarmState(false);
+ mockUseExactAlarmState(false);
+ when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(false);
+
+ final IAlarmListener listener = getNewListener(() -> {});
+ mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
+ 0, null, listener, "test-tag", null, null);
+
+ verify(mService, never()).hasUseExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID);
+ verify(mService, never()).hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE,
+ TEST_CALLING_UID);
+ verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt());
+
+ final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L),
+ isNull(), eq(listener), eq("test-tag"), eq(FLAG_STANDALONE), isNull(), isNull(),
+ eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture(),
+ eq(EXACT_ALLOW_REASON_LISTENER));
+
+ final BroadcastOptions idleOptions = new BroadcastOptions(bundleCaptor.getValue());
+ final int type = idleOptions.getTemporaryAppAllowlistType();
+ assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
+ }
+
+ @Test
+ public void exactAllowWhileIdleListenerBinderCallWithoutPermissionWithoutAllowlist()
+ throws RemoteException {
+ mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
+ mockChangeEnabled(AlarmManager.ENABLE_USE_EXACT_ALARM, true);
+
+ mockScheduleExactAlarmState(false);
+ mockUseExactAlarmState(false);
+ when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(false);
+
+ final IAlarmListener listener = getNewListener(() -> {});
+ mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
+ FLAG_ALLOW_WHILE_IDLE, null, listener, "test-tag", null, null);
+
+ verify(mService, never()).hasUseExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID);
+ verify(mService, never()).hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE,
+ TEST_CALLING_UID);
+ verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt());
+
+ final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L),
+ isNull(), eq(listener), eq("test-tag"),
+ eq(FLAG_STANDALONE | FLAG_ALLOW_WHILE_IDLE_COMPAT), isNull(), isNull(),
+ eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture(),
+ eq(EXACT_ALLOW_REASON_LISTENER));
+
+ final BroadcastOptions idleOptions = new BroadcastOptions(bundleCaptor.getValue());
+ final int type = idleOptions.getTemporaryAppAllowlistType();
+ assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
+ }
+
+ @Test
public void inexactAllowWhileIdleBinderCall() throws RemoteException {
// Both permission and power exemption status don't matter for these alarms.
// We only want to test that the flags and idleOptions are correct.
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
index eb6670e..8f0d014 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
@@ -146,7 +146,7 @@
@Mock
private PolicyWarningUIController mPolicyWarningUIController;
@Mock
- private PackageManagerInternal mPackageManagerInternal;
+ private PackageManagerInternal mMockPackageManagerInternal;
@Before
public void setUp() {
@@ -158,7 +158,8 @@
R.dimen.accessibility_focus_highlight_stroke_width, 1);
mA11ySecurityPolicy = new AccessibilitySecurityPolicy(
- mPolicyWarningUIController, mContext, mMockA11yUserManager);
+ mPolicyWarningUIController, mContext, mMockA11yUserManager,
+ mMockPackageManagerInternal);
mA11ySecurityPolicy.setSendingNonA11yToolNotificationLocked(true);
mA11ySecurityPolicy.setAccessibilityWindowManager(mMockA11yWindowManager);
mA11ySecurityPolicy.setAppWidgetManager(mMockAppWidgetManager);
@@ -237,8 +238,8 @@
@Test
public void resolveValidReportedPackage_uidAndPkgNameMatched_returnPkgName()
throws PackageManager.NameNotFoundException {
- when(mMockPackageManager.getPackageUidAsUser(PACKAGE_NAME,
- PackageManager.MATCH_ANY_USER, TEST_USER_ID)).thenReturn(APP_UID);
+ when(mMockPackageManagerInternal.isSameApp(PACKAGE_NAME, APP_UID, TEST_USER_ID))
+ .thenReturn(true);
assertEquals(mA11ySecurityPolicy.resolveValidReportedPackageLocked(
PACKAGE_NAME, APP_UID, TEST_USER_ID, APP_PID),
@@ -257,8 +258,8 @@
when(mMockAppWidgetManager.getHostedWidgetPackages(widgetHostUid))
.thenReturn(widgetPackages);
- when(mMockPackageManager.getPackageUidAsUser(hostPackageName, TEST_USER_ID))
- .thenReturn(widgetHostUid);
+ when(mMockPackageManagerInternal.isSameApp(hostPackageName, widgetHostUid, TEST_USER_ID))
+ .thenReturn(true);
assertEquals(mA11ySecurityPolicy.resolveValidReportedPackageLocked(
widgetPackageName, widgetHostUid, TEST_USER_ID, widgetHostPid),
@@ -272,8 +273,8 @@
final String[] uidPackages = {PACKAGE_NAME, PACKAGE_NAME2};
when(mMockPackageManager.getPackagesForUid(APP_UID))
.thenReturn(uidPackages);
- when(mMockPackageManager.getPackageUidAsUser(invalidPackageName, TEST_USER_ID))
- .thenThrow(PackageManager.NameNotFoundException.class);
+ when(mMockPackageManagerInternal.isSameApp(invalidPackageName, APP_UID, TEST_USER_ID))
+ .thenReturn(false);
when(mMockAppWidgetManager.getHostedWidgetPackages(APP_UID))
.thenReturn(new ArraySet<>());
mContext.getTestablePermissions().setPermission(
@@ -292,8 +293,8 @@
final String[] uidPackages = {PACKAGE_NAME};
when(mMockPackageManager.getPackagesForUid(APP_UID))
.thenReturn(uidPackages);
- when(mMockPackageManager.getPackageUidAsUser(wantedPackageName, TEST_USER_ID))
- .thenReturn(wantedUid);
+ when(mMockPackageManagerInternal.isSameApp(wantedPackageName, wantedUid, TEST_USER_ID))
+ .thenReturn(true);
when(mMockAppWidgetManager.getHostedWidgetPackages(APP_UID))
.thenReturn(new ArraySet<>());
mContext.getTestablePermissions().setPermission(
@@ -312,8 +313,8 @@
final String[] uidPackages = {PACKAGE_NAME};
when(mMockPackageManager.getPackagesForUid(APP_UID))
.thenReturn(uidPackages);
- when(mMockPackageManager.getPackageUidAsUser(wantedPackageName, TEST_USER_ID))
- .thenReturn(wantedUid);
+ when(mMockPackageManagerInternal.isSameApp(wantedPackageName, wantedUid, TEST_USER_ID))
+ .thenReturn(true);
when(mMockAppWidgetManager.getHostedWidgetPackages(APP_UID))
.thenReturn(new ArraySet<>());
mContext.getTestablePermissions().setPermission(
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 9dd2a61..26590c4 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -744,10 +744,6 @@
* state of calls in the self-managed {@link ConnectionService}. An example use-case is
* exposing these calls to an automotive device via its companion app.
* <p>
- * This meta-data can only be set for an {@link InCallService} which also sets
- * {@link #METADATA_IN_CALL_SERVICE_UI}. Only the default phone/dialer app, or a car-mode
- * {@link InCallService} can see self-managed calls.
- * <p>
* See also {@link Connection#PROPERTY_SELF_MANAGED}.
*/
public static final String METADATA_INCLUDE_SELF_MANAGED_CALLS =
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 8be074d..28ea5a6 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -1186,6 +1186,7 @@
ApnSetting other = (ApnSetting) o;
return mEntryName.equals(other.mEntryName)
+ && Objects.equals(mId, other.mId)
&& Objects.equals(mOperatorNumeric, other.mOperatorNumeric)
&& Objects.equals(mApnName, other.mApnName)
&& Objects.equals(mProxyAddress, other.mProxyAddress)