Merge "From dream, FP auth brings you to the last app" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index b1f587e..ab20edc 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -38,6 +38,7 @@
":display_flags_lib{.generated_srcjars}",
":android.multiuser.flags-aconfig-java{.generated_srcjars}",
":android.app.flags-aconfig-java{.generated_srcjars}",
+ ":android.credentials.flags-aconfig-java{.generated_srcjars}",
]
filegroup {
@@ -355,3 +356,16 @@
aconfig_declarations: "android.hardware.radio.flags-aconfig",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+
+// Credential Manager
+aconfig_declarations {
+ name: "android.credentials.flags-aconfig",
+ package: "android.credentials.flags",
+ srcs: ["core/java/android/credentials/flags.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.credentials.flags-aconfig-java",
+ aconfig_declarations: "android.credentials.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index fce5e4f..e7a5b72 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -1759,12 +1759,17 @@
/**
* Returns the information about the home screen wallpaper if its current wallpaper is a live
- * wallpaper component. Otherwise, if the wallpaper is a static image or is not set, this
- * returns null.
+ * wallpaper component. Otherwise, if the wallpaper is a static image or is not set, or if the
+ * the caller doesn't have the appropriate permissions, this returns {@code null}.
*
* <p>
- * In order to use this, apps should declare a {@code <queries>} tag with the action
- * {@code "android.service.wallpaper.WallpaperService"}. Otherwise,
+ * Before Android U, this method requires the
+ * {@link android.Manifest.permission#QUERY_ALL_PACKAGES} permission.
+ * </p>
+ *
+ * <p>
+ * Starting from Android U, In order to use this, apps should declare a {@code <queries>} tag
+ * with the action {@code "android.service.wallpaper.WallpaperService"}. Otherwise,
* this method will return {@code null} if the caller doesn't otherwise have
* <a href="{@docRoot}training/package-visibility">visibility</a> of the wallpaper package.
* </p>
@@ -1780,8 +1785,15 @@
/**
* Returns the information about the designated wallpaper if its current wallpaper is a live
- * wallpaper component. Otherwise, if the wallpaper is a static image or is not set, this
- * returns null.
+ * wallpaper component. Otherwise, if the wallpaper is a static image or is not set, or if the
+ * the caller doesn't have the appropriate permissions, this returns {@code null}.
+ *
+ * <p>
+ * In order to use this, apps should declare a {@code <queries>} tag
+ * with the action {@code "android.service.wallpaper.WallpaperService"}. Otherwise,
+ * this method will return {@code null} if the caller doesn't otherwise have
+ * <a href="{@docRoot}training/package-visibility">visibility</a> of the wallpaper package.
+ * </p>
*
* @param which Specifies wallpaper to request (home or lock).
* @param userId Owner of the wallpaper.
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index b5efe58..b054d38 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -27,3 +27,11 @@
description: "Feature flag to improve stopped state enforcement"
bug: "296644915"
}
+
+flag {
+ name: "nullable_data_dir"
+ namespace: "package_manager_service"
+ description: "Feature flag to allow ApplicationInfo.dataDir to be null."
+ bug: "302587814"
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/credentials/flags.aconfig b/core/java/android/credentials/flags.aconfig
new file mode 100644
index 0000000..0d0305b
--- /dev/null
+++ b/core/java/android/credentials/flags.aconfig
@@ -0,0 +1,8 @@
+package: "android.credentials.flags"
+
+flag {
+ namespace: "credential_manager"
+ name: "settings_activity_enabled"
+ description: "Enable the Credential Manager Settings Activity APIs"
+ bug: "300014059"
+}
\ No newline at end of file
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f40232b..d09f0a8 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11147,12 +11147,6 @@
public static final String BLUETOOTH_ON_WHILE_DRIVING = "bluetooth_on_while_driving";
/**
- * Volume dialog timeout in ms.
- * @hide
- */
- public static final String VOLUME_DIALOG_DISMISS_TIMEOUT = "volume_dialog_dismiss_timeout";
-
- /**
* What behavior should be invoked when the volume hush gesture is triggered
* One of VOLUME_HUSH_OFF, VOLUME_HUSH_VIBRATE, VOLUME_HUSH_MUTE.
*
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 7674131..6b0dacc 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -32649,7 +32649,7 @@
/**
* Return whether the View allows automatic handwriting initiation. Returns true if automatic
- * handwriting initiation is enabled, and verse visa.
+ * handwriting initiation is enabled, and vice versa.
* @see #setAutoHandwritingEnabled(boolean)
*/
public boolean isAutoHandwritingEnabled() {
diff --git a/core/java/android/webkit/TEST_MAPPING b/core/java/android/webkit/TEST_MAPPING
index c1bc6d7..07f4383 100644
--- a/core/java/android/webkit/TEST_MAPPING
+++ b/core/java/android/webkit/TEST_MAPPING
@@ -26,6 +26,7 @@
},
{
"name": "GtsWebViewTestCases",
+ "keywords": ["internal"],
"options": [
{
"exclude-annotation": "android.test.FlakyTest"
@@ -34,6 +35,7 @@
},
{
"name": "GtsWebViewHostTestCases",
+ "keywords": ["internal"],
"options": [
{
"exclude-annotation": "android.test.FlakyTest"
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 6a1f3f9..ade20d2 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -60,6 +60,12 @@
src: "preinstalled-packages-asl-files.xml",
}
+prebuilt_etc {
+ name: "preinstalled-packages-strict-signature.xml",
+ sub_dir: "sysconfig",
+ src: "preinstalled-packages-strict-signature.xml",
+}
+
// Privapp permission whitelist files
prebuilt_etc {
diff --git a/data/etc/preinstalled-packages-strict-signature.xml b/data/etc/preinstalled-packages-strict-signature.xml
new file mode 100644
index 0000000..3cbfa8c
--- /dev/null
+++ b/data/etc/preinstalled-packages-strict-signature.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<!--
+This XML file declares which preinstalled apps, after updated, need to have strict signature check
+in boot time and avoid the cached results. This is to ensure the updated version still verifies
+against the preinstalled version.
+
+Example usage:
+ <require-strict-signature package="com.foo.bar"/>
+-->
+
+<config></config>
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index 31092536..51b720d 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -430,19 +430,17 @@
key 659 MACRO_4
# Keys defined by HID usages
-key usage 0x0c0067 WINDOW
-key usage 0x0c006F BRIGHTNESS_UP
-key usage 0x0c0070 BRIGHTNESS_DOWN
-key usage 0x0c0079 KEYBOARD_BACKLIGHT_UP
-key usage 0x0c007A KEYBOARD_BACKLIGHT_DOWN
-key usage 0x0c007C KEYBOARD_BACKLIGHT_TOGGLE
-key usage 0x0c0173 MEDIA_AUDIO_TRACK
-key usage 0x0c019C PROFILE_SWITCH
-key usage 0x0c01A2 ALL_APPS
-# TODO(b/297094448): Add stylus button mappings as a fallback when we have a way to determine
-# if a device can actually report it.
-# key usage 0x0d0044 STYLUS_BUTTON_PRIMARY
-# key usage 0x0d005a STYLUS_BUTTON_SECONDARY
+key usage 0x0c0067 WINDOW FALLBACK_USAGE_MAPPING
+key usage 0x0c006F BRIGHTNESS_UP FALLBACK_USAGE_MAPPING
+key usage 0x0c0070 BRIGHTNESS_DOWN FALLBACK_USAGE_MAPPING
+key usage 0x0c0079 KEYBOARD_BACKLIGHT_UP FALLBACK_USAGE_MAPPING
+key usage 0x0c007A KEYBOARD_BACKLIGHT_DOWN FALLBACK_USAGE_MAPPING
+key usage 0x0c007C KEYBOARD_BACKLIGHT_TOGGLE FALLBACK_USAGE_MAPPING
+key usage 0x0c0173 MEDIA_AUDIO_TRACK FALLBACK_USAGE_MAPPING
+key usage 0x0c019C PROFILE_SWITCH FALLBACK_USAGE_MAPPING
+key usage 0x0c01A2 ALL_APPS FALLBACK_USAGE_MAPPING
+key usage 0x0d0044 STYLUS_BUTTON_PRIMARY FALLBACK_USAGE_MAPPING
+key usage 0x0d005a STYLUS_BUTTON_SECONDARY FALLBACK_USAGE_MAPPING
# Joystick and game controller axes.
# Axes that are not mapped will be assigned generic axis numbers by the input subsystem.
diff --git a/media/java/android/media/midi/package.html b/media/java/android/media/midi/package.html
index d9e38e2..ae0c2ab 100644
--- a/media/java/android/media/midi/package.html
+++ b/media/java/android/media/midi/package.html
@@ -451,6 +451,14 @@
int defaultProtocol = info.getDefaultProtocol();
</pre>
+<p>To register for callbacks when MIDI 2.0 devices are added or removed, use
+MidiManager.TRANSPORT_UNIVERSAL_MIDI_PACKETS as the transport.</p>
+
+<pre class=prettyprint>
+midiManager.registerDeviceCallback(
+ MidiManager.TRANSPORT_UNIVERSAL_MIDI_PACKETS, executor, callback);
+</pre>
+
<h1 id=creating_a_midi_2_0_virtual_device_service>Creating a MIDI 2.0 Virtual Device Service</h1>
diff --git a/native/webview/TEST_MAPPING b/native/webview/TEST_MAPPING
index c1bc6d7..07f4383 100644
--- a/native/webview/TEST_MAPPING
+++ b/native/webview/TEST_MAPPING
@@ -26,6 +26,7 @@
},
{
"name": "GtsWebViewTestCases",
+ "keywords": ["internal"],
"options": [
{
"exclude-annotation": "android.test.FlakyTest"
@@ -34,6 +35,7 @@
},
{
"name": "GtsWebViewHostTestCases",
+ "keywords": ["internal"],
"options": [
{
"exclude-annotation": "android.test.FlakyTest"
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index f6f75de..91d2d1b 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -143,7 +143,6 @@
Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,
Settings.Secure.SCREENSAVER_HOME_CONTROLS_ENABLED,
Settings.Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION,
- Settings.Secure.VOLUME_DIALOG_DISMISS_TIMEOUT,
Settings.Secure.VOLUME_HUSH_GESTURE,
Settings.Secure.MANUAL_RINGER_TOGGLE_COUNT,
Settings.Secure.LOW_POWER_WARNING_ACKNOWLEDGED,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 8d13f01..bec1447 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -212,7 +212,6 @@
VALIDATORS.put(Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.SCREENSAVER_HOME_CONTROLS_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION, BOOLEAN_VALIDATOR);
- VALIDATORS.put(Secure.VOLUME_DIALOG_DISMISS_TIMEOUT, NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.VOLUME_HUSH_GESTURE, NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(
Secure.ENABLED_NOTIFICATION_LISTENERS,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 5646abe..12a0770 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -18,6 +18,7 @@
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
import static com.android.keyguard.KeyguardClockSwitch.LARGE;
import static com.android.keyguard.KeyguardClockSwitch.SMALL;
import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
@@ -208,7 +209,7 @@
public void setLockscreenClockY(int clockY) {
if (mView.screenOffsetYPadding != clockY) {
mView.screenOffsetYPadding = clockY;
- mView.updateClockTargetRegions();
+ mView.post(() -> mView.updateClockTargetRegions());
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index f7a4d47..a8bf229 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -97,9 +97,9 @@
import com.android.settingslib.Utils;
import com.android.settingslib.drawable.CircleFramedDrawable;
import com.android.systemui.Gefingerpoken;
-import com.android.systemui.res.R;
import com.android.systemui.classifier.FalsingA11yDelegate;
import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.res.R;
import com.android.systemui.shade.TouchLogger;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.policy.BaseUserSwitcherAdapter;
@@ -1191,6 +1191,7 @@
mPopup.setOnItemClickListener((parent, view, pos, id) -> {
if (mFalsingManager.isFalseTap(LOW_PENALTY)) return;
if (!view.isEnabled()) return;
+ if (mPopup == null) return;
// Subtract one for the header
UserRecord user = adapter.getItem(pos - 1);
if (user.isManageUsers || user.isAddSupervisedUser) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 51308b8..3f5ec7d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -3297,29 +3297,27 @@
if (unlockPossible) {
mFingerprintCancelSignal = new CancellationSignal();
+ final FingerprintAuthenticateOptions fingerprintAuthenticateOptions =
+ new FingerprintAuthenticateOptions.Builder()
+ .setUserId(userId)
+ .build();
+ if (mFingerprintInteractiveToAuthProvider != null) {
+ fingerprintAuthenticateOptions.setVendorReason(
+ mFingerprintInteractiveToAuthProvider.getVendorExtension(userId));
+ }
+
if (!isUnlockingWithFingerprintAllowed()) {
mLogger.v("startListeningForFingerprint - detect");
mFpm.detectFingerprint(
mFingerprintCancelSignal,
mFingerprintDetectionCallback,
- new FingerprintAuthenticateOptions.Builder()
- .setUserId(userId)
- .setVendorReason(
- mFingerprintInteractiveToAuthProvider.getVendorExtension(
- getCurrentUser()))
- .build());
+ fingerprintAuthenticateOptions);
} else {
mLogger.v("startListeningForFingerprint");
mFpm.authenticate(null /* crypto */, mFingerprintCancelSignal,
mFingerprintAuthenticationCallback,
null /* handler */,
- new FingerprintAuthenticateOptions.Builder()
- .setUserId(userId)
- .setVendorReason(
- mFingerprintInteractiveToAuthProvider.getVendorExtension(
- getCurrentUser()))
- .build()
- );
+ fingerprintAuthenticateOptions);
}
setFingerprintRunningState(BIOMETRIC_STATE_RUNNING);
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index a00c3b5..c79952e 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -115,7 +115,7 @@
// TODO(b/292213543): Tracking Bug
@JvmField
val NOTIFICATION_GROUP_EXPANSION_CHANGE =
- unreleasedFlag("notification_group_expansion_change")
+ unreleasedFlag("notification_group_expansion_change", teamfood = true)
// TODO(b/301955929)
@JvmField
@@ -805,7 +805,7 @@
/** Enable haptic slider component in the brightness slider */
@JvmField
- val HAPTIC_BRIGHTNESS_SLIDER = unreleasedFlag("haptic_brightness_slider")
+ val HAPTIC_BRIGHTNESS_SLIDER = unreleasedFlag("haptic_brightness_slider", teamfood = true)
// TODO(b/287205379): Tracking bug
@JvmField
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 70df09d..69fe46a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -83,7 +83,7 @@
*/
public abstract class QSTileImpl<TState extends State> implements QSTile, LifecycleOwner, Dumpable {
protected final String TAG = "Tile." + getClass().getSimpleName();
- protected static final boolean DEBUG = Log.isLoggable("Tile", Log.DEBUG);
+ protected final boolean DEBUG = Log.isLoggable("Tile", Log.DEBUG);
private static final long DEFAULT_STALE_TIMEOUT = 10 * DateUtils.MINUTE_IN_MILLIS;
protected static final Object ARG_SHOW_TRANSIENT_ENABLING = new Object();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index fc9ecb3..9cf9714 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -42,13 +42,13 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.logging.KeyguardLogger;
-import com.android.systemui.res.R;
import com.android.systemui.battery.BatteryMeterViewController;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.log.core.LogLevel;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
import com.android.systemui.shade.ShadeViewStateProvider;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -67,6 +67,8 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserInfoController;
+import com.android.systemui.statusbar.ui.binder.KeyguardStatusBarViewBinder;
+import com.android.systemui.statusbar.ui.viewmodel.KeyguardStatusBarViewModel;
import com.android.systemui.user.ui.viewmodel.StatusBarUserChipViewModel;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.settings.SecureSettings;
@@ -110,6 +112,7 @@
private final KeyguardStateController mKeyguardStateController;
private final KeyguardBypassController mKeyguardBypassController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final KeyguardStatusBarViewModel mKeyguardStatusBarViewModel;
private final BiometricUnlockController mBiometricUnlockController;
private final SysuiStatusBarStateController mStatusBarStateController;
private final StatusBarContentInsetsProvider mInsetsProvider;
@@ -283,6 +286,7 @@
KeyguardStateController keyguardStateController,
KeyguardBypassController bypassController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
+ KeyguardStatusBarViewModel keyguardStatusBarViewModel,
BiometricUnlockController biometricUnlockController,
SysuiStatusBarStateController statusBarStateController,
StatusBarContentInsetsProvider statusBarContentInsetsProvider,
@@ -309,6 +313,7 @@
mKeyguardStateController = keyguardStateController;
mKeyguardBypassController = bypassController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mKeyguardStatusBarViewModel = keyguardStatusBarViewModel;
mBiometricUnlockController = biometricUnlockController;
mStatusBarStateController = statusBarStateController;
mInsetsProvider = statusBarContentInsetsProvider;
@@ -356,6 +361,9 @@
super.onInit();
mCarrierTextController.init();
mBatteryMeterViewController.init();
+ if (isMigrationEnabled()) {
+ KeyguardStatusBarViewBinder.bind(mView, mKeyguardStatusBarViewModel);
+ }
}
@Override
@@ -380,7 +388,7 @@
// whenever the shade is opened on top of lockscreen, and then re-attached when the
// shade is closed. So, we need to re-add the IconManager each time we're re-attached to
// get icon updates.
- if (mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW)) {
+ if (isMigrationEnabled()) {
mStatusBarIconController.addIconGroup(mTintedIconManager);
}
}
@@ -455,6 +463,10 @@
/** Sets the dozing state. */
public void setDozing(boolean dozing) {
+ if (isMigrationEnabled()) {
+ // [KeyguardStatusBarViewModel] will automatically handle dozing.
+ return;
+ }
mDozing = dozing;
}
@@ -502,6 +514,10 @@
if (!isKeyguardShowing()) {
return;
}
+ if (isMigrationEnabled()) {
+ // [KeyguardStatusBarViewBinder] will handle view state updates.
+ return;
+ }
float alphaQsExpansion = 1 - Math.min(
1, mShadeViewStateProvider.getLockscreenShadeDragProgress() * 2);
@@ -539,6 +555,11 @@
* Updates the {@link KeyguardStatusBarView} state based on the provided values.
*/
public void updateViewState(float alpha, int visibility) {
+ if (isMigrationEnabled()) {
+ // [KeyguardStatusBarViewBinder] will handle view state updates.
+ return;
+ }
+
if (mDisableStateTracker.isDisabled()) {
visibility = View.INVISIBLE;
}
@@ -646,10 +667,19 @@
* @param alpha a value between 0 and 1. -1 if the value is to be reset/ignored.
*/
public void setAlpha(float alpha) {
+ if (isMigrationEnabled()) {
+ // [KeyguardStatusBarViewBinder] will handle view state updates.
+ return;
+ }
+
mExplicitAlpha = alpha;
updateViewState();
}
+ private boolean isMigrationEnabled() {
+ return mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW);
+ }
+
private final ContentObserver mVolumeSettingObserver = new ContentObserver(null) {
@Override
public void onChange(boolean selfChange) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ui/binder/KeyguardStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/ui/binder/KeyguardStatusBarViewBinder.kt
new file mode 100644
index 0000000..c63ef9e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ui/binder/KeyguardStatusBarViewBinder.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.ui.binder
+
+import android.view.View
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.statusbar.phone.KeyguardStatusBarView
+import com.android.systemui.statusbar.ui.viewmodel.KeyguardStatusBarViewModel
+
+/** Binds [KeyguardStatusBarViewModel] to [KeyguardStatusBarView]. */
+object KeyguardStatusBarViewBinder {
+ @JvmStatic
+ fun bind(
+ view: KeyguardStatusBarView,
+ viewModel: KeyguardStatusBarViewModel,
+ ) {
+ view.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ viewModel.isVisible.collect { isVisible ->
+ view.visibility = if (isVisible) View.VISIBLE else View.INVISIBLE
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt
new file mode 100644
index 0000000..ddfed87
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt
@@ -0,0 +1,54 @@
+/*
+ * 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.statusbar.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * A view model for the status bar displayed on keyguard (lockscreen).
+ *
+ * Note: This view model is for the status bar view as a whole. Certain icons may have their own
+ * individual view models, such as
+ * [com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel] or
+ * [com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel].
+ */
+@SysUISingleton
+class KeyguardStatusBarViewModel
+@Inject
+constructor(
+ @Application scope: CoroutineScope,
+ keyguardInteractor: KeyguardInteractor,
+) {
+ /** True if this view should be visible and false otherwise. */
+ val isVisible: StateFlow<Boolean> =
+ combine(
+ keyguardInteractor.isDozing,
+ keyguardInteractor.statusBarState,
+ ) { isDozing, statusBarState ->
+ !isDozing && statusBarState == StatusBarState.KEYGUARD
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 3c6d90d..727d649 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -135,7 +135,6 @@
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.util.AlphaTintDrawableWrapper;
import com.android.systemui.util.RoundedCornerProgressDrawable;
-import com.android.systemui.util.settings.SecureSettings;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -305,8 +304,6 @@
private @DevicePostureController.DevicePostureInt int mDevicePosture;
private int mOrientation;
private final FeatureFlags mFeatureFlags;
- private final SecureSettings mSecureSettings;
- private int mDialogTimeoutMillis;
public VolumeDialogImpl(
Context context,
@@ -323,8 +320,7 @@
DevicePostureController devicePostureController,
Looper looper,
DumpManager dumpManager,
- FeatureFlags featureFlags,
- SecureSettings secureSettings) {
+ FeatureFlags featureFlags) {
mFeatureFlags = featureFlags;
mContext =
new ContextThemeWrapper(context, R.style.volume_dialog_theme);
@@ -355,8 +351,6 @@
mUseBackgroundBlur =
mContext.getResources().getBoolean(R.bool.config_volumeDialogUseBackgroundBlur);
mInteractionJankMonitor = interactionJankMonitor;
- mSecureSettings = secureSettings;
- mDialogTimeoutMillis = DIALOG_TIMEOUT_MILLIS;
dumpManager.registerDumpable("VolumeDialogImpl", this);
@@ -521,8 +515,6 @@
mDialog.setContentView(R.layout.volume_dialog);
mDialogView = mDialog.findViewById(R.id.volume_dialog);
mDialogView.setAlpha(0);
- mDialogTimeoutMillis = mSecureSettings.getInt(Settings.Secure.VOLUME_DIALOG_DISMISS_TIMEOUT,
- DIALOG_TIMEOUT_MILLIS);
mDialog.setCanceledOnTouchOutside(true);
mDialog.setOnShowListener(dialog -> {
mDialogView.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
@@ -535,7 +527,7 @@
.alpha(1)
.translationX(0)
.setDuration(mDialogShowAnimationDurationMs)
- .setListener(getJankListener(getDialogView(), TYPE_SHOW, mDialogTimeoutMillis))
+ .setListener(getJankListener(getDialogView(), TYPE_SHOW, DIALOG_TIMEOUT_MILLIS))
.setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator())
.withEndAction(() -> {
if (!Prefs.getBoolean(mContext, Prefs.Key.TOUCHED_RINGER_TOGGLE, false)) {
@@ -1522,7 +1514,7 @@
AccessibilityManager.FLAG_CONTENT_TEXT
| AccessibilityManager.FLAG_CONTENT_CONTROLS);
}
- return mAccessibilityMgr.getRecommendedTimeoutMillis(mDialogTimeoutMillis,
+ return mAccessibilityMgr.getRecommendedTimeoutMillis(DIALOG_TIMEOUT_MILLIS,
AccessibilityManager.FLAG_CONTENT_CONTROLS);
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
index 624691b..cc9f3e1 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
@@ -31,7 +31,6 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.volume.CsdWarningDialog;
import com.android.systemui.volume.VolumeComponent;
import com.android.systemui.volume.VolumeDialogComponent;
@@ -64,8 +63,7 @@
CsdWarningDialog.Factory csdFactory,
DevicePostureController devicePostureController,
DumpManager dumpManager,
- FeatureFlags featureFlags,
- SecureSettings secureSettings) {
+ FeatureFlags featureFlags) {
VolumeDialogImpl impl = new VolumeDialogImpl(
context,
volumeDialogController,
@@ -81,8 +79,7 @@
devicePostureController,
Looper.getMainLooper(),
dumpManager,
- featureFlags,
- secureSettings);
+ featureFlags);
impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
impl.setAutomute(true);
impl.setSilentMode(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
index 2b1ce0e..05842a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
@@ -49,14 +49,22 @@
import com.android.keyguard.CarrierTextController;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.keyguard.TestScopeProvider;
import com.android.keyguard.logging.KeyguardLogger;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.battery.BatteryMeterViewController;
+import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository;
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
import com.android.systemui.flags.FakeFeatureFlagsClassic;
import com.android.systemui.flags.Flags;
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
+import com.android.systemui.scene.SceneTestUtils;
+import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags;
import com.android.systemui.shade.ShadeViewStateProvider;
+import com.android.systemui.shade.data.repository.FakeShadeRepository;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -66,6 +74,7 @@
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserInfoController;
+import com.android.systemui.statusbar.ui.viewmodel.KeyguardStatusBarViewModel;
import com.android.systemui.user.ui.viewmodel.StatusBarUserChipViewModel;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.settings.SecureSettings;
@@ -79,6 +88,8 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import kotlinx.coroutines.test.TestScope;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -133,16 +144,37 @@
private KeyguardStatusBarView mKeyguardStatusBarView;
private KeyguardStatusBarViewController mController;
private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
+ private final TestScope mTestScope = TestScopeProvider.getTestScope();
+ private final FakeKeyguardRepository mKeyguardRepository = new FakeKeyguardRepository();
+ private final SceneTestUtils mSceneTestUtils = new SceneTestUtils(this);
+ private KeyguardInteractor mKeyguardInteractor;
+ private KeyguardStatusBarViewModel mViewModel;
@Before
public void setup() throws Exception {
mFeatureFlags.set(Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW, false);
mShadeViewStateProvider = new TestShadeViewStateProvider();
+ mShadeViewStateProvider = new TestShadeViewStateProvider();
MockitoAnnotations.initMocks(this);
when(mIconManagerFactory.create(any(), any())).thenReturn(mIconManager);
+ mKeyguardInteractor = new KeyguardInteractor(
+ mKeyguardRepository,
+ mCommandQueue,
+ mFeatureFlags,
+ mSceneTestUtils.getSceneContainerFlags(),
+ mSceneTestUtils.getDeviceEntryRepository(),
+ new FakeKeyguardBouncerRepository(),
+ new FakeConfigurationRepository(),
+ new FakeShadeRepository(),
+ () -> mSceneTestUtils.sceneInteractor());
+ mViewModel =
+ new KeyguardStatusBarViewModel(
+ mTestScope.getBackgroundScope(),
+ mKeyguardInteractor);
+
allowTestableLooperAsMainThread();
TestableLooper.get(this).runWithLooper(() -> {
mKeyguardStatusBarView =
@@ -169,6 +201,7 @@
mKeyguardStateController,
mKeyguardBypassController,
mKeyguardUpdateMonitor,
+ mViewModel,
mBiometricUnlockController,
mStatusBarStateController,
mStatusBarContentInsetsProvider,
@@ -455,6 +488,94 @@
}
@Test
+ public void updateViewState_dozingTrue_flagOff_viewHidden() {
+ mFeatureFlags.set(Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW, false);
+ mController.init();
+ mController.onViewAttached();
+ updateStateToKeyguard();
+
+ mController.setDozing(true);
+ mController.updateViewState();
+
+ assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.INVISIBLE);
+ }
+
+ @Test
+ public void updateViewState_dozingFalse_flagOff_viewShown() {
+ mFeatureFlags.set(Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW, false);
+ mController.init();
+ mController.onViewAttached();
+ updateStateToKeyguard();
+
+ mController.setDozing(false);
+ mController.updateViewState();
+
+ assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void updateViewState_flagOn_doesNothing() {
+ mFeatureFlags.set(Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW, true);
+ mController.init();
+ mController.onViewAttached();
+ updateStateToKeyguard();
+
+ mKeyguardStatusBarView.setVisibility(View.GONE);
+ mKeyguardStatusBarView.setAlpha(0.456f);
+
+ mController.updateViewState();
+
+ assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mKeyguardStatusBarView.getAlpha()).isEqualTo(0.456f);
+ }
+
+ @Test
+ public void updateViewStateWithAlphaAndVis_flagOn_doesNothing() {
+ mFeatureFlags.set(Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW, true);
+ mController.init();
+ mController.onViewAttached();
+ updateStateToKeyguard();
+
+ mKeyguardStatusBarView.setVisibility(View.GONE);
+ mKeyguardStatusBarView.setAlpha(0.456f);
+
+ mController.updateViewState(0.789f, View.VISIBLE);
+
+ assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mKeyguardStatusBarView.getAlpha()).isEqualTo(0.456f);
+ }
+
+ @Test
+ public void setAlpha_flagOn_doesNothing() {
+ mFeatureFlags.set(Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW, true);
+ mController.init();
+ mController.onViewAttached();
+ updateStateToKeyguard();
+
+ mKeyguardStatusBarView.setAlpha(0.456f);
+
+ mController.setAlpha(0.123f);
+
+ assertThat(mKeyguardStatusBarView.getAlpha()).isEqualTo(0.456f);
+ }
+
+ @Test
+ public void setDozing_flagOn_doesNothing() {
+ mFeatureFlags.set(Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW, true);
+ mController.init();
+ mController.onViewAttached();
+ updateStateToKeyguard();
+ assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.VISIBLE);
+
+ mController.setDozing(true);
+ mController.updateViewState();
+
+ // setDozing(true) should typically cause the view to hide. But since the flag is on, we
+ // should ignore these set dozing calls and stay the same visibility.
+ assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
public void setAlpha_explicitAlpha_setsExplicitAlpha() {
mController.onViewAttached();
updateStateToKeyguard();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
new file mode 100644
index 0000000..f8ec8d1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
@@ -0,0 +1,104 @@
+/*
+ * 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.statusbar.ui.viewmodel
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+
+@SmallTest
+class KeyguardStatusBarViewModelTest : SysuiTestCase() {
+ private val testScope = TestScope()
+ private val sceneTestUtils = SceneTestUtils(this)
+ private val keyguardRepository = FakeKeyguardRepository()
+ private val keyguardInteractor =
+ KeyguardInteractor(
+ keyguardRepository,
+ mock<CommandQueue>(),
+ FakeFeatureFlagsClassic(),
+ sceneTestUtils.sceneContainerFlags,
+ sceneTestUtils.deviceEntryRepository,
+ FakeKeyguardBouncerRepository(),
+ FakeConfigurationRepository(),
+ FakeShadeRepository()
+ ) {
+ sceneTestUtils.sceneInteractor()
+ }
+
+ private val underTest =
+ KeyguardStatusBarViewModel(
+ testScope.backgroundScope,
+ keyguardInteractor,
+ )
+
+ @Test
+ fun isVisible_dozing_false() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.isVisible)
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+
+ keyguardRepository.setIsDozing(true)
+
+ assertThat(latest).isFalse()
+ }
+
+ @Test
+ fun isVisible_statusBarStateShade_false() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.isVisible)
+
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+
+ assertThat(latest).isFalse()
+ }
+
+ @Test
+ fun isVisible_statusBarStateShadeLocked_false() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.isVisible)
+
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED)
+
+ assertThat(latest).isFalse()
+ }
+
+ @Test
+ fun isVisible_statusBarStateKeyguard_andNotDozing_true() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.isVisible)
+
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ keyguardRepository.setIsDozing(false)
+
+ assertThat(latest).isTrue()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index daf8877..28fc5db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -42,7 +42,6 @@
import android.content.res.Configuration;
import android.media.AudioManager;
import android.os.SystemClock;
-import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.Log;
@@ -71,7 +70,6 @@
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.FakeConfigurationController;
-import com.android.systemui.util.settings.FakeSettings;
import org.junit.After;
import org.junit.Before;
@@ -136,8 +134,6 @@
private FakeFeatureFlags mFeatureFlags;
private int mLongestHideShowAnimationDuration = 250;
- private FakeSettings mSecureSettings;
-
@Rule
public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule();
@@ -166,8 +162,6 @@
mFeatureFlags = new FakeFeatureFlags();
- mSecureSettings = new FakeSettings();
-
mDialog = new VolumeDialogImpl(
getContext(),
mVolumeDialogController,
@@ -183,8 +177,7 @@
mPostureController,
mTestableLooper.getLooper(),
mDumpManager,
- mFeatureFlags,
- mSecureSettings);
+ mFeatureFlags);
mDialog.init(0, null);
State state = createShellState();
mDialog.onStateChangedH(state);
@@ -249,18 +242,6 @@
}
@Test
- public void testSetTimeoutValue_ComputeTimeout() {
- mSecureSettings.putInt(Settings.Secure.VOLUME_DIALOG_DISMISS_TIMEOUT, 7000);
- Mockito.reset(mAccessibilityMgr);
- mDialog.init(0, null);
- mDialog.rescheduleTimeoutH();
- verify(mAccessibilityMgr).getRecommendedTimeoutMillis(
- 7000,
- AccessibilityManager.FLAG_CONTENT_CONTROLS);
- }
-
-
- @Test
public void testComputeTimeout_tooltip() {
Mockito.reset(mAccessibilityMgr);
mDialog.showCaptionsTooltip();
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index caf1684..40b29d7 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -340,6 +340,10 @@
// A map of preloaded package names and the path to its app metadata file path.
private final ArrayMap<String, String> mAppMetadataFilePaths = new ArrayMap<>();
+ // A set of pre-installed package names that requires strict signature verification once
+ // updated to avoid cached/potentially tampered results.
+ private final Set<String> mPreinstallPackagesWithStrictSignatureCheck = new ArraySet<>();
+
/**
* Map of system pre-defined, uniquely named actors; keys are namespace,
* value maps actor name to package name.
@@ -542,6 +546,10 @@
return mAppMetadataFilePaths;
}
+ public Set<String> getPreinstallPackagesWithStrictSignatureCheck() {
+ return mPreinstallPackagesWithStrictSignatureCheck;
+ }
+
/**
* Only use for testing. Do NOT use in production code.
* @param readPermissions false to create an empty SystemConfig; true to read the permissions.
@@ -1485,6 +1493,17 @@
mAppMetadataFilePaths.put(packageName, path);
}
} break;
+ case "require-strict-signature": {
+ if (android.security.Flags.extendVbChainToUpdatedApk()) {
+ String packageName = parser.getAttributeValue(null, "package");
+ if (TextUtils.isEmpty(packageName)) {
+ Slog.w(TAG, "<" + name + "> without valid package in " + permFile
+ + " at " + parser.getPositionDescription());
+ } else {
+ mPreinstallPackagesWithStrictSignatureCheck.add(packageName);
+ }
+ }
+ } break;
default: {
Slog.w(TAG, "Tag " + name + " is unknown in "
+ permFile + " at " + parser.getPositionDescription());
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 4ef2f1e..3d347be 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -1025,10 +1025,12 @@
handles.add(id);
}
- // If the device is running in headless system user mode then allow
- // User 0 to access camera.
- if (UserManager.isHeadlessSystemUserMode()) {
- handles.add(UserHandle.USER_SYSTEM);
+ if (Flags.cameraHsumPermission()) {
+ // If the device is running in headless system user mode then allow
+ // User 0 to access camera.
+ if (UserManager.isHeadlessSystemUserMode()) {
+ handles.add(UserHandle.USER_SYSTEM);
+ }
}
return handles;
diff --git a/services/core/java/com/android/server/integrity/TEST_MAPPING b/services/core/java/com/android/server/integrity/TEST_MAPPING
index 4959204..be8d2e1 100644
--- a/services/core/java/com/android/server/integrity/TEST_MAPPING
+++ b/services/core/java/com/android/server/integrity/TEST_MAPPING
@@ -10,6 +10,7 @@
},
{
"name": "GtsSecurityHostTestCases",
+ "keywords": ["internal"],
"options": [
{
"include-filter": "com.google.android.security.gts.AppIntegrityManagerTest"
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 8f71a9b..c6388e7 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -3728,7 +3728,7 @@
final ScanResult scanResult = scanResultPair.first;
boolean shouldHideSystemApp = scanResultPair.second;
final InstallRequest installRequest = new InstallRequest(
- parsedPackage, parseFlags, scanFlags, user, scanResult);
+ parsedPackage, parseFlags, scanFlags, user, scanResult, disabledPkgSetting);
String existingApexModuleName = null;
synchronized (mPm.mLock) {
@@ -3962,6 +3962,7 @@
final String disabledPkgName = pkgAlreadyExists
? pkgSetting.getPackageName() : parsedPackage.getPackageName();
final boolean isSystemPkgUpdated;
+ final PackageSetting disabledPkgSetting;
final boolean isUpgrade;
synchronized (mPm.mLock) {
isUpgrade = mPm.isDeviceUpgrading();
@@ -3975,8 +3976,7 @@
+ "and install it as non-updated system app.");
mPm.mSettings.removeDisabledSystemPackageLPw(disabledPkgName);
}
- final PackageSetting disabledPkgSetting =
- mPm.mSettings.getDisabledSystemPkgLPr(disabledPkgName);
+ disabledPkgSetting = mPm.mSettings.getDisabledSystemPkgLPr(disabledPkgName);
isSystemPkgUpdated = disabledPkgSetting != null;
if (DEBUG_INSTALL && isSystemPkgUpdated) {
@@ -4048,6 +4048,23 @@
// equal to the version on the /data partition. Throw an exception and use
// the application already installed on the /data partition.
if (scanSystemPartition && isSystemPkgUpdated && !isSystemPkgBetter) {
+ // For some updated system packages, during addForInit we want to ensure the
+ // PackageSetting has the correct SigningDetails compares to the original version on
+ // the system partition. For the check to happen later during the /data scan, update
+ // the disabled package setting per the original APK on a system partition so that it
+ // can be trusted during reconcile.
+ if (needSignatureMatchToSystem(parsedPackage.getPackageName())) {
+ final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+ final ParseResult<SigningDetails> result =
+ ParsingPackageUtils.getSigningDetails(input, parsedPackage,
+ false /*skipVerify*/);
+ if (result.isError()) {
+ throw new PrepareFailure("Failed collect during scanSystemPackageLI",
+ result.getException());
+ }
+ disabledPkgSetting.setSigningDetails(result.getResult());
+ }
+
// In the case of a skipped package, commitReconciledScanResultLocked is not called to
// add the object to the "live" data structures, so this is the final mutation step
// for the package. Which means it needs to be finalized here to cache derived fields.
@@ -4065,19 +4082,16 @@
// Verify certificates against what was last scanned. Force re-collecting certificate in two
// special cases:
// 1) when scanning system, force re-collect only if system is upgrading.
- // 2) when scanning /data, force re-collect only if the app is privileged (updated from
- // preinstall, or treated as privileged, e.g. due to shared user ID).
+ // 2) when scanning /data, force re-collect only if the package name is allowlisted.
final boolean forceCollect = scanSystemPartition ? isUpgrade
- : PackageManagerServiceUtils.isApkVerificationForced(pkgSetting);
+ : pkgAlreadyExists && needSignatureMatchToSystem(pkgSetting.getPackageName());
if (DEBUG_VERIFY && forceCollect) {
Slog.d(TAG, "Force collect certificate of " + parsedPackage.getPackageName());
}
- // Full APK verification can be skipped during certificate collection, only if the file is
- // in verified partition, or can be verified on access (when apk verity is enabled). In both
- // cases, only data in Signing Block is verified instead of the whole file.
- final boolean skipVerify = scanSystemPartition
- || (forceCollect && canSkipForcedPackageVerification(parsedPackage));
+ // APK verification can be skipped during certificate collection, only if the file is in a
+ // verified partition.
+ final boolean skipVerify = scanSystemPartition;
ScanPackageUtils.collectCertificatesLI(pkgSetting, parsedPackage,
mPm.getSettingsVersionForPackage(parsedPackage), forceCollect, skipVerify,
mPm.isPreNMR1Upgrade());
@@ -4196,22 +4210,15 @@
}
/**
- * Returns if forced apk verification can be skipped for the whole package, including splits.
+ * Returns whether the package needs a signature verification against the pre-installed version
+ * at boot.
*/
- private boolean canSkipForcedPackageVerification(AndroidPackage pkg) {
- if (!VerityUtils.hasFsverity(pkg.getBaseApkPath())) {
+ private boolean needSignatureMatchToSystem(String packageName) {
+ if (!android.security.Flags.extendVbChainToUpdatedApk()) {
return false;
}
- // TODO: Allow base and splits to be verified individually.
- String[] splitCodePaths = pkg.getSplitCodePaths();
- if (!ArrayUtils.isEmpty(splitCodePaths)) {
- for (int i = 0; i < splitCodePaths.length; i++) {
- if (!VerityUtils.hasFsverity(splitCodePaths[i])) {
- return false;
- }
- }
- }
- return true;
+ return mPm.mInjector.getSystemConfig().getPreinstallPackagesWithStrictSignatureCheck()
+ .contains(packageName);
}
/**
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index a4ee3c8..1c7024b 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -58,7 +58,6 @@
import java.io.File;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
@@ -190,7 +189,7 @@
// addForInit
InstallRequest(ParsedPackage parsedPackage, int parseFlags, int scanFlags,
- @Nullable UserHandle user, ScanResult scanResult) {
+ @Nullable UserHandle user, ScanResult scanResult, PackageSetting disabledPs) {
if (user != null) {
mUserId = user.getIdentifier();
} else {
@@ -206,6 +205,7 @@
mPackageMetrics = null; // No logging from this code path
mSessionId = -1;
mRequireUserAction = USER_ACTION_UNSPECIFIED;
+ mDisabledPs = disabledPs;
}
@Nullable
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 8e91f42..bcb7bde 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -521,11 +521,8 @@
}
/**
- * Make sure the updated priv app is signed with the same key as the original APK file on the
- * /system partition.
- *
- * <p>The rationale is that {@code disabledPkg} is a PackageSetting backed by xml files in /data
- * and is not tamperproof.
+ * Verifies the updated system app has a signature that is consistent with the pre-installed
+ * version or the signing lineage.
*/
private static boolean matchSignatureInSystem(@NonNull String packageName,
@NonNull SigningDetails signingDetails, PackageSetting disabledPkgSetting) {
@@ -559,17 +556,12 @@
== FSVERITY_ENABLED;
}
- /** Returns true to force apk verification if the package is considered privileged. */
- static boolean isApkVerificationForced(@Nullable PackageSetting ps) {
- // TODO(b/154310064): re-enable.
- return false;
- }
-
/**
* Verifies that signatures match.
* @returns {@code true} if the compat signatures were matched; otherwise, {@code false}.
* @throws PackageManagerException if the signatures did not match.
*/
+ @SuppressWarnings("ReferenceEquality")
public static boolean verifySignatures(PackageSetting pkgSetting,
@Nullable SharedUserSetting sharedUserSetting,
PackageSetting disabledPkgSetting, SigningDetails parsedSignatures,
@@ -578,13 +570,23 @@
final String packageName = pkgSetting.getPackageName();
boolean compatMatch = false;
if (pkgSetting.getSigningDetails().getSignatures() != null) {
- // Already existing package. Make sure signatures match
+ // For an already existing package, make sure the parsed signatures from the package
+ // match the one in PackageSetting.
boolean match = parsedSignatures.checkCapability(
pkgSetting.getSigningDetails(),
SigningDetails.CertCapabilities.INSTALLED_DATA)
|| pkgSetting.getSigningDetails().checkCapability(
parsedSignatures,
SigningDetails.CertCapabilities.ROLLBACK);
+ // Also make sure the parsed signatures are consistent with the disabled package
+ // setting, if any. The additional UNKNOWN check is because disabled package settings
+ // may not have SigningDetails currently, and we don't want to cause an uninstall.
+ if (android.security.Flags.extendVbChainToUpdatedApk()
+ && match && disabledPkgSetting != null
+ && disabledPkgSetting.getSigningDetails() != SigningDetails.UNKNOWN) {
+ match = matchSignatureInSystem(packageName, parsedSignatures, disabledPkgSetting);
+ }
+
if (!match && compareCompat) {
match = matchSignaturesCompat(packageName, pkgSetting.getSignatures(),
parsedSignatures);
@@ -603,11 +605,6 @@
SigningDetails.CertCapabilities.ROLLBACK);
}
- if (!match && isApkVerificationForced(disabledPkgSetting)) {
- match = matchSignatureInSystem(packageName, pkgSetting.getSigningDetails(),
- disabledPkgSetting);
- }
-
if (!match && isRollback) {
// Since a rollback can only be initiated for an APK previously installed on the
// device allow rolling back to a previous signing key even if the rollback
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index c263978..6e3b538 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -27,6 +27,7 @@
import static android.os.Process.INVALID_UID;
import static android.os.Process.PACKAGE_INFO_GID;
import static android.os.Process.SYSTEM_UID;
+
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import static com.android.server.pm.PackageManagerService.WRITE_USER_PACKAGE_RESTRICTIONS;
import static com.android.server.pm.SharedUidMigration.BEST_EFFORT;
@@ -5211,7 +5212,7 @@
File dataDir = PackageInfoUtils.getDataDir(ps, user.id);
pw.print(" dataDir=");
- pw.println(dataDir.getAbsolutePath());
+ pw.println(dataDir == null ? "null" : dataDir.getAbsolutePath());
final PackageUserStateInternal pus = ps.readUserState(user.id);
pw.print(" firstInstallTime=");
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index 0fd7a37..e12b70f 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -64,6 +64,7 @@
},
{
"name": "GtsSecurityHostTestCases",
+ "keywords": ["internal"],
"options": [
{
"include-filter": "com.google.android.security.gts.PackageVerifierTest"
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 31856f1..f4f03f4 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -544,11 +544,6 @@
*/
public boolean compileLayouts(@NonNull PackageStateInternal ps, @NonNull AndroidPackage pkg) {
try {
- final String packageName = pkg.getPackageName();
- final String apkPath = pkg.getSplits().get(0).getPath();
- // TODO(b/143971007): Use a cross-user directory
- File dataDir = PackageInfoUtils.getDataDir(ps, UserHandle.myUserId());
- final String outDexFile = dataDir.getAbsolutePath() + "/code_cache/compiled_view.dex";
if (ps.isPrivileged() || pkg.isUseEmbeddedDex()
|| pkg.isDefaultToDeviceProtectedStorage()) {
// Privileged apps prefer to load trusted code so they don't use compiled views.
@@ -558,6 +553,14 @@
// selinux permissions required for writing to user_de.
return false;
}
+ final String packageName = pkg.getPackageName();
+ final String apkPath = pkg.getSplits().get(0).getPath();
+ final File dataDir = PackageInfoUtils.getDataDir(ps, UserHandle.myUserId());
+ if (dataDir == null) {
+ // The app is not installed on the target user and doesn't have a data dir
+ return false;
+ }
+ final String outDexFile = dataDir.getAbsolutePath() + "/code_cache/compiled_view.dex";
Log.i("PackageManager", "Compiling layouts in " + packageName + " (" + apkPath +
") to " + outDexFile);
final long callingId = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/pm/dex/ViewCompiler.java b/services/core/java/com/android/server/pm/dex/ViewCompiler.java
index 6405ea5..00269224 100644
--- a/services/core/java/com/android/server/pm/dex/ViewCompiler.java
+++ b/services/core/java/com/android/server/pm/dex/ViewCompiler.java
@@ -40,8 +40,11 @@
public boolean compileLayouts(PackageStateInternal ps, String apkPath) {
try {
final String packageName = ps.getPackageName();
- // TODO(b/143971007): Use a cross-user directory
File dataDir = PackageInfoUtils.getDataDir(ps, UserHandle.myUserId());
+ if (dataDir == null) {
+ // The app is not installed on the target user and doesn't have a data dir
+ return false;
+ }
final String outDexFile = dataDir.getAbsolutePath() + "/code_cache/compiled_view.dex";
Log.i("PackageManager", "Compiling layouts in " + packageName + " (" + apkPath +
") to " + outDexFile);
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index fac98b8..774be9e 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1545,6 +1545,17 @@
mTargetDisplays.add(dc);
}
+ for (int i = 0; i < mTargets.size(); ++i) {
+ final DisplayArea da = mTargets.get(i).mContainer.asDisplayArea();
+ if (da == null) continue;
+ if (da.isVisibleRequested()) {
+ mController.mValidateDisplayVis.remove(da);
+ } else {
+ // In case something accidentally hides a displayarea and nothing shows it again.
+ mController.mValidateDisplayVis.add(da);
+ }
+ }
+
if (mOverrideOptions != null) {
info.setAnimationOptions(mOverrideOptions);
if (mOverrideOptions.getType() == ANIM_OPEN_CROSS_PROFILE_APPS) {
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 7d2933a..c92a781 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -150,6 +150,13 @@
final ArrayList<ActivityRecord> mValidateActivityCompat = new ArrayList<>();
/**
+ * List of display areas which were last sent as "closing"-type and haven't yet had a
+ * corresponding "opening"-type transition. A mismatch here is usually related to issues in
+ * keyguard unlock.
+ */
+ final ArrayList<DisplayArea> mValidateDisplayVis = new ArrayList<>();
+
+ /**
* Currently playing transitions (in the order they were started). When finished, records are
* removed from this list.
*/
@@ -941,6 +948,15 @@
ar.getSyncTransaction().setPosition(ar.getSurfaceControl(), tmpPos.x, tmpPos.y);
}
mValidateActivityCompat.clear();
+ for (int i = 0; i < mValidateDisplayVis.size(); ++i) {
+ final DisplayArea da = mValidateDisplayVis.get(i);
+ if (!da.isAttached() || da.getSurfaceControl() == null) continue;
+ if (da.isVisibleRequested()) {
+ Slog.e(TAG, "DisplayArea became visible outside of a transition: " + da);
+ da.getSyncTransaction().show(da.getSurfaceControl());
+ }
+ }
+ mValidateDisplayVis.clear();
}
/**
diff --git a/services/foldables/devicestateprovider/proguard.flags b/services/foldables/devicestateprovider/proguard.flags
new file mode 100644
index 0000000..069cbc6
--- /dev/null
+++ b/services/foldables/devicestateprovider/proguard.flags
@@ -0,0 +1 @@
+-keep,allowoptimization,allowaccessmodification class com.android.server.policy.TentModeDeviceStatePolicy { *; }
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/TentModeDeviceStatePolicy.java b/services/foldables/devicestateprovider/src/com/android/server/policy/TentModeDeviceStatePolicy.java
new file mode 100644
index 0000000..5f2cf3c
--- /dev/null
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/TentModeDeviceStatePolicy.java
@@ -0,0 +1,140 @@
+/*
+ * 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.server.policy;
+
+import static com.android.server.devicestate.DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS;
+import static com.android.server.devicestate.DeviceState.FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP;
+import static com.android.server.devicestate.DeviceState.FLAG_EMULATED_ONLY;
+import static com.android.server.devicestate.DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE;
+import static com.android.server.devicestate.DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL;
+import static com.android.server.policy.FoldableDeviceStateProvider.DeviceStateConfiguration.createConfig;
+import static com.android.server.policy.FoldableDeviceStateProvider.DeviceStateConfiguration.createTentModeClosedState;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
+import android.hardware.display.DisplayManager;
+
+import com.android.server.devicestate.DeviceStatePolicy;
+import com.android.server.devicestate.DeviceStateProvider;
+import com.android.server.policy.FoldableDeviceStateProvider.DeviceStateConfiguration;
+
+/**
+ * Device state policy for a foldable device that supports tent mode: a mode when the device
+ * keeps the outer display on until reaching a certain hinge angle threshold.
+ *
+ * Contains configuration for {@link FoldableDeviceStateProvider}.
+ */
+public class TentModeDeviceStatePolicy extends DeviceStatePolicy {
+
+ private static final int DEVICE_STATE_CLOSED = 0;
+ private static final int DEVICE_STATE_HALF_OPENED = 1;
+ private static final int DEVICE_STATE_OPENED = 2;
+ private static final int DEVICE_STATE_REAR_DISPLAY_STATE = 3;
+ private static final int DEVICE_STATE_CONCURRENT_INNER_DEFAULT = 4;
+
+ private static final int TENT_MODE_SWITCH_ANGLE_DEGREES = 90;
+ private static final int TABLE_TOP_MODE_SWITCH_ANGLE_DEGREES = 125;
+ private static final int MIN_CLOSED_ANGLE_DEGREES = 0;
+ private static final int MAX_CLOSED_ANGLE_DEGREES = 5;
+
+ private final DeviceStateProvider mProvider;
+
+ /**
+ * Creates TentModeDeviceStatePolicy
+ *
+ * @param context Android context
+ * @param hingeAngleSensor hinge angle sensor that will be used to switch between states
+ * @param hallSensor hall sensor that will be used to switch between states
+ * @param closeAngleDegrees if non-zero, this angle will be used as a threshold to switch
+ * between folded and unfolded modes, otherwise when folding the
+ * display switch will happen at 0 degrees
+ */
+ public TentModeDeviceStatePolicy(@NonNull Context context,
+ @NonNull Sensor hingeAngleSensor, @NonNull Sensor hallSensor, int closeAngleDegrees) {
+ super(context);
+
+ final SensorManager sensorManager = mContext.getSystemService(SensorManager.class);
+ final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+
+ final DeviceStateConfiguration[] configuration = createConfiguration(closeAngleDegrees);
+
+ mProvider = new FoldableDeviceStateProvider(mContext, sensorManager, hingeAngleSensor,
+ hallSensor, displayManager, configuration);
+ }
+
+ private DeviceStateConfiguration[] createConfiguration(int closeAngleDegrees) {
+ return new DeviceStateConfiguration[]{
+ createClosedConfiguration(closeAngleDegrees),
+ createConfig(DEVICE_STATE_HALF_OPENED,
+ /* name= */ "HALF_OPENED",
+ (provider) -> {
+ final float hingeAngle = provider.getHingeAngle();
+ return hingeAngle >= MAX_CLOSED_ANGLE_DEGREES
+ && hingeAngle <= TABLE_TOP_MODE_SWITCH_ANGLE_DEGREES;
+ }),
+ createConfig(DEVICE_STATE_OPENED,
+ /* name= */ "OPENED",
+ (provider) -> true),
+ createConfig(DEVICE_STATE_REAR_DISPLAY_STATE,
+ /* name= */ "REAR_DISPLAY_STATE",
+ /* flags= */ FLAG_EMULATED_ONLY,
+ (provider) -> false),
+ createConfig(DEVICE_STATE_CONCURRENT_INNER_DEFAULT,
+ /* name= */ "CONCURRENT_INNER_DEFAULT",
+ /* flags= */ FLAG_EMULATED_ONLY | FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP
+ | FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL
+ | FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE,
+ (provider) -> false)
+ };
+ }
+
+ private DeviceStateConfiguration createClosedConfiguration(int closeAngleDegrees) {
+ if (closeAngleDegrees > 0) {
+ // Switch displays at closeAngleDegrees in both ways (folding and unfolding)
+ return createConfig(
+ DEVICE_STATE_CLOSED,
+ /* name= */ "CLOSED",
+ /* flags= */ FLAG_CANCEL_OVERRIDE_REQUESTS,
+ (provider) -> {
+ final float hingeAngle = provider.getHingeAngle();
+ return hingeAngle <= closeAngleDegrees;
+ }
+ );
+ }
+
+ // Switch to the outer display only at 0 degrees but use TENT_MODE_SWITCH_ANGLE_DEGREES
+ // angle when switching to the inner display
+ return createTentModeClosedState(DEVICE_STATE_CLOSED,
+ /* name= */ "CLOSED",
+ /* flags= */ FLAG_CANCEL_OVERRIDE_REQUESTS,
+ MIN_CLOSED_ANGLE_DEGREES,
+ MAX_CLOSED_ANGLE_DEGREES,
+ TENT_MODE_SWITCH_ANGLE_DEGREES);
+ }
+
+ @Override
+ public DeviceStateProvider getDeviceStateProvider() {
+ return mProvider;
+ }
+
+ @Override
+ public void configureDeviceForState(int state, @NonNull Runnable onComplete) {
+ onComplete.run();
+ }
+}
diff --git a/services/permission/TEST_MAPPING b/services/permission/TEST_MAPPING
index 24323c8..00bfcd3 100644
--- a/services/permission/TEST_MAPPING
+++ b/services/permission/TEST_MAPPING
@@ -1,4 +1,17 @@
{
+ "platinum-test": [
+ {
+ "name": "CtsPermissionTestCases",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.PlatinumTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
"presubmit": [
{
"name": "CtsPermissionTestCases",
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
index 6c39275..b8f726b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
@@ -224,7 +224,7 @@
val scanRequest = ScanRequest(parsedPackage, null, null, null, null,
null, null, null, 0, 0, false, null, null)
val scanResult = ScanResult(scanRequest, null, null, false, 0, null, null, null)
- var installRequest = InstallRequest(parsedPackage, 0, 0, UserHandle(0), scanResult)
+ var installRequest = InstallRequest(parsedPackage, 0, 0, UserHandle(0), scanResult, null)
val latestInfoSetting =
mSharedLibrariesImpl.getStaticSharedLibLatestVersionSetting(installRequest)!!
@@ -309,7 +309,7 @@
val testInfo = libOfStatic(TEST_LIB_PACKAGE_NAME, TEST_LIB_NAME, 1L)
val scanResult = ScanResult(mock(), null, null,
false, 0, null, testInfo, null)
- var installRequest = InstallRequest(mock(), 0, 0, UserHandle(0), scanResult)
+ var installRequest = InstallRequest(mock(), 0, 0, UserHandle(0), scanResult, null)
val allowedInfos = mSharedLibrariesImpl.getAllowedSharedLibInfos(installRequest)
@@ -332,7 +332,7 @@
null, null, null, 0, 0, false, null, null)
val scanResult = ScanResult(scanRequest, packageSetting, null,
false, 0, null, null, listOf(testInfo))
- var installRequest = InstallRequest(parsedPackage, 0, 0, UserHandle(0), scanResult)
+ var installRequest = InstallRequest(parsedPackage, 0, 0, UserHandle(0), scanResult, null)
val allowedInfos = mSharedLibrariesImpl.getAllowedSharedLibInfos(installRequest)