summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/test-current.txt1
-rw-r--r--core/java/android/app/Notification.java1
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintManager.java81
-rw-r--r--core/java/android/hardware/fingerprint/IFingerprintService.aidl8
-rw-r--r--core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl1
-rw-r--r--core/java/android/provider/CallLog.java11
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java11
-rw-r--r--core/java/android/view/IDisplayWindowInsetsController.aidl7
-rw-r--r--core/java/android/view/IWindowManager.aidl10
-rw-r--r--core/java/android/view/WindowManagerPolicyConstants.java7
-rw-r--r--core/java/android/widget/TextView.java97
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java10
-rw-r--r--core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java4
-rw-r--r--core/java/com/android/internal/widget/ResolverDrawerLayout.java17
-rw-r--r--core/res/res/layout/notification_material_action_list.xml1
-rw-r--r--core/res/res/values/attrs.xml7
-rw-r--r--core/res/res/values/config.xml15
-rw-r--r--core/res/res/values/public.xml2
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--data/etc/privapp-permissions-platform.xml4
-rw-r--r--diff25
-rw-r--r--packages/CarSystemUI/TEST_MAPPING11
-rw-r--r--packages/CarSystemUI/res/layout/headsup_container_bottom.xml7
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java6
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/CarSystemUiTest.java32
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java9
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java22
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java7
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/wm/BarControlPolicy.java250
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java168
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsInsetsControllerHost.java173
-rw-r--r--packages/CarSystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/HvacControllerTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonRoleHolderControllerTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonSelectionStateControllerTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarViewTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainerTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/notification/NotificationVisibilityLoggerTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppDetectorTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppListenerTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewMediatorTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java29
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewControllerTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/wm/BarControlPolicyTest.java195
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java111
-rw-r--r--packages/Shell/AndroidManifest.xml5
-rw-r--r--packages/SystemUI/res/layout/qs_footer_impl.xml2
-rw-r--r--packages/SystemUI/res/values/ids.xml2
-rw-r--r--packages/SystemUI/res/values/strings.xml21
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java85
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java41
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java445
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java39
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java72
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java2
-rw-r--r--packages/WAPPushManager/AndroidManifest.xml2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java7
-rw-r--r--services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java25
-rw-r--r--services/core/java/com/android/server/biometrics/AuthService.java25
-rw-r--r--services/core/java/com/android/server/biometrics/BiometricServiceBase.java30
-rw-r--r--services/core/java/com/android/server/biometrics/Utils.java64
-rw-r--r--services/core/java/com/android/server/biometrics/face/FaceService.java5
-rw-r--r--services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java101
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java14
-rw-r--r--services/core/java/com/android/server/policy/WindowManagerPolicy.java4
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java13
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java214
-rw-r--r--services/core/java/com/android/server/wm/InsetsPolicy.java58
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java8
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java25
-rw-r--r--services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java59
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java40
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java25
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java71
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java18
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java18
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java32
-rwxr-xr-xtelephony/java/android/telephony/CarrierConfigManager.java11
108 files changed, 2632 insertions, 567 deletions
diff --git a/api/test-current.txt b/api/test-current.txt
index 5172e592f750..519bbbb9bc39 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -34,6 +34,7 @@ package android {
public static final class R.bool {
field public static final int config_assistantOnTopOfDream = 17891333; // 0x1110005
field public static final int config_perDisplayFocusEnabled = 17891332; // 0x1110004
+ field public static final int config_remoteInsetsControllerControlsSystemBars = 17891334; // 0x1110006
}
public static final class R.string {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 79d2a8102358..76226888bf1a 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -4801,7 +4801,6 @@ public class Notification implements Parcelable
contentView.setViewVisibility(R.id.time, View.GONE);
contentView.setImageViewIcon(R.id.profile_badge, null);
contentView.setViewVisibility(R.id.profile_badge, View.GONE);
- contentView.setViewVisibility(R.id.alerted_icon, View.GONE);
mN.mUsesStandardHeader = false;
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index d57a7e4b97f0..6900105c4d4c 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -19,6 +19,7 @@ package android.hardware.fingerprint;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.MANAGE_FINGERPRINT;
import static android.Manifest.permission.USE_BIOMETRIC;
+import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.Manifest.permission.USE_FINGERPRINT;
import android.annotation.NonNull;
@@ -75,11 +76,13 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
private static final int MSG_ERROR = 104;
private static final int MSG_REMOVED = 105;
private static final int MSG_ENUMERATED = 106;
+ private static final int MSG_FINGERPRINT_DETECTED = 107;
private IFingerprintService mService;
private Context mContext;
private IBinder mToken = new Binder();
private AuthenticationCallback mAuthenticationCallback;
+ private FingerprintDetectionCallback mFingerprintDetectionCallback;
private EnrollmentCallback mEnrollmentCallback;
private RemovalCallback mRemovalCallback;
private EnumerateCallback mEnumerateCallback;
@@ -107,6 +110,13 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
}
}
+ private class OnFingerprintDetectionCancelListener implements OnCancelListener {
+ @Override
+ public void onCancel() {
+ cancelFingerprintDetect();
+ }
+ }
+
/**
* A wrapper class for the crypto objects supported by FingerprintManager. Currently the
* framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
@@ -272,6 +282,18 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
};
/**
+ * Callback structure provided for {@link #detectFingerprint(CancellationSignal,
+ * FingerprintDetectionCallback, int)}.
+ * @hide
+ */
+ public interface FingerprintDetectionCallback {
+ /**
+ * Invoked when a fingerprint has been detected.
+ */
+ void onFingerprintDetected(int userId, boolean isStrongBiometric);
+ }
+
+ /**
* Callback structure provided to {@link FingerprintManager#enroll(byte[], CancellationSignal,
* int, int, EnrollmentCallback)} must provide an implementation of this for listening to
* fingerprint events.
@@ -454,6 +476,35 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
}
/**
+ * Uses the fingerprint hardware to detect for the presence of a finger, without giving details
+ * about accept/reject/lockout.
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public void detectFingerprint(@NonNull CancellationSignal cancel,
+ @NonNull FingerprintDetectionCallback callback, int userId) {
+ if (mService == null) {
+ return;
+ }
+
+ if (cancel.isCanceled()) {
+ Slog.w(TAG, "Detection already cancelled");
+ return;
+ } else {
+ cancel.setOnCancelListener(new OnFingerprintDetectionCancelListener());
+ }
+
+ mFingerprintDetectionCallback = callback;
+
+ try {
+ mService.detectFingerprint(mToken, userId, mServiceReceiver,
+ mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Remote exception when requesting finger detect", e);
+ }
+ }
+
+ /**
* Request fingerprint enrollment. This call warms up the fingerprint hardware
* and starts scanning for fingerprints. Progress will be indicated by callbacks to the
* {@link EnrollmentCallback} object. It terminates when
@@ -797,6 +848,10 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
sendEnumeratedResult((Long) msg.obj /* deviceId */, msg.arg1 /* fingerId */,
msg.arg2 /* groupId */);
break;
+ case MSG_FINGERPRINT_DETECTED:
+ sendFingerprintDetected(msg.arg1 /* userId */,
+ (boolean) msg.obj /* isStrongBiometric */);
+ break;
}
}
};
@@ -891,6 +946,14 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
}
}
+ private void sendFingerprintDetected(int userId, boolean isStrongBiometric) {
+ if (mFingerprintDetectionCallback == null) {
+ Slog.e(TAG, "sendFingerprintDetected, callback null");
+ return;
+ }
+ mFingerprintDetectionCallback.onFingerprintDetected(userId, isStrongBiometric);
+ }
+
/**
* @hide
*/
@@ -927,6 +990,18 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
}
}
+ private void cancelFingerprintDetect() {
+ if (mService == null) {
+ return;
+ }
+
+ try {
+ mService.cancelFingerprintDetect(mToken, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/**
* @hide
*/
@@ -1032,6 +1107,12 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
fp).sendToTarget();
}
+ @Override
+ public void onFingerprintDetected(long deviceId, int userId, boolean isStrongBiometric) {
+ mHandler.obtainMessage(MSG_FINGERPRINT_DETECTED, userId, 0, isStrongBiometric)
+ .sendToTarget();
+ }
+
@Override // binder call
public void onAuthenticationFailed(long deviceId) {
mHandler.obtainMessage(MSG_AUTHENTICATION_FAILED).sendToTarget();
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index c5c375543adc..8aa36d7d5d76 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -33,6 +33,11 @@ interface IFingerprintService {
void authenticate(IBinder token, long sessionId, int userId,
IFingerprintServiceReceiver receiver, int flags, String opPackageName);
+ // Uses the fingerprint hardware to detect for the presence of a finger, without giving details
+ // about accept/reject/lockout.
+ void detectFingerprint(IBinder token, int userId, IFingerprintServiceReceiver receiver,
+ String opPackageName);
+
// This method prepares the service to start authenticating, but doesn't start authentication.
// This is protected by the MANAGE_BIOMETRIC signatuer permission. This method should only be
// called from BiometricService. The additional uid, pid, userId arguments should be determined
@@ -48,6 +53,9 @@ interface IFingerprintService {
// Cancel authentication for the given sessionId
void cancelAuthentication(IBinder token, String opPackageName);
+ // Cancel finger detection
+ void cancelFingerprintDetect(IBinder token, String opPackageName);
+
// Same as above, except this is protected by the MANAGE_BIOMETRIC signature permission. Takes
// an additional uid, pid, userid.
void cancelAuthenticationFromService(IBinder token, String opPackageName,
diff --git a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
index 4412cee31bb0..a84b81e1eb81 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
@@ -26,6 +26,7 @@ oneway interface IFingerprintServiceReceiver {
void onAcquired(long deviceId, int acquiredInfo, int vendorCode);
void onAuthenticationSucceeded(long deviceId, in Fingerprint fp, int userId,
boolean isStrongBiometric);
+ void onFingerprintDetected(long deviceId, int userId, boolean isStrongBiometric);
void onAuthenticationFailed(long deviceId);
void onError(long deviceId, int error, int vendorCode);
void onRemoved(long deviceId, int fingerId, int groupId, int remaining);
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index a0e92b398c4e..276f16216b4d 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -99,6 +99,13 @@ public class CallLog {
public static final String LIMIT_PARAM_KEY = "limit";
/**
+ * Form of {@link #CONTENT_URI} which limits the query results to a single result.
+ */
+ private static final Uri CONTENT_URI_LIMIT_1 = CONTENT_URI.buildUpon()
+ .appendQueryParameter(LIMIT_PARAM_KEY, "1")
+ .build();
+
+ /**
* Query parameter used to specify the starting record to return.
* <p>
* TYPE: integer
@@ -932,11 +939,11 @@ public class CallLog {
Cursor c = null;
try {
c = resolver.query(
- CONTENT_URI,
+ CONTENT_URI_LIMIT_1,
new String[] {NUMBER},
TYPE + " = " + OUTGOING_TYPE,
null,
- DEFAULT_SORT_ORDER + " LIMIT 1");
+ DEFAULT_SORT_ORDER);
if (c == null || !c.moveToFirst()) {
return "";
}
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index c52b02bb6a3c..dfd305330b14 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1903,6 +1903,17 @@ public abstract class NotificationListenerService extends Service {
/**
* @hide
*/
+ public @NonNull Ranking withAudiblyAlertedInfo(@Nullable Ranking previous) {
+ if (previous != null && previous.mLastAudiblyAlertedMs > 0
+ && this.mLastAudiblyAlertedMs <= 0) {
+ this.mLastAudiblyAlertedMs = previous.mLastAudiblyAlertedMs;
+ }
+ return this;
+ }
+
+ /**
+ * @hide
+ */
public void populate(Ranking other) {
populate(other.mKey,
other.mRank,
diff --git a/core/java/android/view/IDisplayWindowInsetsController.aidl b/core/java/android/view/IDisplayWindowInsetsController.aidl
index 429c3aeba9b3..a0d4a6587bcf 100644
--- a/core/java/android/view/IDisplayWindowInsetsController.aidl
+++ b/core/java/android/view/IDisplayWindowInsetsController.aidl
@@ -27,6 +27,13 @@ import android.view.InsetsState;
oneway interface IDisplayWindowInsetsController {
/**
+ * Called when top focused window changes to determine whether or not to take over insets
+ * control. Won't be called if config_remoteInsetsControllerControlsSystemBars is false.
+ * @param packageName: Passes the top package name
+ */
+ void topFocusedWindowChanged(String packageName);
+
+ /**
* @see IWindow#insetsChanged
*/
void insetsChanged(in InsetsState insetsState);
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 58597cf3fb6d..00fc67214f75 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -388,16 +388,6 @@ interface IWindowManager
oneway void hideTransientBars(int displayId);
/**
- * When set to {@code true} the system bars will always be shown. This is true even if an app
- * requests to be fullscreen by setting the system ui visibility flags. The
- * functionality was added for the automotive case as a way to guarantee required content stays
- * on screen at all times.
- *
- * @hide
- */
- oneway void setForceShowSystemBars(boolean show);
-
- /**
* Called by System UI to notify of changes to the visibility of Recents.
*/
oneway void setRecentsVisibility(boolean visible);
diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java
index 492ab6f8a3d5..8c355202b63f 100644
--- a/core/java/android/view/WindowManagerPolicyConstants.java
+++ b/core/java/android/view/WindowManagerPolicyConstants.java
@@ -49,6 +49,13 @@ public interface WindowManagerPolicyConstants {
int PRESENCE_INTERNAL = 1 << 0;
int PRESENCE_EXTERNAL = 1 << 1;
+ // Alternate bars position values
+ int ALT_BAR_UNKNOWN = -1;
+ int ALT_BAR_LEFT = 1 << 0;
+ int ALT_BAR_RIGHT = 1 << 1;
+ int ALT_BAR_BOTTOM = 1 << 2;
+ int ALT_BAR_TOP = 1 << 3;
+
// Navigation bar position values
int NAV_BAR_INVALID = -1;
int NAV_BAR_LEFT = 1 << 0;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index f2bc89e9deb6..e16bf5a18808 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -395,6 +395,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private static final int EMS = LINES;
private static final int PIXELS = 2;
+ // Maximum text length for single line input.
+ private static final int MAX_LENGTH_FOR_SINGLE_LINE_EDIT_TEXT = 5000;
+ private InputFilter.LengthFilter mSingleLineLengthFilter = null;
+
private static final RectF TEMP_RECTF = new RectF();
/** @hide */
@@ -1589,7 +1593,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// Same as setSingleLine(), but make sure the transformation method and the maximum number
// of lines of height are unchanged for multi-line TextViews.
setInputTypeSingleLine(singleLine);
- applySingleLine(singleLine, singleLine, singleLine);
+ applySingleLine(singleLine, singleLine, singleLine,
+ // Does not apply automated max length filter since length filter will be resolved
+ // later in this function.
+ false
+ );
if (singleLine && getKeyListener() == null && ellipsize == ELLIPSIZE_NOT_SET) {
ellipsize = ELLIPSIZE_END;
@@ -1633,7 +1641,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
setTransformationMethod(PasswordTransformationMethod.getInstance());
}
- if (maxlength >= 0) {
+ // For addressing b/145128646
+ // For the performance reason, we limit characters for single line text field.
+ if (bufferType == BufferType.EDITABLE && singleLine && maxlength == -1) {
+ mSingleLineLengthFilter = new InputFilter.LengthFilter(
+ MAX_LENGTH_FOR_SINGLE_LINE_EDIT_TEXT);
+ }
+
+ if (mSingleLineLengthFilter != null) {
+ setFilters(new InputFilter[] { mSingleLineLengthFilter });
+ } else if (maxlength >= 0) {
setFilters(new InputFilter[] { new InputFilter.LengthFilter(maxlength) });
} else {
setFilters(NO_FILTERS);
@@ -6590,7 +6607,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (mSingleLine != singleLine || forceUpdate) {
// Change single line mode, but only change the transformation if
// we are not in password mode.
- applySingleLine(singleLine, !isPassword, true);
+ applySingleLine(singleLine, !isPassword, true, true);
}
if (!isSuggestionsEnabled()) {
@@ -10229,6 +10246,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* Note that the default conditions are not necessarily those that were in effect prior this
* method, and you may want to reset these properties to your custom values.
*
+ * Note that due to performance reasons, by setting single line for the EditText, the maximum
+ * text length is set to 5000 if no other character limitation are applied.
+ *
* @attr ref android.R.styleable#TextView_singleLine
*/
@android.view.RemotableViewMethod
@@ -10236,7 +10256,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// Could be used, but may break backward compatibility.
// if (mSingleLine == singleLine) return;
setInputTypeSingleLine(singleLine);
- applySingleLine(singleLine, true, true);
+ applySingleLine(singleLine, true, true, true);
}
/**
@@ -10256,14 +10276,40 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
private void applySingleLine(boolean singleLine, boolean applyTransformation,
- boolean changeMaxLines) {
+ boolean changeMaxLines, boolean changeMaxLength) {
mSingleLine = singleLine;
+
if (singleLine) {
setLines(1);
setHorizontallyScrolling(true);
if (applyTransformation) {
setTransformationMethod(SingleLineTransformationMethod.getInstance());
}
+
+ if (!changeMaxLength) return;
+
+ // Single line length filter is only applicable editable text.
+ if (mBufferType != BufferType.EDITABLE) return;
+
+ final InputFilter[] prevFilters = getFilters();
+ for (InputFilter filter: getFilters()) {
+ // We don't add LengthFilter if already there.
+ if (filter instanceof InputFilter.LengthFilter) return;
+ }
+
+ if (mSingleLineLengthFilter == null) {
+ mSingleLineLengthFilter = new InputFilter.LengthFilter(
+ MAX_LENGTH_FOR_SINGLE_LINE_EDIT_TEXT);
+ }
+
+ final InputFilter[] newFilters = new InputFilter[prevFilters.length + 1];
+ System.arraycopy(prevFilters, 0, newFilters, 0, prevFilters.length);
+ newFilters[prevFilters.length] = mSingleLineLengthFilter;
+
+ setFilters(newFilters);
+
+ // Since filter doesn't apply to existing text, trigger filter by setting text.
+ setText(getText());
} else {
if (changeMaxLines) {
setMaxLines(Integer.MAX_VALUE);
@@ -10272,6 +10318,47 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (applyTransformation) {
setTransformationMethod(null);
}
+
+ if (!changeMaxLength) return;
+
+ // Single line length filter is only applicable editable text.
+ if (mBufferType != BufferType.EDITABLE) return;
+
+ final InputFilter[] prevFilters = getFilters();
+ if (prevFilters.length == 0) return;
+
+ // Short Circuit: if mSingleLineLengthFilter is not allocated, nobody sets automated
+ // single line char limit filter.
+ if (mSingleLineLengthFilter == null) return;
+
+ // If we need to remove mSingleLineLengthFilter, we need to allocate another array.
+ // Since filter list is expected to be small and want to avoid unnecessary array
+ // allocation, check if there is mSingleLengthFilter first.
+ int targetIndex = -1;
+ for (int i = 0; i < prevFilters.length; ++i) {
+ if (prevFilters[i] == mSingleLineLengthFilter) {
+ targetIndex = i;
+ break;
+ }
+ }
+ if (targetIndex == -1) return; // not found. Do nothing.
+
+ if (prevFilters.length == 1) {
+ setFilters(NO_FILTERS);
+ return;
+ }
+
+ // Create new array which doesn't include mSingleLengthFilter.
+ final InputFilter[] newFilters = new InputFilter[prevFilters.length - 1];
+ System.arraycopy(prevFilters, 0, newFilters, 0, targetIndex);
+ System.arraycopy(
+ prevFilters,
+ targetIndex + 1,
+ newFilters,
+ targetIndex,
+ prevFilters.length - targetIndex - 1);
+ setFilters(newFilters);
+ mSingleLineLengthFilter = null;
}
}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 14cf258f18ab..3a89dcd96487 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -2640,7 +2640,10 @@ public class ChooserActivity extends ResolverActivity implements
}
RecyclerView recyclerView = mChooserMultiProfilePagerAdapter.getActiveAdapterView();
ChooserGridAdapter gridAdapter = mChooserMultiProfilePagerAdapter.getCurrentRootAdapter();
- if (gridAdapter == null || recyclerView == null) {
+ // Skip height calculation if recycler view was scrolled to prevent it inaccurately
+ // calculating the height, as the logic below does not account for the scrolled offset.
+ if (gridAdapter == null || recyclerView == null
+ || recyclerView.computeVerticalScrollOffset() != 0) {
return;
}
@@ -3127,6 +3130,11 @@ public class ChooserActivity extends ResolverActivity implements
ChooserGridAdapter currentRootAdapter =
mChooserMultiProfilePagerAdapter.getCurrentRootAdapter();
currentRootAdapter.updateDirectShareExpansion();
+ // This fixes an edge case where after performing a variety of gestures, vertical scrolling
+ // ends up disabled. That's because at some point the old tab's vertical scrolling is
+ // disabled and the new tab's is enabled. For context, see b/159997845
+ setVerticalScrollEnabled(true);
+ mResolverDrawerLayout.scrollNestedScrollableChildBackToTop();
}
@Override
diff --git a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
index ffa6041721c6..3a65a324f9d6 100644
--- a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
@@ -252,8 +252,10 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
@Override
protected void setupContainerPadding(View container) {
+ int initialBottomPadding = getContext().getResources().getDimensionPixelSize(
+ R.dimen.resolver_empty_state_container_padding_bottom);
container.setPadding(container.getPaddingLeft(), container.getPaddingTop(),
- container.getPaddingRight(), container.getPaddingBottom() + mBottomOffset);
+ container.getPaddingRight(), initialBottomPadding + mBottomOffset);
}
class ChooserProfileDescriptor extends ProfileDescriptor {
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index 3f708f84750c..90eeabb47e9a 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -464,11 +464,7 @@ public class ResolverDrawerLayout extends ViewGroup {
smoothScrollTo(mCollapsibleHeight + mUncollapsibleHeight, yvel);
mDismissOnScrollerFinished = true;
} else {
- if (isNestedListChildScrolled()) {
- mNestedListChild.smoothScrollToPosition(0);
- } else if (isNestedRecyclerChildScrolled()) {
- mNestedRecyclerChild.smoothScrollToPosition(0);
- }
+ scrollNestedScrollableChildBackToTop();
smoothScrollTo(yvel < 0 ? 0 : mCollapsibleHeight, yvel);
}
}
@@ -493,6 +489,17 @@ public class ResolverDrawerLayout extends ViewGroup {
return handled;
}
+ /**
+ * Scroll nested scrollable child back to top if it has been scrolled.
+ */
+ public void scrollNestedScrollableChildBackToTop() {
+ if (isNestedListChildScrolled()) {
+ mNestedListChild.smoothScrollToPosition(0);
+ } else if (isNestedRecyclerChildScrolled()) {
+ mNestedRecyclerChild.smoothScrollToPosition(0);
+ }
+ }
+
private void onSecondaryPointerUp(MotionEvent ev) {
final int pointerIndex = ev.getActionIndex();
final int pointerId = ev.getPointerId(pointerIndex);
diff --git a/core/res/res/layout/notification_material_action_list.xml b/core/res/res/layout/notification_material_action_list.xml
index 3615b9e2f9cb..df271f0f2942 100644
--- a/core/res/res/layout/notification_material_action_list.xml
+++ b/core/res/res/layout/notification_material_action_list.xml
@@ -25,6 +25,7 @@
android:id="@+id/actions_container_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:gravity="end"
android:orientation="horizontal"
android:paddingEnd="@dimen/bubble_gone_padding_end"
>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 8a4676dec6b3..2f1bcdc76afb 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1259,7 +1259,12 @@
<!-- Can be combined with <var>text</var> and its variations to
allow multiple lines of text in the field. If this flag is not set,
the text field will be constrained to a single line. Corresponds to
- {@link android.text.InputType#TYPE_TEXT_FLAG_MULTI_LINE}. -->
+ {@link android.text.InputType#TYPE_TEXT_FLAG_MULTI_LINE}.
+
+ Note: If this flag is not set and the text field doesn't have max length limit, the
+ framework automatically set maximum length of the characters to 5000 for the
+ performance reasons.
+ -->
<flag name="textMultiLine" value="0x00020001" />
<!-- Can be combined with <var>text</var> and its variations to
indicate that though the regular text view should not be multiple
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9f4b6f2142c0..8bbb2cec8a9d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -712,10 +712,17 @@
case, this can be disabled (set to false). -->
<bool name="config_enableCarDockHomeLaunch">true</bool>
- <!-- Control whether to force the display of System UI Bars at all times regardless of
- System Ui Flags. This can be useful in the Automotive case if there's a requirement for
- a UI element to be on screen at all times. -->
- <bool name="config_forceShowSystemBars">false</bool>
+ <!-- Control whether to force apps to give up control over the display of system bars at all
+ times regardless of System Ui Flags.
+ In the Automotive case, this is helpful if there's a requirement for an UI element to be on
+ screen at all times. Setting this to true also gives System UI the ability to override the
+ visibility controls for the system through the usage of the
+ "SYSTEM_BAR_VISIBILITY_OVERRIDE" setting.
+ Ex: Only setting the config to true will force show system bars for the entire system.
+ Ex: Setting the config to true and the "SYSTEM_BAR_VISIBILITY_OVERRIDE" setting to
+ "immersive.status=apps" will force show navigation bar for all apps and force hide status
+ bar for all apps. -->
+ <bool name="config_remoteInsetsControllerControlsSystemBars">false</bool>
<!-- HDMI behavior -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index e2fbbf46608f..e00aff1af37b 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3024,6 +3024,8 @@
<!-- @hide @TestApi -->
<public type="bool" name="config_assistantOnTopOfDream" id="0x01110005" />
+ <!-- @hide @TestApi -->
+ <public type="bool" name="config_remoteInsetsControllerControlsSystemBars" id="0x01110006" />
<!-- ===============================================================
Resources added in version S of the platform
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 6901eb21d9fe..dcd0b51772a7 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1670,7 +1670,7 @@
<java-symbol type="bool" name="config_enableCarDockHomeLaunch" />
<java-symbol type="bool" name="config_enableLockBeforeUnlockScreen" />
<java-symbol type="bool" name="config_enableLockScreenRotation" />
- <java-symbol type="bool" name="config_forceShowSystemBars" />
+ <java-symbol type="bool" name="config_remoteInsetsControllerControlsSystemBars" />
<java-symbol type="bool" name="config_lidControlsScreenLock" />
<java-symbol type="bool" name="config_lidControlsSleep" />
<java-symbol type="bool" name="config_lockDayNightMode" />
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index e6f4e27100de..5cdcaba6ce48 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -427,6 +427,10 @@ applications that come with the platform
<permission name="android.permission.CAPTURE_AUDIO_OUTPUT" />
<!-- Permissions required for CTS test - AdbManagerTest -->
<permission name="android.permission.MANAGE_DEBUGGING" />
+ <!-- Permissions required for ATS tests - AtsCarHostTestCases, AtsCarDeviceApp -->
+ <permission name="android.car.permission.CAR_DRIVING_STATE" />
+ <!-- Permissions required for ATS tests - AtsDeviceInfo, AtsAudioDeviceTestCases -->
+ <permission name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME" />
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/diff b/diff
new file mode 100644
index 000000000000..5c75d88e6789
--- /dev/null
+++ b/diff
@@ -0,0 +1,25 @@
+diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+index fc43882..832dc91 100644
+--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
++++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+@@ -67,6 +67,7 @@ import java.util.Arrays;
+ import java.util.HashSet;
+ import java.util.List;
+ import java.util.Set;
++import java.util.NoSuchElementException;
+
+ /**
+ * This class represents an accessibility client - either an AccessibilityService or a UiAutomation.
+@@ -978,7 +979,11 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
+ /* ignore */
+ }
+ if (mService != null) {
+- mService.unlinkToDeath(this, 0);
++ try {
++ mService.unlinkToDeath(this, 0);
++ }catch(NoSuchElementException e) {
++ Slog.e(LOG_TAG, "Failed unregistering death link");
++ }
+ mService = null;
+ }
+
diff --git a/packages/CarSystemUI/TEST_MAPPING b/packages/CarSystemUI/TEST_MAPPING
index 6056ddfc0bc7..c18a12aeb2aa 100644
--- a/packages/CarSystemUI/TEST_MAPPING
+++ b/packages/CarSystemUI/TEST_MAPPING
@@ -8,5 +8,16 @@
}
]
}
+ ],
+
+ "carsysui-presubmit": [
+ {
+ "name": "CarSystemUITests",
+ "options" : [
+ {
+ "include-annotation": "com.android.systemui.car.CarSystemUiTest"
+ }
+ ]
+ }
]
}
diff --git a/packages/CarSystemUI/res/layout/headsup_container_bottom.xml b/packages/CarSystemUI/res/layout/headsup_container_bottom.xml
index 1782d2536035..5aab0a172b99 100644
--- a/packages/CarSystemUI/res/layout/headsup_container_bottom.xml
+++ b/packages/CarSystemUI/res/layout/headsup_container_bottom.xml
@@ -29,13 +29,12 @@
android:orientation="horizontal"
app:layout_constraintGuide_begin="@dimen/headsup_scrim_height"/>
- <!-- Include a FocusParkingView at the beginning or end. The rotary controller "parks" the
- focus here when the user navigates to another window. This is also used to prevent
- wrap-around which is why it must be first or last in Tab order. -->
+ <!-- Include a FocusParkingView at the beginning. The rotary controller "parks" the focus here
+ when the user navigates to another window. This is also used to prevent wrap-around. -->
<com.android.car.ui.FocusParkingView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- app:layout_constraintLeft_toLeftOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<View
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
index 5bf989a971b9..496742680893 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -67,6 +67,8 @@ import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.volume.VolumeDialogComponent;
+import com.android.systemui.wm.DisplayImeController;
+import com.android.systemui.wm.DisplaySystemBarsController;
import javax.inject.Named;
import javax.inject.Singleton;
@@ -97,6 +99,10 @@ public abstract class CarSystemUIModule {
groupManager, configurationController);
}
+ @Binds
+ abstract DisplayImeController bindDisplayImeController(
+ DisplaySystemBarsController displaySystemBarsController);
+
@Singleton
@Provides
@Named(LEAK_REPORT_EMAIL_NAME)
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarSystemUiTest.java b/packages/CarSystemUI/src/com/android/systemui/car/CarSystemUiTest.java
new file mode 100644
index 000000000000..5f593b06c511
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarSystemUiTest.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 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.car;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates that a test class should be run as part of CarSystemUI presubmit
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Documented
+public @interface CarSystemUiTest {
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java
index 37dfce4e16ce..35b2080dddf9 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java
@@ -16,10 +16,12 @@
package com.android.systemui.car.navigationbar;
+import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES;
import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.InsetsState.ITYPE_TOP_GESTURES;
import static android.view.InsetsState.containsType;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
@@ -368,13 +370,15 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
height,
- WindowManager.LayoutParams.TYPE_STATUS_BAR,
+ WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
PixelFormat.TRANSLUCENT);
lp.setTitle("TopCarNavigationBar");
+ lp.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR, ITYPE_TOP_GESTURES};
+ lp.setFitInsetsTypes(0);
lp.windowAnimations = 0;
lp.gravity = Gravity.TOP;
mWindowManager.addView(mTopNavigationBarWindow, lp);
@@ -388,13 +392,14 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
height,
- WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
PixelFormat.TRANSLUCENT);
lp.setTitle("BottomCarNavigationBar");
+ lp.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR, ITYPE_BOTTOM_GESTURES};
lp.windowAnimations = 0;
lp.gravity = Gravity.BOTTOM;
mWindowManager.addView(mBottomNavigationBarWindow, lp);
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java
index 029d4c7fa2fb..0ced4021ce38 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java
@@ -16,7 +16,10 @@
package com.android.systemui.car.navigationbar;
+import static android.view.WindowInsets.Type.systemBars;
+
import android.content.Context;
+import android.graphics.Insets;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
@@ -79,9 +82,28 @@ public class CarNavigationBarView extends LinearLayout {
@Override
public WindowInsets onApplyWindowInsets(WindowInsets windowInsets) {
+ applyMargins(windowInsets.getInsets(systemBars()));
return windowInsets;
}
+ private void applyMargins(Insets insets) {
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ View child = getChildAt(i);
+ if (child.getLayoutParams() instanceof LayoutParams) {
+ LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ if (lp.rightMargin != insets.right || lp.leftMargin != insets.left
+ || lp.topMargin != insets.top || lp.bottomMargin != insets.bottom) {
+ lp.rightMargin = insets.right;
+ lp.leftMargin = insets.left;
+ lp.topMargin = insets.top;
+ lp.bottomMargin = insets.bottom;
+ child.requestLayout();
+ }
+ }
+ }
+ }
+
// Used to forward touch events even if the touch was initiated from a child component
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java
index d60bc418ece2..adf8d4d5acf8 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java
@@ -148,10 +148,9 @@ public class NavigationBarViewFactory {
CarNavigationBarView view = (CarNavigationBarView) View.inflate(mContext, barLayout,
/* root= */ null);
- // Include a FocusParkingView at the end. The rotary controller "parks" the focus here when
- // the user navigates to another window. This is also used to prevent wrap-around which is
- // why it must be first or last in Tab order.
- view.addView(new FocusParkingView(mContext));
+ // Include a FocusParkingView at the beginning. The rotary controller "parks" the focus here
+ // when the user navigates to another window. This is also used to prevent wrap-around.
+ view.addView(new FocusParkingView(mContext), 0);
mCachedViewMap.put(type, view);
return mCachedViewMap.get(type);
diff --git a/packages/CarSystemUI/src/com/android/systemui/wm/BarControlPolicy.java b/packages/CarSystemUI/src/com/android/systemui/wm/BarControlPolicy.java
new file mode 100644
index 000000000000..5f9665ff7632
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/wm/BarControlPolicy.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2020 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.wm;
+
+import android.car.settings.CarSettings;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.view.WindowInsets;
+
+import androidx.annotation.VisibleForTesting;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * Util class to load PolicyControl and allow for querying if a package matches immersive filters.
+ * Similar to {@link com.android.server.wm.PolicyControl}, but separate due to CarSystemUI needing
+ * to set its own policies for system bar visibilities.
+ *
+ * This forces immersive mode behavior for one or both system bars (based on a package
+ * list).
+ *
+ * Control by setting {@link Settings.Global#POLICY_CONTROL_AUTO} to one or more name-value pairs.
+ * e.g.
+ * to force immersive mode everywhere:
+ * "immersive.full=*"
+ * to force hide status bars for com.package1 but not com.package2:
+ * "immersive.status=com.package1,-com.package2"
+ *
+ * Separate multiple name-value pairs with ':'
+ * e.g. "immersive.status=com.package:immersive.navigation=*"
+ */
+public class BarControlPolicy {
+
+ private static final String TAG = "BarControlPolicy";
+ private static final boolean DEBUG = false;
+
+ private static final String NAME_IMMERSIVE_FULL = "immersive.full";
+ private static final String NAME_IMMERSIVE_STATUS = "immersive.status";
+ private static final String NAME_IMMERSIVE_NAVIGATION = "immersive.navigation";
+
+ @VisibleForTesting
+ static String sSettingValue;
+ @VisibleForTesting
+ static Filter sImmersiveStatusFilter;
+ private static Filter sImmersiveNavigationFilter;
+
+ /** Loads values from the POLICY_CONTROL setting to set filters. */
+ static boolean reloadFromSetting(Context context) {
+ if (DEBUG) Slog.d(TAG, "reloadFromSetting()");
+ String value = null;
+ try {
+ value = Settings.Global.getStringForUser(context.getContentResolver(),
+ CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
+ UserHandle.USER_CURRENT);
+ if (sSettingValue == value || sSettingValue != null && sSettingValue.equals(value)) {
+ return false;
+ }
+ setFilters(value);
+ sSettingValue = value;
+ } catch (Throwable t) {
+ Slog.w(TAG, "Error loading policy control, value=" + value, t);
+ return false;
+ }
+ return true;
+ }
+
+ /** Used in testing to reset BarControlPolicy. */
+ @VisibleForTesting
+ static void reset() {
+ sSettingValue = null;
+ sImmersiveStatusFilter = null;
+ sImmersiveNavigationFilter = null;
+ }
+
+ /**
+ * Registers a content observer to listen to updates to the SYSTEM_BAR_VISIBILITY_OVERRIDE flag.
+ */
+ static void registerContentObserver(Context context, Handler handler, FilterListener listener) {
+ context.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE), false,
+ new ContentObserver(handler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ if (reloadFromSetting(context)) {
+ listener.onFilterUpdated();
+ }
+ }
+ }, UserHandle.USER_ALL);
+ }
+
+ /**
+ * Returns bar visibilities based on POLICY_CONTROL_AUTO filters and window policies.
+ * @return int[], where the first value is the inset types that should be shown, and the second
+ * is the inset types that should be hidden.
+ */
+ @WindowInsets.Type.InsetsType
+ static int[] getBarVisibilities(String packageName) {
+ int hideTypes = 0;
+ int showTypes = 0;
+ if (matchesStatusFilter(packageName)) {
+ hideTypes |= WindowInsets.Type.statusBars();
+ } else {
+ showTypes |= WindowInsets.Type.statusBars();
+ }
+ if (matchesNavigationFilter(packageName)) {
+ hideTypes |= WindowInsets.Type.navigationBars();
+ } else {
+ showTypes |= WindowInsets.Type.navigationBars();
+ }
+
+ return new int[] {showTypes, hideTypes};
+ }
+
+ private static boolean matchesStatusFilter(String packageName) {
+ return sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(packageName);
+ }
+
+ private static boolean matchesNavigationFilter(String packageName) {
+ return sImmersiveNavigationFilter != null
+ && sImmersiveNavigationFilter.matches(packageName);
+ }
+
+ private static void setFilters(String value) {
+ if (DEBUG) Slog.d(TAG, "setFilters: " + value);
+ sImmersiveStatusFilter = null;
+ sImmersiveNavigationFilter = null;
+ if (value != null) {
+ String[] nvps = value.split(":");
+ for (String nvp : nvps) {
+ int i = nvp.indexOf('=');
+ if (i == -1) continue;
+ String n = nvp.substring(0, i);
+ String v = nvp.substring(i + 1);
+ if (n.equals(NAME_IMMERSIVE_FULL)) {
+ Filter f = Filter.parse(v);
+ sImmersiveStatusFilter = sImmersiveNavigationFilter = f;
+ } else if (n.equals(NAME_IMMERSIVE_STATUS)) {
+ Filter f = Filter.parse(v);
+ sImmersiveStatusFilter = f;
+ } else if (n.equals(NAME_IMMERSIVE_NAVIGATION)) {
+ Filter f = Filter.parse(v);
+ sImmersiveNavigationFilter = f;
+ }
+ }
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "immersiveStatusFilter: " + sImmersiveStatusFilter);
+ Slog.d(TAG, "immersiveNavigationFilter: " + sImmersiveNavigationFilter);
+ }
+ }
+
+ private static class Filter {
+ private static final String ALL = "*";
+
+ private final ArraySet<String> mWhitelist;
+ private final ArraySet<String> mBlacklist;
+
+ private Filter(ArraySet<String> whitelist, ArraySet<String> blacklist) {
+ mWhitelist = whitelist;
+ mBlacklist = blacklist;
+ }
+
+ boolean matches(String packageName) {
+ if (packageName == null) return false;
+ if (onBlacklist(packageName)) return false;
+ return onWhitelist(packageName);
+ }
+
+ private boolean onBlacklist(String packageName) {
+ return mBlacklist.contains(packageName) || mBlacklist.contains(ALL);
+ }
+
+ private boolean onWhitelist(String packageName) {
+ return mWhitelist.contains(ALL) || mWhitelist.contains(packageName);
+ }
+
+ void dump(PrintWriter pw) {
+ pw.print("Filter[");
+ dump("whitelist", mWhitelist, pw); pw.print(',');
+ dump("blacklist", mBlacklist, pw); pw.print(']');
+ }
+
+ private void dump(String name, ArraySet<String> set, PrintWriter pw) {
+ pw.print(name); pw.print("=(");
+ int n = set.size();
+ for (int i = 0; i < n; i++) {
+ if (i > 0) pw.print(',');
+ pw.print(set.valueAt(i));
+ }
+ pw.print(')');
+ }
+
+ @Override
+ public String toString() {
+ StringWriter sw = new StringWriter();
+ dump(new PrintWriter(sw, true));
+ return sw.toString();
+ }
+
+ // value = comma-delimited list of tokens, where token = (package name|*)
+ // e.g. "com.package1", or "com.android.systemui, com.android.keyguard" or "*"
+ static Filter parse(String value) {
+ if (value == null) return null;
+ ArraySet<String> whitelist = new ArraySet<String>();
+ ArraySet<String> blacklist = new ArraySet<String>();
+ for (String token : value.split(",")) {
+ token = token.trim();
+ if (token.startsWith("-") && token.length() > 1) {
+ token = token.substring(1);
+ blacklist.add(token);
+ } else {
+ whitelist.add(token);
+ }
+ }
+ return new Filter(whitelist, blacklist);
+ }
+ }
+
+ /**
+ * Interface to listen for updates to the filter triggered by the content observer listening to
+ * the SYSTEM_BAR_VISIBILITY_OVERRIDE flag.
+ */
+ interface FilterListener {
+
+ /** Callback triggered when the content observer updates the filter. */
+ void onFilterUpdated();
+ }
+
+ private BarControlPolicy() {}
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java
new file mode 100644
index 000000000000..a831464e7987
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2020 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.wm;
+
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.IDisplayWindowInsetsController;
+import android.view.InsetsController;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
+import android.view.WindowInsets;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.systemui.TransactionPool;
+import com.android.systemui.dagger.qualifiers.Main;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Controller that maps between displays and {@link IDisplayWindowInsetsController} in order to
+ * give system bar control to SystemUI.
+ * {@link R.bool#config_remoteInsetsControllerControlsSystemBars} determines whether this controller
+ * takes control or not.
+ */
+@Singleton
+public class DisplaySystemBarsController extends DisplayImeController {
+
+ private static final String TAG = "DisplaySystemBarsController";
+
+ private SparseArray<PerDisplay> mPerDisplaySparseArray;
+
+ @Inject
+ public DisplaySystemBarsController(
+ SystemWindows syswin,
+ DisplayController displayController,
+ @Main Handler mainHandler,
+ TransactionPool transactionPool) {
+ super(syswin, displayController, mainHandler, transactionPool);
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ PerDisplay pd = new PerDisplay(displayId);
+ try {
+ mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, pd);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to set insets controller on display " + displayId);
+ }
+ // Lazy loading policy control filters instead of during boot.
+ if (mPerDisplaySparseArray == null) {
+ mPerDisplaySparseArray = new SparseArray<>();
+ BarControlPolicy.reloadFromSetting(mSystemWindows.mContext);
+ BarControlPolicy.registerContentObserver(mSystemWindows.mContext, mHandler, () -> {
+ int size = mPerDisplaySparseArray.size();
+ for (int i = 0; i < size; i++) {
+ mPerDisplaySparseArray.valueAt(i).modifyDisplayWindowInsets();
+ }
+ });
+ }
+ mPerDisplaySparseArray.put(displayId, pd);
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ try {
+ mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, null);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to remove insets controller on display " + displayId);
+ }
+ mPerDisplaySparseArray.remove(displayId);
+ }
+
+ @VisibleForTesting
+ class PerDisplay extends IDisplayWindowInsetsController.Stub {
+
+ int mDisplayId;
+ InsetsController mInsetsController;
+ InsetsState mInsetsState = new InsetsState();
+ String mPackageName;
+
+ PerDisplay(int displayId) {
+ mDisplayId = displayId;
+ mInsetsController = new InsetsController(
+ new DisplaySystemBarsInsetsControllerHost(mHandler, this));
+ }
+
+ @Override
+ public void insetsChanged(InsetsState insetsState) {
+ if (mInsetsState.equals(insetsState)) {
+ return;
+ }
+ mInsetsState.set(insetsState, true /* copySources */);
+ mInsetsController.onStateChanged(insetsState);
+ if (mPackageName != null) {
+ modifyDisplayWindowInsets();
+ }
+ }
+
+ @Override
+ public void insetsControlChanged(InsetsState insetsState,
+ InsetsSourceControl[] activeControls) {
+ mInsetsController.onControlsChanged(activeControls);
+ }
+
+ @Override
+ public void hideInsets(@WindowInsets.Type.InsetsType int types, boolean fromIme) {
+ mInsetsController.hide(types);
+ }
+
+ @Override
+ public void showInsets(@WindowInsets.Type.InsetsType int types, boolean fromIme) {
+ mInsetsController.show(types);
+ }
+
+ @Override
+ public void topFocusedWindowChanged(String packageName) {
+ // If both package names are null or both package names are equal, return.
+ if (mPackageName == packageName
+ || (mPackageName != null && mPackageName.equals(packageName))) {
+ return;
+ }
+ mPackageName = packageName;
+ modifyDisplayWindowInsets();
+ }
+
+ private void modifyDisplayWindowInsets() {
+ if (mPackageName == null) {
+ return;
+ }
+ int[] barVisibilities = BarControlPolicy.getBarVisibilities(mPackageName);
+ updateInsetsState(barVisibilities[0], /* visible= */ true);
+ updateInsetsState(barVisibilities[1], /* visible= */ false);
+ showInsets(barVisibilities[0], /* fromIme= */ false);
+ hideInsets(barVisibilities[1], /* fromIme= */ false);
+ try {
+ mSystemWindows.mWmService.modifyDisplayWindowInsets(mDisplayId, mInsetsState);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to update window manager service.");
+ }
+ }
+
+ private void updateInsetsState(@WindowInsets.Type.InsetsType int types, boolean visible) {
+ ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
+ for (int i = internalTypes.size() - 1; i >= 0; i--) {
+ mInsetsState.getSource(internalTypes.valueAt(i)).setVisible(visible);
+ }
+ }
+ }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsInsetsControllerHost.java b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsInsetsControllerHost.java
new file mode 100644
index 000000000000..2f8da44ba851
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsInsetsControllerHost.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2020 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.wm;
+
+import android.annotation.NonNull;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.IDisplayWindowInsetsController;
+import android.view.InsetsController;
+import android.view.InsetsState;
+import android.view.SurfaceControl;
+import android.view.SyncRtSurfaceTransactionApplier;
+import android.view.WindowInsets;
+import android.view.WindowInsetsAnimation;
+import android.view.WindowInsetsController;
+import android.view.inputmethod.InputMethodManager;
+
+import java.util.List;
+
+/**
+ * Implements {@link InsetsController.Host} for usage by
+ * {@link DisplaySystemBarsController.PerDisplay} instances in {@link DisplaySystemBarsController}.
+ * @hide
+ */
+public class DisplaySystemBarsInsetsControllerHost implements InsetsController.Host {
+
+ private static final String TAG = DisplaySystemBarsInsetsControllerHost.class.getSimpleName();
+
+ private final Handler mHandler;
+ private final IDisplayWindowInsetsController mController;
+ private final float[] mTmpFloat9 = new float[9];
+
+ public DisplaySystemBarsInsetsControllerHost(
+ Handler handler, IDisplayWindowInsetsController controller) {
+ mHandler = handler;
+ mController = controller;
+ }
+
+ @Override
+ public Handler getHandler() {
+ return mHandler;
+ }
+
+ @Override
+ public void notifyInsetsChanged() {
+ // no-op
+ }
+
+ @Override
+ public void dispatchWindowInsetsAnimationPrepare(@NonNull WindowInsetsAnimation animation) {
+ // no-op
+ }
+
+ @Override
+ public WindowInsetsAnimation.Bounds dispatchWindowInsetsAnimationStart(
+ @NonNull WindowInsetsAnimation animation,
+ @NonNull WindowInsetsAnimation.Bounds bounds) {
+ return null;
+ }
+
+ @Override
+ public WindowInsets dispatchWindowInsetsAnimationProgress(@NonNull WindowInsets insets,
+ @NonNull List<WindowInsetsAnimation> runningAnimations) {
+ return null;
+ }
+
+ @Override
+ public void dispatchWindowInsetsAnimationEnd(@NonNull WindowInsetsAnimation animation) {
+ // no-op
+ }
+
+ @Override
+ public void applySurfaceParams(final SyncRtSurfaceTransactionApplier.SurfaceParams... params) {
+ for (int i = params.length - 1; i >= 0; i--) {
+ SyncRtSurfaceTransactionApplier.applyParams(
+ new SurfaceControl.Transaction(), params[i], mTmpFloat9);
+ }
+
+ }
+
+ @Override
+ public void updateCompatSysUiVisibility(
+ @InsetsState.InternalInsetsType int type, boolean visible, boolean hasControl) {
+ // no-op
+ }
+
+ @Override
+ public void onInsetsModified(InsetsState insetsState) {
+ try {
+ mController.insetsChanged(insetsState);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to send insets to controller");
+ }
+ }
+
+ @Override
+ public boolean hasAnimationCallbacks() {
+ return false;
+ }
+
+ @Override
+ public void setSystemBarsAppearance(
+ @WindowInsetsController.Appearance int appearance,
+ @WindowInsetsController.Appearance int mask) {
+ // no-op
+ }
+
+ @Override
+ public @WindowInsetsController.Appearance int getSystemBarsAppearance() {
+ return 0;
+ }
+
+ @Override
+ public void setSystemBarsBehavior(@WindowInsetsController.Behavior int behavior) {
+ // no-op
+ }
+
+ @Override
+ public @WindowInsetsController.Behavior int getSystemBarsBehavior() {
+ return 0;
+ }
+
+ @Override
+ public void releaseSurfaceControlFromRt(SurfaceControl surfaceControl) {
+ surfaceControl.release();
+ }
+
+ @Override
+ public void addOnPreDrawRunnable(Runnable r) {
+ mHandler.post(r);
+ }
+
+ @Override
+ public void postInsetsAnimationCallback(Runnable r) {
+ mHandler.post(r);
+ }
+
+ @Override
+ public InputMethodManager getInputMethodManager() {
+ return null;
+ }
+
+ @Override
+ public String getRootViewTitle() {
+ return null;
+ }
+
+ @Override
+ public int dipToPx(int dips) {
+ return 0;
+ }
+
+ @Override
+ public IBinder getWindowToken() {
+ return null;
+ }
+}
diff --git a/packages/CarSystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java b/packages/CarSystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java
index fe59cbf20a13..d769cacadf1d 100644
--- a/packages/CarSystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java
@@ -33,6 +33,7 @@ import androidx.test.internal.runner.ClassPathScanner.ExternalClassNameFilter;
import com.android.systemui.SysuiBaseFragmentTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.car.CarSystemUiTest;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -55,6 +56,7 @@ import java.util.Collections;
* test suite causes errors, such as the incorrect settings provider being cached.
* For an example, see {@link com.android.systemui.DependencyTest}.
*/
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@SmallTest
public class AAAPlusPlusVerifySysuiRequiredTestPropertiesTest extends SysuiTestCase {
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/HvacControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/HvacControllerTest.java
index 7996170ba7d6..e179ef1ce2a4 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/HvacControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/HvacControllerTest.java
@@ -32,6 +32,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.car.CarServiceProvider;
+import com.android.systemui.car.CarSystemUiTest;
import org.junit.Before;
import org.junit.Test;
@@ -39,6 +40,7 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java
index 189e240169c3..62dc23624520 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java
@@ -41,6 +41,7 @@ import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.car.CarServiceProvider;
+import com.android.systemui.car.CarSystemUiTest;
import com.android.systemui.car.navigationbar.CarNavigationBarController;
import com.android.systemui.car.window.OverlayViewGlobalStateController;
import com.android.systemui.keyguard.DismissCallbackRegistry;
@@ -59,6 +60,7 @@ import org.mockito.MockitoAnnotations;
import dagger.Lazy;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonRoleHolderControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonRoleHolderControllerTest.java
index a57736bb3502..4b8268052324 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonRoleHolderControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonRoleHolderControllerTest.java
@@ -39,6 +39,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.car.CarDeviceProvisionedController;
+import com.android.systemui.car.CarSystemUiTest;
import com.android.systemui.tests.R;
import org.junit.Before;
@@ -49,6 +50,7 @@ import org.mockito.MockitoAnnotations;
import java.util.List;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonSelectionStateControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonSelectionStateControllerTest.java
index 893057e222a9..f623c26d12b6 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonSelectionStateControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonSelectionStateControllerTest.java
@@ -28,6 +28,7 @@ import android.widget.LinearLayout;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.car.CarSystemUiTest;
import com.android.systemui.tests.R;
import org.junit.Before;
@@ -38,6 +39,7 @@ import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.List;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java
index e84e42c77245..dec8b8ecdfb4 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java
@@ -31,6 +31,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.car.CarSystemUiTest;
import com.android.systemui.car.hvac.HvacController;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -41,6 +42,7 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarTest.java
index 0caa86f0eab2..d9edfa960858 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarTest.java
@@ -45,6 +45,7 @@ import com.android.internal.view.AppearanceRegion;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.car.CarDeviceProvisionedController;
+import com.android.systemui.car.CarSystemUiTest;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.LightBarController;
@@ -63,6 +64,7 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarViewTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarViewTest.java
index 19e394f69af4..47fd8201d197 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarViewTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarViewTest.java
@@ -30,6 +30,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.car.CarSystemUiTest;
import org.junit.After;
import org.junit.Before;
@@ -38,6 +39,7 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java
index bcaa5e9a03ee..173f5487c728 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java
@@ -37,6 +37,7 @@ import android.widget.LinearLayout;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.car.CarSystemUiTest;
import com.android.systemui.statusbar.AlphaOptimizedImageView;
import com.android.systemui.tests.R;
@@ -45,6 +46,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatcher;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainerTest.java
index ccaeb458fe54..384888ab42c3 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainerTest.java
@@ -30,6 +30,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.car.CarDeviceProvisionedController;
+import com.android.systemui.car.CarSystemUiTest;
import com.android.systemui.car.window.OverlayViewGlobalStateController;
import org.junit.Before;
@@ -38,6 +39,7 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/NotificationVisibilityLoggerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/NotificationVisibilityLoggerTest.java
index 89dac58cd2a7..d51aeb18135d 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/NotificationVisibilityLoggerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/NotificationVisibilityLoggerTest.java
@@ -37,6 +37,7 @@ import com.android.car.notification.NotificationDataManager;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.car.CarSystemUiTest;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -49,6 +50,7 @@ import org.mockito.MockitoAnnotations;
import java.util.Collections;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppDetectorTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppDetectorTest.java
index 77620f3fb345..421e2109356d 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppDetectorTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppDetectorTest.java
@@ -37,6 +37,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.car.CarDeviceProvisionedController;
+import com.android.systemui.car.CarSystemUiTest;
import org.junit.Before;
import org.junit.Test;
@@ -44,6 +45,7 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppListenerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppListenerTest.java
index 73f9f6a55afc..20576e9ec11f 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppListenerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppListenerTest.java
@@ -35,6 +35,7 @@ import android.view.DisplayInfo;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.car.CarSystemUiTest;
import org.junit.Before;
import org.junit.Test;
@@ -46,6 +47,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java
index 797dbf515b7e..2e9d43b595a1 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java
@@ -36,6 +36,7 @@ import android.view.ViewGroup;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.car.CarSystemUiTest;
import com.android.systemui.car.window.OverlayViewGlobalStateController;
import org.junit.Before;
@@ -44,6 +45,7 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewMediatorTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewMediatorTest.java
index a808e2d40e26..de6feb64391f 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewMediatorTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewMediatorTest.java
@@ -25,6 +25,7 @@ import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import com.android.systemui.car.CarServiceProvider;
+import com.android.systemui.car.CarSystemUiTest;
import org.junit.Before;
import org.junit.Test;
@@ -32,6 +33,7 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java
index eca51e34995c..31f1170c9603 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java
@@ -24,6 +24,8 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadsetClient;
import android.content.Intent;
import android.os.Handler;
@@ -33,25 +35,32 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.car.CarSystemUiTest;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase {
private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
+ private static final String BLUETOOTH_REMOTE_ADDRESS = "00:11:22:33:44:55";
private ConnectedDeviceVoiceRecognitionNotifier mVoiceRecognitionNotifier;
+ private TestableLooper mTestableLooper;
private Handler mTestHandler;
+ private BluetoothDevice mBluetoothDevice;
@Before
public void setUp() throws Exception {
- TestableLooper testableLooper = TestableLooper.get(this);
- mTestHandler = spy(new Handler(testableLooper.getLooper()));
+ mTestableLooper = TestableLooper.get(this);
+ mTestHandler = spy(new Handler(mTestableLooper.getLooper()));
+ mBluetoothDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(
+ BLUETOOTH_REMOTE_ADDRESS);
mVoiceRecognitionNotifier = new ConnectedDeviceVoiceRecognitionNotifier(
mContext, mTestHandler);
mVoiceRecognitionNotifier.onBootCompleted();
@@ -61,8 +70,10 @@ public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase {
public void testReceiveIntent_started_showToast() {
Intent intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
intent.putExtra(BluetoothHeadsetClient.EXTRA_VOICE_RECOGNITION, VOICE_RECOGNITION_STARTED);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- waitForIdleSync();
+ mTestableLooper.processAllMessages();
verify(mTestHandler).post(any());
}
@@ -71,8 +82,10 @@ public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase {
public void testReceiveIntent_invalidExtra_noToast() {
Intent intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
intent.putExtra(BluetoothHeadsetClient.EXTRA_VOICE_RECOGNITION, INVALID_VALUE);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- waitForIdleSync();
+ mTestableLooper.processAllMessages();
verify(mTestHandler, never()).post(any());
}
@@ -80,8 +93,10 @@ public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase {
@Test
public void testReceiveIntent_noExtra_noToast() {
Intent intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- waitForIdleSync();
+ mTestableLooper.processAllMessages();
verify(mTestHandler, never()).post(any());
}
@@ -89,8 +104,10 @@ public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase {
@Test
public void testReceiveIntent_invalidIntent_noToast() {
Intent intent = new Intent(BluetoothHeadsetClient.ACTION_AUDIO_STATE_CHANGED);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- waitForIdleSync();
+ mTestableLooper.processAllMessages();
verify(mTestHandler, never()).post(any());
}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java
index 45a05ac69bd7..7311cdb68a3c 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java
@@ -39,6 +39,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.car.CarDeviceProvisionedController;
+import com.android.systemui.car.CarSystemUiTest;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.tests.R;
@@ -52,6 +53,7 @@ import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.List;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewControllerTest.java
index c24a3b52e348..e784761f6d5d 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewControllerTest.java
@@ -29,6 +29,7 @@ import android.view.ViewGroup;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.car.CarSystemUiTest;
import com.android.systemui.tests.R;
import org.junit.Before;
@@ -39,6 +40,7 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
index cba42e5a9be4..20f9bc8ec1cb 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
@@ -32,6 +32,7 @@ import android.view.ViewStub;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.car.CarSystemUiTest;
import com.android.systemui.car.navigationbar.CarNavigationBarController;
import com.android.systemui.tests.R;
@@ -44,6 +45,7 @@ import org.mockito.MockitoAnnotations;
import java.util.Arrays;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/wm/BarControlPolicyTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/wm/BarControlPolicyTest.java
new file mode 100644
index 000000000000..da7cb8e4f6ac
--- /dev/null
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/wm/BarControlPolicyTest.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2020 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.wm;
+
+import static android.view.WindowInsets.Type.navigationBars;
+import static android.view.WindowInsets.Type.statusBars;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.car.settings.CarSettings;
+import android.provider.Settings;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class BarControlPolicyTest extends SysuiTestCase {
+
+ private static final String PACKAGE_NAME = "sample.app";
+
+ @Before
+ public void setUp() {
+ BarControlPolicy.reset();
+ }
+
+ @After
+ public void tearDown() {
+ Settings.Global.clearProviderForTest();
+ }
+
+ @Test
+ public void reloadFromSetting_notSet_doesNotSetFilters() {
+ BarControlPolicy.reloadFromSetting(mContext);
+
+ assertThat(BarControlPolicy.sImmersiveStatusFilter).isNull();
+ }
+
+ @Test
+ public void reloadFromSetting_invalidPolicyControlString_doesNotSetFilters() {
+ String text = "sample text";
+ Settings.Global.putString(
+ mContext.getContentResolver(),
+ CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
+ text
+ );
+
+ BarControlPolicy.reloadFromSetting(mContext);
+
+ assertThat(BarControlPolicy.sImmersiveStatusFilter).isNull();
+ }
+
+ @Test
+ public void reloadFromSetting_validPolicyControlString_setsFilters() {
+ String text = "immersive.status=" + PACKAGE_NAME;
+ Settings.Global.putString(
+ mContext.getContentResolver(),
+ CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
+ text
+ );
+
+ BarControlPolicy.reloadFromSetting(mContext);
+
+ assertThat(BarControlPolicy.sImmersiveStatusFilter).isNotNull();
+ }
+
+ @Test
+ public void reloadFromSetting_filtersSet_doesNotSetFiltersAgain() {
+ String text = "immersive.status=" + PACKAGE_NAME;
+ Settings.Global.putString(
+ mContext.getContentResolver(),
+ CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
+ text
+ );
+
+ BarControlPolicy.reloadFromSetting(mContext);
+
+ assertThat(BarControlPolicy.reloadFromSetting(mContext)).isFalse();
+ }
+
+ @Test
+ public void getBarVisibilities_policyControlNotSet_showsSystemBars() {
+ int[] visibilities = BarControlPolicy.getBarVisibilities(PACKAGE_NAME);
+
+ assertThat(visibilities[0]).isEqualTo(statusBars() | navigationBars());
+ assertThat(visibilities[1]).isEqualTo(0);
+ }
+
+ @Test
+ public void getBarVisibilities_immersiveStatusForAppAndMatchingApp_hidesStatusBar() {
+ Settings.Global.putString(
+ mContext.getContentResolver(),
+ CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
+ "immersive.status=" + PACKAGE_NAME);
+ BarControlPolicy.reloadFromSetting(mContext);
+
+ int[] visibilities = BarControlPolicy.getBarVisibilities(PACKAGE_NAME);
+
+ assertThat(visibilities[0]).isEqualTo(navigationBars());
+ assertThat(visibilities[1]).isEqualTo(statusBars());
+ }
+
+ @Test
+ public void getBarVisibilities_immersiveStatusForAppAndNonMatchingApp_showsSystemBars() {
+ Settings.Global.putString(
+ mContext.getContentResolver(),
+ CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
+ "immersive.status=" + PACKAGE_NAME);
+ BarControlPolicy.reloadFromSetting(mContext);
+
+ int[] visibilities = BarControlPolicy.getBarVisibilities("sample2.app");
+
+ assertThat(visibilities[0]).isEqualTo(statusBars() | navigationBars());
+ assertThat(visibilities[1]).isEqualTo(0);
+ }
+
+ @Test
+ public void getBarVisibilities_immersiveStatusForAppsAndNonApp_showsSystemBars() {
+ Settings.Global.putString(
+ mContext.getContentResolver(),
+ CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
+ "immersive.status=apps");
+ BarControlPolicy.reloadFromSetting(mContext);
+
+ int[] visibilities = BarControlPolicy.getBarVisibilities(PACKAGE_NAME);
+
+ assertThat(visibilities[0]).isEqualTo(statusBars() | navigationBars());
+ assertThat(visibilities[1]).isEqualTo(0);
+ }
+
+ @Test
+ public void getBarVisibilities_immersiveFullForAppAndMatchingApp_hidesSystemBars() {
+ Settings.Global.putString(
+ mContext.getContentResolver(),
+ CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
+ "immersive.full=" + PACKAGE_NAME);
+ BarControlPolicy.reloadFromSetting(mContext);
+
+ int[] visibilities = BarControlPolicy.getBarVisibilities(PACKAGE_NAME);
+
+ assertThat(visibilities[0]).isEqualTo(0);
+ assertThat(visibilities[1]).isEqualTo(statusBars() | navigationBars());
+ }
+
+ @Test
+ public void getBarVisibilities_immersiveFullForAppAndNonMatchingApp_showsSystemBars() {
+ Settings.Global.putString(
+ mContext.getContentResolver(),
+ CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
+ "immersive.full=" + PACKAGE_NAME);
+ BarControlPolicy.reloadFromSetting(mContext);
+
+ int[] visibilities = BarControlPolicy.getBarVisibilities("sample2.app");
+
+ assertThat(visibilities[0]).isEqualTo(statusBars() | navigationBars());
+ assertThat(visibilities[1]).isEqualTo(0);
+ }
+
+ @Test
+ public void getBarVisibilities_immersiveFullForAppsAndNonApp_showsSystemBars() {
+ Settings.Global.putString(
+ mContext.getContentResolver(),
+ CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
+ "immersive.full=apps");
+ BarControlPolicy.reloadFromSetting(mContext);
+
+ int[] visibilities = BarControlPolicy.getBarVisibilities(PACKAGE_NAME);
+
+ assertThat(visibilities[0]).isEqualTo(statusBars() | navigationBars());
+ assertThat(visibilities[1]).isEqualTo(0);
+ }
+}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java
new file mode 100644
index 000000000000..29cc8eec4bc3
--- /dev/null
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2020 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.wm;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.verify;
+
+import android.car.settings.CarSettings;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.IWindowManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.TransactionPool;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class DisplaySystemBarsControllerTest extends SysuiTestCase {
+
+ private DisplaySystemBarsController mController;
+
+ private static final int DISPLAY_ID = 1;
+
+ @Mock
+ private SystemWindows mSystemWindows;
+ @Mock
+ private IWindowManager mIWindowManager;
+ @Mock
+ private DisplayController mDisplayController;
+ @Mock
+ private Handler mHandler;
+ @Mock
+ private TransactionPool mTransactionPool;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mSystemWindows.mContext = mContext;
+ mSystemWindows.mWmService = mIWindowManager;
+
+ mController = new DisplaySystemBarsController(
+ mSystemWindows,
+ mDisplayController,
+ mHandler,
+ mTransactionPool
+ );
+ }
+
+ @Test
+ public void onDisplayAdded_setsDisplayWindowInsetsControllerOnWMService()
+ throws RemoteException {
+ mController.onDisplayAdded(DISPLAY_ID);
+
+ verify(mIWindowManager).setDisplayWindowInsetsController(
+ eq(DISPLAY_ID), any(DisplaySystemBarsController.PerDisplay.class));
+ }
+
+ @Test
+ public void onDisplayAdded_loadsBarControlPolicyFilters() {
+ String text = "sample text";
+ Settings.Global.putString(
+ mContext.getContentResolver(),
+ CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
+ text
+ );
+
+ mController.onDisplayAdded(DISPLAY_ID);
+
+ assertThat(BarControlPolicy.sSettingValue).isEqualTo(text);
+ }
+
+ @Test
+ public void onDisplayRemoved_unsetsDisplayWindowInsetsControllerInWMService()
+ throws RemoteException {
+ mController.onDisplayAdded(DISPLAY_ID);
+
+ mController.onDisplayRemoved(DISPLAY_ID);
+
+ verify(mIWindowManager).setDisplayWindowInsetsController(
+ DISPLAY_ID, /* displayWindowInsetsController= */ null);
+ }
+}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index d50dc7cba52b..7f7afcbf11f5 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -317,6 +317,11 @@
<!-- Permissions required for CTS test - AdbManagerTest -->
<uses-permission android:name="android.permission.MANAGE_DEBUGGING" />
+ <!-- Permissions required for ATS tests - AtsCarHostTestCases, AtsCarDeviceApp -->
+ <uses-permission android:name="android.car.permission.CAR_DRIVING_STATE" />
+ <!-- Permissions required for ATS tests - AtsDeviceInfo, AtsAudioDeviceTestCases -->
+ <uses-permission android:name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME" />
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index 5c00af5705e9..436188a83d4f 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -62,7 +62,7 @@
android:gravity="center_vertical"
android:focusable="true"
android:singleLine="true"
- android:ellipsize="end"
+ android:ellipsize="marquee"
android:textAppearance="@style/TextAppearance.QS.Status"
android:layout_marginEnd="4dp"
android:visibility="gone"/>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 8212d6159737..a56f6f56836a 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -92,6 +92,8 @@
<item type="id" name="requires_remeasuring"/>
+ <item type="id" name="secondary_home_handle" />
+
<!-- Whether the icon is from a notification for which targetSdk < L -->
<item type="id" name="icon_is_pre_L"/>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index db45a60ab7c0..f51cffcdfb5c 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2404,17 +2404,26 @@
<!-- Title for notification & dialog that the user's phone last shut down because it got too hot. [CHAR LIMIT=40] -->
<string name="thermal_shutdown_title">Phone turned off due to heat</string>
- <!-- Message body for notification that user's phone last shut down because it got too hot. [CHAR LIMIT=100] -->
- <string name="thermal_shutdown_message">Your phone is now running normally</string>
- <!-- Text body for dialog alerting user that their phone last shut down because it got too hot. [CHAR LIMIT=450] -->
+ <!-- Message body for notification that user's phone last shut down because it got too hot. [CHAR LIMIT=120] -->
+ <string name="thermal_shutdown_message">Your phone is now running normally.\nTap for more info</string>
+ <!-- Text body for dialog alerting user that their phone last shut down because it got too hot. [CHAR LIMIT=500] -->
<string name="thermal_shutdown_dialog_message">Your phone was too hot, so it turned off to cool down. Your phone is now running normally.\n\nYour phone may get too hot if you:\n\t&#8226; Use resource-intensive apps (such as gaming, video, or navigation apps)\n\t&#8226; Download or upload large files\n\t&#8226; Use your phone in high temperatures</string>
+ <!-- Text help link for care instructions for overheating devices [CHAR LIMIT=40] -->
+ <string name="thermal_shutdown_dialog_help_text">See care steps</string>
+ <!-- URL for care instructions for overheating devices -->
+ <string name="thermal_shutdown_dialog_help_url" translatable="false"></string>
<!-- Title for notification (and dialog) that user's phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=30] -->
<string name="high_temp_title">Phone is getting warm</string>
- <!-- Message body for notification that user's phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=100] -->
- <string name="high_temp_notif_message">Some features limited while phone cools down</string>
- <!-- Text body for dialog alerting user that their phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=300] -->
+ <!-- Message body for notification that user's phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=120] -->
+ <string name="high_temp_notif_message">Some features limited while phone cools down.\nTap for more info</string>
+ <!-- Text body for dialog alerting user that their phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=350] -->
<string name="high_temp_dialog_message">Your phone will automatically try to cool down. You can still use your phone, but it may run slower.\n\nOnce your phone has cooled down, it will run normally.</string>
+ <!-- Text help link for care instructions for overheating devices [CHAR LIMIT=40] -->
+ <string name="high_temp_dialog_help_text">See care steps</string>
+ <!-- URL for care instructions for overheating devices -->
+ <string name="high_temp_dialog_help_url" translatable="false"></string>
+
<!-- Title for alarm dialog alerting user the usb adapter has reached a certain temperature that should disconnect charging cable immediately. [CHAR LIMIT=30] -->
<string name="high_temp_alarm_title">Unplug charger</string>
<!-- Text body for dialog alerting user the usb adapter has reached a certain temperature that should disconnect charging cable immediately. [CHAR LIMIT=300] -->
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 3acbfb87c3f4..c07c982b7451 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1072,6 +1072,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
}
+ private boolean isEncryptedOrLockdown(int userId) {
+ final int strongAuth = mStrongAuthTracker.getStrongAuthForUser(userId);
+ final boolean isLockDown =
+ containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW)
+ || containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+ final boolean isEncrypted = containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT);
+ return isEncrypted || isLockDown;
+ }
+
public boolean userNeedsStrongAuth() {
return mStrongAuthTracker.getStrongAuthForUser(getCurrentUser())
!= LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
@@ -1248,6 +1257,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
};
+ // Trigger the fingerprint success path so the bouncer can be shown
+ private final FingerprintManager.FingerprintDetectionCallback mFingerprintDetectionCallback
+ = this::handleFingerprintAuthenticated;
+
private FingerprintManager.AuthenticationCallback mFingerprintAuthenticationCallback
= new AuthenticationCallback() {
@@ -2050,8 +2063,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mFingerprintCancelSignal.cancel();
}
mFingerprintCancelSignal = new CancellationSignal();
- mFpm.authenticate(null, mFingerprintCancelSignal, 0, mFingerprintAuthenticationCallback,
- null, userId);
+
+ if (isEncryptedOrLockdown(userId)) {
+ mFpm.detectFingerprint(mFingerprintCancelSignal, mFingerprintDetectionCallback,
+ userId);
+ } else {
+ mFpm.authenticate(null, mFingerprintCancelSignal, 0,
+ mFingerprintAuthenticationCallback, null, userId);
+ }
+
setFingerprintRunningState(BIOMETRIC_STATE_RUNNING);
}
}
@@ -2087,7 +2107,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private boolean isUnlockWithFingerprintPossible(int userId) {
return mFpm != null && mFpm.isHardwareDetected() && !isFingerprintDisabled(userId)
- && mFpm.getEnrolledFingerprints(userId).size() > 0;
+ && mFpm.hasEnrolledTemplates(userId);
}
private boolean isUnlockWithFacePossible(int userId) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
index 40a93e1cdc47..d017bc0e31c2 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
@@ -24,7 +24,8 @@ import android.content.pm.ShortcutInfo;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
-import android.graphics.Rect;
+import android.graphics.Path;
+import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
@@ -41,15 +42,15 @@ import com.android.systemui.R;
*/
public class BubbleIconFactory extends BaseIconFactory {
+ private int mBadgeSize;
+
protected BubbleIconFactory(Context context) {
super(context, context.getResources().getConfiguration().densityDpi,
context.getResources().getDimensionPixelSize(R.dimen.individual_bubble_size));
- }
-
- int getBadgeSize() {
- return mContext.getResources().getDimensionPixelSize(
+ mBadgeSize = mContext.getResources().getDimensionPixelSize(
com.android.launcher3.icons.R.dimen.profile_badge_size);
}
+
/**
* Returns the drawable that the developer has provided to display in the bubble.
*/
@@ -79,25 +80,34 @@ public class BubbleIconFactory extends BaseIconFactory {
* will include the workprofile indicator on the badge if appropriate.
*/
BitmapInfo getBadgeBitmap(Drawable userBadgedAppIcon, boolean isImportantConversation) {
- Bitmap userBadgedBitmap = createIconBitmap(
- userBadgedAppIcon, 1f, getBadgeSize());
- ShadowGenerator shadowGenerator = new ShadowGenerator(getBadgeSize());
- if (!isImportantConversation) {
- Canvas c = new Canvas();
- c.setBitmap(userBadgedBitmap);
- shadowGenerator.recreateIcon(Bitmap.createBitmap(userBadgedBitmap), c);
- return createIconBitmap(userBadgedBitmap);
- } else {
- float ringStrokeWidth = mContext.getResources().getDimensionPixelSize(
+ ShadowGenerator shadowGenerator = new ShadowGenerator(mBadgeSize);
+ Bitmap userBadgedBitmap = createIconBitmap(userBadgedAppIcon, 1f, mBadgeSize);
+
+ if (userBadgedAppIcon instanceof AdaptiveIconDrawable) {
+ userBadgedBitmap = Bitmap.createScaledBitmap(
+ getCircleBitmap((AdaptiveIconDrawable) userBadgedAppIcon, /* size */
+ userBadgedAppIcon.getIntrinsicWidth()),
+ mBadgeSize, mBadgeSize, /* filter */ true);
+ }
+
+ if (isImportantConversation) {
+ final float ringStrokeWidth = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.importance_ring_stroke_width);
- int importantConversationColor = mContext.getResources().getColor(
+ final int importantConversationColor = mContext.getResources().getColor(
com.android.settingslib.R.color.important_conversation, null);
Bitmap badgeAndRing = Bitmap.createBitmap(userBadgedBitmap.getWidth(),
userBadgedBitmap.getHeight(), userBadgedBitmap.getConfig());
Canvas c = new Canvas(badgeAndRing);
- Rect dest = new Rect((int) ringStrokeWidth, (int) ringStrokeWidth,
- c.getHeight() - (int) ringStrokeWidth, c.getWidth() - (int) ringStrokeWidth);
- c.drawBitmap(userBadgedBitmap, null, dest, null);
+
+ final int bitmapTop = (int) ringStrokeWidth;
+ final int bitmapLeft = (int) ringStrokeWidth;
+ final int bitmapWidth = c.getWidth() - 2 * (int) ringStrokeWidth;
+ final int bitmapHeight = c.getHeight() - 2 * (int) ringStrokeWidth;
+
+ Bitmap scaledBitmap = Bitmap.createScaledBitmap(userBadgedBitmap, bitmapWidth,
+ bitmapHeight, /* filter */ true);
+ c.drawBitmap(scaledBitmap, bitmapTop, bitmapLeft, /* paint */null);
+
Paint ringPaint = new Paint();
ringPaint.setStyle(Paint.Style.STROKE);
ringPaint.setColor(importantConversationColor);
@@ -105,11 +115,48 @@ public class BubbleIconFactory extends BaseIconFactory {
ringPaint.setStrokeWidth(ringStrokeWidth);
c.drawCircle(c.getWidth() / 2, c.getHeight() / 2, c.getWidth() / 2 - ringStrokeWidth,
ringPaint);
+
shadowGenerator.recreateIcon(Bitmap.createBitmap(badgeAndRing), c);
return createIconBitmap(badgeAndRing);
+ } else {
+ Canvas c = new Canvas();
+ c.setBitmap(userBadgedBitmap);
+ shadowGenerator.recreateIcon(Bitmap.createBitmap(userBadgedBitmap), c);
+ return createIconBitmap(userBadgedBitmap);
}
}
+ public Bitmap getCircleBitmap(AdaptiveIconDrawable icon, int size) {
+ Drawable foreground = icon.getForeground();
+ Drawable background = icon.getBackground();
+ Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas();
+ canvas.setBitmap(bitmap);
+
+ // Clip canvas to circle.
+ Path circlePath = new Path();
+ circlePath.addCircle(/* x */ size / 2f,
+ /* y */ size / 2f,
+ /* radius */ size / 2f,
+ Path.Direction.CW);
+ canvas.clipPath(circlePath);
+
+ // Draw background.
+ background.setBounds(0, 0, size, size);
+ background.draw(canvas);
+
+ // Draw foreground. The foreground and background drawables are derived from adaptive icons
+ // Some icon shapes fill more space than others, so adaptive icons are normalized to about
+ // the same size. This size is smaller than the original bounds, so we estimate
+ // the difference in this offset.
+ int offset = size / 5;
+ foreground.setBounds(-offset, -offset, size + offset, size + offset);
+ foreground.draw(canvas);
+
+ canvas.setBitmap(null);
+ return bitmap;
+ }
+
/**
* Returns a {@link BitmapInfo} for the entire bubble icon including the badge.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 749b537ea364..c61a36c12059 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -1586,6 +1586,11 @@ public class BubbleStackView extends FrameLayout
Log.d(TAG, "setSelectedBubble: " + bubbleToSelect);
}
+ if (bubbleToSelect == null) {
+ mBubbleData.setShowingOverflow(false);
+ return;
+ }
+
// Ignore this new bubble only if it is the exact same bubble object. Otherwise, we'll want
// to re-render it even if it has the same key (equals() returns true). If the currently
// expanded bubble is removed and instantly re-added, we'll get back a new Bubble instance
@@ -1594,10 +1599,11 @@ public class BubbleStackView extends FrameLayout
if (mExpandedBubble == bubbleToSelect) {
return;
}
- if (bubbleToSelect == null || bubbleToSelect.getKey() != BubbleOverflow.KEY) {
- mBubbleData.setShowingOverflow(false);
- } else {
+
+ if (bubbleToSelect.getKey() == BubbleOverflow.KEY) {
mBubbleData.setShowingOverflow(true);
+ } else {
+ mBubbleData.setShowingOverflow(false);
}
if (mIsExpanded && mIsExpansionAnimating) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
index e8f0e069c98d..d0642ccf9714 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
@@ -33,23 +33,31 @@ class MediaDataCombineLatest @Inject constructor(
init {
dataSource.addListener(object : MediaDataManager.Listener {
override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {
- if (oldKey != null && !oldKey.equals(key)) {
- val s = entries[oldKey]?.second
- entries[key] = data to entries[oldKey]?.second
- entries.remove(oldKey)
+ if (oldKey != null && oldKey != key && entries.contains(oldKey)) {
+ entries[key] = data to entries.remove(oldKey)?.second
+ update(key, oldKey)
} else {
entries[key] = data to entries[key]?.second
+ update(key, key)
}
- update(key, oldKey)
}
override fun onMediaDataRemoved(key: String) {
remove(key)
}
})
deviceSource.addListener(object : MediaDeviceManager.Listener {
- override fun onMediaDeviceChanged(key: String, data: MediaDeviceData?) {
- entries[key] = entries[key]?.first to data
- update(key, key)
+ override fun onMediaDeviceChanged(
+ key: String,
+ oldKey: String?,
+ data: MediaDeviceData?
+ ) {
+ if (oldKey != null && oldKey != key && entries.contains(oldKey)) {
+ entries[key] = entries.remove(oldKey)?.first to data
+ update(key, oldKey)
+ } else {
+ entries[key] = entries[key]?.first to data
+ update(key, key)
+ }
}
override fun onKeyRemoved(key: String) {
remove(key)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
index 7ae2dc5c0941..143f8496e7aa 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
@@ -71,7 +71,8 @@ class MediaDeviceManager @Inject constructor(
val controller = data.token?.let {
MediaController(context, it)
}
- entry = Token(key, controller, localMediaManagerFactory.create(data.packageName))
+ entry = Token(key, oldKey, controller,
+ localMediaManagerFactory.create(data.packageName))
entries[key] = entry
entry.start()
}
@@ -98,23 +99,24 @@ class MediaDeviceManager @Inject constructor(
}
}
- private fun processDevice(key: String, device: MediaDevice?) {
+ private fun processDevice(key: String, oldKey: String?, device: MediaDevice?) {
val enabled = device != null
val data = MediaDeviceData(enabled, device?.iconWithoutBackground, device?.name)
listeners.forEach {
- it.onMediaDeviceChanged(key, data)
+ it.onMediaDeviceChanged(key, oldKey, data)
}
}
interface Listener {
/** Called when the route has changed for a given notification. */
- fun onMediaDeviceChanged(key: String, data: MediaDeviceData?)
+ fun onMediaDeviceChanged(key: String, oldKey: String?, data: MediaDeviceData?)
/** Called when the notification was removed. */
fun onKeyRemoved(key: String)
}
private inner class Token(
val key: String,
+ val oldKey: String?,
val controller: MediaController?,
val localMediaManager: LocalMediaManager
) : LocalMediaManager.DeviceCallback {
@@ -125,7 +127,7 @@ class MediaDeviceManager @Inject constructor(
set(value) {
if (!started || value != field) {
field = value
- processDevice(key, value)
+ processDevice(key, oldKey, value)
}
}
fun start() {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
index ead17867844a..72019315139b 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
@@ -21,7 +21,6 @@ import android.animation.Animator;
import android.animation.RectEvaluator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
-import android.content.Context;
import android.graphics.Rect;
import android.view.SurfaceControl;
@@ -56,13 +55,15 @@ public class PipAnimationController {
public static final int TRANSITION_DIRECTION_TO_PIP = 2;
public static final int TRANSITION_DIRECTION_TO_FULLSCREEN = 3;
public static final int TRANSITION_DIRECTION_TO_SPLIT_SCREEN = 4;
+ public static final int TRANSITION_DIRECTION_REMOVE_STACK = 5;
@IntDef(prefix = { "TRANSITION_DIRECTION_" }, value = {
TRANSITION_DIRECTION_NONE,
TRANSITION_DIRECTION_SAME,
TRANSITION_DIRECTION_TO_PIP,
TRANSITION_DIRECTION_TO_FULLSCREEN,
- TRANSITION_DIRECTION_TO_SPLIT_SCREEN
+ TRANSITION_DIRECTION_TO_SPLIT_SCREEN,
+ TRANSITION_DIRECTION_REMOVE_STACK
})
@Retention(RetentionPolicy.SOURCE)
public @interface TransitionDirection {}
@@ -88,7 +89,7 @@ public class PipAnimationController {
});
@Inject
- PipAnimationController(Context context, PipSurfaceTransactionHelper helper) {
+ PipAnimationController(PipSurfaceTransactionHelper helper) {
mSurfaceTransactionHelper = helper;
}
@@ -338,6 +339,10 @@ public class PipAnimationController {
@Override
void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
+ if (getTransitionDirection() == TRANSITION_DIRECTION_REMOVE_STACK) {
+ // while removing the pip stack, no extra work needs to be done here.
+ return;
+ }
getSurfaceTransactionHelper()
.resetScale(tx, leash, getDestinationBounds())
.crop(tx, leash, getDestinationBounds())
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index 0141dee04086..c4152fa9a4e7 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -24,6 +24,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_ALPHA;
import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_NONE;
+import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_REMOVE_STACK;
import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_SAME;
import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_FULLSCREEN;
import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
@@ -337,23 +338,32 @@ public class PipTaskOrganizer extends TaskOrganizer implements
+ " mInPip=" + mInPip + " mExitingPip=" + mExitingPip + " mToken=" + mToken);
return;
}
- getUpdateHandler().post(() -> {
- try {
- // Reset the task bounds first to ensure the activity configuration is reset as well
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.setBounds(mToken, null);
- WindowOrganizer.applyTransaction(wct);
-
- ActivityTaskManager.getService().removeStacksInWindowingModes(
- new int[]{ WINDOWING_MODE_PINNED });
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to remove PiP", e);
- }
- });
+
+ // removePipImmediately is expected when the following animation finishes.
+ mUpdateHandler.post(() -> mPipAnimationController
+ .getAnimator(mLeash, mLastReportedBounds, 1f, 0f)
+ .setTransitionDirection(TRANSITION_DIRECTION_REMOVE_STACK)
+ .setPipAnimationCallback(mPipAnimationCallback)
+ .setDuration(mEnterExitAnimationDuration)
+ .start());
mInitialState.remove(mToken.asBinder());
mExitingPip = true;
}
+ private void removePipImmediately() {
+ try {
+ // Reset the task bounds first to ensure the activity configuration is reset as well
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setBounds(mToken, null);
+ WindowOrganizer.applyTransaction(wct);
+
+ ActivityTaskManager.getService().removeStacksInWindowingModes(
+ new int[]{ WINDOWING_MODE_PINNED });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to remove PiP", e);
+ }
+ }
+
@Override
public void onTaskAppeared(ActivityManager.RunningTaskInfo info, SurfaceControl leash) {
Objects.requireNonNull(info, "Requires RunningTaskInfo");
@@ -803,7 +813,10 @@ public class PipTaskOrganizer extends TaskOrganizer implements
+ "directly");
}
mLastReportedBounds.set(destinationBounds);
- if (isInPipDirection(direction) && type == ANIM_TYPE_ALPHA) {
+ if (direction == TRANSITION_DIRECTION_REMOVE_STACK) {
+ removePipImmediately();
+ return;
+ } else if (isInPipDirection(direction) && type == ANIM_TYPE_ALPHA) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index 10b04c0129e4..6abbbbeaa397 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -24,6 +24,7 @@ import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioAttributes;
@@ -376,13 +377,15 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
return;
}
mHighTempWarning = true;
+ final String message = mContext.getString(R.string.high_temp_notif_message);
final Notification.Builder nb =
new Notification.Builder(mContext, NotificationChannels.ALERTS)
.setSmallIcon(R.drawable.ic_device_thermostat_24)
.setWhen(0)
.setShowWhen(false)
.setContentTitle(mContext.getString(R.string.high_temp_title))
- .setContentText(mContext.getString(R.string.high_temp_notif_message))
+ .setContentText(message)
+ .setStyle(new Notification.BigTextStyle().bigText(message))
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setContentIntent(pendingBroadcast(ACTION_CLICKED_TEMP_WARNING))
.setDeleteIntent(pendingBroadcast(ACTION_DISMISSED_TEMP_WARNING))
@@ -402,6 +405,23 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
d.setPositiveButton(com.android.internal.R.string.ok, null);
d.setShowForAllUsers(true);
d.setOnDismissListener(dialog -> mHighTempDialog = null);
+ final String url = mContext.getString(R.string.high_temp_dialog_help_url);
+ if (!url.isEmpty()) {
+ d.setNeutralButton(R.string.high_temp_dialog_help_text,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ final Intent helpIntent =
+ new Intent(Intent.ACTION_VIEW)
+ .setData(Uri.parse(url))
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ Dependency.get(ActivityStarter.class).startActivity(helpIntent,
+ true /* dismissShade */, resultCode -> {
+ mHighTempDialog = null;
+ });
+ }
+ });
+ }
d.show();
mHighTempDialog = d;
}
@@ -420,19 +440,38 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
d.setPositiveButton(com.android.internal.R.string.ok, null);
d.setShowForAllUsers(true);
d.setOnDismissListener(dialog -> mThermalShutdownDialog = null);
+ final String url = mContext.getString(R.string.thermal_shutdown_dialog_help_url);
+ if (!url.isEmpty()) {
+ d.setNeutralButton(R.string.thermal_shutdown_dialog_help_text,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ final Intent helpIntent =
+ new Intent(Intent.ACTION_VIEW)
+ .setData(Uri.parse(url))
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ Dependency.get(ActivityStarter.class).startActivity(helpIntent,
+ true /* dismissShade */, resultCode -> {
+ mThermalShutdownDialog = null;
+ });
+ }
+ });
+ }
d.show();
mThermalShutdownDialog = d;
}
@Override
public void showThermalShutdownWarning() {
+ final String message = mContext.getString(R.string.thermal_shutdown_message);
final Notification.Builder nb =
new Notification.Builder(mContext, NotificationChannels.ALERTS)
.setSmallIcon(R.drawable.ic_device_thermostat_24)
.setWhen(0)
.setShowWhen(false)
.setContentTitle(mContext.getString(R.string.thermal_shutdown_title))
- .setContentText(mContext.getString(R.string.thermal_shutdown_message))
+ .setContentText(message)
+ .setStyle(new Notification.BigTextStyle().bigText(message))
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setContentIntent(pendingBroadcast(ACTION_CLICKED_THERMAL_SHUTDOWN_WARNING))
.setDeleteIntent(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index c4bb4e86e41e..6e4ab9a3323a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -78,6 +78,8 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
private SettingsButton mSettingsButton;
protected View mSettingsContainer;
private PageIndicator mPageIndicator;
+ private TextView mBuildText;
+ private boolean mShouldShowBuildText;
private boolean mQsDisabled;
private QSPanel mQsPanel;
@@ -147,6 +149,7 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
mActionsContainer = findViewById(R.id.qs_footer_actions_container);
mEditContainer = findViewById(R.id.qs_footer_actions_edit_container);
+ mBuildText = findViewById(R.id.build);
// RenderThread is doing more harm than good when touching the header (to expand quick
// settings), so disable it for this view
@@ -162,16 +165,19 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
}
private void setBuildText() {
- TextView v = findViewById(R.id.build);
- if (v == null) return;
+ if (mBuildText == null) return;
if (DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)) {
- v.setText(mContext.getString(
+ mBuildText.setText(mContext.getString(
com.android.internal.R.string.bugreport_status,
Build.VERSION.RELEASE_OR_CODENAME,
Build.ID));
- v.setVisibility(View.VISIBLE);
+ // Set as selected for marquee before its made visible, then it won't be announced when
+ // it's made visible.
+ mBuildText.setSelected(true);
+ mShouldShowBuildText = true;
} else {
- v.setVisibility(View.GONE);
+ mShouldShowBuildText = false;
+ mBuildText.setSelected(false);
}
}
@@ -321,6 +327,8 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
mMultiUserSwitch.setVisibility(showUserSwitcher() ? View.VISIBLE : View.INVISIBLE);
mEditContainer.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
mSettingsButton.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
+
+ mBuildText.setVisibility(mExpanded && mShouldShowBuildText ? View.VISIBLE : View.GONE);
}
private boolean showUserSwitcher() {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index d6e1a16bc69e..85444303490d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -214,9 +214,9 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
private Animator mScreenshotAnimation;
private Runnable mOnCompleteRunnable;
private Animator mDismissAnimation;
- private boolean mInDarkMode = false;
- private boolean mDirectionLTR = true;
- private boolean mOrientationPortrait = true;
+ private boolean mInDarkMode;
+ private boolean mDirectionLTR;
+ private boolean mOrientationPortrait;
private float mCornerSizeX;
private float mDismissDeltaY;
@@ -245,9 +245,6 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
}
};
- /**
- * @param context everything needs a context :(
- */
@Inject
public GlobalScreenshot(
Context context, @Main Resources resources,
@@ -320,6 +317,104 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
inoutInfo.touchableRegion.set(touchRegion);
}
+ void takeScreenshotFullscreen(Consumer<Uri> finisher, Runnable onComplete) {
+ mOnCompleteRunnable = onComplete;
+
+ mDisplay.getRealMetrics(mDisplayMetrics);
+ takeScreenshotInternal(
+ finisher,
+ new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
+ }
+
+ void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
+ Insets visibleInsets, int taskId, int userId, ComponentName topComponent,
+ Consumer<Uri> finisher, Runnable onComplete) {
+ // TODO: use task Id, userId, topComponent for smart handler
+
+ mOnCompleteRunnable = onComplete;
+ if (aspectRatiosMatch(screenshot, visibleInsets, screenshotScreenBounds)) {
+ saveScreenshot(screenshot, finisher, screenshotScreenBounds, visibleInsets, false);
+ } else {
+ saveScreenshot(screenshot, finisher,
+ new Rect(0, 0, screenshot.getWidth(), screenshot.getHeight()), Insets.NONE,
+ true);
+ }
+ }
+
+ /**
+ * Displays a screenshot selector
+ */
+ @SuppressLint("ClickableViewAccessibility")
+ void takeScreenshotPartial(final Consumer<Uri> finisher, Runnable onComplete) {
+ dismissScreenshot("new screenshot requested", true);
+ mOnCompleteRunnable = onComplete;
+
+ mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
+ mScreenshotSelectorView.setOnTouchListener((v, event) -> {
+ ScreenshotSelectorView view = (ScreenshotSelectorView) v;
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ view.startSelection((int) event.getX(), (int) event.getY());
+ return true;
+ case MotionEvent.ACTION_MOVE:
+ view.updateSelection((int) event.getX(), (int) event.getY());
+ return true;
+ case MotionEvent.ACTION_UP:
+ view.setVisibility(View.GONE);
+ mWindowManager.removeView(mScreenshotLayout);
+ final Rect rect = view.getSelectionRect();
+ if (rect != null) {
+ if (rect.width() != 0 && rect.height() != 0) {
+ // Need mScreenshotLayout to handle it after the view disappears
+ mScreenshotLayout.post(() -> takeScreenshotInternal(finisher, rect));
+ }
+ }
+
+ view.stopSelection();
+ return true;
+ }
+
+ return false;
+ });
+ mScreenshotLayout.post(() -> {
+ mScreenshotSelectorView.setVisibility(View.VISIBLE);
+ mScreenshotSelectorView.requestFocus();
+ });
+ }
+
+ /**
+ * Cancels screenshot request
+ */
+ void stopScreenshot() {
+ // If the selector layer still presents on screen, we remove it and resets its state.
+ if (mScreenshotSelectorView.getSelectionRect() != null) {
+ mWindowManager.removeView(mScreenshotLayout);
+ mScreenshotSelectorView.stopSelection();
+ }
+ }
+
+ /**
+ * Clears current screenshot
+ */
+ void dismissScreenshot(String reason, boolean immediate) {
+ Log.v(TAG, "clearing screenshot: " + reason);
+ mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
+ mScreenshotLayout.getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
+ if (!immediate) {
+ mDismissAnimation = createScreenshotDismissAnimation();
+ mDismissAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ clearScreenshot();
+ }
+ });
+ mDismissAnimation.start();
+ } else {
+ clearScreenshot();
+ }
+ }
+
private void onConfigChanged(Configuration newConfig) {
boolean needsUpdate = false;
// dark mode
@@ -408,15 +503,12 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
}
return mScreenshotLayout.onApplyWindowInsets(insets);
});
- mScreenshotLayout.setOnKeyListener(new View.OnKeyListener() {
- @Override
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK) {
- dismissScreenshot("back pressed", true);
- return true;
- }
- return false;
+ mScreenshotLayout.setOnKeyListener((v, keyCode, event) -> {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ dismissScreenshot("back pressed", true);
+ return true;
}
+ return false;
});
// Get focus so that the key events go to the layout.
mScreenshotLayout.setFocusableInTouchMode(true);
@@ -471,59 +563,19 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
}
/**
- * Updates the window focusability. If the window is already showing, then it updates the
- * window immediately, otherwise the layout params will be applied when the window is next
- * shown.
- */
- private void setWindowFocusable(boolean focusable) {
- if (focusable) {
- mWindowLayoutParams.flags &= ~FLAG_NOT_FOCUSABLE;
- } else {
- mWindowLayoutParams.flags |= FLAG_NOT_FOCUSABLE;
- }
- if (mScreenshotLayout.isAttachedToWindow()) {
- mWindowManager.updateViewLayout(mScreenshotLayout, mWindowLayoutParams);
- }
- }
-
- /**
- * Creates a new worker thread and saves the screenshot to the media store.
- */
- private void saveScreenshotInWorkerThread(
- Consumer<Uri> finisher, @Nullable ActionsReadyListener actionsReadyListener) {
- SaveImageInBackgroundData data = new SaveImageInBackgroundData();
- data.image = mScreenBitmap;
- data.finisher = finisher;
- data.mActionsReadyListener = actionsReadyListener;
-
- if (mSaveInBgTask != null) {
- // just log success/failure for the pre-existing screenshot
- mSaveInBgTask.setActionsReadyListener(new ActionsReadyListener() {
- @Override
- void onActionsReady(SavedImageData imageData) {
- logSuccessOnActionsReady(imageData);
- }
- });
- }
-
- mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data);
- mSaveInBgTask.execute();
- }
-
- /**
* Takes a screenshot of the current display and shows an animation.
*/
- private void takeScreenshot(Consumer<Uri> finisher, Rect crop) {
+ private void takeScreenshotInternal(Consumer<Uri> finisher, Rect crop) {
// copy the input Rect, since SurfaceControl.screenshot can mutate it
Rect screenRect = new Rect(crop);
int rot = mDisplay.getRotation();
int width = crop.width();
int height = crop.height();
- takeScreenshot(SurfaceControl.screenshot(crop, width, height, rot), finisher, screenRect,
+ saveScreenshot(SurfaceControl.screenshot(crop, width, height, rot), finisher, screenRect,
Insets.NONE, true);
}
- private void takeScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
+ private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
Insets screenInsets, boolean showFlash) {
dismissScreenshot("new screenshot requested", true);
@@ -561,85 +613,6 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
startAnimation(finisher, screenRect, screenInsets, showFlash);
}
- void takeScreenshot(Consumer<Uri> finisher, Runnable onComplete) {
- mOnCompleteRunnable = onComplete;
-
- mDisplay.getRealMetrics(mDisplayMetrics);
- takeScreenshot(
- finisher,
- new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
- }
-
- void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
- Insets visibleInsets, int taskId, int userId, ComponentName topComponent,
- Consumer<Uri> finisher, Runnable onComplete) {
- // TODO: use task Id, userId, topComponent for smart handler
-
- mOnCompleteRunnable = onComplete;
- if (aspectRatiosMatch(screenshot, visibleInsets, screenshotScreenBounds)) {
- takeScreenshot(screenshot, finisher, screenshotScreenBounds, visibleInsets, false);
- } else {
- takeScreenshot(screenshot, finisher,
- new Rect(0, 0, screenshot.getWidth(), screenshot.getHeight()), Insets.NONE,
- true);
- }
- }
-
- /**
- * Displays a screenshot selector
- */
- @SuppressLint("ClickableViewAccessibility")
- void takeScreenshotPartial(final Consumer<Uri> finisher, Runnable onComplete) {
- dismissScreenshot("new screenshot requested", true);
- mOnCompleteRunnable = onComplete;
-
- mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
- mScreenshotSelectorView.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- ScreenshotSelectorView view = (ScreenshotSelectorView) v;
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- view.startSelection((int) event.getX(), (int) event.getY());
- return true;
- case MotionEvent.ACTION_MOVE:
- view.updateSelection((int) event.getX(), (int) event.getY());
- return true;
- case MotionEvent.ACTION_UP:
- view.setVisibility(View.GONE);
- mWindowManager.removeView(mScreenshotLayout);
- final Rect rect = view.getSelectionRect();
- if (rect != null) {
- if (rect.width() != 0 && rect.height() != 0) {
- // Need mScreenshotLayout to handle it after the view disappears
- mScreenshotLayout.post(() -> takeScreenshot(finisher, rect));
- }
- }
-
- view.stopSelection();
- return true;
- }
-
- return false;
- }
- });
- mScreenshotLayout.post(() -> {
- mScreenshotSelectorView.setVisibility(View.VISIBLE);
- mScreenshotSelectorView.requestFocus();
- });
- }
-
- /**
- * Cancels screenshot request
- */
- void stopScreenshot() {
- // If the selector layer still presents on screen, we remove it and resets its state.
- if (mScreenshotSelectorView.getSelectionRect() != null) {
- mWindowManager.removeView(mScreenshotLayout);
- mScreenshotSelectorView.stopSelection();
- }
- }
-
/**
* Save the bitmap but don't show the normal screenshot UI.. just a toast (or notification on
* failure).
@@ -670,55 +643,78 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
});
}
- private boolean isUserSetupComplete() {
- return Settings.Secure.getInt(mContext.getContentResolver(),
- SETTINGS_SECURE_USER_SETUP_COMPLETE, 0) == 1;
+ /**
+ * Starts the animation after taking the screenshot
+ */
+ private void startAnimation(final Consumer<Uri> finisher, Rect screenRect, Insets screenInsets,
+ boolean showFlash) {
+
+ // If power save is on, show a toast so there is some visual indication that a
+ // screenshot has been taken.
+ PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ if (powerManager.isPowerSaveMode()) {
+ Toast.makeText(mContext, R.string.screenshot_saved_title, Toast.LENGTH_SHORT).show();
+ }
+
+ mScreenshotHandler.post(() -> {
+ if (!mScreenshotLayout.isAttachedToWindow()) {
+ mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
+ }
+ mScreenshotAnimatedView.setImageDrawable(
+ createScreenDrawable(mScreenBitmap, screenInsets));
+ setAnimatedViewSize(screenRect.width(), screenRect.height());
+ // Show when the animation starts
+ mScreenshotAnimatedView.setVisibility(View.GONE);
+
+ mScreenshotPreview.setImageDrawable(createScreenDrawable(mScreenBitmap, screenInsets));
+ // make static preview invisible (from gone) so we can query its location on screen
+ mScreenshotPreview.setVisibility(View.INVISIBLE);
+
+ mScreenshotHandler.post(() -> {
+ mScreenshotLayout.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
+
+ mScreenshotAnimation =
+ createScreenshotDropInAnimation(screenRect, showFlash);
+
+ saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() {
+ @Override
+ void onActionsReady(SavedImageData imageData) {
+ showUiOnActionsReady(imageData);
+ }
+ });
+
+ // Play the shutter sound to notify that we've taken a screenshot
+ mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
+
+ mScreenshotPreview.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ mScreenshotPreview.buildLayer();
+ mScreenshotAnimation.start();
+ });
+ });
}
/**
- * Clears current screenshot
+ * Creates a new worker thread and saves the screenshot to the media store.
*/
- void dismissScreenshot(String reason, boolean immediate) {
- Log.v(TAG, "clearing screenshot: " + reason);
- mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
- mScreenshotLayout.getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
- if (!immediate) {
- mDismissAnimation = createScreenshotDismissAnimation();
- mDismissAnimation.addListener(new AnimatorListenerAdapter() {
+ private void saveScreenshotInWorkerThread(
+ Consumer<Uri> finisher, @Nullable ActionsReadyListener actionsReadyListener) {
+ SaveImageInBackgroundData data = new SaveImageInBackgroundData();
+ data.image = mScreenBitmap;
+ data.finisher = finisher;
+ data.mActionsReadyListener = actionsReadyListener;
+
+ if (mSaveInBgTask != null) {
+ // just log success/failure for the pre-existing screenshot
+ mSaveInBgTask.setActionsReadyListener(new ActionsReadyListener() {
@Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- clearScreenshot();
+ void onActionsReady(SavedImageData imageData) {
+ logSuccessOnActionsReady(imageData);
}
});
- mDismissAnimation.start();
- } else {
- clearScreenshot();
}
- }
- private void clearScreenshot() {
- if (mScreenshotLayout.isAttachedToWindow()) {
- mWindowManager.removeView(mScreenshotLayout);
- }
-
- // Clear any references to the bitmap
- mScreenshotPreview.setImageDrawable(null);
- mScreenshotAnimatedView.setImageDrawable(null);
- mScreenshotAnimatedView.setVisibility(View.GONE);
- mActionsContainerBackground.setVisibility(View.GONE);
- mActionsContainer.setVisibility(View.GONE);
- mBackgroundProtection.setAlpha(0f);
- mDismissButton.setVisibility(View.GONE);
- mScreenshotPreview.setVisibility(View.GONE);
- mScreenshotPreview.setLayerType(View.LAYER_TYPE_NONE, null);
- mScreenshotPreview.setContentDescription(
- mContext.getResources().getString(R.string.screenshot_preview_description));
- mScreenshotLayout.setAlpha(1);
- mDismissButton.setTranslationY(0);
- mActionsContainer.setTranslationY(0);
- mActionsContainerBackground.setTranslationY(0);
- mScreenshotPreview.setTranslationY(0);
+ mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data);
+ mSaveInBgTask.execute();
}
/**
@@ -768,56 +764,6 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
}
}
- /**
- * Starts the animation after taking the screenshot
- */
- private void startAnimation(final Consumer<Uri> finisher, Rect screenRect, Insets screenInsets,
- boolean showFlash) {
-
- // If power save is on, show a toast so there is some visual indication that a
- // screenshot has been taken.
- PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- if (powerManager.isPowerSaveMode()) {
- Toast.makeText(mContext, R.string.screenshot_saved_title, Toast.LENGTH_SHORT).show();
- }
-
- mScreenshotHandler.post(() -> {
- if (!mScreenshotLayout.isAttachedToWindow()) {
- mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
- }
- mScreenshotAnimatedView.setImageDrawable(
- createScreenDrawable(mScreenBitmap, screenInsets));
- setAnimatedViewSize(screenRect.width(), screenRect.height());
- // Show when the animation starts
- mScreenshotAnimatedView.setVisibility(View.GONE);
-
- mScreenshotPreview.setImageDrawable(createScreenDrawable(mScreenBitmap, screenInsets));
- // make static preview invisible (from gone) so we can query its location on screen
- mScreenshotPreview.setVisibility(View.INVISIBLE);
-
- mScreenshotHandler.post(() -> {
- mScreenshotLayout.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
-
- mScreenshotAnimation =
- createScreenshotDropInAnimation(screenRect, showFlash);
-
- saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() {
- @Override
- void onActionsReady(SavedImageData imageData) {
- showUiOnActionsReady(imageData);
- }
- });
-
- // Play the shutter sound to notify that we've taken a screenshot
- mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
-
- mScreenshotPreview.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- mScreenshotPreview.buildLayer();
- mScreenshotAnimation.start();
- });
- });
- }
-
private AnimatorSet createScreenshotDropInAnimation(Rect bounds, boolean showFlash) {
Rect previewBounds = new Rect();
mScreenshotPreview.getBoundsOnScreen(previewBounds);
@@ -1070,6 +1016,30 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
return animSet;
}
+ private void clearScreenshot() {
+ if (mScreenshotLayout.isAttachedToWindow()) {
+ mWindowManager.removeView(mScreenshotLayout);
+ }
+
+ // Clear any references to the bitmap
+ mScreenshotPreview.setImageDrawable(null);
+ mScreenshotAnimatedView.setImageDrawable(null);
+ mScreenshotAnimatedView.setVisibility(View.GONE);
+ mActionsContainerBackground.setVisibility(View.GONE);
+ mActionsContainer.setVisibility(View.GONE);
+ mBackgroundProtection.setAlpha(0f);
+ mDismissButton.setVisibility(View.GONE);
+ mScreenshotPreview.setVisibility(View.GONE);
+ mScreenshotPreview.setLayerType(View.LAYER_TYPE_NONE, null);
+ mScreenshotPreview.setContentDescription(
+ mContext.getResources().getString(R.string.screenshot_preview_description));
+ mScreenshotLayout.setAlpha(1);
+ mDismissButton.setTranslationY(0);
+ mActionsContainer.setTranslationY(0);
+ mActionsContainerBackground.setTranslationY(0);
+ mScreenshotPreview.setTranslationY(0);
+ }
+
private void setAnimatedViewSize(int width, int height) {
ViewGroup.LayoutParams layoutParams = mScreenshotAnimatedView.getLayoutParams();
layoutParams.width = width;
@@ -1077,6 +1047,27 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
mScreenshotAnimatedView.setLayoutParams(layoutParams);
}
+ /**
+ * Updates the window focusability. If the window is already showing, then it updates the
+ * window immediately, otherwise the layout params will be applied when the window is next
+ * shown.
+ */
+ private void setWindowFocusable(boolean focusable) {
+ if (focusable) {
+ mWindowLayoutParams.flags &= ~FLAG_NOT_FOCUSABLE;
+ } else {
+ mWindowLayoutParams.flags |= FLAG_NOT_FOCUSABLE;
+ }
+ if (mScreenshotLayout.isAttachedToWindow()) {
+ mWindowManager.updateViewLayout(mScreenshotLayout, mWindowLayoutParams);
+ }
+ }
+
+ private boolean isUserSetupComplete() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ SETTINGS_SECURE_USER_SETUP_COMPLETE, 0) == 1;
+ }
+
/** Does the aspect ratio of the bitmap with insets removed match the bounds. */
private boolean aspectRatiosMatch(Bitmap bitmap, Insets bitmapInsets, Rect screenBounds) {
int insettedWidth = bitmap.getWidth() - bitmapInsets.left - bitmapInsets.right;
@@ -1128,7 +1119,7 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
if (insets.left < 0 || insets.top < 0 || insets.right < 0 || insets.bottom < 0) {
// Are any of the insets negative, meaning the bitmap is smaller than the bounds so need
// to fill in the background of the drawable.
- return new LayerDrawable(new Drawable[] {
+ return new LayerDrawable(new Drawable[]{
new ColorDrawable(Color.BLACK), insetDrawable});
} else {
return insetDrawable;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 9f8a9bb4a432..6f143da5405a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -102,7 +102,7 @@ public class TakeScreenshotService extends Service {
switch (msg.what) {
case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:
- mScreenshot.takeScreenshot(uriConsumer, onComplete);
+ mScreenshot.takeScreenshotFullscreen(uriConsumer, onComplete);
break;
case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION:
mScreenshot.takeScreenshotPartial(uriConsumer, onComplete);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 423f85f2ddd0..bd65ef06f3a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -282,7 +282,7 @@ public final class NotificationEntry extends ListEntry {
+ " doesn't match existing key " + mKey);
}
- mRanking = ranking;
+ mRanking = ranking.withAudiblyAlertedInfo(mRanking);
}
/*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 582e3e5b6c34..a7d83b3b2774 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -37,6 +37,8 @@ import android.widget.RemoteViews;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.ImageMessageConsumer;
import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.media.MediaDataManagerKt;
+import com.android.systemui.media.MediaFeatureFlag;
import com.android.systemui.statusbar.InflationTask;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.SmartReplyController;
@@ -71,6 +73,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
public static final String TAG = "NotifContentInflater";
private boolean mInflateSynchronously = false;
+ private final boolean mIsMediaInQS;
private final NotificationRemoteInputManager mRemoteInputManager;
private final NotifRemoteViewCache mRemoteViewCache;
private final Lazy<SmartReplyConstants> mSmartReplyConstants;
@@ -85,12 +88,14 @@ public class NotificationContentInflater implements NotificationRowContentBinder
Lazy<SmartReplyConstants> smartReplyConstants,
Lazy<SmartReplyController> smartReplyController,
ConversationNotificationProcessor conversationProcessor,
+ MediaFeatureFlag mediaFeatureFlag,
@Background Executor bgExecutor) {
mRemoteViewCache = remoteViewCache;
mRemoteInputManager = remoteInputManager;
mSmartReplyConstants = smartReplyConstants;
mSmartReplyController = smartReplyController;
mConversationProcessor = conversationProcessor;
+ mIsMediaInQS = mediaFeatureFlag.getEnabled();
mBgExecutor = bgExecutor;
}
@@ -135,7 +140,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder
bindParams.usesIncreasedHeight,
bindParams.usesIncreasedHeadsUpHeight,
callback,
- mRemoteInputManager.getRemoteViewsOnClickHandler());
+ mRemoteInputManager.getRemoteViewsOnClickHandler(),
+ mIsMediaInQS);
if (mInflateSynchronously) {
task.onPostExecute(task.doInBackground());
} else {
@@ -711,6 +717,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
private RemoteViews.OnClickHandler mRemoteViewClickHandler;
private CancellationSignal mCancellationSignal;
private final ConversationNotificationProcessor mConversationProcessor;
+ private final boolean mIsMediaInQS;
private AsyncInflationTask(
Executor bgExecutor,
@@ -726,7 +733,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder
boolean usesIncreasedHeight,
boolean usesIncreasedHeadsUpHeight,
InflationCallback callback,
- RemoteViews.OnClickHandler remoteViewClickHandler) {
+ RemoteViews.OnClickHandler remoteViewClickHandler,
+ boolean isMediaFlagEnabled) {
mEntry = entry;
mRow = row;
mSmartReplyConstants = smartReplyConstants;
@@ -742,6 +750,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
mRemoteViewClickHandler = remoteViewClickHandler;
mCallback = callback;
mConversationProcessor = conversationProcessor;
+ mIsMediaInQS = isMediaFlagEnabled;
entry.setInflationTask(this);
}
@@ -765,7 +774,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder
packageContext = new RtlEnabledContext(packageContext);
}
Notification notification = sbn.getNotification();
- if (notification.isMediaNotification()) {
+ if (notification.isMediaNotification() && !(mIsMediaInQS
+ && MediaDataManagerKt.isMediaNotification(sbn))) {
MediaNotificationProcessor processor = new MediaNotificationProcessor(mContext,
packageContext);
processor.processNotification(notification, recoveredBuilder);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index c87b9986ca55..91cc8963b947 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -41,8 +41,8 @@ import com.android.systemui.statusbar.notification.row.StackScrollerDecorView
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.SectionProvider
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.children
-import com.android.systemui.util.takeUntil
import com.android.systemui.util.foldToSparseArray
+import com.android.systemui.util.takeUntil
import javax.inject.Inject
/**
@@ -166,6 +166,9 @@ class NotificationSectionsManager @Inject internal constructor(
peopleHubSubscription?.unsubscribe()
peopleHubSubscription = null
peopleHeaderView = reinflateView(peopleHeaderView, layoutInflater, R.layout.people_strip)
+ .apply {
+ setOnHeaderClickListener(View.OnClickListener { onGentleHeaderClick() })
+ }
if (ENABLE_SNOOZED_CONVERSATION_HUB) {
peopleHubSubscription = peopleHubViewAdapter.bindView(peopleHubViewBoundary)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt
index 8f77a1d776e4..b13e7fb839ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt
@@ -93,6 +93,8 @@ class PeopleHubView(context: Context, attrs: AttributeSet) :
}
}
+ fun setOnHeaderClickListener(listener: OnClickListener) = label.setOnClickListener(listener)
+
private inner class PersonDataListenerImpl(val avatarView: ImageView) :
DataListener<PersonViewModel?> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 27daf8615a31..837543c9bdae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -591,6 +591,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
.registerDisplayListener(this, new Handler(Looper.getMainLooper()));
mOrientationHandle = new QuickswitchOrientedNavHandle(getContext());
+ mOrientationHandle.setId(R.id.secondary_home_handle);
getBarTransitions().addDarkIntensityListener(mOrientationHandleIntensityListener);
mOrientationParams = new WindowManager.LayoutParams(0, 0,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index 812ce1c62677..6dd96f92b344 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -82,6 +82,7 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C
private boolean mClockVisibleByUser = true;
private boolean mAttached;
+ private boolean mScreenReceiverRegistered;
private Calendar mCalendar;
private String mClockFormatString;
private SimpleDateFormat mClockFormat;
@@ -213,6 +214,14 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
+ if (mScreenReceiverRegistered) {
+ mScreenReceiverRegistered = false;
+ mBroadcastDispatcher.unregisterReceiver(mScreenReceiver);
+ if (mSecondsHandler != null) {
+ mSecondsHandler.removeCallbacks(mSecondTick);
+ mSecondsHandler = null;
+ }
+ }
if (mAttached) {
mBroadcastDispatcher.unregisterReceiver(mIntentReceiver);
mAttached = false;
@@ -363,12 +372,14 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C
mSecondsHandler.postAtTime(mSecondTick,
SystemClock.uptimeMillis() / 1000 * 1000 + 1000);
}
+ mScreenReceiverRegistered = true;
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_SCREEN_ON);
mBroadcastDispatcher.registerReceiver(mScreenReceiver, filter);
}
} else {
if (mSecondsHandler != null) {
+ mScreenReceiverRegistered = false;
mBroadcastDispatcher.unregisterReceiver(mScreenReceiver);
mSecondsHandler.removeCallbacks(mSecondTick);
mSecondsHandler = null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index 07de388598b7..c43ad36d4462 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -106,9 +106,9 @@ public abstract class HeadsUpManager extends AlertingNotificationManager {
public void updateNotification(@NonNull String key, boolean alert) {
super.updateNotification(key, alert);
- AlertEntry alertEntry = getHeadsUpEntry(key);
- if (alert && alertEntry != null) {
- setEntryPinned((HeadsUpEntry) alertEntry, shouldHeadsUpBecomePinned(alertEntry.mEntry));
+ HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
+ if (alert && headsUpEntry != null) {
+ setEntryPinned(headsUpEntry, shouldHeadsUpBecomePinned(headsUpEntry.mEntry));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 18a7adda3f7d..cf83603997c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -284,6 +284,9 @@ public class MobileSignalController extends SignalController<
mNetworkToIconLookup.put(toDisplayIconKey(
TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE),
TelephonyIcons.NR_5G_PLUS);
+ mNetworkToIconLookup.put(toIconKey(
+ TelephonyManager.NETWORK_TYPE_NR),
+ TelephonyIcons.NR_5G);
}
private String getIconKey() {
@@ -306,9 +309,9 @@ public class MobileSignalController extends SignalController<
case TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO:
return toIconKey(TelephonyManager.NETWORK_TYPE_LTE) + "_CA_Plus";
case TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA:
- return "5G";
+ return toIconKey(TelephonyManager.NETWORK_TYPE_NR);
case TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE:
- return "5G_Plus";
+ return toIconKey(TelephonyManager.NETWORK_TYPE_NR) + "_Plus";
default:
return "unsupported";
}
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
index 926f653153ee..89f469a438a9 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
@@ -259,6 +259,11 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
mHandler.post(() -> startAnimation(false /* show */, false /* forceRestart */));
}
+ @Override
+ public void topFocusedWindowChanged(String packageName) {
+ // no-op
+ }
+
/**
* Sends the local visibility state back to window manager. Needed for legacy adjustForIme.
*/
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 9e056cf16ec7..6362812d4fed 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -174,6 +174,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
when(mFaceManager.isHardwareDetected()).thenReturn(true);
when(mFaceManager.hasEnrolledTemplates()).thenReturn(true);
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+ when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
when(mUserManager.isUserUnlocked(anyInt())).thenReturn(true);
when(mUserManager.isPrimaryUser()).thenReturn(true);
when(mStrongAuthTracker.getStub()).thenReturn(mock(IStrongAuthTracker.Stub.class));
@@ -419,6 +421,43 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
@Test
+ public void testTriesToAuthenticateFingerprint_whenKeyguard() {
+ mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
+ mTestableLooper.processAllMessages();
+
+ verify(mFingerprintManager).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
+ verify(mFingerprintManager, never()).detectFingerprint(any(), any(), anyInt());
+ }
+
+ @Test
+ public void testFingerprintDoesNotAuth_whenEncrypted() {
+ testFingerprintWhenStrongAuth(
+ KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT);
+ }
+
+ @Test
+ public void testFingerprintDoesNotAuth_whenDpmLocked() {
+ testFingerprintWhenStrongAuth(
+ KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW);
+ }
+
+ @Test
+ public void testFingerprintDoesNotAuth_whenUserLockdown() {
+ testFingerprintWhenStrongAuth(
+ KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+ }
+
+ private void testFingerprintWhenStrongAuth(int strongAuth) {
+ when(mStrongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn(strongAuth);
+ mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
+ mTestableLooper.processAllMessages();
+
+ verify(mFingerprintManager, never())
+ .authenticate(any(), any(), anyInt(), any(), any(), anyInt());
+ verify(mFingerprintManager).detectFingerprint(any(), any(), anyInt());
+ }
+
+ @Test
public void testTriesToAuthenticate_whenBouncer() {
mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true);
mTestableLooper.processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
index dbc5596d9f4e..492b33e3c4a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
@@ -22,6 +22,7 @@ import static org.mockito.Mockito.any;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import android.graphics.Color;
@@ -47,6 +48,7 @@ import java.util.Map;
public class MediaDataCombineLatestTest extends SysuiTestCase {
private static final String KEY = "TEST_KEY";
+ private static final String OLD_KEY = "TEST_KEY_OLD";
private static final String APP = "APP";
private static final String PACKAGE = "PKG";
private static final int BG_COLOR = Color.RED;
@@ -97,7 +99,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
@Test
public void eventNotEmittedWithoutMedia() {
// WHEN device source emits an event without media data
- mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData);
+ mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData);
// THEN an event isn't emitted
verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any());
}
@@ -105,7 +107,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
@Test
public void emitEventAfterDeviceFirst() {
// GIVEN that a device event has already been received
- mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData);
+ mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData);
// WHEN media event is received
mDataListener.onMediaDataLoaded(KEY, null, mMediaData);
// THEN the listener receives a combined event
@@ -119,7 +121,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
// GIVEN that media event has already been received
mDataListener.onMediaDataLoaded(KEY, null, mMediaData);
// WHEN device event is received
- mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData);
+ mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData);
// THEN the listener receives a combined event
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture());
@@ -127,6 +129,64 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
}
@Test
+ public void migrateKeyMediaFirst() {
+ // GIVEN that media and device info has already been received
+ mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData);
+ mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
+ reset(mListener);
+ // WHEN a key migration event is received
+ mDataListener.onMediaDataLoaded(KEY, OLD_KEY, mMediaData);
+ // THEN the listener receives a combined event
+ ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
+ verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture());
+ assertThat(captor.getValue().getDevice()).isNotNull();
+ }
+
+ @Test
+ public void migrateKeyDeviceFirst() {
+ // GIVEN that media and device info has already been received
+ mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData);
+ mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
+ reset(mListener);
+ // WHEN a key migration event is received
+ mDeviceListener.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData);
+ // THEN the listener receives a combined event
+ ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
+ verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture());
+ assertThat(captor.getValue().getDevice()).isNotNull();
+ }
+
+ @Test
+ public void migrateKeyMediaAfter() {
+ // GIVEN that media and device info has already been received
+ mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData);
+ mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
+ mDeviceListener.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData);
+ reset(mListener);
+ // WHEN a second key migration event is received for media
+ mDataListener.onMediaDataLoaded(KEY, OLD_KEY, mMediaData);
+ // THEN the key has already been migrated
+ ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
+ verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture());
+ assertThat(captor.getValue().getDevice()).isNotNull();
+ }
+
+ @Test
+ public void migrateKeyDeviceAfter() {
+ // GIVEN that media and device info has already been received
+ mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData);
+ mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
+ mDataListener.onMediaDataLoaded(KEY, OLD_KEY, mMediaData);
+ reset(mListener);
+ // WHEN a second key migration event is received for the device
+ mDeviceListener.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData);
+ // THEN the key has already be migrated
+ ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
+ verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture());
+ assertThat(captor.getValue().getDevice()).isNotNull();
+ }
+
+ @Test
public void mediaDataRemoved() {
// WHEN media data is removed without first receiving device or data
mDataListener.onMediaDataRemoved(KEY);
@@ -143,7 +203,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
@Test
public void mediaDataRemovedAfterDeviceEvent() {
- mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData);
+ mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData);
mDataListener.onMediaDataRemoved(KEY);
verify(mListener).onMediaDataRemoved(eq(KEY));
}
@@ -152,7 +212,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
public void mediaDataKeyUpdated() {
// GIVEN that device and media events have already been received
mDataListener.onMediaDataLoaded(KEY, null, mMediaData);
- mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData);
+ mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData);
// WHEN the key is changed
mDataListener.onMediaDataLoaded("NEW_KEY", KEY, mMediaData);
// THEN the listener gets a load event with the correct keys
@@ -163,7 +223,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
@Test
public void getDataIncludesDevice() {
// GIVEN that device and media events have been received
- mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData);
+ mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData);
mDataListener.onMediaDataLoaded(KEY, null, mMediaData);
// THEN the result of getData includes device info
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
index fc22eeb3ea68..3c6e19f0ec6f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
@@ -166,7 +166,7 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
// THEN the listener for the old key should removed.
verify(lmm).unregisterCallback(any())
// AND a new device event emitted
- val data = captureDeviceData(KEY)
+ val data = captureDeviceData(KEY, KEY_OLD)
assertThat(data.enabled).isTrue()
assertThat(data.name).isEqualTo(DEVICE_NAME)
}
@@ -179,13 +179,14 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
// WHEN the new key is the same as the old key
manager.onMediaDataLoaded(KEY, KEY, mediaData)
// THEN no event should be emitted
- verify(listener, never()).onMediaDeviceChanged(eq(KEY), any())
+ verify(listener, never()).onMediaDeviceChanged(eq(KEY), eq(null), any())
}
@Test
fun unknownOldKey() {
- manager.onMediaDataLoaded(KEY, "unknown", mediaData)
- verify(listener).onMediaDeviceChanged(eq(KEY), any())
+ val oldKey = "unknown"
+ manager.onMediaDataLoaded(KEY, oldKey, mediaData)
+ verify(listener).onMediaDeviceChanged(eq(KEY), eq(oldKey), any())
}
@Test
@@ -223,7 +224,7 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
manager.removeListener(listener)
// THEN it doesn't receive device events
manager.onMediaDataLoaded(KEY, null, mediaData)
- verify(listener, never()).onMediaDeviceChanged(eq(KEY), any())
+ verify(listener, never()).onMediaDeviceChanged(eq(KEY), eq(null), any())
}
@Test
@@ -318,9 +319,9 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
return captor.getValue()
}
- fun captureDeviceData(key: String): MediaDeviceData {
+ fun captureDeviceData(key: String, oldKey: String? = null): MediaDeviceData {
val captor = ArgumentCaptor.forClass(MediaDeviceData::class.java)
- verify(listener).onMediaDeviceChanged(eq(key), captor.capture())
+ verify(listener).onMediaDeviceChanged(eq(key), eq(oldKey), captor.capture())
return captor.getValue()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java
index 536cae4380c4..c9c111198f4c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java
@@ -61,8 +61,7 @@ public class PipAnimationControllerTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
mPipAnimationController = new PipAnimationController(
- mContext, new PipSurfaceTransactionHelper(mContext,
- mock(ConfigurationController.class)));
+ new PipSurfaceTransactionHelper(mContext, mock(ConfigurationController.class)));
mLeash = new SurfaceControl.Builder()
.setContainerLayer()
.setName("FakeLeash")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index 25da74137a90..3718cd720659 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -51,6 +51,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.filters.Suppress;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.media.MediaFeatureFlag;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.notification.ConversationNotificationProcessor;
@@ -110,6 +111,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
() -> smartReplyConstants,
() -> smartReplyController,
mConversationNotificationProcessor,
+ mock(MediaFeatureFlag.class),
mock(Executor.class));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
index bedbec6aadce..2198edd0d8bb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
@@ -43,6 +43,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.util.NotificationMessagingUtil;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.media.MediaFeatureFlag;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.plugins.PluginManager;
@@ -198,6 +199,7 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
() -> mock(SmartReplyConstants.class),
() -> mock(SmartReplyController.class),
mock(ConversationNotificationProcessor.class),
+ mock(MediaFeatureFlag.class),
mBgExecutor);
mRowContentBindStage = new RowContentBindStage(
binder,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index b9eb4d1e29c2..0c6409b38d21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -46,6 +46,7 @@ import android.widget.RemoteViews;
import com.android.systemui.TestableDependency;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.bubbles.BubblesTestActivity;
+import com.android.systemui.media.MediaFeatureFlag;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -133,6 +134,7 @@ public class NotificationTestHelper {
() -> mock(SmartReplyConstants.class),
() -> mock(SmartReplyController.class),
mock(ConversationNotificationProcessor.class),
+ mock(MediaFeatureFlag.class),
mock(Executor.class));
contentBinder.setInflateSynchronously(true);
mBindStage = new RowContentBindStage(contentBinder,
diff --git a/packages/WAPPushManager/AndroidManifest.xml b/packages/WAPPushManager/AndroidManifest.xml
index 14e6e91e3a25..a75fb2d47bbd 100644
--- a/packages/WAPPushManager/AndroidManifest.xml
+++ b/packages/WAPPushManager/AndroidManifest.xml
@@ -23,6 +23,8 @@
<permission android:name="com.android.smspush.WAPPUSH_MANAGER_BIND"
android:protectionLevel="signatureOrSystem" />
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
+
<original-package android:name="com.android.smspush" />
<application
android:allowClearUserData="false">
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 6b852adce0f1..40a2816ee7de 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -90,6 +90,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
+import java.util.NoSuchElementException;
import java.util.Set;
/**
@@ -1179,7 +1180,11 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
/* ignore */
}
if (mService != null) {
- mService.unlinkToDeath(this, 0);
+ try {
+ mService.unlinkToDeath(this, 0);
+ } catch (NoSuchElementException e) {
+ Slog.e(LOG_TAG, "Failed unregistering death link");
+ }
mService = null;
}
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
index 5e10916c4491..a4e58a1faeac 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
@@ -649,16 +649,33 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
mReporter.onStartPackageBackup(PM_PACKAGE);
mCurrentPackage = new PackageInfo();
mCurrentPackage.packageName = PM_PACKAGE;
-
try {
- extractPmAgentData(mCurrentPackage);
+ // If we can't even extractPmAgentData(), then we treat the local state as
+ // compromised, just in case. This means that we will clear data and will
+ // start from a clean slate in the next attempt. It's not clear whether that's
+ // the right thing to do, but matches what we have historically done.
+ try {
+ extractPmAgentData(mCurrentPackage);
+ } catch (TaskException e) {
+ throw TaskException.stateCompromised(e); // force stateCompromised
+ }
+ // During sendDataToTransport, we generally trust any thrown TaskException
+ // about whether stateCompromised because those are likely transient;
+ // clearing state for those would have the potential to lead to cascading
+ // failures, as discussed in http://b/144030477.
+ // For specific status codes (e.g. TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED),
+ // cleanUpAgentForTransportStatus() or theoretically handleTransportStatus()
+ // still have the opportunity to perform additional clean-up tasks.
int status = sendDataToTransport(mCurrentPackage);
cleanUpAgentForTransportStatus(status);
} catch (AgentException | TaskException e) {
mReporter.onExtractPmAgentDataError(e);
cleanUpAgentForError(e);
- // PM agent failure is task failure.
- throw TaskException.stateCompromised(e);
+ if (e instanceof TaskException) {
+ throw (TaskException) e;
+ } else {
+ throw TaskException.stateCompromised(e); // PM agent failure is task failure.
+ }
}
}
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 061972c5723a..131267924179 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -28,6 +28,7 @@ import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRIN
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_IRIS;
import static android.hardware.biometrics.BiometricManager.Authenticators;
+import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.biometrics.BiometricPrompt;
@@ -128,6 +129,11 @@ public class AuthService extends SystemService {
return IIrisService.Stub.asInterface(
ServiceManager.getService(Context.IRIS_SERVICE));
}
+
+ @VisibleForTesting
+ public AppOpsManager getAppOps(Context context) {
+ return context.getSystemService(AppOpsManager.class);
+ }
}
private final class AuthServiceImpl extends IAuthService.Stub {
@@ -138,6 +144,8 @@ public class AuthService extends SystemService {
// Only allow internal clients to authenticate with a different userId.
final int callingUserId = UserHandle.getCallingUserId();
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
if (userId == callingUserId) {
checkPermission();
} else {
@@ -146,6 +154,16 @@ public class AuthService extends SystemService {
checkInternalPermission();
}
+ if (!checkAppOps(callingUid, opPackageName, "authenticate()")) {
+ Slog.e(TAG, "Denied by app ops: " + opPackageName);
+ return;
+ }
+
+ if (!Utils.isForeground(callingUid, callingPid)) {
+ Slog.e(TAG, "Caller is not foreground: " + opPackageName);
+ return;
+ }
+
if (token == null || receiver == null || opPackageName == null || bundle == null) {
Slog.e(TAG, "Unable to authenticate, one or more null arguments");
return;
@@ -163,8 +181,6 @@ public class AuthService extends SystemService {
checkInternalPermission();
}
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
final long identity = Binder.clearCallingIdentity();
try {
mBiometricService.authenticate(
@@ -392,4 +408,9 @@ public class AuthService extends SystemService {
"Must have USE_BIOMETRIC permission");
}
}
+
+ private boolean checkAppOps(int uid, String opPackageName, String reason) {
+ return mInjector.getAppOps(getContext()).noteOp(AppOpsManager.OP_USE_BIOMETRIC, uid,
+ opPackageName, null /* attributionTag */, reason) == AppOpsManager.MODE_ALLOWED;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
index 75452ea5fb61..20c004db4c9f 100644
--- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
@@ -722,10 +722,9 @@ public abstract class BiometricServiceBase extends SystemService
}
}
- protected void handleAuthenticated(BiometricAuthenticator.Identifier identifier,
- ArrayList<Byte> token) {
+ protected void handleAuthenticated(boolean authenticated,
+ BiometricAuthenticator.Identifier identifier, ArrayList<Byte> token) {
ClientMonitor client = mCurrentClient;
- final boolean authenticated = identifier.getBiometricId() != 0;
if (client != null && client.onAuthenticated(identifier, authenticated, token)) {
removeClient(client);
@@ -1019,7 +1018,7 @@ public abstract class BiometricServiceBase extends SystemService
return false;
}
- if (requireForeground && !(isForegroundActivity(uid, pid) || isCurrentClient(
+ if (requireForeground && !(Utils.isForeground(uid, pid) || isCurrentClient(
opPackageName))) {
Slog.w(getTag(), "Rejecting " + opPackageName + "; not in foreground");
return false;
@@ -1042,29 +1041,6 @@ public abstract class BiometricServiceBase extends SystemService
return mKeyguardPackage.equals(clientPackage);
}
- private boolean isForegroundActivity(int uid, int pid) {
- try {
- final List<ActivityManager.RunningAppProcessInfo> procs =
- ActivityManager.getService().getRunningAppProcesses();
- if (procs == null) {
- Slog.e(getTag(), "Processes null, defaulting to true");
- return true;
- }
-
- int N = procs.size();
- for (int i = 0; i < N; i++) {
- ActivityManager.RunningAppProcessInfo proc = procs.get(i);
- if (proc.pid == pid && proc.uid == uid
- && proc.importance <= IMPORTANCE_FOREGROUND_SERVICE) {
- return true;
- }
- }
- } catch (RemoteException e) {
- Slog.w(getTag(), "am.getRunningAppProcesses() failed");
- }
- return false;
- }
-
/**
* Calls the HAL to switch states to the new task. If there's already a current task,
* it calls cancel() and sets mPendingClient to begin when the current task finishes
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index 14378da0a90b..c661f452820d 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -16,20 +16,38 @@
package com.android.server.biometrics;
+import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
import static android.hardware.biometrics.BiometricManager.Authenticators;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
+
+import android.annotation.NonNull;
+import android.app.ActivityManager;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.BiometricPrompt.AuthenticationResultType;
import android.os.Build;
import android.os.Bundle;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
+import com.android.internal.R;
+import com.android.internal.widget.LockPatternUtils;
+
+import java.util.List;
+
public class Utils {
+ private static final String TAG = "BiometricUtils";
+
public static boolean isDebugEnabled(Context context, int targetUserId) {
if (targetUserId == UserHandle.USER_NULL) {
return false;
@@ -256,4 +274,50 @@ public class Utils {
throw new IllegalArgumentException("Unsupported dismissal reason: " + reason);
}
}
+
+ public static boolean isForeground(int callingUid, int callingPid) {
+ try {
+ final List<ActivityManager.RunningAppProcessInfo> procs =
+ ActivityManager.getService().getRunningAppProcesses();
+ if (procs == null) {
+ Slog.e(TAG, "No running app processes found, defaulting to true");
+ return true;
+ }
+
+ for (int i = 0; i < procs.size(); i++) {
+ ActivityManager.RunningAppProcessInfo proc = procs.get(i);
+ if (proc.pid == callingPid && proc.uid == callingUid
+ && proc.importance <= IMPORTANCE_FOREGROUND_SERVICE) {
+ return true;
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "am.getRunningAppProcesses() failed");
+ }
+ return false;
+ }
+
+ public static boolean isKeyguard(Context context, String clientPackage) {
+ final boolean hasPermission = context.checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL)
+ == PackageManager.PERMISSION_GRANTED;
+
+ final ComponentName keyguardComponent = ComponentName.unflattenFromString(
+ context.getResources().getString(R.string.config_keyguardComponent));
+ final String keyguardPackage = keyguardComponent != null
+ ? keyguardComponent.getPackageName() : null;
+ return hasPermission && keyguardPackage != null && keyguardPackage.equals(clientPackage);
+ }
+
+ private static boolean containsFlag(int haystack, int needle) {
+ return (haystack & needle) != 0;
+ }
+
+ public static boolean isUserEncryptedOrLockdown(@NonNull LockPatternUtils lpu, int user) {
+ final int strongAuth = lpu.getStrongAuthForUser(user);
+ final boolean isEncrypted = containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT);
+ final boolean isLockDown = containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW)
+ || containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+ Slog.d(TAG, "isEncrypted: " + isEncrypted + " isLockdown: " + isLockDown);
+ return isEncrypted || isLockDown;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index 72e1bbbcba60..e5a1898459a2 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -896,8 +896,9 @@ public class FaceService extends BiometricServiceBase {
public void onAuthenticated(final long deviceId, final int faceId, final int userId,
ArrayList<Byte> token) {
mHandler.post(() -> {
- Face face = new Face("", faceId, deviceId);
- FaceService.super.handleAuthenticated(face, token);
+ final Face face = new Face("", faceId, deviceId);
+ final boolean authenticated = faceId != 0;
+ FaceService.super.handleAuthenticated(authenticated, face, token);
});
}
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
index 6b7ba6a56d82..a90fee6788a8 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -56,6 +56,7 @@ import android.os.SELinux;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
+import android.util.EventLog;
import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
@@ -64,6 +65,7 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.DumpUtils;
+import com.android.internal.widget.LockPatternUtils;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.biometrics.AuthenticationClient;
import com.android.server.biometrics.BiometricServiceBase;
@@ -72,6 +74,7 @@ import com.android.server.biometrics.ClientMonitor;
import com.android.server.biometrics.Constants;
import com.android.server.biometrics.EnumerateClient;
import com.android.server.biometrics.RemovalClient;
+import com.android.server.biometrics.Utils;
import org.json.JSONArray;
import org.json.JSONException;
@@ -124,6 +127,8 @@ public class FingerprintService extends BiometricServiceBase {
}
private final class FingerprintAuthClient extends AuthenticationClientImpl {
+ private final boolean mDetectOnly;
+
@Override
protected boolean isFingerprint() {
return true;
@@ -133,9 +138,10 @@ public class FingerprintService extends BiometricServiceBase {
DaemonWrapper daemon, long halDeviceId, IBinder token,
ServiceListener listener, int targetUserId, int groupId, long opId,
boolean restricted, String owner, int cookie,
- boolean requireConfirmation) {
+ boolean requireConfirmation, boolean detectOnly) {
super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId,
restricted, owner, cookie, requireConfirmation);
+ mDetectOnly = detectOnly;
}
@Override
@@ -177,6 +183,10 @@ public class FingerprintService extends BiometricServiceBase {
return super.handleFailedAttempt();
}
+
+ boolean isDetectOnly() {
+ return mDetectOnly;
+ }
}
/**
@@ -234,18 +244,64 @@ public class FingerprintService extends BiometricServiceBase {
}
@Override // Binder call
- public void authenticate(final IBinder token, final long opId, final int groupId,
+ public void authenticate(final IBinder token, final long opId, final int userId,
final IFingerprintServiceReceiver receiver, final int flags,
final String opPackageName) {
- updateActiveGroup(groupId, opPackageName);
+ // Keyguard check must be done on the caller's binder identity, since it also checks
+ // permission.
+ final boolean isKeyguard = Utils.isKeyguard(getContext(), opPackageName);
+
+ // Clear calling identity when checking LockPatternUtils for StrongAuth flags.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (isKeyguard && Utils.isUserEncryptedOrLockdown(mLockPatternUtils, userId)) {
+ // If this happens, something in KeyguardUpdateMonitor is wrong.
+ // SafetyNet for b/79776455
+ EventLog.writeEvent(0x534e4554, "79776455");
+ Slog.e(TAG, "Authenticate invoked when user is encrypted or lockdown");
+ return;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+
+ updateActiveGroup(userId, opPackageName);
final boolean restricted = isRestricted();
final AuthenticationClientImpl client = new FingerprintAuthClient(getContext(),
mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
- mCurrentUserId, groupId, opId, restricted, opPackageName,
- 0 /* cookie */, false /* requireConfirmation */);
+ mCurrentUserId, userId, opId, restricted, opPackageName,
+ 0 /* cookie */, false /* requireConfirmation */, false /* detectOnly */);
authenticateInternal(client, opId, opPackageName);
}
+ @Override
+ public void detectFingerprint(final IBinder token, final int userId,
+ final IFingerprintServiceReceiver receiver, final String opPackageName) {
+ checkPermission(USE_BIOMETRIC_INTERNAL);
+ if (!Utils.isKeyguard(getContext(), opPackageName)) {
+ Slog.w(TAG, "detectFingerprint called from non-sysui package: " + opPackageName);
+ return;
+ }
+
+ if (!Utils.isUserEncryptedOrLockdown(mLockPatternUtils, userId)) {
+ // If this happens, something in KeyguardUpdateMonitor is wrong. This should only
+ // ever be invoked when the user is encrypted or lockdown.
+ Slog.e(TAG, "detectFingerprint invoked when user is not encrypted or lockdown");
+ return;
+ }
+
+ Slog.d(TAG, "detectFingerprint, owner: " + opPackageName + ", user: " + userId);
+
+ updateActiveGroup(userId, opPackageName);
+ final boolean restricted = isRestricted();
+ final int operationId = 0;
+ final AuthenticationClientImpl client = new FingerprintAuthClient(getContext(),
+ mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
+ mCurrentUserId, userId, operationId, restricted, opPackageName,
+ 0 /* cookie */, false /* requireConfirmation */, true /* detectOnly */);
+ authenticateInternal(client, operationId, opPackageName);
+ }
+
@Override // Binder call
public void prepareForAuthentication(IBinder token, long opId, int groupId,
IBiometricServiceReceiverInternal wrapperReceiver, String opPackageName,
@@ -257,7 +313,7 @@ public class FingerprintService extends BiometricServiceBase {
mDaemonWrapper, mHalDeviceId, token,
new BiometricPromptServiceListenerImpl(wrapperReceiver),
mCurrentUserId, groupId, opId, restricted, opPackageName, cookie,
- false /* requireConfirmation */);
+ false /* requireConfirmation */, false /* detectOnly */);
authenticateInternal(client, opId, opPackageName, callingUid, callingPid,
callingUserId);
}
@@ -275,6 +331,17 @@ public class FingerprintService extends BiometricServiceBase {
}
@Override // Binder call
+ public void cancelFingerprintDetect(final IBinder token, final String opPackageName) {
+ checkPermission(USE_BIOMETRIC_INTERNAL);
+ if (!Utils.isKeyguard(getContext(), opPackageName)) {
+ Slog.w(TAG, "cancelFingerprintDetect called from non-sysui package: "
+ + opPackageName);
+ return;
+ }
+ cancelAuthenticationInternal(token, opPackageName);
+ }
+
+ @Override // Binder call
public void cancelAuthenticationFromService(final IBinder token, final String opPackageName,
int callingUid, int callingPid, int callingUserId, boolean fromClient) {
checkPermission(MANAGE_BIOMETRIC);
@@ -518,7 +585,12 @@ public class FingerprintService extends BiometricServiceBase {
BiometricAuthenticator.Identifier biometric, int userId)
throws RemoteException {
if (mFingerprintServiceReceiver != null) {
- if (biometric == null || biometric instanceof Fingerprint) {
+ final ClientMonitor client = getCurrentClient();
+ if (client instanceof FingerprintAuthClient
+ && ((FingerprintAuthClient) client).isDetectOnly()) {
+ mFingerprintServiceReceiver
+ .onFingerprintDetected(deviceId, userId, isStrongBiometric());
+ } else if (biometric == null || biometric instanceof Fingerprint) {
mFingerprintServiceReceiver.onAuthenticationSucceeded(deviceId,
(Fingerprint) biometric, userId, isStrongBiometric());
} else {
@@ -575,6 +647,7 @@ public class FingerprintService extends BiometricServiceBase {
private final LockoutReceiver mLockoutReceiver = new LockoutReceiver();
protected final ResetFailedAttemptsForUserRunnable mResetFailedAttemptsForCurrentUserRunnable =
new ResetFailedAttemptsForUserRunnable();
+ private final LockPatternUtils mLockPatternUtils;
/**
* Receives callbacks from the HAL.
@@ -608,8 +681,17 @@ public class FingerprintService extends BiometricServiceBase {
public void onAuthenticated(final long deviceId, final int fingerId, final int groupId,
ArrayList<Byte> token) {
mHandler.post(() -> {
- Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
- FingerprintService.super.handleAuthenticated(fp, token);
+ boolean authenticated = fingerId != 0;
+ final ClientMonitor client = getCurrentClient();
+ if (client instanceof FingerprintAuthClient) {
+ if (((FingerprintAuthClient) client).isDetectOnly()) {
+ Slog.w(TAG, "Detect-only. Device is encrypted or locked down");
+ authenticated = true;
+ }
+ }
+
+ final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
+ FingerprintService.super.handleAuthenticated(authenticated, fp, token);
});
}
@@ -722,6 +804,7 @@ public class FingerprintService extends BiometricServiceBase {
mAlarmManager = context.getSystemService(AlarmManager.class);
context.registerReceiver(mLockoutReceiver, new IntentFilter(getLockoutResetIntent()),
getLockoutBroadcastPermission(), null /* handler */);
+ mLockPatternUtils = new LockPatternUtils(context);
}
@Override
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 9963cf7e212d..bc7554c54eb0 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -4224,6 +4224,20 @@ public class PermissionManagerService extends IPermissionManager.Stub {
revokePermissionFromPackageForUser(p.getPackageName(),
bp.getName(), true, userId, callback));
}
+ } else {
+ mPackageManagerInt.forEachPackage(p -> {
+ PackageSetting ps = mPackageManagerInt.getPackageSetting(
+ p.getPackageName());
+ if (ps == null) {
+ return;
+ }
+ PermissionsState permissionsState = ps.getPermissionsState();
+ if (permissionsState.getInstallPermissionState(bp.getName()) != null) {
+ permissionsState.revokeInstallPermission(bp);
+ permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
+ MASK_PERMISSION_FLAGS_ALL, 0);
+ }
+ });
}
it.remove();
}
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index b3e162d473db..b9431a6968f9 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -141,6 +141,10 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
@IntDef({NAV_BAR_LEFT, NAV_BAR_RIGHT, NAV_BAR_BOTTOM})
@interface NavigationBarPosition {}
+ @Retention(SOURCE)
+ @IntDef({ALT_BAR_UNKNOWN, ALT_BAR_LEFT, ALT_BAR_RIGHT, ALT_BAR_BOTTOM, ALT_BAR_TOP})
+ @interface AltBarPosition {}
+
/**
* Pass this event to the user / app. To be returned from
* {@link #interceptKeyBeforeQueueing}.
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 5d3f3c0401ef..cdbc7d2f4861 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -5774,6 +5774,19 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mRemoteInsetsController = controller;
}
+ /**
+ * Notifies the remote insets controller that the top focused window has changed.
+ *
+ * @param packageName The name of the package that is open in the top focused window.
+ */
+ void topFocusedWindowChanged(String packageName) {
+ try {
+ mRemoteInsetsController.topFocusedWindowChanged(packageName);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to deliver package in top focused window change", e);
+ }
+ }
+
void notifyInsetsChanged() {
try {
mRemoteInsetsController.insetsChanged(
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 0d467c50aa11..6a90f2994238 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -102,6 +102,11 @@ import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManagerGlobal.ADD_OKAY;
import static android.view.WindowManagerPolicyConstants.ACTION_HDMI_PLUGGED;
+import static android.view.WindowManagerPolicyConstants.ALT_BAR_BOTTOM;
+import static android.view.WindowManagerPolicyConstants.ALT_BAR_LEFT;
+import static android.view.WindowManagerPolicyConstants.ALT_BAR_RIGHT;
+import static android.view.WindowManagerPolicyConstants.ALT_BAR_TOP;
+import static android.view.WindowManagerPolicyConstants.ALT_BAR_UNKNOWN;
import static android.view.WindowManagerPolicyConstants.EXTRA_HDMI_PLUGGED_STATE;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
@@ -314,6 +319,17 @@ public class DisplayPolicy {
private int[] mNavigationBarHeightForRotationInCarMode = new int[4];
private int[] mNavigationBarWidthForRotationInCarMode = new int[4];
+ // Alternative status bar for when flexible insets mapping is used to place the status bar on
+ // another side of the screen.
+ private WindowState mStatusBarAlt = null;
+ @WindowManagerPolicy.AltBarPosition
+ private int mStatusBarAltPosition = ALT_BAR_UNKNOWN;
+ // Alternative navigation bar for when flexible insets mapping is used to place the navigation
+ // bar elsewhere on the screen.
+ private WindowState mNavigationBarAlt = null;
+ @WindowManagerPolicy.AltBarPosition
+ private int mNavigationBarAltPosition = ALT_BAR_UNKNOWN;
+
/** See {@link #getNavigationBarFrameHeight} */
private int[] mNavigationBarFrameHeightForRotationDefault = new int[4];
@@ -386,11 +402,6 @@ public class DisplayPolicy {
private int mForcingShowNavBarLayer;
private boolean mForceShowSystemBars;
- /**
- * Force the display of system bars regardless of other settings.
- */
- private boolean mForceShowSystemBarsFromExternal;
-
private boolean mShowingDream;
private boolean mLastShowingDream;
private boolean mDreamingLockscreen;
@@ -436,7 +447,7 @@ public class DisplayPolicy {
case MSG_REQUEST_TRANSIENT_BARS:
synchronized (mLock) {
WindowState targetBar = (msg.arg1 == MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS)
- ? mStatusBar : mNavigationBar;
+ ? getStatusBar() : getNavigationBar();
if (targetBar != null) {
requestTransientBars(targetBar);
}
@@ -480,7 +491,6 @@ public class DisplayPolicy {
final Resources r = mContext.getResources();
mCarDockEnablesAccelerometer = r.getBoolean(R.bool.config_carDockEnablesAccelerometer);
mDeskDockEnablesAccelerometer = r.getBoolean(R.bool.config_deskDockEnablesAccelerometer);
- mForceShowSystemBarsFromExternal = r.getBoolean(R.bool.config_forceShowSystemBars);
mAccessibilityManager = (AccessibilityManager) mContext.getSystemService(
Context.ACCESSIBILITY_SERVICE);
@@ -500,6 +510,7 @@ public class DisplayPolicy {
if (mStatusBar != null) {
requestTransientBars(mStatusBar);
}
+ checkAltBarSwipeForTransientBars(ALT_BAR_TOP);
}
}
@@ -510,6 +521,7 @@ public class DisplayPolicy {
&& mNavigationBarPosition == NAV_BAR_BOTTOM) {
requestTransientBars(mNavigationBar);
}
+ checkAltBarSwipeForTransientBars(ALT_BAR_BOTTOM);
}
}
@@ -526,6 +538,7 @@ public class DisplayPolicy {
excludedRegion)) {
requestTransientBars(mNavigationBar);
}
+ checkAltBarSwipeForTransientBars(ALT_BAR_RIGHT);
}
excludedRegion.recycle();
}
@@ -543,6 +556,7 @@ public class DisplayPolicy {
excludedRegion)) {
requestTransientBars(mNavigationBar);
}
+ checkAltBarSwipeForTransientBars(ALT_BAR_LEFT);
}
excludedRegion.recycle();
}
@@ -644,6 +658,15 @@ public class DisplayPolicy {
mHandler.post(mGestureNavigationSettingsObserver::register);
}
+ private void checkAltBarSwipeForTransientBars(@WindowManagerPolicy.AltBarPosition int pos) {
+ if (mStatusBarAlt != null && mStatusBarAltPosition == pos) {
+ requestTransientBars(mStatusBarAlt);
+ }
+ if (mNavigationBarAlt != null && mNavigationBarAltPosition == pos) {
+ requestTransientBars(mNavigationBarAlt);
+ }
+ }
+
void systemReady() {
mSystemGestures.systemReady();
if (mService.mPointerLocationEnabled) {
@@ -698,17 +721,6 @@ public class DisplayPolicy {
return mDockMode;
}
- /**
- * @see WindowManagerService.setForceShowSystemBars
- */
- void setForceShowSystemBars(boolean forceShowSystemBars) {
- mForceShowSystemBarsFromExternal = forceShowSystemBars;
- }
-
- boolean getForceShowSystemBars() {
- return mForceShowSystemBarsFromExternal;
- }
-
public boolean hasNavigationBar() {
return mHasNavigationBar;
}
@@ -916,6 +928,14 @@ public class DisplayPolicy {
}
break;
}
+
+ // Check if alternate bars positions were updated.
+ if (mStatusBarAlt == win) {
+ mStatusBarAltPosition = getAltBarPosition(attrs);
+ }
+ if (mNavigationBarAlt == win) {
+ mNavigationBarAltPosition = getAltBarPosition(attrs);
+ }
}
/**
@@ -955,10 +975,9 @@ public class DisplayPolicy {
mContext.enforcePermission(
android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
"DisplayPolicy");
- if (mStatusBar != null) {
- if (mStatusBar.isAlive()) {
- return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
- }
+ if ((mStatusBar != null && mStatusBar.isAlive())
+ || (mStatusBarAlt != null && mStatusBarAlt.isAlive())) {
+ return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
}
break;
case TYPE_NOTIFICATION_SHADE:
@@ -975,10 +994,9 @@ public class DisplayPolicy {
mContext.enforcePermission(
android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
"DisplayPolicy");
- if (mNavigationBar != null) {
- if (mNavigationBar.isAlive()) {
- return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
- }
+ if ((mNavigationBar != null && mNavigationBar.isAlive())
+ || (mNavigationBarAlt != null && mNavigationBarAlt.isAlive())) {
+ return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
}
break;
case TYPE_NAVIGATION_BAR_PANEL:
@@ -1010,6 +1028,23 @@ public class DisplayPolicy {
android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
"DisplayPolicy");
enforceSingleInsetsTypeCorrespondingToWindowType(attrs.providesInsetsTypes);
+
+ for (@InternalInsetsType int insetType : attrs.providesInsetsTypes) {
+ switch (insetType) {
+ case ITYPE_STATUS_BAR:
+ if ((mStatusBar != null && mStatusBar.isAlive())
+ || (mStatusBarAlt != null && mStatusBarAlt.isAlive())) {
+ return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
+ }
+ break;
+ case ITYPE_NAVIGATION_BAR:
+ if ((mNavigationBar != null && mNavigationBar.isAlive())
+ || (mNavigationBarAlt != null && mNavigationBarAlt.isAlive())) {
+ return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
+ }
+ break;
+ }
+ }
}
return ADD_OKAY;
}
@@ -1101,7 +1136,19 @@ public class DisplayPolicy {
break;
default:
if (attrs.providesInsetsTypes != null) {
- for (int insetsType : attrs.providesInsetsTypes) {
+ for (@InternalInsetsType int insetsType : attrs.providesInsetsTypes) {
+ switch (insetsType) {
+ case ITYPE_STATUS_BAR:
+ mStatusBarAlt = win;
+ mStatusBarController.setWindow(mStatusBarAlt);
+ mStatusBarAltPosition = getAltBarPosition(attrs);
+ break;
+ case ITYPE_NAVIGATION_BAR:
+ mNavigationBarAlt = win;
+ mNavigationBarController.setWindow(mNavigationBarAlt);
+ mNavigationBarAltPosition = getAltBarPosition(attrs);
+ break;
+ }
mDisplayContent.setInsetProvider(insetsType, win, null);
}
}
@@ -1109,6 +1156,22 @@ public class DisplayPolicy {
}
}
+ @WindowManagerPolicy.AltBarPosition
+ private int getAltBarPosition(WindowManager.LayoutParams params) {
+ switch (params.gravity) {
+ case Gravity.LEFT:
+ return ALT_BAR_LEFT;
+ case Gravity.RIGHT:
+ return ALT_BAR_RIGHT;
+ case Gravity.BOTTOM:
+ return ALT_BAR_BOTTOM;
+ case Gravity.TOP:
+ return ALT_BAR_TOP;
+ default:
+ return ALT_BAR_UNKNOWN;
+ }
+ }
+
TriConsumer<DisplayFrames, WindowState, Rect> getImeSourceFrameProvider() {
return (displayFrames, windowState, inOutFrame) -> {
if (mNavigationBar != null && navigationBarPosition(displayFrames.mDisplayWidth,
@@ -1149,12 +1212,14 @@ public class DisplayPolicy {
* @param win The window being removed.
*/
void removeWindowLw(WindowState win) {
- if (mStatusBar == win) {
+ if (mStatusBar == win || mStatusBarAlt == win) {
mStatusBar = null;
+ mStatusBarAlt = null;
mStatusBarController.setWindow(null);
mDisplayContent.setInsetProvider(ITYPE_STATUS_BAR, null, null);
- } else if (mNavigationBar == win) {
+ } else if (mNavigationBar == win || mNavigationBarAlt == win) {
mNavigationBar = null;
+ mNavigationBarAlt = null;
mNavigationBarController.setWindow(null);
mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, null, null);
} else if (mNotificationShade == win) {
@@ -1180,7 +1245,7 @@ public class DisplayPolicy {
}
WindowState getStatusBar() {
- return mStatusBar;
+ return mStatusBar != null ? mStatusBar : mStatusBarAlt;
}
WindowState getNotificationShade() {
@@ -1188,7 +1253,7 @@ public class DisplayPolicy {
}
WindowState getNavigationBar() {
- return mNavigationBar;
+ return mNavigationBar != null ? mNavigationBar : mNavigationBarAlt;
}
/**
@@ -1250,6 +1315,46 @@ public class DisplayPolicy {
return R.anim.dock_left_enter;
}
}
+ } else if (win == mStatusBarAlt || win == mNavigationBarAlt) {
+ if (win.getAttrs().windowAnimations != 0) {
+ return ANIMATION_STYLEABLE;
+ }
+
+ int pos = (win == mStatusBarAlt) ? mStatusBarAltPosition : mNavigationBarAltPosition;
+
+ boolean isExitOrHide = transit == TRANSIT_EXIT || transit == TRANSIT_HIDE;
+ boolean isEnterOrShow = transit == TRANSIT_ENTER || transit == TRANSIT_SHOW;
+
+ switch (pos) {
+ case ALT_BAR_LEFT:
+ if (isExitOrHide) {
+ return R.anim.dock_left_exit;
+ } else if (isEnterOrShow) {
+ return R.anim.dock_left_enter;
+ }
+ break;
+ case ALT_BAR_RIGHT:
+ if (isExitOrHide) {
+ return R.anim.dock_right_exit;
+ } else if (isEnterOrShow) {
+ return R.anim.dock_right_enter;
+ }
+ break;
+ case ALT_BAR_BOTTOM:
+ if (isExitOrHide) {
+ return R.anim.dock_bottom_exit;
+ } else if (isEnterOrShow) {
+ return R.anim.dock_bottom_enter;
+ }
+ break;
+ case ALT_BAR_TOP:
+ if (isExitOrHide) {
+ return R.anim.dock_top_exit;
+ } else if (isEnterOrShow) {
+ return R.anim.dock_top_enter;
+ }
+ break;
+ }
}
if (transit == TRANSIT_PREVIEW_DONE) {
@@ -1608,7 +1713,7 @@ public class DisplayPolicy {
mInputConsumer = null;
Slog.v(TAG, INPUT_CONSUMER_NAVIGATION + " dismissed.");
}
- } else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) {
+ } else if (mInputConsumer == null && getStatusBar() != null && canHideNavigationBar()) {
mInputConsumer = mDisplayContent.getInputMonitor().createInputConsumer(
mHandler.getLooper(),
INPUT_CONSUMER_NAVIGATION,
@@ -2694,10 +2799,10 @@ public class DisplayPolicy {
mDreamingLockscreen = mService.mPolicy.isKeyguardShowingAndNotOccluded();
}
- if (mStatusBar != null) {
+ if (getStatusBar() != null) {
if (DEBUG_LAYOUT) Slog.i(TAG, "force=" + mForceStatusBar
+ " top=" + mTopFullscreenOpaqueWindowState);
- final boolean forceShowStatusBar = (mStatusBar.getAttrs().privateFlags
+ final boolean forceShowStatusBar = (getStatusBar().getAttrs().privateFlags
& PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR) != 0;
final boolean notificationShadeForcesShowingNavigation =
mNotificationShade != null
@@ -3183,6 +3288,16 @@ public class DisplayPolicy {
return mNavigationBarPosition;
}
+ @WindowManagerPolicy.AltBarPosition
+ int getAlternateStatusBarPosition() {
+ return mStatusBarAltPosition;
+ }
+
+ @WindowManagerPolicy.AltBarPosition
+ int getAlternateNavBarPosition() {
+ return mNavigationBarAltPosition;
+ }
+
/**
* A new window has been focused.
*/
@@ -3344,8 +3459,8 @@ public class DisplayPolicy {
final boolean isFullscreen = (visibility & (View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)) != 0
|| (PolicyControl.getWindowFlags(win, win.mAttrs) & FLAG_FULLSCREEN) != 0
- || (mStatusBar != null && insetsPolicy.isHidden(ITYPE_STATUS_BAR))
- || (mNavigationBar != null && insetsPolicy.isHidden(
+ || (getStatusBar() != null && insetsPolicy.isHidden(ITYPE_STATUS_BAR))
+ || (getNavigationBar() != null && insetsPolicy.isHidden(
ITYPE_NAVIGATION_BAR));
final int behavior = win.mAttrs.insetsFlags.behavior;
final boolean isImmersive = (visibility & (View.SYSTEM_UI_FLAG_IMMERSIVE
@@ -3547,8 +3662,7 @@ public class DisplayPolicy {
// We need to force system bars when the docked stack is visible, when the freeform stack
// is focused but also when we are resizing for the transitions when docked stack
// visibility changes.
- mForceShowSystemBars = dockedStackVisible || win.inFreeformWindowingMode() || resizing
- || mForceShowSystemBarsFromExternal;
+ mForceShowSystemBars = dockedStackVisible || win.inFreeformWindowingMode() || resizing;
final boolean forceOpaqueStatusBar = mForceShowSystemBars && !isKeyguardShowing();
// apply translucent bar vis flags
@@ -3608,7 +3722,7 @@ public class DisplayPolicy {
final boolean hideNavBarSysui =
(vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0;
- final boolean transientStatusBarAllowed = mStatusBar != null
+ final boolean transientStatusBarAllowed = getStatusBar() != null
&& (notificationShadeHasFocus || (!mForceShowSystemBars
&& (hideStatusBarWM || (hideStatusBarSysui && immersiveSticky))));
@@ -3766,7 +3880,7 @@ public class DisplayPolicy {
// TODO(b/118118435): Remove this after migration
private boolean isImmersiveMode(int vis) {
final int flags = View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
- return mNavigationBar != null
+ return getNavigationBar() != null
&& (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
&& (vis & flags) != 0
&& canHideNavigationBar();
@@ -3774,7 +3888,7 @@ public class DisplayPolicy {
private boolean isImmersiveMode(WindowState win) {
final int behavior = win.mAttrs.insetsFlags.behavior;
- return mNavigationBar != null
+ return getNavigationBar() != null
&& canHideNavigationBar()
&& (behavior == BEHAVIOR_SHOW_BARS_BY_SWIPE
|| behavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE)
@@ -3855,8 +3969,8 @@ public class DisplayPolicy {
public void takeScreenshot(int screenshotType, int source) {
if (mScreenshotHelper != null) {
mScreenshotHelper.takeScreenshot(screenshotType,
- mStatusBar != null && mStatusBar.isVisibleLw(),
- mNavigationBar != null && mNavigationBar.isVisibleLw(),
+ getStatusBar() != null && getStatusBar().isVisibleLw(),
+ getNavigationBar() != null && getNavigationBar().isVisibleLw(),
source, mHandler, null /* completionConsumer */);
}
}
@@ -3894,6 +4008,11 @@ public class DisplayPolicy {
if (mStatusBar != null) {
pw.print(prefix); pw.print("mStatusBar="); pw.print(mStatusBar);
}
+ if (mStatusBarAlt != null) {
+ pw.print(prefix); pw.print("mStatusBarAlt="); pw.print(mStatusBarAlt);
+ pw.print(prefix); pw.print("mStatusBarAltPosition=");
+ pw.println(mStatusBarAltPosition);
+ }
if (mNotificationShade != null) {
pw.print(prefix); pw.print("mExpandedPanel="); pw.print(mNotificationShade);
}
@@ -3905,6 +4024,11 @@ public class DisplayPolicy {
pw.print(prefix); pw.print("mNavigationBarPosition=");
pw.println(mNavigationBarPosition);
}
+ if (mNavigationBarAlt != null) {
+ pw.print(prefix); pw.print("mNavigationBarAlt="); pw.println(mNavigationBarAlt);
+ pw.print(prefix); pw.print("mNavigationBarAltPosition=");
+ pw.println(mNavigationBarAltPosition);
+ }
if (mFocusedWindow != null) {
pw.print(prefix); pw.print("mFocusedWindow="); pw.println(mFocusedWindow);
}
@@ -3923,8 +4047,8 @@ public class DisplayPolicy {
}
pw.print(prefix); pw.print("mTopIsFullscreen="); pw.println(mTopIsFullscreen);
pw.print(prefix); pw.print("mForceStatusBar="); pw.print(mForceStatusBar);
- pw.print(prefix); pw.print("mForceShowSystemBarsFromExternal=");
- pw.print(mForceShowSystemBarsFromExternal);
+ pw.print(prefix); pw.print("mRemoteInsetsControllerControlsSystemBars");
+ pw.print(mDisplayContent.getInsetsPolicy().getRemoteInsetsControllerControlsSystemBars());
pw.print(" mAllowLockscreenWhenOn="); pw.println(mAllowLockscreenWhenOn);
mStatusBarController.dump(pw, prefix);
mNavigationBarController.dump(pw, prefix);
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 254356d673e3..82e7555c62ac 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -46,7 +46,9 @@ import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsAnimation;
import android.view.WindowInsetsAnimation.Bounds;
import android.view.WindowInsetsAnimationControlListener;
+import android.view.WindowManager;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.DisplayThread;
@@ -97,12 +99,31 @@ class InsetsPolicy {
private BarWindow mStatusBar = new BarWindow(StatusBarManager.WINDOW_STATUS_BAR);
private BarWindow mNavBar = new BarWindow(StatusBarManager.WINDOW_NAVIGATION_BAR);
private boolean mAnimatingShown;
+
+ /**
+ * Let remote insets controller control system bars regardless of other settings.
+ */
+ private boolean mRemoteInsetsControllerControlsSystemBars;
private final float[] mTmpFloat9 = new float[9];
InsetsPolicy(InsetsStateController stateController, DisplayContent displayContent) {
mStateController = stateController;
mDisplayContent = displayContent;
mPolicy = displayContent.getDisplayPolicy();
+ mRemoteInsetsControllerControlsSystemBars = mPolicy.getContext().getResources().getBoolean(
+ R.bool.config_remoteInsetsControllerControlsSystemBars);
+ }
+
+ boolean getRemoteInsetsControllerControlsSystemBars() {
+ return mRemoteInsetsControllerControlsSystemBars;
+ }
+
+ /**
+ * Used only for testing.
+ */
+ @VisibleForTesting
+ void setRemoteInsetsControllerControlsSystemBars(boolean controlsSystemBars) {
+ mRemoteInsetsControllerControlsSystemBars = controlsSystemBars;
}
/** Updates the target which can control system bars. */
@@ -256,6 +277,11 @@ class InsetsPolicy {
// Notification shade has control anyways, no reason to force anything.
return focusedWin;
}
+ if (remoteInsetsControllerControlsSystemBars(focusedWin)) {
+ mDisplayContent.mRemoteInsetsControlTarget.topFocusedWindowChanged(
+ focusedWin.mAttrs.packageName);
+ return mDisplayContent.mRemoteInsetsControlTarget;
+ }
if (forceShowsSystemBarsForWindowingMode) {
// Status bar is forcibly shown for the windowing mode which is a steady state.
// We don't want the client to control the status bar, and we will dispatch the real
@@ -285,6 +311,11 @@ class InsetsPolicy {
// Notification shade has control anyways, no reason to force anything.
return focusedWin;
}
+ if (remoteInsetsControllerControlsSystemBars(focusedWin)) {
+ mDisplayContent.mRemoteInsetsControlTarget.topFocusedWindowChanged(
+ focusedWin.mAttrs.packageName);
+ return mDisplayContent.mRemoteInsetsControlTarget;
+ }
if (forceShowsSystemBarsForWindowingMode) {
// Navigation bar is forcibly shown for the windowing mode which is a steady state.
// We don't want the client to control the navigation bar, and we will dispatch the real
@@ -300,6 +331,28 @@ class InsetsPolicy {
return focusedWin;
}
+ /**
+ * Determines whether the remote insets controller should take control of system bars for all
+ * windows.
+ */
+ boolean remoteInsetsControllerControlsSystemBars(@Nullable WindowState focusedWin) {
+ if (focusedWin == null) {
+ return false;
+ }
+ if (!mRemoteInsetsControllerControlsSystemBars) {
+ return false;
+ }
+ if (mDisplayContent == null || mDisplayContent.mRemoteInsetsControlTarget == null) {
+ // No remote insets control target to take control of insets.
+ return false;
+ }
+ // If necessary, auto can control application windows when
+ // config_remoteInsetsControllerControlsSystemBars is set to true. This is useful in cases
+ // where we want to dictate system bar inset state for applications.
+ return focusedWin.getAttrs().type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
+ && focusedWin.getAttrs().type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
+ }
+
private boolean forceShowsStatusBarTransiently() {
final WindowState win = mPolicy.getStatusBar();
return win != null && (win.mAttrs.privateFlags & PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR) != 0;
@@ -321,10 +374,7 @@ class InsetsPolicy {
// We need to force system bars when the docked stack is visible, when the freeform stack
// is visible but also when we are resizing for the transitions when docked stack
// visibility changes.
- return isDockedStackVisible
- || isFreeformStackVisible
- || isResizing
- || mPolicy.getForceShowSystemBars();
+ return isDockedStackVisible || isFreeformStackVisible || isResizing;
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 4700864c03bc..84a389349204 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2300,8 +2300,12 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
- boolean resumedOnDisplay = false;
final DisplayContent display = getChildAt(displayNdx);
+ if (display.shouldSleep()) {
+ continue;
+ }
+
+ boolean resumedOnDisplay = false;
for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
@@ -2390,7 +2394,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// process the keyguard going away, which can happen before the sleep
// token is released. As a result, it is important we resume the
// activity here.
- resumeFocusedStacksTopActivities();
+ stack.resumeTopActivityUncheckedLocked(null, null);
}
// The visibility update must not be called before resuming the top, so the
// display orientation can be updated first if needed. Otherwise there may
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ef81c0a5d206..b7ac54f3470e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2163,6 +2163,10 @@ public class WindowManagerService extends IWindowManager.Stub
throw new IllegalArgumentException(
"Window type can not be changed after the window is added.");
}
+ if (!Arrays.equals(win.mAttrs.providesInsetsTypes, attrs.providesInsetsTypes)) {
+ throw new IllegalArgumentException(
+ "Insets types can not be changed after the window is added.");
+ }
// Odd choice but less odd than embedding in copyFrom()
if ((attrs.privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY)
@@ -5827,27 +5831,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- @Override
- public void setForceShowSystemBars(boolean show) {
- boolean isAutomotive = mContext.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_AUTOMOTIVE);
- if (!isAutomotive) {
- throw new UnsupportedOperationException("Force showing system bars is only supported"
- + "for Automotive use cases.");
- }
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Caller does not hold permission "
- + android.Manifest.permission.STATUS_BAR);
- }
- synchronized (mGlobalLock) {
- final PooledConsumer c = PooledLambda.obtainConsumer(
- DisplayPolicy::setForceShowSystemBars, PooledLambda.__(), show);
- mRoot.forAllDisplayPolicies(c);
- c.recycle();
- }
- }
-
public void setNavBarVirtualKeyHapticFeedbackEnabled(boolean enabled) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR)
!= PackageManager.PERMISSION_GRANTED) {
diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index ec56e1ebc8e0..b5c9375fcc0d 100644
--- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -122,6 +122,7 @@ import com.android.server.testing.shadows.ShadowBackupDataOutput;
import com.android.server.testing.shadows.ShadowEventLog;
import com.android.server.testing.shadows.ShadowSystemServiceRegistry;
+import com.google.common.base.Charsets;
import com.google.common.truth.IterableSubject;
import org.junit.After;
@@ -1910,7 +1911,8 @@ public class KeyValueBackupTaskTest {
}
@Test
- public void testRunTask_whenTransportReturnsError_updatesFilesAndCleansUp() throws Exception {
+ public void testRunTask_whenTransportReturnsErrorForGenericPackage_updatesFilesAndCleansUp()
+ throws Exception {
TransportMock transportMock = setUpInitializedTransport(mTransport);
when(transportMock.transport.performBackup(
argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
@@ -1926,6 +1928,39 @@ public class KeyValueBackupTaskTest {
assertCleansUpFilesAndAgent(mTransport, PACKAGE_1);
}
+ /**
+ * Checks that TRANSPORT_ERROR during @pm@ backup keeps the state file untouched.
+ * http://b/144030477
+ */
+ @Test
+ public void testRunTask_whenTransportReturnsErrorForPm_updatesFilesAndCleansUp()
+ throws Exception {
+ // TODO(tobiast): Refactor this method to share code with
+ // testRunTask_whenTransportReturnsErrorForGenericPackage_updatesFilesAndCleansUp
+ // See patchset 7 of http://ag/11762961
+ final PackageData packageData = PM_PACKAGE;
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ when(transportMock.transport.performBackup(
+ argThat(packageInfo(packageData)), any(), anyInt()))
+ .thenReturn(BackupTransport.TRANSPORT_ERROR);
+
+ byte[] pmStateBytes = "fake @pm@ state for testing".getBytes(Charsets.UTF_8);
+
+ Path pmStatePath = createPmStateFile(pmStateBytes.clone());
+ PackageManagerBackupAgent pmAgent = spy(createPmAgent());
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, packageData);
+ runTask(task);
+ verify(pmAgent, never()).onBackup(any(), any(), any());
+
+ assertThat(Files.readAllBytes(pmStatePath)).isEqualTo(pmStateBytes.clone());
+
+ boolean existed = deletePmStateFile();
+ assertThat(existed).isTrue();
+ // unbindAgent() is skipped for @pm@. Comment in KeyValueBackupTask.java:
+ // "For PM metadata (for which applicationInfo is null) there is no agent-bound state."
+ assertCleansUpFiles(mTransport, packageData);
+ }
+
@Test
public void testRunTask_whenTransportGetBackupQuotaThrowsForPm() throws Exception {
TransportMock transportMock = setUpInitializedTransport(mTransport);
@@ -2707,21 +2742,29 @@ public class KeyValueBackupTaskTest {
* </ul>
* </ul>
*/
- private void createPmStateFile() throws IOException {
- createPmStateFile(mTransport);
+ private Path createPmStateFile() throws IOException {
+ return createPmStateFile("pmState".getBytes());
+ }
+
+ private Path createPmStateFile(byte[] bytes) throws IOException {
+ return createPmStateFile(bytes, mTransport);
+ }
+
+ private Path createPmStateFile(TransportData transport) throws IOException {
+ return createPmStateFile("pmState".getBytes(), mTransport);
}
- /** @see #createPmStateFile() */
- private void createPmStateFile(TransportData transport) throws IOException {
- Files.write(getStateFile(transport, PM_PACKAGE), "pmState".getBytes());
+ /** @see #createPmStateFile(byte[]) */
+ private Path createPmStateFile(byte[] bytes, TransportData transport) throws IOException {
+ return Files.write(getStateFile(transport, PM_PACKAGE), bytes);
}
/**
* Forces transport initialization and call to {@link
* UserBackupManagerService#resetBackupState(File)}
*/
- private void deletePmStateFile() throws IOException {
- Files.deleteIfExists(getStateFile(mTransport, PM_PACKAGE));
+ private boolean deletePmStateFile() throws IOException {
+ return Files.deleteIfExists(getStateFile(mTransport, PM_PACKAGE));
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
index 30bb38a075be..ad0b092b5543 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -28,6 +28,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
@@ -72,6 +73,8 @@ public class AuthServiceTest {
IIrisService mIrisService;
@Mock
IFaceService mFaceService;
+ @Mock
+ AppOpsManager mAppOpsManager;
@Before
public void setUp() {
@@ -90,6 +93,7 @@ public class AuthServiceTest {
when(mInjector.getFingerprintService()).thenReturn(mFingerprintService);
when(mInjector.getFaceService()).thenReturn(mFaceService);
when(mInjector.getIrisService()).thenReturn(mIrisService);
+ when(mInjector.getAppOps(any())).thenReturn(mAppOpsManager);
}
@Test
@@ -137,7 +141,9 @@ public class AuthServiceTest {
// TODO(b/141025588): Check that an exception is thrown when the userId != callingUserId
@Test
- public void testAuthenticate_callsBiometricServiceAuthenticate() throws Exception {
+ public void testAuthenticate_appOpsOk_callsBiometricServiceAuthenticate() throws Exception {
+ when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_USE_BIOMETRIC), anyInt(), any(), any(),
+ any())).thenReturn(AppOpsManager.MODE_ALLOWED);
mAuthService = new AuthService(mContext, mInjector);
mAuthService.onStart();
@@ -167,6 +173,38 @@ public class AuthServiceTest {
}
@Test
+ public void testAuthenticate_appOpsDenied_doesNotCallBiometricService() throws Exception {
+ when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_USE_BIOMETRIC), anyInt(), any(), any(),
+ any())).thenReturn(AppOpsManager.MODE_ERRORED);
+ mAuthService = new AuthService(mContext, mInjector);
+ mAuthService.onStart();
+
+ final Binder token = new Binder();
+ final Bundle bundle = new Bundle();
+ final long sessionId = 0;
+ final int userId = 0;
+
+ mAuthService.mImpl.authenticate(
+ token,
+ sessionId,
+ userId,
+ mReceiver,
+ TEST_OP_PACKAGE_NAME,
+ bundle);
+ waitForIdle();
+ verify(mBiometricService, never()).authenticate(
+ eq(token),
+ eq(sessionId),
+ eq(userId),
+ eq(mReceiver),
+ eq(TEST_OP_PACKAGE_NAME),
+ eq(bundle),
+ eq(Binder.getCallingUid()),
+ eq(Binder.getCallingPid()),
+ eq(UserHandle.getCallingUserId()));
+ }
+
+ @Test
public void testCanAuthenticate_callsBiometricServiceCanAuthenticate() throws Exception {
mAuthService = new AuthService(mContext, mInjector);
mAuthService.onStart();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index d6e038d8d027..85736ab11e65 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -96,13 +96,10 @@ import android.platform.test.annotations.Presubmit;
import android.util.DisplayMetrics;
import android.view.DisplayCutout;
import android.view.Gravity;
-import android.view.IDisplayWindowInsetsController;
import android.view.IDisplayWindowRotationCallback;
import android.view.IDisplayWindowRotationController;
import android.view.ISystemGestureExclusionListener;
import android.view.IWindowManager;
-import android.view.InsetsSourceControl;
-import android.view.InsetsState;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceControl.Transaction;
@@ -954,28 +951,6 @@ public class DisplayContentTests extends WindowTestsBase {
assertEquals(mAppWindow, mDisplayContent.computeImeControlTarget());
}
- private IDisplayWindowInsetsController createDisplayWindowInsetsController() {
- return new IDisplayWindowInsetsController.Stub() {
-
- @Override
- public void insetsChanged(InsetsState insetsState) throws RemoteException {
- }
-
- @Override
- public void insetsControlChanged(InsetsState insetsState,
- InsetsSourceControl[] insetsSourceControls) throws RemoteException {
- }
-
- @Override
- public void showInsets(int i, boolean b) throws RemoteException {
- }
-
- @Override
- public void hideInsets(int i, boolean b) throws RemoteException {
- }
- };
- }
-
@Test
public void testUpdateSystemGestureExclusion() throws Exception {
final DisplayContent dc = createNewDisplay();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 1922351ac1eb..2f3afbcb6d72 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -40,8 +40,13 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DEC
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
+import static android.view.WindowManagerPolicyConstants.ALT_BAR_BOTTOM;
+import static android.view.WindowManagerPolicyConstants.ALT_BAR_LEFT;
+import static android.view.WindowManagerPolicyConstants.ALT_BAR_RIGHT;
+import static android.view.WindowManagerPolicyConstants.ALT_BAR_TOP;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
@@ -60,6 +65,7 @@ import android.util.Pair;
import android.util.SparseArray;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
+import android.view.Gravity;
import android.view.InsetsState;
import android.view.WindowInsets.Side;
import android.view.WindowInsets.Type;
@@ -67,7 +73,6 @@ import android.view.WindowManager;
import androidx.test.filters.SmallTest;
-import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.utils.WmDisplayCutout;
import org.junit.Before;
@@ -148,6 +153,8 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
@Test
public void addingWindow_withInsetsTypes() {
+ mDisplayPolicy.removeWindowLw(mStatusBarWindow); // Removes the existing one.
+
WindowState win = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel");
win.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR, ITYPE_TOP_GESTURES};
win.getFrameLw().set(0, 0, 500, 100);
@@ -197,6 +204,47 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
}
@Test
+ public void addingWindow_variousGravities_alternateBarPosUpdated() {
+ mDisplayPolicy.removeWindowLw(mNavBarWindow); // Removes the existing one.
+
+ WindowState win1 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel1");
+ win1.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR};
+ win1.mAttrs.gravity = Gravity.TOP;
+ win1.getFrameLw().set(0, 0, 200, 500);
+ addWindow(win1);
+
+ assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_TOP);
+ mDisplayPolicy.removeWindowLw(win1);
+
+ WindowState win2 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel2");
+ win2.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR};
+ win2.mAttrs.gravity = Gravity.BOTTOM;
+ win2.getFrameLw().set(0, 0, 200, 500);
+ addWindow(win2);
+
+ assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_BOTTOM);
+ mDisplayPolicy.removeWindowLw(win2);
+
+ WindowState win3 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel3");
+ win3.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR};
+ win3.mAttrs.gravity = Gravity.LEFT;
+ win3.getFrameLw().set(0, 0, 200, 500);
+ addWindow(win3);
+
+ assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_LEFT);
+ mDisplayPolicy.removeWindowLw(win3);
+
+ WindowState win4 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel4");
+ win4.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR};
+ win4.mAttrs.gravity = Gravity.RIGHT;
+ win4.getFrameLw().set(0, 0, 200, 500);
+ addWindow(win4);
+
+ assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_RIGHT);
+ mDisplayPolicy.removeWindowLw(win4);
+ }
+
+ @Test
public void layoutWindowLw_fitStatusBars() {
mWindow.mAttrs.setFitInsetsTypes(Type.statusBars());
addWindow(mWindow);
@@ -807,27 +855,6 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
}
@Test
- public void forceShowSystemBars_clearsSystemUIFlags() {
- mDisplayPolicy.mLastSystemUiFlags |= SYSTEM_UI_FLAG_FULLSCREEN;
- mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
- mWindow.mAttrs.flags =
- FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
- mWindow.mSystemUiVisibility = SYSTEM_UI_FLAG_FULLSCREEN;
- mDisplayPolicy.setForceShowSystemBars(true);
- addWindow(mWindow);
-
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
- // triggers updateSystemUiVisibilityLw which will reset the flags as needed
- int finishPostLayoutPolicyLw = mDisplayPolicy.focusChangedLw(mWindow, mWindow);
-
- assertEquals(WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT, finishPostLayoutPolicyLw);
- assertEquals(0, mDisplayPolicy.mLastSystemUiFlags);
- assertEquals(0, mWindow.mAttrs.systemUiVisibility);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- }
-
- @Test
public void testScreenDecorWindows() {
final WindowState decorWindow = createWindow(null, TYPE_APPLICATION_OVERLAY, "decorWindow");
mWindow.mAttrs.flags = FLAG_NOT_FOCUSABLE | FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index c794e1a3b328..87bc7f1bf781 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -172,8 +172,9 @@ public class InsetsPolicyTest extends WindowTestsBase {
}
@Test
- public void testControlsForDispatch_forceShowSystemBarsFromExternal_appHasNoControl() {
- mDisplayContent.getDisplayPolicy().setForceShowSystemBars(true);
+ public void testControlsForDispatch_remoteInsetsControllerControlsBars_appHasNoControl() {
+ mDisplayContent.setRemoteInsetsController(createDisplayWindowInsetsController());
+ mDisplayContent.getInsetsPolicy().setRemoteInsetsControllerControlsSystemBars(true);
addWindow(TYPE_STATUS_BAR, "statusBar");
addWindow(TYPE_NAVIGATION_BAR, "navBar");
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index 51db099676b0..c848736a64c2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -896,6 +896,24 @@ public class RootActivityContainerTests extends ActivityTestsBase {
assertEquals(taskDisplayArea.getTopStack(), taskDisplayArea.getRootHomeTask());
}
+ @Test
+ public void testResumeFocusedStackOnSleepingDisplay() {
+ // Create an activity on secondary display.
+ final TestDisplayContent secondDisplay = addNewDisplayContentAt(
+ DisplayContent.POSITION_TOP);
+ final ActivityStack stack = secondDisplay.getDefaultTaskDisplayArea()
+ .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final ActivityRecord activity = new ActivityBuilder(mService).setStack(stack).build();
+ spyOn(activity);
+ spyOn(stack);
+
+ // Cannot resumed activities on secondary display if the display should sleep.
+ doReturn(true).when(secondDisplay).shouldSleep();
+ mRootWindowContainer.resumeFocusedStacksTopActivities();
+ verify(stack, never()).resumeTopActivityUncheckedLocked(any(), any());
+ verify(activity, never()).makeActiveIfNeeded(any());
+ }
+
/**
* Mock {@link RootWindowContainer#resolveHomeActivity} for returning consistent activity
* info for test cases.
diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
index 31a102ae3bad..ef74861e9422 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
@@ -25,8 +25,8 @@ import static android.view.Gravity.BOTTOM;
import static android.view.Gravity.LEFT;
import static android.view.Gravity.RIGHT;
import static android.view.Gravity.TOP;
-import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
-import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
+import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
@@ -190,7 +190,7 @@ public class ScreenDecorWindowTests {
@Test
public void testProvidesInsetsTypes() {
- int[] providesInsetsTypes = new int[]{ITYPE_STATUS_BAR};
+ int[] providesInsetsTypes = new int[]{ITYPE_CLIMATE_BAR};
final View win = createWindow("StatusBarSubPanel", TOP, MATCH_PARENT, mDecorThickness, RED,
FLAG_LAYOUT_IN_SCREEN, 0, providesInsetsTypes);
@@ -199,7 +199,7 @@ public class ScreenDecorWindowTests {
private View createDecorWindow(int gravity, int width, int height) {
int[] providesInsetsTypes =
- new int[]{gravity == TOP ? ITYPE_STATUS_BAR : ITYPE_NAVIGATION_BAR};
+ new int[]{gravity == TOP ? ITYPE_CLIMATE_BAR : ITYPE_EXTRA_NAVIGATION_BAR};
return createWindow("decorWindow", gravity, width, height, RED,
FLAG_LAYOUT_IN_SCREEN, PRIVATE_FLAG_IS_SCREEN_DECOR, providesInsetsTypes);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index f52905ef6ae9..499bf668decf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -60,24 +60,6 @@ public class WindowManagerServiceTests extends WindowTestsBase {
@Rule
public ExpectedException mExpectedException = ExpectedException.none();
- @Test
- public void testForceShowSystemBarsThrowsExceptionForNonAutomotive() {
- if (!isAutomotive()) {
- mExpectedException.expect(UnsupportedOperationException.class);
-
- mWm.setForceShowSystemBars(true);
- }
- }
-
- @Test
- public void testForceShowSystemBarsDoesNotThrowExceptionForAutomotiveWithStatusBarPermission() {
- if (isAutomotive()) {
- mExpectedException.none();
-
- mWm.setForceShowSystemBars(true);
- }
- }
-
private boolean isAutomotive() {
return getInstrumentation().getTargetContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_AUTOMOTIVE);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index a1e5b80eb2ed..156298c86d41 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -41,11 +41,15 @@ import static org.mockito.Mockito.mock;
import android.content.Context;
import android.content.Intent;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
import android.view.Display;
import android.view.DisplayInfo;
+import android.view.IDisplayWindowInsetsController;
import android.view.IWindow;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
import android.view.SurfaceControl.Transaction;
import android.view.View;
import android.view.WindowManager;
@@ -123,7 +127,7 @@ class WindowTestsBase extends SystemServiceTestsBase {
mChildAppWindowBelow = createCommonWindow(mAppWindow,
TYPE_APPLICATION_MEDIA_OVERLAY,
"mChildAppWindowBelow");
- mDisplayContent.getDisplayPolicy().setForceShowSystemBars(false);
+ mDisplayContent.getInsetsPolicy().setRemoteInsetsControllerControlsSystemBars(false);
// Adding a display will cause freezing the display. Make sure to wait until it's
// unfrozen to not run into race conditions with the tests.
@@ -344,6 +348,32 @@ class WindowTestsBase extends SystemServiceTestsBase {
return createNewDisplay(displayInfo, false /* supportIme */);
}
+ IDisplayWindowInsetsController createDisplayWindowInsetsController() {
+ return new IDisplayWindowInsetsController.Stub() {
+
+ @Override
+ public void insetsChanged(InsetsState insetsState) throws RemoteException {
+ }
+
+ @Override
+ public void insetsControlChanged(InsetsState insetsState,
+ InsetsSourceControl[] insetsSourceControls) throws RemoteException {
+ }
+
+ @Override
+ public void showInsets(int i, boolean b) throws RemoteException {
+ }
+
+ @Override
+ public void hideInsets(int i, boolean b) throws RemoteException {
+ }
+
+ @Override
+ public void topFocusedWindowChanged(String packageName) {
+ }
+ };
+ }
+
/** Sets the default minimum task size to 1 so that tests can use small task sizes */
void removeGlobalMinSizeRestriction() {
mWm.mAtmService.mRootWindowContainer.mDefaultMinSizeOfResizeableTaskDp = 1;
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index bf708d96e9b1..b6152e200e65 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2373,6 +2373,16 @@ public class CarrierConfigManager {
"call_forwarding_blocks_while_roaming_string_array";
/**
+ * Call forwarding number prefixes defined by {@link
+ * #KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY} which will be allowed while the
+ * device is reporting that it is roaming and IMS is registered over LTE or Wi-Fi.
+ * By default this value is {@code true}.
+ * @hide
+ */
+ public static final String KEY_SUPPORT_IMS_CALL_FORWARDING_WHILE_ROAMING_BOOL =
+ "support_ims_call_forwarding_while_roaming_bool";
+
+ /**
* The day of the month (1-31) on which the data cycle rolls over.
* <p>
* If the current month does not have this day, the cycle will roll over at
@@ -4213,6 +4223,7 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_SHOW_VIDEO_CALL_CHARGES_ALERT_DIALOG_BOOL, false);
sDefaults.putStringArray(KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY,
null);
+ sDefaults.putBoolean(KEY_SUPPORT_IMS_CALL_FORWARDING_WHILE_ROAMING_BOOL, true);
sDefaults.putInt(KEY_LTE_EARFCNS_RSRP_BOOST_INT, 0);
sDefaults.putStringArray(KEY_BOOSTED_LTE_EARFCNS_STRING_ARRAY, null);
sDefaults.putBoolean(KEY_USE_ONLY_RSRP_FOR_LTE_SIGNAL_BAR_BOOL, false);