summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp7
-rw-r--r--apex/media/framework/api/system-current.txt4
-rw-r--r--apex/media/framework/java/android/media/MediaTranscodeManager.java49
-rw-r--r--core/api/current.txt44
-rw-r--r--core/api/system-current.txt5
-rw-r--r--core/api/test-current.txt5
-rw-r--r--core/java/android/accessibilityservice/AccessibilityServiceInfo.java22
-rw-r--r--core/java/android/app/ContextImpl.java2
-rw-r--r--core/java/android/app/Notification.java6
-rw-r--r--core/java/android/app/WallpaperManager.java25
-rw-r--r--core/java/android/app/WindowTokenClient.java8
-rw-r--r--core/java/android/appwidget/AppWidgetHostView.java2
-rw-r--r--core/java/android/content/Intent.java27
-rw-r--r--core/java/android/hardware/display/DisplayManager.java3
-rw-r--r--core/java/android/net/vcn/VcnControlPlaneIkeConfig.java42
-rw-r--r--core/java/android/net/vcn/persistablebundleutils/CertUtils.java22
-rw-r--r--core/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java513
-rw-r--r--core/java/android/os/StrictMode.java5
-rw-r--r--core/java/android/os/incremental/IIncrementalService.aidl10
-rw-r--r--core/java/android/telephony/PhoneStateListener.java6
-rw-r--r--core/java/android/telephony/TelephonyCallback.java48
-rw-r--r--core/java/android/telephony/TelephonyRegistryManager.java5
-rw-r--r--core/java/android/util/FeatureFlagUtils.java2
-rw-r--r--core/java/android/view/Display.java88
-rw-r--r--core/java/android/view/DisplayInfo.java19
-rw-r--r--core/java/android/view/textclassifier/TextClassificationConstants.java5
-rw-r--r--core/java/com/android/internal/jank/FrameTracker.java32
-rw-r--r--core/java/com/android/internal/os/BinderCallsStats.java24
-rw-r--r--core/java/com/android/internal/telephony/IPhoneStateListener.aidl3
-rw-r--r--core/java/com/android/internal/util/ScreenRecordHelper.java49
-rw-r--r--core/res/res/values/attrs.xml9
-rw-r--r--core/res/res/values/public.xml1
-rw-r--r--core/res/res/values/strings.xml6
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java65
-rw-r--r--core/tests/mockingcoretests/src/android/view/DisplayTests.java531
-rw-r--r--data/etc/services.core.protolog.json6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt2
-rw-r--r--packages/Connectivity/service/Android.bp18
-rw-r--r--packages/Connectivity/service/jarjar-rules.txt13
-rw-r--r--packages/CtsShim/build/shim/AndroidManifest.xml3
-rw-r--r--packages/SystemUI/res/drawable/qs_footer_action_chip_background.xml37
-rw-r--r--packages/SystemUI/res/drawable/qs_footer_action_chip_background_borderless.xml35
-rw-r--r--packages/SystemUI/res/layout/qs_footer_impl.xml2
-rw-r--r--packages/SystemUI/res/layout/qs_footer_impl_two_lines.xml159
-rw-r--r--packages/SystemUI/res/layout/qs_panel.xml5
-rw-r--r--packages/SystemUI/res/values/colors.xml1
-rw-r--r--packages/SystemUI/res/values/config.xml2
-rw-r--r--packages/SystemUI/res/values/dimens.xml3
-rw-r--r--packages/SystemUI/res/values/flags.xml2
-rw-r--r--packages/SystemUI/res/values/strings.xml6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/SwipeHelper.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistManager.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/TimeLimitedMotionEventBuffer.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt117
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/CropView.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java238
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java63
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java55
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java37
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/classifier/TimeLimitedMotionEventBufferTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt147
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java21
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java38
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java43
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java34
-rw-r--r--services/core/java/com/android/server/appop/DiscreteRegistry.java97
-rw-r--r--services/core/java/com/android/server/appop/HistoricalRegistry.java9
-rw-r--r--services/core/java/com/android/server/clipboard/ClipboardService.java6
-rw-r--r--services/core/java/com/android/server/display/WifiDisplayAdapter.java7
-rw-r--r--services/core/java/com/android/server/hdmi/OneTouchPlayAction.java35
-rw-r--r--services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java2
-rw-r--r--services/core/java/com/android/server/locksettings/TEST_MAPPING11
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java26
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java10
-rw-r--r--services/core/java/com/android/server/power/FaceDownDetector.java71
-rw-r--r--services/core/java/com/android/server/vcn/VcnGatewayConnection.java4
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java32
-rw-r--r--services/core/java/com/android/server/wm/ConfigurationContainer.java3
-rw-r--r--services/incremental/BinderIncrementalService.cpp6
-rw-r--r--services/incremental/BinderIncrementalService.h3
-rw-r--r--services/incremental/IncrementalService.cpp57
-rw-r--r--services/incremental/IncrementalService.h8
-rw-r--r--services/incremental/test/IncrementalServiceTest.cpp69
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/power/FaceDownDetectorTest.java (renamed from services/tests/servicestests/src/com/android/server/power/FaceDownDetectorTest.java)148
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java420
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java178
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java1
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt74
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt91
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt59
-rw-r--r--tests/net/Android.bp2
-rw-r--r--tests/net/TEST_MAPPING17
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java32
-rw-r--r--tests/vcn/assets/client-end-cert.pem21
-rw-r--r--tests/vcn/assets/client-private-key.key28
-rw-r--r--tests/vcn/java/android/net/vcn/VcnControlPlaneIkeConfigTest.java7
-rw-r--r--tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java189
-rw-r--r--tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java30
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java7
125 files changed, 3952 insertions, 941 deletions
diff --git a/Android.bp b/Android.bp
index 3907734a171d..c5980b911b17 100644
--- a/Android.bp
+++ b/Android.bp
@@ -932,18 +932,21 @@ filegroup {
],
}
-// keep these files in sync with the package/Tethering/jarjar-rules.txt for the tethering module.
+// keep these files in sync with the package/Tethering/jarjar-rules.txt and
+// package/Connectivity/jarjar-rules.txt for the tethering module and connectivity module.
filegroup {
- name: "framework-tethering-shared-srcs",
+ name: "framework-connectivity-shared-srcs",
srcs: [
"core/java/android/util/IndentingPrintWriter.java",
"core/java/android/util/LocalLog.java",
+ // This should be android.util.IndentingPrintWriter, but it's not available in all branches.
"core/java/com/android/internal/util/IndentingPrintWriter.java",
"core/java/com/android/internal/util/IState.java",
"core/java/com/android/internal/util/MessageUtils.java",
"core/java/com/android/internal/util/State.java",
"core/java/com/android/internal/util/StateMachine.java",
"core/java/com/android/internal/util/TrafficStatsConstants.java",
+ "core/java/com/android/internal/util/WakeupMessage.java",
],
}
diff --git a/apex/media/framework/api/system-current.txt b/apex/media/framework/api/system-current.txt
index af2d2f75b823..6158e2ece55f 100644
--- a/apex/media/framework/api/system-current.txt
+++ b/apex/media/framework/api/system-current.txt
@@ -47,12 +47,16 @@ package android.media {
public static final class MediaTranscodeManager.TranscodingSession {
method public void cancel();
+ method public int getErrorCode();
method @IntRange(from=0, to=100) public int getProgress();
method public int getResult();
method public int getSessionId();
method public int getStatus();
method public void setOnProgressUpdateListener(@NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingSession.OnProgressUpdateListener);
method public void setOnProgressUpdateListener(int, @NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingSession.OnProgressUpdateListener);
+ field public static final int ERROR_DROPPED_BY_SERVICE = 1; // 0x1
+ field public static final int ERROR_NONE = 0; // 0x0
+ field public static final int ERROR_SERVICE_DIED = 2; // 0x2
field public static final int RESULT_CANCELED = 4; // 0x4
field public static final int RESULT_ERROR = 3; // 0x3
field public static final int RESULT_NONE = 1; // 0x1
diff --git a/apex/media/framework/java/android/media/MediaTranscodeManager.java b/apex/media/framework/java/android/media/MediaTranscodeManager.java
index b29bed978a21..30d1896a23ac 100644
--- a/apex/media/framework/java/android/media/MediaTranscodeManager.java
+++ b/apex/media/framework/java/android/media/MediaTranscodeManager.java
@@ -217,7 +217,8 @@ public final class MediaTranscodeManager {
// Updates the session status and result.
session.updateStatusAndResult(TranscodingSession.STATUS_FINISHED,
- TranscodingSession.RESULT_SUCCESS);
+ TranscodingSession.RESULT_SUCCESS,
+ TranscodingSession.ERROR_NONE);
// Notifies client the session is done.
if (session.mListener != null && session.mListenerExecutor != null) {
@@ -241,7 +242,7 @@ public final class MediaTranscodeManager {
// Updates the session status and result.
session.updateStatusAndResult(TranscodingSession.STATUS_FINISHED,
- TranscodingSession.RESULT_ERROR);
+ TranscodingSession.RESULT_ERROR, errorCode);
// Notifies client the session failed.
if (session.mListener != null && session.mListenerExecutor != null) {
@@ -330,7 +331,8 @@ public final class MediaTranscodeManager {
if (session.getStatus() == TranscodingSession.STATUS_RUNNING) {
session.updateStatusAndResult(TranscodingSession.STATUS_FINISHED,
- TranscodingSession.RESULT_ERROR);
+ TranscodingSession.RESULT_ERROR,
+ TranscodingSession.ERROR_SERVICE_DIED);
// Remove the session from pending sessions.
mPendingTranscodingSessions.remove(entry.getKey());
@@ -1239,6 +1241,33 @@ public final class MediaTranscodeManager {
@Retention(RetentionPolicy.SOURCE)
public @interface Result {}
+
+ // The error code exposed here should be in sync with:
+ // frameworks/av/media/libmediatranscoding/aidl/android/media/TranscodingErrorCode.aidl
+ /** @hide */
+ @IntDef(prefix = { "TRANSCODING_SESSION_ERROR_" }, value = {
+ ERROR_NONE,
+ ERROR_DROPPED_BY_SERVICE,
+ ERROR_SERVICE_DIED})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TranscodingSessionErrorCode{}
+ /**
+ * Constant indicating that no error occurred.
+ */
+ public static final int ERROR_NONE = 0;
+
+ /**
+ * Constant indicating that the session is dropped by Transcoding service due to hitting
+ * the limit, e.g. too many back to back transcoding happen in a short time frame.
+ */
+ public static final int ERROR_DROPPED_BY_SERVICE = 1;
+
+ /**
+ * Constant indicating the backing transcoding service is died. Client should enqueue the
+ * the request again.
+ */
+ public static final int ERROR_SERVICE_DIED = 2;
+
/** Listener that gets notified when the progress changes. */
@FunctionalInterface
public interface OnProgressUpdateListener {
@@ -1272,6 +1301,8 @@ public final class MediaTranscodeManager {
@GuardedBy("mLock")
private @Result int mResult = RESULT_NONE;
@GuardedBy("mLock")
+ private @TranscodingSessionErrorCode int mErrorCode = ERROR_NONE;
+ @GuardedBy("mLock")
private boolean mHasRetried = false;
// The original request that associated with this session.
private final TranscodingRequest mRequest;
@@ -1325,10 +1356,20 @@ public final class MediaTranscodeManager {
}
private void updateStatusAndResult(@Status int sessionStatus,
- @Result int sessionResult) {
+ @Result int sessionResult, @TranscodingSessionErrorCode int errorCode) {
synchronized (mLock) {
mStatus = sessionStatus;
mResult = sessionResult;
+ mErrorCode = errorCode;
+ }
+ }
+
+ /**
+ * Retrieve the error code associated with the RESULT_ERROR.
+ */
+ public @TranscodingSessionErrorCode int getErrorCode() {
+ synchronized (mLock) {
+ return mErrorCode;
}
}
diff --git a/core/api/current.txt b/core/api/current.txt
index 52e9b265246b..17acbf3e1771 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -818,6 +818,7 @@ package android {
field public static final int installLocation = 16843447; // 0x10102b7
field public static final int interactiveUiTimeout = 16844181; // 0x1010595
field public static final int interpolator = 16843073; // 0x1010141
+ field public static final int isAccessibilityTool = 16844353; // 0x1010641
field public static final int isAlwaysSyncable = 16843571; // 0x1010333
field public static final int isAsciiCapable = 16843753; // 0x10103e9
field public static final int isAuxiliary = 16843647; // 0x101037f
@@ -3102,6 +3103,7 @@ package android.accessibilityservice {
method public int getNonInteractiveUiTimeoutMillis();
method public android.content.pm.ResolveInfo getResolveInfo();
method public String getSettingsActivityName();
+ method public boolean isAccessibilityTool();
method public String loadDescription(android.content.pm.PackageManager);
method public CharSequence loadSummary(android.content.pm.PackageManager);
method public void setInteractiveUiTimeoutMillis(@IntRange(from=0) int);
@@ -3853,7 +3855,7 @@ package android.app {
method @Deprecated public void onTabUnselected(android.app.ActionBar.Tab, android.app.FragmentTransaction);
}
- public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
+ @UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
ctor public Activity();
method public void addContentView(android.view.View, android.view.ViewGroup.LayoutParams);
method public void closeContextMenu();
@@ -4769,9 +4771,9 @@ package android.app {
}
public class Dialog implements android.content.DialogInterface android.view.KeyEvent.Callback android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
- ctor public Dialog(@NonNull android.content.Context);
- ctor public Dialog(@NonNull android.content.Context, @StyleRes int);
- ctor protected Dialog(@NonNull android.content.Context, boolean, @Nullable android.content.DialogInterface.OnCancelListener);
+ ctor public Dialog(@NonNull @UiContext android.content.Context);
+ ctor public Dialog(@NonNull @UiContext android.content.Context, @StyleRes int);
+ ctor protected Dialog(@NonNull @UiContext android.content.Context, boolean, @Nullable android.content.DialogInterface.OnCancelListener);
method public void addContentView(@NonNull android.view.View, @Nullable android.view.ViewGroup.LayoutParams);
method public void cancel();
method public void closeOptionsMenu();
@@ -4785,7 +4787,7 @@ package android.app {
method public boolean dispatchTrackballEvent(@NonNull android.view.MotionEvent);
method public <T extends android.view.View> T findViewById(@IdRes int);
method @Nullable public android.app.ActionBar getActionBar();
- method @NonNull public final android.content.Context getContext();
+ method @NonNull @UiContext public final android.content.Context getContext();
method @Nullable public android.view.View getCurrentFocus();
method @NonNull public android.view.LayoutInflater getLayoutInflater();
method @Nullable public final android.app.Activity getOwnerActivity();
@@ -10344,10 +10346,10 @@ package android.content {
method @NonNull public android.content.Context createContext(@NonNull android.content.ContextParams);
method public abstract android.content.Context createContextForSplit(String) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract android.content.Context createDeviceProtectedStorageContext();
- method public abstract android.content.Context createDisplayContext(@NonNull android.view.Display);
+ method @DisplayContext public abstract android.content.Context createDisplayContext(@NonNull android.view.Display);
method public abstract android.content.Context createPackageContext(String, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method @NonNull public android.content.Context createWindowContext(int, @Nullable android.os.Bundle);
- method @NonNull public android.content.Context createWindowContext(@NonNull android.view.Display, int, @Nullable android.os.Bundle);
+ method @NonNull @UiContext public android.content.Context createWindowContext(int, @Nullable android.os.Bundle);
+ method @NonNull @UiContext public android.content.Context createWindowContext(@NonNull android.view.Display, int, @Nullable android.os.Bundle);
method public abstract String[] databaseList();
method public abstract boolean deleteDatabase(String);
method public abstract boolean deleteFile(String);
@@ -10512,7 +10514,7 @@ package android.content {
field public static final String JOB_SCHEDULER_SERVICE = "jobscheduler";
field public static final String KEYGUARD_SERVICE = "keyguard";
field public static final String LAUNCHER_APPS_SERVICE = "launcherapps";
- field public static final String LAYOUT_INFLATER_SERVICE = "layout_inflater";
+ field @UiContext public static final String LAYOUT_INFLATER_SERVICE = "layout_inflater";
field public static final String LOCATION_SERVICE = "location";
field public static final String MEDIA_COMMUNICATION_SERVICE = "media_communication";
field public static final String MEDIA_METRICS_SERVICE = "media_metrics";
@@ -10557,12 +10559,12 @@ package android.content {
field public static final String VIBRATOR_MANAGER_SERVICE = "vibrator_manager";
field public static final String VIBRATOR_SERVICE = "vibrator";
field public static final String VPN_MANAGEMENT_SERVICE = "vpn_management";
- field public static final String WALLPAPER_SERVICE = "wallpaper";
+ field @UiContext public static final String WALLPAPER_SERVICE = "wallpaper";
field public static final String WIFI_AWARE_SERVICE = "wifiaware";
field public static final String WIFI_P2P_SERVICE = "wifip2p";
field public static final String WIFI_RTT_RANGING_SERVICE = "wifirtt";
field public static final String WIFI_SERVICE = "wifi";
- field public static final String WINDOW_SERVICE = "window";
+ field @UiContext public static final String WINDOW_SERVICE = "window";
}
public final class ContextParams {
@@ -19056,7 +19058,7 @@ package android.inputmethodservice {
method public void startInternalChanges();
}
- public class InputMethodService extends android.inputmethodservice.AbstractInputMethodService {
+ @UiContext public class InputMethodService extends android.inputmethodservice.AbstractInputMethodService {
ctor public InputMethodService();
method @Deprecated public boolean enableHardwareAcceleration();
method public int getBackDisposition();
@@ -42140,7 +42142,7 @@ package android.telephony {
}
public static interface TelephonyCallback.CallStateListener {
- method @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) public void onCallStateChanged(int, @Nullable String);
+ method public void onCallStateChanged(int);
}
public static interface TelephonyCallback.CarrierNetworkListener {
@@ -46567,9 +46569,9 @@ package android.view {
public class GestureDetector {
ctor @Deprecated public GestureDetector(android.view.GestureDetector.OnGestureListener, android.os.Handler);
ctor @Deprecated public GestureDetector(android.view.GestureDetector.OnGestureListener);
- ctor public GestureDetector(android.content.Context, android.view.GestureDetector.OnGestureListener);
- ctor public GestureDetector(android.content.Context, android.view.GestureDetector.OnGestureListener, android.os.Handler);
- ctor public GestureDetector(android.content.Context, android.view.GestureDetector.OnGestureListener, android.os.Handler, boolean);
+ ctor public GestureDetector(@UiContext android.content.Context, android.view.GestureDetector.OnGestureListener);
+ ctor public GestureDetector(@UiContext android.content.Context, android.view.GestureDetector.OnGestureListener, android.os.Handler);
+ ctor public GestureDetector(@UiContext android.content.Context, android.view.GestureDetector.OnGestureListener, android.os.Handler, boolean);
method public boolean isLongpressEnabled();
method public boolean onGenericMotionEvent(android.view.MotionEvent);
method public boolean onTouchEvent(android.view.MotionEvent);
@@ -47258,7 +47260,7 @@ package android.view {
method public abstract android.view.LayoutInflater cloneInContext(android.content.Context);
method public final android.view.View createView(String, String, android.util.AttributeSet) throws java.lang.ClassNotFoundException, android.view.InflateException;
method @Nullable public final android.view.View createView(@NonNull android.content.Context, @NonNull String, @Nullable String, @Nullable android.util.AttributeSet) throws java.lang.ClassNotFoundException, android.view.InflateException;
- method public static android.view.LayoutInflater from(android.content.Context);
+ method public static android.view.LayoutInflater from(@UiContext android.content.Context);
method public android.content.Context getContext();
method public final android.view.LayoutInflater.Factory getFactory();
method public final android.view.LayoutInflater.Factory2 getFactory2();
@@ -48152,7 +48154,7 @@ package android.view {
method public final boolean getClipToOutline();
method @Nullable public final android.view.contentcapture.ContentCaptureSession getContentCaptureSession();
method public CharSequence getContentDescription();
- method public final android.content.Context getContext();
+ method @UiContext public final android.content.Context getContext();
method protected android.view.ContextMenu.ContextMenuInfo getContextMenuInfo();
method public final boolean getDefaultFocusHighlightEnabled();
method public static int getDefaultSize(int, int);
@@ -48951,7 +48953,7 @@ package android.view {
public class ViewConfiguration {
ctor @Deprecated public ViewConfiguration();
- method public static android.view.ViewConfiguration get(android.content.Context);
+ method public static android.view.ViewConfiguration get(@UiContext android.content.Context);
method @Deprecated @FloatRange(from=1.0) public static float getAmbiguousGestureMultiplier();
method public static long getDefaultActionModeHideDuration();
method public static int getDoubleTapTimeout();
@@ -49509,7 +49511,7 @@ package android.view {
}
public abstract class Window {
- ctor public Window(android.content.Context);
+ ctor public Window(@UiContext android.content.Context);
method public abstract void addContentView(android.view.View, android.view.ViewGroup.LayoutParams);
method public void addFlags(int);
method public final void addOnFrameMetricsAvailableListener(@NonNull android.view.Window.OnFrameMetricsAvailableListener, android.os.Handler);
@@ -49524,7 +49526,7 @@ package android.view {
method public int getColorMode();
method public final android.view.Window getContainer();
method public android.transition.Scene getContentScene();
- method public final android.content.Context getContext();
+ method @UiContext public final android.content.Context getContext();
method @Nullable public abstract android.view.View getCurrentFocus();
method @NonNull public abstract android.view.View getDecorView();
method public static int getDefaultFeatures(android.content.Context);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 5ca4d35d4c22..b0b2c6cdf0f7 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -380,7 +380,7 @@ package android.accounts {
package android.app {
- public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
+ @UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
method public void convertFromTranslucent();
method public boolean convertToTranslucent(android.app.Activity.TranslucentConversionListener, android.app.ActivityOptions);
method @Deprecated public boolean isBackgroundVisibleBehind();
@@ -11403,7 +11403,7 @@ package android.telephony {
field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_CALL_ATTRIBUTES_CHANGED = 27; // 0x1b
field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_CALL_DISCONNECT_CAUSE_CHANGED = 26; // 0x1a
field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_CALL_FORWARDING_INDICATOR_CHANGED = 4; // 0x4
- field @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) public static final int EVENT_CALL_STATE_CHANGED = 6; // 0x6
+ field public static final int EVENT_CALL_STATE_CHANGED = 6; // 0x6
field public static final int EVENT_CARRIER_NETWORK_CHANGED = 17; // 0x11
field @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static final int EVENT_CELL_INFO_CHANGED = 11; // 0xb
field @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static final int EVENT_CELL_LOCATION_CHANGED = 5; // 0x5
@@ -11415,6 +11415,7 @@ package android.telephony {
field public static final int EVENT_DISPLAY_INFO_CHANGED = 21; // 0x15
field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_EMERGENCY_NUMBER_LIST_CHANGED = 25; // 0x19
field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED = 28; // 0x1c
+ field @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) public static final int EVENT_LEGACY_CALL_STATE_CHANGED = 36; // 0x24
field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_MESSAGE_WAITING_INDICATOR_CHANGED = 3; // 0x3
field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_OEM_HOOK_RAW = 15; // 0xf
field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int EVENT_OUTGOING_EMERGENCY_CALL = 29; // 0x1d
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 7b5b1989c1e5..99ea1ab6570f 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -87,7 +87,7 @@ package android.animation {
package android.app {
- public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
+ @UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
method public void onMovedToDisplay(int, android.content.res.Configuration);
}
@@ -1176,7 +1176,7 @@ package android.hardware.soundtrigger {
package android.inputmethodservice {
- public class InputMethodService extends android.inputmethodservice.AbstractInputMethodService {
+ @UiContext public class InputMethodService extends android.inputmethodservice.AbstractInputMethodService {
field public static final long FINISH_INPUT_NO_FALLBACK_CONNECTION = 156215187L; // 0x94fa793L
}
@@ -2325,7 +2325,6 @@ package android.util {
field public static final String FFLAG_PREFIX = "sys.fflag.";
field public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid";
field public static final String PERSIST_PREFIX = "persist.sys.fflag.override.";
- field public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press";
field public static final String SETTINGS_WIFITRACKER2 = "settings_wifitracker2";
}
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index b15fa27485be..856ed507c524 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -580,6 +580,13 @@ public class AccessibilityServiceInfo implements Parcelable {
private int mHtmlDescriptionRes;
/**
+ * Whether the service is for accessibility.
+ *
+ * @hide
+ */
+ private boolean mIsAccessibilityTool = false;
+
+ /**
* Creates a new instance.
*/
public AccessibilityServiceInfo() {
@@ -708,6 +715,8 @@ public class AccessibilityServiceInfo implements Parcelable {
if (peekedValue != null) {
mHtmlDescriptionRes = peekedValue.resourceId;
}
+ mIsAccessibilityTool = asAttributes.getBoolean(
+ R.styleable.AccessibilityService_isAccessibilityTool, false);
asAttributes.recycle();
} catch (NameNotFoundException e) {
throw new XmlPullParserException( "Unable to create context for: "
@@ -1036,6 +1045,15 @@ public class AccessibilityServiceInfo implements Parcelable {
}
/**
+ * Indicates if the service is used to assist users with disabilities.
+ *
+ * @return {@code true} if the property is set to true.
+ */
+ public boolean isAccessibilityTool() {
+ return mIsAccessibilityTool;
+ }
+
+ /**
* {@inheritDoc}
*/
public int describeContents() {
@@ -1061,6 +1079,7 @@ public class AccessibilityServiceInfo implements Parcelable {
parcel.writeInt(mAnimatedImageRes);
parcel.writeInt(mHtmlDescriptionRes);
parcel.writeString(mNonLocalizedDescription);
+ parcel.writeBoolean(mIsAccessibilityTool);
}
private void initFromParcel(Parcel parcel) {
@@ -1082,6 +1101,7 @@ public class AccessibilityServiceInfo implements Parcelable {
mAnimatedImageRes = parcel.readInt();
mHtmlDescriptionRes = parcel.readInt();
mNonLocalizedDescription = parcel.readString();
+ mIsAccessibilityTool = parcel.readBoolean();
}
@Override
@@ -1136,6 +1156,8 @@ public class AccessibilityServiceInfo implements Parcelable {
stringBuilder.append(", ");
stringBuilder.append("summary: ").append(mNonLocalizedSummary);
stringBuilder.append(", ");
+ stringBuilder.append("isAccessibilityTool: ").append(mIsAccessibilityTool);
+ stringBuilder.append(", ");
appendCapabilities(stringBuilder, mCapabilities);
return stringBuilder.toString();
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 996c66b3437b..3af07635c185 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1995,7 +1995,7 @@ class ContextImpl extends Context {
final String errorMessage = "Tried to access visual service "
+ SystemServiceRegistry.getSystemServiceClassName(name)
+ " from a non-visual Context:" + getOuterContext();
- final String message = "Visual services, such as WindowManager, WallpaperService "
+ final String message = "Visual services, such as WindowManager"
+ "or LayoutInflater should be accessed from Activity or other visual "
+ "Context. Use an Activity or a Context created with "
+ "Context#createWindowContext(int, Bundle), which are adjusted to "
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index b00cfcb0d020..59b0032a151c 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1611,7 +1611,8 @@ public class Notification implements Parcelable
/**
* {@code SemanticAction}: Mark the conversation associated with the notification as a
- * priority. Note that this is only for use by the notification assistant services.
+ * priority. Note that this is only for use by the notification assistant services. The
+ * type will be ignored for actions an app adds to its own notifications.
* @hide
*/
@SystemApi
@@ -1619,7 +1620,8 @@ public class Notification implements Parcelable
/**
* {@code SemanticAction}: Mark content as a potential phishing attempt.
- * Note that this is only for use by the notification assistant services.
+ * Note that this is only for use by the notification assistant services. The type will
+ * be ignored for actions an app adds to its own notifications.
* @hide
*/
@SystemApi
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index bd99348b235a..8e53b5ba1c9f 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -650,7 +650,6 @@ public class WallpaperManager {
*/
@RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE)
public Drawable getDrawable() {
- assertUiContext("getDrawable");
final ColorManagementProxy cmProxy = getColorManagementProxy();
Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM, cmProxy);
if (bm != null) {
@@ -718,7 +717,6 @@ public class WallpaperManager {
*/
public Drawable getBuiltInDrawable(int outWidth, int outHeight, boolean scaleToFit,
float horizontalAlignment, float verticalAlignment, @SetWallpaperFlags int which) {
- assertUiContext("getBuiltInDrawable");
if (sGlobals.mService == null) {
Log.w(TAG, "WallpaperService not running");
throw new RuntimeException(new DeadSystemException());
@@ -884,7 +882,6 @@ public class WallpaperManager {
* null pointer if these is none.
*/
public Drawable peekDrawable() {
- assertUiContext("peekDrawable");
final ColorManagementProxy cmProxy = getColorManagementProxy();
Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false, FLAG_SYSTEM, cmProxy);
if (bm != null) {
@@ -927,7 +924,6 @@ public class WallpaperManager {
*/
@RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE)
public Drawable peekFastDrawable() {
- assertUiContext("peekFastDrawable");
final ColorManagementProxy cmProxy = getColorManagementProxy();
Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false, FLAG_SYSTEM, cmProxy);
if (bm != null) {
@@ -1084,6 +1080,7 @@ public class WallpaperManager {
return getWallpaperColors(which, mContext.getUserId());
}
+ // TODO(b/181083333): add multiple root display area support on this API.
/**
* Get the primary colors of the wallpaper configured in the given user.
* @param which wallpaper type. Must be either {@link #FLAG_SYSTEM} or
@@ -1094,7 +1091,7 @@ public class WallpaperManager {
*/
@UnsupportedAppUsage
public @Nullable WallpaperColors getWallpaperColors(int which, int userId) {
- assertUiContext("getWallpaperColors");
+ StrictMode.assertUiContext(mContext, "getWallpaperColors");
return sGlobals.getWallpaperColors(which, userId, mContext.getDisplayId());
}
@@ -1333,7 +1330,6 @@ public class WallpaperManager {
@RequiresPermission(android.Manifest.permission.SET_WALLPAPER)
public int setResource(@RawRes int resid, @SetWallpaperFlags int which)
throws IOException {
- assertUiContext("setResource");
if (sGlobals.mService == null) {
Log.w(TAG, "WallpaperService not running");
throw new RuntimeException(new DeadSystemException());
@@ -1637,6 +1633,7 @@ public class WallpaperManager {
}
}
+ // TODO(b/181083333): add multiple root display area support on this API.
/**
* Returns the desired minimum width for the wallpaper. Callers of
* {@link #setBitmap(android.graphics.Bitmap)} or
@@ -1654,7 +1651,7 @@ public class WallpaperManager {
* @see #getDesiredMinimumHeight()
*/
public int getDesiredMinimumWidth() {
- assertUiContext("getDesiredMinimumWidth");
+ StrictMode.assertUiContext(mContext, "getDesiredMinimumWidth");
if (sGlobals.mService == null) {
Log.w(TAG, "WallpaperService not running");
throw new RuntimeException(new DeadSystemException());
@@ -1666,6 +1663,7 @@ public class WallpaperManager {
}
}
+ // TODO(b/181083333): add multiple root display area support on this API.
/**
* Returns the desired minimum height for the wallpaper. Callers of
* {@link #setBitmap(android.graphics.Bitmap)} or
@@ -1683,7 +1681,7 @@ public class WallpaperManager {
* @see #getDesiredMinimumWidth()
*/
public int getDesiredMinimumHeight() {
- assertUiContext("getDesiredMinimumHeight");
+ StrictMode.assertUiContext(mContext, "getDesiredMinimumHeight");
if (sGlobals.mService == null) {
Log.w(TAG, "WallpaperService not running");
throw new RuntimeException(new DeadSystemException());
@@ -1695,6 +1693,7 @@ public class WallpaperManager {
}
}
+ // TODO(b/181083333): add multiple root display area support on this API.
/**
* For use only by the current home application, to specify the size of
* wallpaper it would like to use. This allows such applications to have
@@ -1714,7 +1713,7 @@ public class WallpaperManager {
* @param minimumHeight Desired minimum height
*/
public void suggestDesiredDimensions(int minimumWidth, int minimumHeight) {
- assertUiContext("suggestDesiredDimensions");
+ StrictMode.assertUiContext(mContext, "suggestDesiredDimensions");
try {
/**
* The framework makes no attempt to limit the window size
@@ -1757,6 +1756,7 @@ public class WallpaperManager {
}
}
+ // TODO(b/181083333): add multiple root display area support on this API.
/**
* Specify extra padding that the wallpaper should have outside of the display.
* That is, the given padding supplies additional pixels the wallpaper should extend
@@ -1770,7 +1770,7 @@ public class WallpaperManager {
*/
@RequiresPermission(android.Manifest.permission.SET_WALLPAPER_HINTS)
public void setDisplayPadding(Rect padding) {
- assertUiContext("setDisplayPadding");
+ StrictMode.assertUiContext(mContext, "setDisplayPadding");
try {
if (sGlobals.mService == null) {
Log.w(TAG, "WallpaperService not running");
@@ -2023,7 +2023,6 @@ public class WallpaperManager {
*/
@RequiresPermission(android.Manifest.permission.SET_WALLPAPER)
public void clear() throws IOException {
- assertUiContext("clear");
setStream(openDefaultWallpaper(mContext, FLAG_SYSTEM), null, false);
}
@@ -2176,10 +2175,6 @@ public class WallpaperManager {
return mCmProxy;
}
- private void assertUiContext(final String methodName) {
- StrictMode.assertUiContext(mContext, methodName);
- }
-
/**
* A hidden class to help {@link Globals#getCurrentWallpaperLocked} handle color management.
* @hide
diff --git a/core/java/android/app/WindowTokenClient.java b/core/java/android/app/WindowTokenClient.java
index 29792ac47a36..2298e84d755e 100644
--- a/core/java/android/app/WindowTokenClient.java
+++ b/core/java/android/app/WindowTokenClient.java
@@ -85,8 +85,10 @@ public class WindowTokenClient extends IWindowToken.Stub {
context.destroy();
mContextRef.clear();
}
- // If a secondary display is detached, release all views attached to this token.
- WindowManagerGlobal.getInstance().closeAll(this, mContextRef.getClass().getName(),
- "WindowContext");
+ ActivityThread.currentActivityThread().getHandler().post(() -> {
+ // If the tracked window token is detached, release all views attached to this token.
+ WindowManagerGlobal.getInstance().closeAll(WindowTokenClient.this,
+ "#onWindowTokenRemoved()", "WindowTokenClient");
+ });
}
}
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index d79fac58cf12..8fd0de7dbb39 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -849,6 +849,7 @@ public class AppWidgetHostView extends FrameLayout {
public void setColorResources(@NonNull SparseIntArray colorMapping) {
mColorResources = RemoteViews.ColorResources.create(mContext, colorMapping);
mLayoutId = -1;
+ mViewMode = VIEW_MODE_NOINIT;
reapplyLastRemoteViews();
}
@@ -863,6 +864,7 @@ public class AppWidgetHostView extends FrameLayout {
if (mColorResources != null) {
mColorResources = null;
mLayoutId = -1;
+ mViewMode = VIEW_MODE_NOINIT;
reapplyLastRemoteViews();
}
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index d352b273f882..de17fda82d71 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -6761,6 +6761,12 @@ public class Intent implements Parcelable, Cloneable {
* #putExtras(Bundle)} when the provided Bundle has not been unparceled.
*/
private static final int LOCAL_FLAG_UNFILTERED_EXTRAS = 1 << 3;
+
+ /**
+ * Local flag indicating this instance was created from a {@link Uri}.
+ */
+ private static final int LOCAL_FLAG_FROM_URI = 1 << 4;
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// toUri() and parseUri() options.
@@ -7173,6 +7179,16 @@ public class Intent implements Parcelable, Cloneable {
* @see #toUri
*/
public static Intent parseUri(String uri, @UriFlags int flags) throws URISyntaxException {
+ Intent intent = parseUriInternal(uri, flags);
+ intent.mLocalFlags |= LOCAL_FLAG_FROM_URI;
+ return intent;
+ }
+
+ /**
+ * @see #parseUri(String, int)
+ */
+ private static Intent parseUriInternal(String uri, @UriFlags int flags)
+ throws URISyntaxException {
int i = 0;
try {
final boolean androidApp = uri.startsWith("android-app:");
@@ -7392,7 +7408,9 @@ public class Intent implements Parcelable, Cloneable {
}
public static Intent getIntentOld(String uri) throws URISyntaxException {
- return getIntentOld(uri, 0);
+ Intent intent = getIntentOld(uri, 0);
+ intent.mLocalFlags |= LOCAL_FLAG_FROM_URI;
+ return intent;
}
private static Intent getIntentOld(String uri, int flags) throws URISyntaxException {
@@ -11353,6 +11371,13 @@ public class Intent implements Parcelable, Cloneable {
StrictMode.onUnsafeIntentLaunch(this);
} else if ((mLocalFlags & LOCAL_FLAG_UNFILTERED_EXTRAS) != 0) {
StrictMode.onUnsafeIntentLaunch(this);
+ } else if ((mLocalFlags & LOCAL_FLAG_FROM_URI) != 0
+ && !(mCategories != null && mCategories.contains(CATEGORY_BROWSABLE)
+ && mComponent == null)) {
+ // Since the docs for #URI_ALLOW_UNSAFE recommend setting the category to browsable
+ // for an implicit Intent parsed from a URI a violation should be reported if these
+ // conditions are not met.
+ StrictMode.onUnsafeIntentLaunch(this);
}
}
}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index bbf421da6b48..2c3e7f18a3ab 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -68,6 +68,9 @@ public final class DisplayManager {
* {@link #EXTRA_WIFI_DISPLAY_STATUS} extra.
* </p><p>
* This broadcast is only sent to registered receivers and can only be sent by the system.
+ * </p><p>
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission is required to
+ * receive this broadcast.
* </p>
* @hide
*/
diff --git a/core/java/android/net/vcn/VcnControlPlaneIkeConfig.java b/core/java/android/net/vcn/VcnControlPlaneIkeConfig.java
index de086f63b14d..22d7faf2fe18 100644
--- a/core/java/android/net/vcn/VcnControlPlaneIkeConfig.java
+++ b/core/java/android/net/vcn/VcnControlPlaneIkeConfig.java
@@ -19,11 +19,13 @@ package android.net.vcn;
import static android.net.vcn.VcnControlPlaneConfig.CONFIG_TYPE_IKE;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.net.ipsec.ike.IkeSessionParams;
import android.net.ipsec.ike.TunnelModeChildSessionParams;
+import android.net.vcn.persistablebundleutils.IkeSessionParamsUtils;
+import android.net.vcn.persistablebundleutils.TunnelModeChildSessionParamsUtils;
import android.os.PersistableBundle;
import android.util.ArraySet;
+import android.util.Log;
import java.util.Objects;
@@ -38,14 +40,11 @@ import java.util.Objects;
public final class VcnControlPlaneIkeConfig extends VcnControlPlaneConfig {
private static final String TAG = VcnControlPlaneIkeConfig.class.getSimpleName();
- // STOPSHIP: b/163604823 Make mIkeParams and mChildParams @NonNull when it is supported to
- // construct mIkeParams and mChildParams from PersistableBundles.
-
private static final String IKE_PARAMS_KEY = "mIkeParams";
- @Nullable private final IkeSessionParams mIkeParams;
+ @NonNull private final IkeSessionParams mIkeParams;
private static final String CHILD_PARAMS_KEY = "mChildParams";
- @Nullable private final TunnelModeChildSessionParams mChildParams;
+ @NonNull private final TunnelModeChildSessionParams mChildParams;
private static final ArraySet<String> BUNDLE_KEY_SET = new ArraySet<>();
@@ -80,11 +79,19 @@ public final class VcnControlPlaneIkeConfig extends VcnControlPlaneConfig {
final PersistableBundle ikeParamsBundle = in.getPersistableBundle(IKE_PARAMS_KEY);
final PersistableBundle childParamsBundle = in.getPersistableBundle(CHILD_PARAMS_KEY);
- // STOPSHIP: b/163604823 Support constructing mIkeParams and mChildParams from
- // PersistableBundles.
+ Objects.requireNonNull(ikeParamsBundle, "IKE Session Params was null");
+ Objects.requireNonNull(childParamsBundle, "Child Session Params was null");
+
+ mIkeParams = IkeSessionParamsUtils.fromPersistableBundle(ikeParamsBundle);
+ mChildParams = TunnelModeChildSessionParamsUtils.fromPersistableBundle(childParamsBundle);
+
+ for (String key : in.keySet()) {
+ if (!BUNDLE_KEY_SET.contains(key)) {
+ Log.w(TAG, "Found an unexpected key in the PersistableBundle: " + key);
+ }
+ }
- mIkeParams = null;
- mChildParams = null;
+ validate();
}
private void validate() {
@@ -101,9 +108,11 @@ public final class VcnControlPlaneIkeConfig extends VcnControlPlaneConfig {
@NonNull
public PersistableBundle toPersistableBundle() {
final PersistableBundle result = super.toPersistableBundle();
-
- // STOPSHIP: b/163604823 Support converting mIkeParams and mChildParams to
- // PersistableBundles.
+ result.putPersistableBundle(
+ IKE_PARAMS_KEY, IkeSessionParamsUtils.toPersistableBundle(mIkeParams));
+ result.putPersistableBundle(
+ CHILD_PARAMS_KEY,
+ TunnelModeChildSessionParamsUtils.toPersistableBundle(mChildParams));
return result;
}
@@ -134,10 +143,9 @@ public final class VcnControlPlaneIkeConfig extends VcnControlPlaneConfig {
VcnControlPlaneIkeConfig other = (VcnControlPlaneIkeConfig) o;
- // STOPSHIP: b/163604823 Also check mIkeParams and mChildParams when it is supported to
- // construct mIkeParams and mChildParams from PersistableBundles. They are not checked
- // now so that VcnGatewayConnectionConfigTest and VcnConfigTest can pass.
- return super.equals(o);
+ return super.equals(o)
+ && Objects.equals(mIkeParams, other.mIkeParams)
+ && Objects.equals(mChildParams, other.mChildParams);
}
/** @hide */
diff --git a/core/java/android/net/vcn/persistablebundleutils/CertUtils.java b/core/java/android/net/vcn/persistablebundleutils/CertUtils.java
index b6036b4a6fd1..35b318687773 100644
--- a/core/java/android/net/vcn/persistablebundleutils/CertUtils.java
+++ b/core/java/android/net/vcn/persistablebundleutils/CertUtils.java
@@ -18,18 +18,24 @@ package android.net.vcn.persistablebundleutils;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Objects;
/**
- * CertUtils provides utility methods for constructing Certificate.
+ * CertUtils provides utility methods for constructing Certificate and PrivateKey.
*
* @hide
*/
public class CertUtils {
private static final String CERT_TYPE_X509 = "X.509";
+ private static final String PRIVATE_KEY_TYPE_RSA = "RSA";
/** Decodes an ASN.1 DER encoded Certificate */
public static X509Certificate certificateFromByteArray(byte[] derEncoded) {
@@ -43,4 +49,18 @@ public class CertUtils {
throw new IllegalArgumentException("Fail to decode certificate", e);
}
}
+
+ /** Decodes a PKCS#8 encoded RSA private key */
+ public static RSAPrivateKey privateKeyFromByteArray(byte[] pkcs8Encoded) {
+ Objects.requireNonNull(pkcs8Encoded, "pkcs8Encoded was null");
+ PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(pkcs8Encoded);
+
+ try {
+ KeyFactory keyFactory = KeyFactory.getInstance(PRIVATE_KEY_TYPE_RSA);
+
+ return (RSAPrivateKey) keyFactory.generatePrivate(privateKeySpec);
+ } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
+ throw new IllegalArgumentException("Fail to decode PrivateKey", e);
+ }
+ }
}
diff --git a/core/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java b/core/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java
new file mode 100644
index 000000000000..9d3462cb0b2e
--- /dev/null
+++ b/core/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java
@@ -0,0 +1,513 @@
+/*
+ * Copyright (C) 2021 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 android.net.vcn.persistablebundleutils;
+
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.InetAddresses;
+import android.net.eap.EapSessionConfig;
+import android.net.ipsec.ike.IkeSaProposal;
+import android.net.ipsec.ike.IkeSessionParams;
+import android.net.ipsec.ike.IkeSessionParams.ConfigRequestIpv4PcscfServer;
+import android.net.ipsec.ike.IkeSessionParams.ConfigRequestIpv6PcscfServer;
+import android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig;
+import android.net.ipsec.ike.IkeSessionParams.IkeAuthDigitalSignLocalConfig;
+import android.net.ipsec.ike.IkeSessionParams.IkeAuthDigitalSignRemoteConfig;
+import android.net.ipsec.ike.IkeSessionParams.IkeAuthEapConfig;
+import android.net.ipsec.ike.IkeSessionParams.IkeAuthPskConfig;
+import android.net.ipsec.ike.IkeSessionParams.IkeConfigRequest;
+import android.os.PersistableBundle;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.vcn.util.PersistableBundleUtils;
+
+import java.net.InetAddress;
+import java.security.PrivateKey;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Abstract utility class to convert IkeSessionParams to/from PersistableBundle.
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = Visibility.PRIVATE)
+public final class IkeSessionParamsUtils {
+ private static final String SERVER_HOST_NAME_KEY = "SERVER_HOST_NAME_KEY";
+ private static final String SA_PROPOSALS_KEY = "SA_PROPOSALS_KEY";
+ private static final String LOCAL_ID_KEY = "LOCAL_ID_KEY";
+ private static final String REMOTE_ID_KEY = "REMOTE_ID_KEY";
+ private static final String LOCAL_AUTH_KEY = "LOCAL_AUTH_KEY";
+ private static final String REMOTE_AUTH_KEY = "REMOTE_AUTH_KEY";
+ private static final String CONFIG_REQUESTS_KEY = "CONFIG_REQUESTS_KEY";
+ private static final String RETRANS_TIMEOUTS_KEY = "RETRANS_TIMEOUTS_KEY";
+ private static final String HARD_LIFETIME_SEC_KEY = "HARD_LIFETIME_SEC_KEY";
+ private static final String SOFT_LIFETIME_SEC_KEY = "SOFT_LIFETIME_SEC_KEY";
+ private static final String DPD_DELAY_SEC_KEY = "DPD_DELAY_SEC_KEY";
+ private static final String NATT_KEEPALIVE_DELAY_SEC_KEY = "NATT_KEEPALIVE_DELAY_SEC_KEY";
+ private static final String IKE_OPTIONS_KEY = "IKE_OPTIONS_KEY";
+
+ private static final Set<Integer> IKE_OPTIONS = new ArraySet<>();
+
+ static {
+ IKE_OPTIONS.add(IkeSessionParams.IKE_OPTION_ACCEPT_ANY_REMOTE_ID);
+ IKE_OPTIONS.add(IkeSessionParams.IKE_OPTION_EAP_ONLY_AUTH);
+ IKE_OPTIONS.add(IkeSessionParams.IKE_OPTION_MOBIKE);
+ }
+
+ /** Serializes an IkeSessionParams to a PersistableBundle. */
+ @NonNull
+ public static PersistableBundle toPersistableBundle(@NonNull IkeSessionParams params) {
+ if (params.getConfiguredNetwork() != null || params.getIke3gppExtension() != null) {
+ throw new IllegalStateException(
+ "Cannot convert a IkeSessionParams with a caller configured network or with"
+ + " 3GPP extension enabled");
+ }
+
+ final PersistableBundle result = new PersistableBundle();
+
+ result.putString(SERVER_HOST_NAME_KEY, params.getServerHostname());
+
+ final PersistableBundle saProposalBundle =
+ PersistableBundleUtils.fromList(
+ params.getSaProposals(), IkeSaProposalUtils::toPersistableBundle);
+ result.putPersistableBundle(SA_PROPOSALS_KEY, saProposalBundle);
+
+ result.putPersistableBundle(
+ LOCAL_ID_KEY,
+ IkeIdentificationUtils.toPersistableBundle(params.getLocalIdentification()));
+ result.putPersistableBundle(
+ REMOTE_ID_KEY,
+ IkeIdentificationUtils.toPersistableBundle(params.getRemoteIdentification()));
+
+ result.putPersistableBundle(
+ LOCAL_AUTH_KEY, AuthConfigUtils.toPersistableBundle(params.getLocalAuthConfig()));
+ result.putPersistableBundle(
+ REMOTE_AUTH_KEY, AuthConfigUtils.toPersistableBundle(params.getRemoteAuthConfig()));
+
+ final List<ConfigRequest> reqList = new ArrayList<>();
+ for (IkeConfigRequest req : params.getConfigurationRequests()) {
+ reqList.add(new ConfigRequest(req));
+ }
+ final PersistableBundle configReqListBundle =
+ PersistableBundleUtils.fromList(reqList, ConfigRequest::toPersistableBundle);
+ result.putPersistableBundle(CONFIG_REQUESTS_KEY, configReqListBundle);
+
+ result.putIntArray(RETRANS_TIMEOUTS_KEY, params.getRetransmissionTimeoutsMillis());
+ result.putInt(HARD_LIFETIME_SEC_KEY, params.getHardLifetimeSeconds());
+ result.putInt(SOFT_LIFETIME_SEC_KEY, params.getSoftLifetimeSeconds());
+ result.putInt(DPD_DELAY_SEC_KEY, params.getDpdDelaySeconds());
+ result.putInt(NATT_KEEPALIVE_DELAY_SEC_KEY, params.getNattKeepAliveDelaySeconds());
+
+ final List<Integer> enabledIkeOptions = new ArrayList<>();
+ for (int option : IKE_OPTIONS) {
+ if (params.hasIkeOption(option)) {
+ enabledIkeOptions.add(option);
+ }
+ }
+
+ final int[] optionArray = enabledIkeOptions.stream().mapToInt(i -> i).toArray();
+ result.putIntArray(IKE_OPTIONS_KEY, optionArray);
+
+ return result;
+ }
+
+ /** Constructs an IkeSessionParams by deserializing a PersistableBundle. */
+ @NonNull
+ public static IkeSessionParams fromPersistableBundle(@NonNull PersistableBundle in) {
+ Objects.requireNonNull(in, "PersistableBundle is null");
+
+ final IkeSessionParams.Builder builder = new IkeSessionParams.Builder();
+
+ builder.setServerHostname(in.getString(SERVER_HOST_NAME_KEY));
+
+ PersistableBundle proposalBundle = in.getPersistableBundle(SA_PROPOSALS_KEY);
+ Objects.requireNonNull(in, "SA Proposals was null");
+ List<IkeSaProposal> saProposals =
+ PersistableBundleUtils.toList(
+ proposalBundle, IkeSaProposalUtils::fromPersistableBundle);
+ for (IkeSaProposal proposal : saProposals) {
+ builder.addSaProposal(proposal);
+ }
+
+ builder.setLocalIdentification(
+ IkeIdentificationUtils.fromPersistableBundle(
+ in.getPersistableBundle(LOCAL_ID_KEY)));
+ builder.setRemoteIdentification(
+ IkeIdentificationUtils.fromPersistableBundle(
+ in.getPersistableBundle(REMOTE_ID_KEY)));
+
+ AuthConfigUtils.setBuilderByReadingPersistableBundle(
+ in.getPersistableBundle(LOCAL_AUTH_KEY),
+ in.getPersistableBundle(REMOTE_AUTH_KEY),
+ builder);
+
+ builder.setRetransmissionTimeoutsMillis(in.getIntArray(RETRANS_TIMEOUTS_KEY));
+ builder.setLifetimeSeconds(
+ in.getInt(HARD_LIFETIME_SEC_KEY), in.getInt(SOFT_LIFETIME_SEC_KEY));
+ builder.setDpdDelaySeconds(in.getInt(DPD_DELAY_SEC_KEY));
+ builder.setNattKeepAliveDelaySeconds(in.getInt(NATT_KEEPALIVE_DELAY_SEC_KEY));
+
+ final PersistableBundle configReqListBundle = in.getPersistableBundle(CONFIG_REQUESTS_KEY);
+ Objects.requireNonNull(configReqListBundle, "Config request list was null");
+ final List<ConfigRequest> reqList =
+ PersistableBundleUtils.toList(configReqListBundle, ConfigRequest::new);
+ for (ConfigRequest req : reqList) {
+ switch (req.type) {
+ case ConfigRequest.IPV4_P_CSCF_ADDRESS:
+ if (req.address == null) {
+ builder.addPcscfServerRequest(AF_INET);
+ } else {
+ builder.addPcscfServerRequest(req.address);
+ }
+ break;
+ case ConfigRequest.IPV6_P_CSCF_ADDRESS:
+ if (req.address == null) {
+ builder.addPcscfServerRequest(AF_INET6);
+ } else {
+ builder.addPcscfServerRequest(req.address);
+ }
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Unrecognized config request type: " + req.type);
+ }
+ }
+
+ // Clear IKE Options that are by default enabled
+ for (int option : IKE_OPTIONS) {
+ builder.removeIkeOption(option);
+ }
+
+ final int[] optionArray = in.getIntArray(IKE_OPTIONS_KEY);
+ for (int option : optionArray) {
+ builder.addIkeOption(option);
+ }
+
+ return builder.build();
+ }
+
+ private static final class AuthConfigUtils {
+ private static final int IKE_AUTH_METHOD_PSK = 1;
+ private static final int IKE_AUTH_METHOD_PUB_KEY_SIGNATURE = 2;
+ private static final int IKE_AUTH_METHOD_EAP = 3;
+
+ private static final String AUTH_METHOD_KEY = "AUTH_METHOD_KEY";
+
+ @NonNull
+ public static PersistableBundle toPersistableBundle(@NonNull IkeAuthConfig authConfig) {
+ if (authConfig instanceof IkeAuthPskConfig) {
+ IkeAuthPskConfig config = (IkeAuthPskConfig) authConfig;
+ return IkeAuthPskConfigUtils.toPersistableBundle(
+ config, createPersistableBundle(IKE_AUTH_METHOD_PSK));
+ } else if (authConfig instanceof IkeAuthDigitalSignLocalConfig) {
+ IkeAuthDigitalSignLocalConfig config = (IkeAuthDigitalSignLocalConfig) authConfig;
+ return IkeAuthDigitalSignConfigUtils.toPersistableBundle(
+ config, createPersistableBundle(IKE_AUTH_METHOD_PUB_KEY_SIGNATURE));
+ } else if (authConfig instanceof IkeAuthDigitalSignRemoteConfig) {
+ IkeAuthDigitalSignRemoteConfig config = (IkeAuthDigitalSignRemoteConfig) authConfig;
+ return IkeAuthDigitalSignConfigUtils.toPersistableBundle(
+ config, createPersistableBundle(IKE_AUTH_METHOD_PUB_KEY_SIGNATURE));
+ } else if (authConfig instanceof IkeAuthEapConfig) {
+ IkeAuthEapConfig config = (IkeAuthEapConfig) authConfig;
+ return IkeAuthEapConfigUtils.toPersistableBundle(
+ config, createPersistableBundle(IKE_AUTH_METHOD_EAP));
+ } else {
+ throw new IllegalStateException("Invalid IkeAuthConfig subclass");
+ }
+ }
+
+ private static PersistableBundle createPersistableBundle(int type) {
+ final PersistableBundle result = new PersistableBundle();
+ result.putInt(AUTH_METHOD_KEY, type);
+ return result;
+ }
+
+ public static void setBuilderByReadingPersistableBundle(
+ @NonNull PersistableBundle localAuthBundle,
+ @NonNull PersistableBundle remoteAuthBundle,
+ @NonNull IkeSessionParams.Builder builder) {
+ Objects.requireNonNull(localAuthBundle, "localAuthBundle was null");
+ Objects.requireNonNull(remoteAuthBundle, "remoteAuthBundle was null");
+
+ final int localMethodType = localAuthBundle.getInt(AUTH_METHOD_KEY);
+ final int remoteMethodType = remoteAuthBundle.getInt(AUTH_METHOD_KEY);
+ switch (localMethodType) {
+ case IKE_AUTH_METHOD_PSK:
+ if (remoteMethodType != IKE_AUTH_METHOD_PSK) {
+ throw new IllegalArgumentException(
+ "Expect remote auth method to be PSK based, but was "
+ + remoteMethodType);
+ }
+ IkeAuthPskConfigUtils.setBuilderByReadingPersistableBundle(
+ localAuthBundle, remoteAuthBundle, builder);
+ return;
+ case IKE_AUTH_METHOD_PUB_KEY_SIGNATURE:
+ if (remoteMethodType != IKE_AUTH_METHOD_PUB_KEY_SIGNATURE) {
+ throw new IllegalArgumentException(
+ "Expect remote auth method to be digital signature based, but was "
+ + remoteMethodType);
+ }
+ IkeAuthDigitalSignConfigUtils.setBuilderByReadingPersistableBundle(
+ localAuthBundle, remoteAuthBundle, builder);
+ return;
+ case IKE_AUTH_METHOD_EAP:
+ if (remoteMethodType != IKE_AUTH_METHOD_PUB_KEY_SIGNATURE) {
+ throw new IllegalArgumentException(
+ "When using EAP for local authentication, expect remote auth"
+ + " method to be digital signature based, but was "
+ + remoteMethodType);
+ }
+ IkeAuthEapConfigUtils.setBuilderByReadingPersistableBundle(
+ localAuthBundle, remoteAuthBundle, builder);
+ return;
+ default:
+ throw new IllegalArgumentException(
+ "Invalid EAP method type " + localMethodType);
+ }
+ }
+ }
+
+ private static final class IkeAuthPskConfigUtils {
+ private static final String PSK_KEY = "PSK_KEY";
+
+ @NonNull
+ public static PersistableBundle toPersistableBundle(
+ @NonNull IkeAuthPskConfig config, @NonNull PersistableBundle result) {
+ result.putPersistableBundle(
+ PSK_KEY, PersistableBundleUtils.fromByteArray(config.getPsk()));
+ return result;
+ }
+
+ public static void setBuilderByReadingPersistableBundle(
+ @NonNull PersistableBundle localAuthBundle,
+ @NonNull PersistableBundle remoteAuthBundle,
+ @NonNull IkeSessionParams.Builder builder) {
+ Objects.requireNonNull(localAuthBundle, "localAuthBundle was null");
+ Objects.requireNonNull(remoteAuthBundle, "remoteAuthBundle was null");
+
+ final PersistableBundle localPskBundle = localAuthBundle.getPersistableBundle(PSK_KEY);
+ final PersistableBundle remotePskBundle =
+ remoteAuthBundle.getPersistableBundle(PSK_KEY);
+ Objects.requireNonNull(localAuthBundle, "Local PSK was null");
+ Objects.requireNonNull(remoteAuthBundle, "Remote PSK was null");
+
+ final byte[] localPsk = PersistableBundleUtils.toByteArray(localPskBundle);
+ final byte[] remotePsk = PersistableBundleUtils.toByteArray(remotePskBundle);
+ if (!Arrays.equals(localPsk, remotePsk)) {
+ throw new IllegalArgumentException("Local PSK and remote PSK are different");
+ }
+ builder.setAuthPsk(localPsk);
+ }
+ }
+
+ private static class IkeAuthDigitalSignConfigUtils {
+ private static final String END_CERT_KEY = "END_CERT_KEY";
+ private static final String INTERMEDIATE_CERTS_KEY = "INTERMEDIATE_CERTS_KEY";
+ private static final String PRIVATE_KEY_KEY = "PRIVATE_KEY_KEY";
+ private static final String TRUST_CERT_KEY = "TRUST_CERT_KEY";
+
+ @NonNull
+ public static PersistableBundle toPersistableBundle(
+ @NonNull IkeAuthDigitalSignLocalConfig config, @NonNull PersistableBundle result) {
+ try {
+ result.putPersistableBundle(
+ END_CERT_KEY,
+ PersistableBundleUtils.fromByteArray(
+ config.getClientEndCertificate().getEncoded()));
+
+ final List<X509Certificate> certList = config.getIntermediateCertificates();
+ final List<byte[]> encodedCertList = new ArrayList<>(certList.size());
+ for (X509Certificate cert : certList) {
+ encodedCertList.add(cert.getEncoded());
+ }
+
+ final PersistableBundle certsBundle =
+ PersistableBundleUtils.fromList(
+ encodedCertList, PersistableBundleUtils::fromByteArray);
+ result.putPersistableBundle(INTERMEDIATE_CERTS_KEY, certsBundle);
+ } catch (CertificateEncodingException e) {
+ throw new IllegalArgumentException("Fail to encode certificate");
+ }
+
+ // TODO: b/170670506 Consider putting PrivateKey in Android KeyStore
+ result.putPersistableBundle(
+ PRIVATE_KEY_KEY,
+ PersistableBundleUtils.fromByteArray(config.getPrivateKey().getEncoded()));
+ return result;
+ }
+
+ @NonNull
+ public static PersistableBundle toPersistableBundle(
+ @NonNull IkeAuthDigitalSignRemoteConfig config, @NonNull PersistableBundle result) {
+ try {
+ X509Certificate caCert = config.getRemoteCaCert();
+ if (caCert != null) {
+ result.putPersistableBundle(
+ TRUST_CERT_KEY,
+ PersistableBundleUtils.fromByteArray(caCert.getEncoded()));
+ }
+ } catch (CertificateEncodingException e) {
+ throw new IllegalArgumentException("Fail to encode the certificate");
+ }
+
+ return result;
+ }
+
+ public static void setBuilderByReadingPersistableBundle(
+ @NonNull PersistableBundle localAuthBundle,
+ @NonNull PersistableBundle remoteAuthBundle,
+ @NonNull IkeSessionParams.Builder builder) {
+ Objects.requireNonNull(localAuthBundle, "localAuthBundle was null");
+ Objects.requireNonNull(remoteAuthBundle, "remoteAuthBundle was null");
+
+ // Deserialize localAuth
+ final PersistableBundle endCertBundle =
+ localAuthBundle.getPersistableBundle(END_CERT_KEY);
+ Objects.requireNonNull(endCertBundle, "End cert was null");
+ final byte[] encodedCert = PersistableBundleUtils.toByteArray(endCertBundle);
+ final X509Certificate endCert = CertUtils.certificateFromByteArray(encodedCert);
+
+ final PersistableBundle certsBundle =
+ localAuthBundle.getPersistableBundle(INTERMEDIATE_CERTS_KEY);
+ Objects.requireNonNull(certsBundle, "Intermediate certs was null");
+ final List<byte[]> encodedCertList =
+ PersistableBundleUtils.toList(certsBundle, PersistableBundleUtils::toByteArray);
+ final List<X509Certificate> certList = new ArrayList<>(encodedCertList.size());
+ for (byte[] encoded : encodedCertList) {
+ certList.add(CertUtils.certificateFromByteArray(encoded));
+ }
+
+ final PersistableBundle privateKeyBundle =
+ localAuthBundle.getPersistableBundle(PRIVATE_KEY_KEY);
+ Objects.requireNonNull(privateKeyBundle, "PrivateKey bundle was null");
+ final PrivateKey privateKey =
+ CertUtils.privateKeyFromByteArray(
+ PersistableBundleUtils.toByteArray(privateKeyBundle));
+
+ // Deserialize remoteAuth
+ final PersistableBundle trustCertBundle =
+ remoteAuthBundle.getPersistableBundle(TRUST_CERT_KEY);
+
+ X509Certificate caCert = null;
+ if (trustCertBundle != null) {
+ final byte[] encodedCaCert = PersistableBundleUtils.toByteArray(trustCertBundle);
+ caCert = CertUtils.certificateFromByteArray(encodedCaCert);
+ }
+
+ builder.setAuthDigitalSignature(caCert, endCert, certList, privateKey);
+ }
+ }
+
+ private static final class IkeAuthEapConfigUtils {
+ private static final String EAP_CONFIG_KEY = "EAP_CONFIG_KEY";
+
+ @NonNull
+ public static PersistableBundle toPersistableBundle(
+ @NonNull IkeAuthEapConfig config, @NonNull PersistableBundle result) {
+ result.putPersistableBundle(
+ EAP_CONFIG_KEY,
+ EapSessionConfigUtils.toPersistableBundle(config.getEapConfig()));
+ return result;
+ }
+
+ public static void setBuilderByReadingPersistableBundle(
+ @NonNull PersistableBundle localAuthBundle,
+ @NonNull PersistableBundle remoteAuthBundle,
+ @NonNull IkeSessionParams.Builder builder) {
+ // Deserialize localAuth
+ final PersistableBundle eapBundle =
+ localAuthBundle.getPersistableBundle(EAP_CONFIG_KEY);
+ Objects.requireNonNull(eapBundle, "EAP Config was null");
+ final EapSessionConfig eapConfig =
+ EapSessionConfigUtils.fromPersistableBundle(eapBundle);
+
+ // Deserialize remoteAuth
+ final PersistableBundle trustCertBundle =
+ remoteAuthBundle.getPersistableBundle(
+ IkeAuthDigitalSignConfigUtils.TRUST_CERT_KEY);
+
+ X509Certificate serverCaCert = null;
+ if (trustCertBundle != null) {
+ final byte[] encodedCaCert = PersistableBundleUtils.toByteArray(trustCertBundle);
+ serverCaCert = CertUtils.certificateFromByteArray(encodedCaCert);
+ }
+ builder.setAuthEap(serverCaCert, eapConfig);
+ }
+ }
+
+ private static final class ConfigRequest {
+ private static final int IPV4_P_CSCF_ADDRESS = 1;
+ private static final int IPV6_P_CSCF_ADDRESS = 2;
+
+ private static final String TYPE_KEY = "type";
+ private static final String ADDRESS_KEY = "address";
+
+ public final int type;
+
+ // Null when it is an empty request
+ @Nullable public final InetAddress address;
+
+ ConfigRequest(IkeConfigRequest config) {
+ if (config instanceof ConfigRequestIpv4PcscfServer) {
+ type = IPV4_P_CSCF_ADDRESS;
+ address = ((ConfigRequestIpv4PcscfServer) config).getAddress();
+ } else if (config instanceof ConfigRequestIpv6PcscfServer) {
+ type = IPV6_P_CSCF_ADDRESS;
+ address = ((ConfigRequestIpv6PcscfServer) config).getAddress();
+ } else {
+ throw new IllegalStateException("Unknown TunnelModeChildConfigRequest");
+ }
+ }
+
+ ConfigRequest(PersistableBundle in) {
+ Objects.requireNonNull(in, "PersistableBundle was null");
+
+ type = in.getInt(TYPE_KEY);
+
+ String addressStr = in.getString(ADDRESS_KEY);
+ if (addressStr == null) {
+ address = null;
+ } else {
+ address = InetAddresses.parseNumericAddress(addressStr);
+ }
+ }
+
+ @NonNull
+ public PersistableBundle toPersistableBundle() {
+ final PersistableBundle result = new PersistableBundle();
+
+ result.putInt(TYPE_KEY, type);
+ if (address != null) {
+ result.putString(ADDRESS_KEY, address.getHostAddress());
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 1189fdb0b04a..df24baaf8ad9 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -2274,10 +2274,9 @@ public final class StrictMode {
*/
public static void assertUiContext(@NonNull Context context, @NonNull String methodName) {
if (vmIncorrectContextUseEnabled() && !context.isUiContext()) {
- final String errorMessage = "Tried to access UI related API" + methodName
+ final String errorMessage = "Tried to access UI related API:" + methodName
+ " from a non-UI Context:" + context;
- final String message = "UI-related services, such as WindowManager, WallpaperService "
- + "or LayoutInflater should be accessed from Activity or other UI "
+ final String message = methodName + " should be accessed from Activity or other UI "
+ "Contexts. Use an Activity or a Context created with "
+ "Context#createWindowContext(int, Bundle), which are adjusted to "
+ "the configuration and visual bounds of an area on screen.";
diff --git a/core/java/android/os/incremental/IIncrementalService.aidl b/core/java/android/os/incremental/IIncrementalService.aidl
index 3fbc28402405..d7bb2262063e 100644
--- a/core/java/android/os/incremental/IIncrementalService.aidl
+++ b/core/java/android/os/incremental/IIncrementalService.aidl
@@ -23,6 +23,7 @@ import android.os.incremental.IStorageLoadingProgressListener;
import android.os.incremental.IStorageHealthListener;
import android.os.incremental.PerUidReadTimeouts;
import android.os.incremental.StorageHealthCheckParams;
+import android.os.PersistableBundle;
/** @hide */
interface IIncrementalService {
@@ -165,4 +166,13 @@ interface IIncrementalService {
* Register storage health status listener.
*/
void unregisterStorageHealthListener(int storageId);
+
+ /**
+ * Metrics key for the duration in milliseconds between now and the oldest pending read. The value is a long.
+ */
+ const @utf8InCpp String METRICS_MILLIS_SINCE_OLDEST_PENDING_READ = "millisSinceOldestPendingRead";
+ /**
+ * Return a bundle containing the requested metrics keys and their values.
+ */
+ PersistableBundle getMetrics(int storageId);
}
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index e37921ec03cc..bbe887f500a9 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -1326,7 +1326,7 @@ public class PhoneStateListener {
() -> mExecutor.execute(() -> psl.onCellLocationChanged(location)));
}
- public void onCallStateChanged(int state, String incomingNumber) {
+ public void onLegacyCallStateChanged(int state, String incomingNumber) {
PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
if (psl == null) return;
@@ -1334,6 +1334,10 @@ public class PhoneStateListener {
() -> mExecutor.execute(() -> psl.onCallStateChanged(state, incomingNumber)));
}
+ public void onCallStateChanged(int state) {
+ // Only used for the new TelephonyCallback class
+ }
+
public void onDataConnectionStateChanged(int state, int networkType) {
PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
if (psl == null) return;
diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java
index a2584cae1b9c..2cadda25a9d3 100644
--- a/core/java/android/telephony/TelephonyCallback.java
+++ b/core/java/android/telephony/TelephonyCallback.java
@@ -20,11 +20,9 @@ import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.compat.annotation.ChangeId;
-import android.compat.annotation.UnsupportedAppUsage;
import android.os.Binder;
import android.os.Build;
import android.telephony.emergency.EmergencyNumber;
@@ -170,12 +168,15 @@ public class TelephonyCallback {
/**
* Event for changes to the device call state.
- *
+ * <p>
+ * Handles callbacks to {@link CallStateListener#onCallStateChanged(int)}.
+ * <p>
+ * Note: This is different from the legacy {@link #EVENT_LEGACY_CALL_STATE_CHANGED} listener
+ * which can include the phone number of the caller. We purposely do not include the phone
+ * number as that information is not required for call state listeners going forward.
* @hide
- * @see CallStateListener#onCallStateChanged
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.READ_CALL_LOG)
public static final int EVENT_CALL_STATE_CHANGED = 6;
/**
@@ -556,6 +557,18 @@ public class TelephonyCallback {
public static final int EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED = 35;
/**
+ * Event for changes to the legacy call state changed listener implemented by
+ * {@link PhoneStateListener#onCallStateChanged(int, String)}. This listener variant is similar
+ * to the new {@link CallStateListener#onCallStateChanged(int)} with the important distinction
+ * that it CAN provide the phone number associated with a call.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_CALL_LOG)
+ public static final int EVENT_LEGACY_CALL_STATE_CHANGED = 36;
+
+ /**
* @hide
*/
@IntDef(prefix = {"EVENT_"}, value = {
@@ -593,7 +606,8 @@ public class TelephonyCallback {
EVENT_BARRING_INFO_CHANGED,
EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED,
EVENT_DATA_ENABLED_CHANGED,
- EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED
+ EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED,
+ EVENT_LEGACY_CALL_STATE_CHANGED
})
@Retention(RetentionPolicy.SOURCE)
public @interface TelephonyEvent {
@@ -723,17 +737,9 @@ public class TelephonyCallback {
* calling {@link TelephonyManager#getCallState()} from within this callback may return a
* different state than the callback reports.
*
- * @param state call state
- * @param phoneNumber call phone number. If application does not have
- * {@link android.Manifest.permission#READ_CALL_LOG} permission or
- * carrier
- * privileges (see {@link TelephonyManager#hasCarrierPrivileges}), an
- * empty string will be
- * passed as an argument.
+ * @param state the current call state
*/
- @RequiresPermission(android.Manifest.permission.READ_CALL_LOG)
- public void onCallStateChanged(@Annotation.CallState int state,
- @Nullable String phoneNumber);
+ public void onCallStateChanged(@Annotation.CallState int state);
}
/**
@@ -1426,13 +1432,17 @@ public class TelephonyCallback {
() -> mExecutor.execute(() -> listener.onCellLocationChanged(location)));
}
- public void onCallStateChanged(int state, String incomingNumber) {
+ public void onLegacyCallStateChanged(int state, String incomingNumber) {
+ // Not used for TelephonyCallback; part of the AIDL which is used by both the legacy
+ // PhoneStateListener and TelephonyCallback.
+ }
+
+ public void onCallStateChanged(int state) {
CallStateListener listener = (CallStateListener) mTelephonyCallbackWeakRef.get();
if (listener == null) return;
Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(() -> listener.onCallStateChanged(state,
- incomingNumber)));
+ () -> mExecutor.execute(() -> listener.onCallStateChanged(state)));
}
public void onDataConnectionStateChanged(int state, int networkType) {
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 459c6e94e4ac..9cda4ae79335 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -861,6 +861,7 @@ public class TelephonyRegistryManager {
eventList.add(TelephonyCallback.EVENT_CELL_LOCATION_CHANGED);
}
+ // Note: Legacy PhoneStateListeners use EVENT_LEGACY_CALL_STATE_CHANGED
if (telephonyCallback instanceof TelephonyCallback.CallStateListener) {
eventList.add(TelephonyCallback.EVENT_CALL_STATE_CHANGED);
}
@@ -1000,8 +1001,10 @@ public class TelephonyRegistryManager {
eventList.add(TelephonyCallback.EVENT_CELL_LOCATION_CHANGED);
}
+ // Note: Legacy call state listeners can get the phone number which is not provided in the
+ // new version in TelephonyCallback.
if ((eventMask & PhoneStateListener.LISTEN_CALL_STATE) != 0) {
- eventList.add(TelephonyCallback.EVENT_CALL_STATE_CHANGED);
+ eventList.add(TelephonyCallback.EVENT_LEGACY_CALL_STATE_CHANGED);
}
if ((eventMask & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) {
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 4d321079416b..919c6e50e3a4 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -39,7 +39,6 @@ public class FeatureFlagUtils {
public static final String FFLAG_OVERRIDE_PREFIX = FFLAG_PREFIX + "override.";
public static final String PERSIST_PREFIX = "persist." + FFLAG_OVERRIDE_PREFIX;
public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid";
- public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press";
public static final String SETTINGS_WIFITRACKER2 = "settings_wifitracker2";
/** @hide */
public static final String SETTINGS_DO_NOT_RESTORE_PRESERVED =
@@ -59,7 +58,6 @@ public class FeatureFlagUtils {
DEFAULT_FLAGS.put("settings_audio_switcher", "true");
DEFAULT_FLAGS.put("settings_systemui_theme", "true");
DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false");
- DEFAULT_FLAGS.put(SCREENRECORD_LONG_PRESS, "false");
DEFAULT_FLAGS.put("settings_wifi_details_datausage_header", "false");
DEFAULT_FLAGS.put("settings_skip_direction_mutable", "true");
DEFAULT_FLAGS.put(SETTINGS_WIFITRACKER2, "true");
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index d138b4b41450..b98ef1410ebf 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -24,8 +24,11 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.TestApi;
+import android.app.ActivityThread;
import android.app.KeyguardManager;
+import android.app.WindowConfiguration;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ComponentName;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -44,11 +47,14 @@ import android.os.SystemClock;
import android.util.DisplayMetrics;
import android.util.Log;
+import com.android.internal.R;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Optional;
/**
* Provides information about the size and density of a logical display.
@@ -112,6 +118,12 @@ public final class Display {
private boolean mMayAdjustByFixedRotation;
/**
+ * Cache if the application is the recents component.
+ * TODO(b/179308296) Remove once Launcher addresses issue
+ */
+ private Optional<Boolean> mIsRecentsComponent = Optional.empty();
+
+ /**
* The default Display id, which is the id of the primary display assuming there is one.
*/
public static final int DEFAULT_DISPLAY = 0;
@@ -678,9 +690,9 @@ public final class Display {
@UnsupportedAppUsage
public DisplayAdjustments getDisplayAdjustments() {
if (mResources != null) {
- final DisplayAdjustments currentAdjustements = mResources.getDisplayAdjustments();
- if (!mDisplayAdjustments.equals(currentAdjustements)) {
- mDisplayAdjustments = new DisplayAdjustments(currentAdjustements);
+ final DisplayAdjustments currentAdjustments = mResources.getDisplayAdjustments();
+ if (!mDisplayAdjustments.equals(currentAdjustments)) {
+ mDisplayAdjustments = new DisplayAdjustments(currentAdjustments);
}
}
@@ -1278,6 +1290,18 @@ public final class Display {
public void getRealSize(Point outSize) {
synchronized (this) {
updateDisplayInfoLocked();
+ if (shouldReportMaxBounds()) {
+ final Rect bounds = mResources.getConfiguration()
+ .windowConfiguration.getMaxBounds();
+ outSize.x = bounds.width();
+ outSize.y = bounds.height();
+ if (DEBUG) {
+ Log.d(TAG, "getRealSize determined from max bounds: " + outSize);
+ }
+ // Skip adjusting by fixed rotation, since if it is necessary, the configuration
+ // should already reflect the expected rotation.
+ return;
+ }
outSize.x = mDisplayInfo.logicalWidth;
outSize.y = mDisplayInfo.logicalHeight;
if (mMayAdjustByFixedRotation) {
@@ -1336,6 +1360,17 @@ public final class Display {
public void getRealMetrics(DisplayMetrics outMetrics) {
synchronized (this) {
updateDisplayInfoLocked();
+ if (shouldReportMaxBounds()) {
+ mDisplayInfo.getMaxBoundsMetrics(outMetrics,
+ CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO,
+ mResources.getConfiguration());
+ if (DEBUG) {
+ Log.d(TAG, "getRealMetrics determined from max bounds: " + outMetrics);
+ }
+ // Skip adjusting by fixed rotation, since if it is necessary, the configuration
+ // should already reflect the expected rotation.
+ return;
+ }
mDisplayInfo.getLogicalMetrics(outMetrics,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
if (mMayAdjustByFixedRotation) {
@@ -1345,6 +1380,53 @@ public final class Display {
}
/**
+ * Determines if {@link WindowConfiguration#getMaxBounds()} should be reported as the
+ * display dimensions. The max bounds field may be smaller than the logical dimensions
+ * when apps need to be sandboxed.
+ *
+ * Depends upon {@link WindowConfiguration#getMaxBounds()} being set in
+ * {@link com.android.server.wm.ConfigurationContainer#providesMaxBounds()}. In most cases, this
+ * value reflects the size of the current DisplayArea.
+ * @return {@code true} when max bounds should be applied.
+ */
+ private boolean shouldReportMaxBounds() {
+ if (mResources == null) {
+ return false;
+ }
+ final Configuration config = mResources.getConfiguration();
+ // TODO(b/179308296) Temporarily exclude Launcher from being given max bounds, by checking
+ // if the caller is the recents component.
+ return config != null && !config.windowConfiguration.getMaxBounds().isEmpty()
+ && !isRecentsComponent();
+ }
+
+ /**
+ * Returns {@code true} when the calling package is the recents component.
+ * TODO(b/179308296) Remove once Launcher addresses issue
+ */
+ boolean isRecentsComponent() {
+ if (mIsRecentsComponent.isPresent()) {
+ return mIsRecentsComponent.get();
+ }
+ if (mResources == null) {
+ return false;
+ }
+ try {
+ String recentsComponent = mResources.getString(R.string.config_recentsComponentName);
+ if (recentsComponent == null) {
+ return false;
+ }
+ String recentsPackage = ComponentName.unflattenFromString(recentsComponent)
+ .getPackageName();
+ mIsRecentsComponent = Optional.of(recentsPackage != null
+ && recentsPackage.equals(ActivityThread.currentPackageName()));
+ return mIsRecentsComponent.get();
+ } catch (Resources.NotFoundException e) {
+ return false;
+ }
+ }
+
+ /**
* Gets the state of the display, such as whether it is on or off.
*
* @return The state of the display: one of {@link #STATE_OFF}, {@link #STATE_ON},
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 655f42308a1f..a8aaeb7846a2 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -24,6 +24,7 @@ import static android.view.DisplayInfoProto.LOGICAL_WIDTH;
import static android.view.DisplayInfoProto.NAME;
import android.annotation.Nullable;
+import android.app.WindowConfiguration;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -615,11 +616,29 @@ public final class DisplayInfo implements Parcelable {
getMetricsWithSize(outMetrics, ci, configuration, appWidth, appHeight);
}
+ /**
+ * Populates {@code outMetrics} with details of the logical display. Bounds are limited
+ * by the logical size of the display.
+ *
+ * @param outMetrics the {@link DisplayMetrics} to be populated
+ * @param compatInfo the {@link CompatibilityInfo} to be applied
+ * @param configuration the {@link Configuration}
+ */
public void getLogicalMetrics(DisplayMetrics outMetrics, CompatibilityInfo compatInfo,
Configuration configuration) {
getMetricsWithSize(outMetrics, compatInfo, configuration, logicalWidth, logicalHeight);
}
+ /**
+ * Similar to {@link #getLogicalMetrics}, but the limiting bounds are determined from
+ * {@link WindowConfiguration#getMaxBounds()}
+ */
+ public void getMaxBoundsMetrics(DisplayMetrics outMetrics, CompatibilityInfo compatInfo,
+ Configuration configuration) {
+ Rect bounds = configuration.windowConfiguration.getMaxBounds();
+ getMetricsWithSize(outMetrics, compatInfo, configuration, bounds.width(), bounds.height());
+ }
+
public int getNaturalWidth() {
return rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180 ?
logicalWidth : logicalHeight;
diff --git a/core/java/android/view/textclassifier/TextClassificationConstants.java b/core/java/android/view/textclassifier/TextClassificationConstants.java
index d0959f97e438..5f3159cd09c8 100644
--- a/core/java/android/view/textclassifier/TextClassificationConstants.java
+++ b/core/java/android/view/textclassifier/TextClassificationConstants.java
@@ -91,8 +91,9 @@ public final class TextClassificationConstants {
"system_textclassifier_api_timeout_in_second";
/**
- * The max amount of characters before and after the selected text that are passed to the
- * TextClassifier for the smart selection.
+ * The maximum amount of characters before and after the selected text that is passed to the
+ * TextClassifier for the smart selection. e.g. If this value is 100, then 100 characters before
+ * the selection and 100 characters after the selection will be passed to the TextClassifier.
*/
private static final String SMART_SELECTION_TRIM_DELTA = "smart_selection_trim_delta";
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index c897002c88e9..5829047ad642 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -16,7 +16,6 @@
package com.android.internal.jank;
-import static android.view.SurfaceControl.JankData.BUFFER_STUFFING;
import static android.view.SurfaceControl.JankData.DISPLAY_HAL;
import static android.view.SurfaceControl.JankData.JANK_APP_DEADLINE_MISSED;
import static android.view.SurfaceControl.JankData.JANK_NONE;
@@ -42,6 +41,7 @@ import android.view.SurfaceControl.JankData.JankType;
import android.view.ThreadedRenderer;
import android.view.ViewRootImpl;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor.Session;
import com.android.internal.util.FrameworkStatsLog;
@@ -163,6 +163,8 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
}, 50);
}
};
+
+ // This callback has a reference to FrameTracker, remember to remove it to avoid leakage.
viewRootWrapper.addSurfaceChangedCallback(mSurfaceChangedCallback);
}
@@ -187,9 +189,13 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
*/
public synchronized void end() {
mEndVsyncId = mChoreographer.getVsyncId();
- Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId);
- if (mEndVsyncId == mBeginVsyncId) {
+ // Cancel the session if:
+ // 1. The session begins and ends at the same vsync id.
+ // 2. The session never begun.
+ if (mEndVsyncId == mBeginVsyncId || mBeginVsyncId == INVALID_ID) {
cancel();
+ } else {
+ Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId);
}
// We don't remove observer here,
// will remove it when all the frame metrics in this duration are called back.
@@ -200,11 +206,19 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
* Cancel the trace session of the CUJ.
*/
public synchronized void cancel() {
- if (mBeginVsyncId == INVALID_ID || mEndVsyncId != INVALID_ID) return;
- Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId);
+ // The session is ongoing, end the trace session.
+ // That means the cancel call is from external invocation, not from end().
+ if (mBeginVsyncId != INVALID_ID && mEndVsyncId == INVALID_ID) {
+ Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId);
+ }
mCancelled = true;
+
+ // Always remove the observers in cancel call to avoid leakage.
removeObservers();
- if (mListener != null) {
+
+ // Notify the listener the session has been cancelled.
+ // We don't notify the listeners if the session never begun.
+ if (mListener != null && mBeginVsyncId != INVALID_ID) {
mListener.onNotifyCujEvents(mSession, InteractionJankMonitor.ACTION_SESSION_CANCEL);
}
}
@@ -393,7 +407,11 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
}
}
- private void removeObservers() {
+ /**
+ * Remove all the registered listeners, observers and callbacks.
+ */
+ @VisibleForTesting
+ public void removeObservers() {
mRendererWrapper.removeObserver(mObserver);
mSurfaceControlWrapper.removeJankStatsListener(this);
if (mSurfaceChangedCallback != null) {
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index b3e8db205220..14b870575769 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -120,8 +120,13 @@ public class BinderCallsStats implements BinderInternal.Observer {
UidEntry uidEntry = mUidEntries.get(mSendUidsToObserver.valueAt(i));
if (uidEntry != null) {
ArrayMap<CallStatKey, CallStat> callStats = uidEntry.mCallStats;
+ final int csize = callStats.size();
+ final ArrayList<CallStat> tmpCallStats = new ArrayList<>(csize);
+ for (int j = 0; j < csize; j++) {
+ tmpCallStats.add(callStats.valueAt(j).clone());
+ }
mCallStatsObserver.noteCallStats(uidEntry.workSourceUid,
- uidEntry.incrementalCallCount, callStats.values()
+ uidEntry.incrementalCallCount, tmpCallStats
);
uidEntry.incrementalCallCount = 0;
for (int j = callStats.size() - 1; j >= 0; j--) {
@@ -830,6 +835,23 @@ public class BinderCallsStats implements BinderInternal.Observer {
}
@Override
+ public CallStat clone() {
+ CallStat clone = new CallStat(callingUid, binderClass, transactionCode,
+ screenInteractive);
+ clone.recordedCallCount = recordedCallCount;
+ clone.callCount = callCount;
+ clone.cpuTimeMicros = cpuTimeMicros;
+ clone.maxCpuTimeMicros = maxCpuTimeMicros;
+ clone.latencyMicros = latencyMicros;
+ clone.maxLatencyMicros = maxLatencyMicros;
+ clone.maxRequestSizeBytes = maxRequestSizeBytes;
+ clone.maxReplySizeBytes = maxReplySizeBytes;
+ clone.exceptionCount = exceptionCount;
+ clone.incrementalCallCount = incrementalCallCount;
+ return clone;
+ }
+
+ @Override
public String toString() {
// This is expensive, but CallStat.toString() is only used for debugging.
String methodName = new BinderTransactionNameResolver().getMethodName(binderClass,
diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
index ee94ef8ddda3..85114e59cc2d 100644
--- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -42,7 +42,8 @@ oneway interface IPhoneStateListener {
// Uses CellIdentity which is Parcelable here; will convert to CellLocation in client.
void onCellLocationChanged(in CellIdentity location);
- void onCallStateChanged(int state, String incomingNumber);
+ void onLegacyCallStateChanged(int state, String incomingNumber);
+ void onCallStateChanged(int state);
void onDataConnectionStateChanged(int state, int networkType);
void onDataActivity(int direction);
void onSignalStrengthsChanged(in SignalStrength signalStrength);
diff --git a/core/java/com/android/internal/util/ScreenRecordHelper.java b/core/java/com/android/internal/util/ScreenRecordHelper.java
deleted file mode 100644
index ec7ed4e0008a..000000000000
--- a/core/java/com/android/internal/util/ScreenRecordHelper.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2018 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.internal.util;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-
-/**
- * Helper class to initiate a screen recording
- */
-public class ScreenRecordHelper {
- private final Context mContext;
-
- /**
- * Create a new ScreenRecordHelper for the given context
- * @param context
- */
- public ScreenRecordHelper(Context context) {
- mContext = context;
- }
-
- /**
- * Show dialog of screen recording options to user.
- */
- public void launchRecordPrompt() {
- final ComponentName launcherComponent = ComponentName.unflattenFromString(
- mContext.getResources().getString(
- com.android.internal.R.string.config_screenRecorderComponent));
- final Intent intent = new Intent();
- intent.setComponent(launcherComponent);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mContext.startActivity(intent);
- }
-}
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 05d29c2685f4..e0a116be371c 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3853,6 +3853,15 @@
<!-- Attribute whether the accessibility service wants to be able to take screenshot. -->
<attr name="canTakeScreenshot" format="boolean" />
+ <!-- Attribute indicating whether the accessibility service is used to assist users with
+ disabilities. This criteria might be defined by the installer. The default is false.
+ <p>
+ Note: If this flag is false, system will show a notification after a duration to
+ inform the user about the privacy implications of the service.
+ </p>
+ -->
+ <attr name="isAccessibilityTool" format="boolean" />
+
<!-- Animated image of the accessibility service purpose or behavior, to help users
understand how the service can help them.-->
<attr name="animatedImageDrawable" format="reference"/>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index b5d1e0ceec02..293018d881d8 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3087,6 +3087,7 @@
<public name="dataExtractionRules"/>
<public name="passwordsActivity"/>
<public name="selectableAsDefault"/>
+ <public name="isAccessibilityTool"/>
</public-group>
<public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 2b1168f14f21..2ffa29b53331 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2778,6 +2778,12 @@
<!-- Displayed to the user to confirm that they have copied text/images to the clipboard [CHAR LIMIT=NONE] -->
<string name="copied">Copied</string>
+ <!-- Displayed to the user to inform them that an app has accessed clipboard data (pasted as in "copy and paste") that was copied from another app [CHAR LIMIT=50] -->
+ <string name="pasted_from_app"><xliff:g id="pasting_app_name" example="Gmail">%1$s</xliff:g> pasted from <xliff:g id="source_app_name" example="Chrome">%2$s</xliff:g></string>
+
+ <!-- Displayed to the user to inform them that an app has accessed clipboard data (pasted as in "copy and paste") [CHAR LIMIT=50] -->
+ <string name="pasted_from_clipboard"><xliff:g id="pasting_app_name" example="Gmail">%1$s</xliff:g> pasted from clipboard</string>
+
<!-- Menu item displayed at the end of a menu to allow users to see another page worth of menu items. This is shown on any app's menu as long as the app has too many items in the menu.-->
<string name="more_item_label">More</string>
<!-- Prepended to the shortcut for a menu item to indicate that the user should hold the MENU button together with the shortcut to invoke the item. For example, if the shortcut to open a new tab in browser is MENU and B together, then this would be prepended to the letter "B" -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 00cc81639608..07938fd3a324 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -558,6 +558,8 @@
<java-symbol type="string" name="prepend_shortcut_label" />
<java-symbol type="string" name="private_dns_broken_detailed" />
<java-symbol type="string" name="paste_as_plain_text" />
+ <java-symbol type="string" name="pasted_from_app" />
+ <java-symbol type="string" name="pasted_from_clipboard" />
<java-symbol type="string" name="replace" />
<java-symbol type="string" name="undo" />
<java-symbol type="string" name="redo" />
diff --git a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
index 9cb7876b3e5a..64bcc1c379ba 100644
--- a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
@@ -98,9 +98,10 @@ public class FrameTrackerTest {
mListenerCapture = ArgumentCaptor.forClass(OnJankDataListener.class);
doNothing().when(mSurfaceControlWrapper).addJankStatsListener(
mListenerCapture.capture(), any());
+ doNothing().when(mSurfaceControlWrapper).removeJankStatsListener(
+ mListenerCapture.capture());
mChoreographer = mock(ChoreographerWrapper.class);
-
Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
mTracker = Mockito.spy(
new FrameTracker(session, handler, mRenderer, mViewRootWrapper,
@@ -127,11 +128,12 @@ public class FrameTrackerTest {
sendFirstWindowFrame(5, JANK_NONE, 101L);
// end the trace session, the last janky frame is after the end() so is discarded.
- when(mChoreographer.getVsyncId()).thenReturn(101L);
+ when(mChoreographer.getVsyncId()).thenReturn(102L);
mTracker.end();
- sendFrame(500, JANK_APP_DEADLINE_MISSED, 102L);
+ sendFrame(5, JANK_NONE, 102L);
+ sendFrame(500, JANK_APP_DEADLINE_MISSED, 103L);
- verify(mRenderer).removeObserver(any());
+ verify(mTracker).removeObservers();
verify(mTracker, never()).triggerPerfetto();
}
@@ -148,11 +150,11 @@ public class FrameTrackerTest {
sendFrame(40, JANK_SURFACEFLINGER_DEADLINE_MISSED, 101L);
// end the trace session
- when(mChoreographer.getVsyncId()).thenReturn(101L);
+ when(mChoreographer.getVsyncId()).thenReturn(102L);
mTracker.end();
sendFrame(4, JANK_NONE, 102L);
- verify(mRenderer).removeObserver(any());
+ verify(mTracker).removeObservers();
// We detected a janky frame - trigger Perfetto
verify(mTracker).triggerPerfetto();
@@ -171,11 +173,11 @@ public class FrameTrackerTest {
sendFrame(4, JANK_NONE, 101L);
// end the trace session
- when(mChoreographer.getVsyncId()).thenReturn(101L);
+ when(mChoreographer.getVsyncId()).thenReturn(102L);
mTracker.end();
sendFrame(4, JANK_NONE, 102L);
- verify(mRenderer).removeObserver(any());
+ verify(mTracker).removeObservers();
// We detected a janky frame - trigger Perfetto
verify(mTracker, never()).triggerPerfetto();
@@ -194,11 +196,11 @@ public class FrameTrackerTest {
sendFrame(40, JANK_APP_DEADLINE_MISSED, 101L);
// end the trace session
- when(mChoreographer.getVsyncId()).thenReturn(101L);
+ when(mChoreographer.getVsyncId()).thenReturn(102L);
mTracker.end();
sendFrame(4, JANK_NONE, 102L);
- verify(mRenderer).removeObserver(any());
+ verify(mTracker).removeObservers();
// We detected a janky frame - trigger Perfetto
verify(mTracker).triggerPerfetto();
@@ -224,7 +226,7 @@ public class FrameTrackerTest {
// One more callback with VSYNC after the end() vsync id.
sendFrame(4, JANK_NONE, 103L);
- verify(mRenderer).removeObserver(any());
+ verify(mTracker).removeObservers();
// We detected a janky frame - trigger Perfetto
verify(mTracker).triggerPerfetto();
@@ -246,11 +248,50 @@ public class FrameTrackerTest {
sendFrame(50, JANK_APP_DEADLINE_MISSED, 102L);
mTracker.cancel();
- verify(mRenderer).removeObserver(any());
+ verify(mTracker).removeObservers();
// Since the tracker has been cancelled, shouldn't trigger perfetto.
verify(mTracker, never()).triggerPerfetto();
}
+ @Test
+ public void testRemoveObserversWhenCancelledInEnd() {
+ when(mChoreographer.getVsyncId()).thenReturn(100L);
+ mTracker.begin();
+ verify(mRenderer, only()).addObserver(any());
+
+ // send first frame - not janky
+ sendFrame(4, JANK_NONE, 100L);
+
+ // send another frame - should be considered janky
+ sendFrame(40, JANK_APP_DEADLINE_MISSED, 101L);
+
+ // end the trace session
+ when(mChoreographer.getVsyncId()).thenReturn(101L);
+ mTracker.end();
+ sendFrame(4, JANK_NONE, 102L);
+
+ // Since the begin vsync id (101) equals to the end vsync id (101), will be treat as cancel.
+ verify(mTracker).cancel();
+
+ // Observers should be removed in this case, or FrameTracker object will be leaked.
+ verify(mTracker).removeObservers();
+
+ // Should never trigger Perfetto since it is a cancel.
+ verify(mTracker, never()).triggerPerfetto();
+ }
+
+ @Test
+ public void testCancelWhenSessionNeverBegun() {
+ mTracker.cancel();
+ verify(mTracker).removeObservers();
+ }
+
+ @Test
+ public void testEndWhenSessionNeverBegun() {
+ mTracker.end();
+ verify(mTracker).removeObservers();
+ }
+
private void sendFirstWindowFrame(long durationMillis,
@JankType int jankType, long vsyncId) {
sendFrame(durationMillis, jankType, vsyncId, true /* firstWindowFrame */);
diff --git a/core/tests/mockingcoretests/src/android/view/DisplayTests.java b/core/tests/mockingcoretests/src/android/view/DisplayTests.java
new file mode 100644
index 000000000000..678f21fe1211
--- /dev/null
+++ b/core/tests/mockingcoretests/src/android/view/DisplayTests.java
@@ -0,0 +1,531 @@
+/*
+ * Copyright (C) 2021 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 android.view;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_90;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManagerGlobal;
+import android.platform.test.annotations.Presubmit;
+import android.util.DisplayMetrics;
+import android.view.DisplayAdjustments.FixedRotationAdjustments;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.mockito.quality.Strictness;
+
+import java.util.function.Consumer;
+
+/**
+ * Tests for {@link Display}.
+ *
+ * <p>Build/Install/Run:
+ *
+ * atest FrameworksMockingCoreTests:android.view.DisplayTests
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class DisplayTests {
+
+ private static final int APP_WIDTH = 272;
+ private static final int APP_HEIGHT = 700;
+ // Tablet size device, ROTATION_0 corresponds to portrait.
+ private static final int LOGICAL_WIDTH = 700;
+ private static final int LOGICAL_HEIGHT = 1800;
+
+ // Bounds of the app when the device is in portrait mode.
+ private static Rect sAppBoundsPortrait = buildAppBounds(LOGICAL_WIDTH, LOGICAL_HEIGHT);
+ private static Rect sAppBoundsLandscape = buildAppBounds(LOGICAL_HEIGHT, LOGICAL_WIDTH);
+
+ private StaticMockitoSession mMockitoSession;
+
+ private DisplayManagerGlobal mDisplayManagerGlobal;
+ private Context mApplicationContext;
+ private DisplayInfo mDisplayInfo = new DisplayInfo();
+
+ @Before
+ public void setupTests() {
+ mMockitoSession = mockitoSession()
+ .mockStatic(DisplayManagerGlobal.class)
+ .strictness(Strictness.LENIENT)
+ .startMocking();
+
+ // Ensure no adjustments are set before each test.
+ mApplicationContext = ApplicationProvider.getApplicationContext();
+ DisplayAdjustments displayAdjustments =
+ mApplicationContext.getResources().getDisplayAdjustments();
+ displayAdjustments.setFixedRotationAdjustments(null);
+ mApplicationContext.getResources().overrideDisplayAdjustments(null);
+ mApplicationContext.getResources().getConfiguration().windowConfiguration.setAppBounds(
+ null);
+ mApplicationContext.getResources().getConfiguration().windowConfiguration.setMaxBounds(
+ null);
+ mDisplayInfo.rotation = ROTATION_0;
+
+ mDisplayManagerGlobal = mock(DisplayManagerGlobal.class);
+ doReturn(mDisplayInfo).when(mDisplayManagerGlobal).getDisplayInfo(anyInt());
+ }
+
+ @After
+ public void teardownTests() {
+ if (mMockitoSession != null) {
+ mMockitoSession.finishMocking();
+ }
+ Mockito.framework().clearInlineMocks();
+ }
+
+ @Test
+ public void testConstructor_defaultDisplayAdjustments_matchesDisplayInfo() {
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ assertThat(display.getDisplayAdjustments()).isEqualTo(
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ DisplayInfo actualDisplayInfo = new DisplayInfo();
+ display.getDisplayInfo(actualDisplayInfo);
+ verifyDisplayInfo(actualDisplayInfo, mDisplayInfo);
+ }
+
+ @Test
+ public void testConstructor_defaultResources_matchesDisplayInfo() {
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ assertThat(display.getDisplayAdjustments()).isEqualTo(
+ mApplicationContext.getResources().getDisplayAdjustments());
+ DisplayInfo actualDisplayInfo = new DisplayInfo();
+ display.getDisplayInfo(actualDisplayInfo);
+ verifyDisplayInfo(actualDisplayInfo, mDisplayInfo);
+ }
+
+ @Test
+ public void testGetRotation_defaultDisplayAdjustments_rotationNotAdjusted() {
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ assertThat(display.getRotation()).isEqualTo(ROTATION_0);
+ }
+
+ @Test
+ public void testGetRotation_displayAdjustmentsWithoutOverride_rotationNotAdjusted() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated, but no override is set.
+ DisplayAdjustments displayAdjustments = DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
+ final FixedRotationAdjustments fixedRotationAdjustments =
+ new FixedRotationAdjustments(ROTATION_90, APP_WIDTH, APP_HEIGHT,
+ DisplayCutout.NO_CUTOUT);
+ displayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments);
+ // GIVEN display is constructed with display adjustments.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ displayAdjustments);
+ // THEN rotation is not adjusted since no override was set.
+ assertThat(display.getRotation()).isEqualTo(ROTATION_0);
+ }
+
+ @Test
+ public void testGetRotation_resourcesWithoutOverride_rotationNotAdjusted() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated, but no override is set.
+ setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN rotation is not adjusted since no override is set.
+ assertThat(display.getRotation()).isEqualTo(ROTATION_0);
+ }
+
+ @Test
+ public void testGetRotation_resourcesWithOverrideDisplayAdjustments_rotationAdjusted() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated, and an override is set.
+ setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN rotation is adjusted since an override is set.
+ assertThat(display.getRotation()).isEqualTo(ROTATION_90);
+ }
+
+ @Test
+ public void testGetRealSize_defaultResourcesPortrait_matchesLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches display orientation.
+ verifyRealSizeIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealSize_defaultResourcesLandscape_matchesRotatedLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches display orientation.
+ verifyRealSizeIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealSize_defaultDisplayAdjustmentsPortrait_matchesLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ // THEN real size matches display orientation.
+ verifyRealSizeIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealSize_defaultDisplayAdjustmentsLandscape_matchesLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ // THEN real size matches display orientation.
+ verifyRealSizeIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealSize_resourcesPortraitWithFixedRotation_notRotatedLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated.
+ setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches display orientation.
+ verifyRealSizeIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealSize_resourcesWithLandscapeFixedRotation_notRotatedLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated.
+ setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches display orientation.
+ verifyRealSizeIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealSize_resourcesWithPortraitOverrideRotation_rotatedLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated, and an override is set.
+ setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches app orientation.
+ verifyRealSizeIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealSize_resourcesWithLandscapeOverrideRotation_rotatedLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated, and an override is set.
+ setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches app orientation.
+ verifyRealSizeIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealSize_resourcesPortraitSandboxed_matchesSandboxBounds() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN app is letterboxed.
+ setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
+ sAppBoundsPortrait);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches app bounds.
+ verifyRealSizeMatchesApp(display, sAppBoundsPortrait);
+ }
+
+ @Test
+ public void testGetRealSize_resourcesLandscapeSandboxed_matchesSandboxBounds() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ // GIVEN app is letterboxed.
+ setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
+ sAppBoundsLandscape);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches app bounds.
+ verifyRealSizeMatchesApp(display, sAppBoundsLandscape);
+ }
+
+ @Test
+ public void testGetRealMetrics_defaultResourcesPortrait_matchesLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches display orientation.
+ verifyRealMetricsIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_defaultResourcesLandscape_matchesRotatedLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches display orientation.
+ verifyRealMetricsIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_defaultDisplayAdjustmentsPortrait_matchesLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ // THEN real metrics matches display orientation.
+ verifyRealMetricsIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_defaultDisplayAdjustmentsLandscape_matchesLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ // THEN real metrics matches display orientation.
+ verifyRealMetricsIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_resourcesPortraitWithFixedRotation_notRotatedLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated.
+ setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches display orientation.
+ verifyRealMetricsIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_resourcesWithLandscapeFixedRotation_notRotatedLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated.
+ setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches display orientation.
+ verifyRealMetricsIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_resourcesWithPortraitOverrideRotation_rotatedLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated with an override.
+ setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches app orientation.
+ verifyRealMetricsIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_resourcesWithLandscapeOverrideRotation_rotatedLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated.
+ setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches app orientation.
+ verifyRealMetricsIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_resourcesPortraitSandboxed_matchesSandboxBounds() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN app is letterboxed.
+ setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
+ sAppBoundsPortrait);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches app bounds.
+ verifyRealMetricsMatchesApp(display, sAppBoundsPortrait);
+ }
+
+ @Test
+ public void testGetRealMetrics_resourcesLandscapeSandboxed_matchesSandboxBounds() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ // GIVEN app is letterboxed.
+ setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
+ sAppBoundsLandscape);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches app bounds.
+ verifyRealMetricsMatchesApp(display, sAppBoundsLandscape);
+ }
+
+ // Given rotated display dimensions, calculate the letterboxed app bounds.
+ private static Rect buildAppBounds(int displayWidth, int displayHeight) {
+ final int midWidth = displayWidth / 2;
+ final int left = midWidth - (APP_WIDTH / 2);
+ final int right = midWidth + (APP_WIDTH / 2);
+ final int midHeight = displayHeight / 2;
+ // Coordinate system starts at top left.
+ final int top = midHeight - (APP_HEIGHT / 2);
+ final int bottom = midHeight + (APP_HEIGHT / 2);
+ return new Rect(left, top, right, bottom);
+ }
+
+ private static void setDisplayInfoLandscape(DisplayInfo displayInfo) {
+ displayInfo.rotation = ROTATION_90;
+ // Flip width & height assignment since the device is rotated.
+ displayInfo.logicalWidth = LOGICAL_HEIGHT;
+ displayInfo.logicalHeight = LOGICAL_WIDTH;
+ }
+
+ private static void setDisplayInfoPortrait(DisplayInfo displayInfo) {
+ displayInfo.rotation = ROTATION_0;
+ displayInfo.logicalWidth = LOGICAL_WIDTH;
+ displayInfo.logicalHeight = LOGICAL_HEIGHT;
+ }
+
+ /**
+ * Set max bounds to be sandboxed to the app bounds, indicating the app is in
+ * size compat mode or letterbox.
+ */
+ private static void setMaxBoundsSandboxedToMatchAppBounds(Resources resources, Rect appBounds) {
+ resources.getConfiguration().windowConfiguration.setMaxBounds(appBounds);
+ }
+
+ /**
+ * Do not compare entire display info, since it is updated to match display the test is run on.
+ */
+ private static void verifyDisplayInfo(DisplayInfo actual, DisplayInfo expected) {
+ assertThat(actual.displayId).isEqualTo(expected.displayId);
+ assertThat(actual.rotation).isEqualTo(expected.rotation);
+ assertThat(actual.logicalWidth).isEqualTo(LOGICAL_WIDTH);
+ assertThat(actual.logicalHeight).isEqualTo(LOGICAL_HEIGHT);
+ }
+
+ private static void verifyRealSizeIsLandscape(Display display) {
+ Point size = new Point();
+ display.getRealSize(size);
+ // Flip the width and height check since the device is rotated.
+ assertThat(size).isEqualTo(new Point(LOGICAL_HEIGHT, LOGICAL_WIDTH));
+ }
+
+ private static void verifyRealMetricsIsLandscape(Display display) {
+ DisplayMetrics metrics = new DisplayMetrics();
+ display.getRealMetrics(metrics);
+ // Flip the width and height check since the device is rotated.
+ assertThat(metrics.widthPixels).isEqualTo(LOGICAL_HEIGHT);
+ assertThat(metrics.heightPixels).isEqualTo(LOGICAL_WIDTH);
+ }
+
+ private static void verifyRealSizeIsPortrait(Display display) {
+ Point size = new Point();
+ display.getRealSize(size);
+ assertThat(size).isEqualTo(new Point(LOGICAL_WIDTH, LOGICAL_HEIGHT));
+ }
+
+ private static void verifyRealMetricsIsPortrait(Display display) {
+ DisplayMetrics metrics = new DisplayMetrics();
+ display.getRealMetrics(metrics);
+ assertThat(metrics.widthPixels).isEqualTo(LOGICAL_WIDTH);
+ assertThat(metrics.heightPixels).isEqualTo(LOGICAL_HEIGHT);
+ }
+
+ private static void verifyRealSizeMatchesApp(Display display, Rect appBounds) {
+ Point size = new Point();
+ display.getRealSize(size);
+ assertThat(size).isEqualTo(new Point(appBounds.width(), appBounds.height()));
+ }
+
+ private static void verifyRealMetricsMatchesApp(Display display, Rect appBounds) {
+ DisplayMetrics metrics = new DisplayMetrics();
+ display.getRealMetrics(metrics);
+ assertThat(metrics.widthPixels).isEqualTo(appBounds.width());
+ assertThat(metrics.heightPixels).isEqualTo(appBounds.height());
+ }
+
+ private static FixedRotationAdjustments setOverrideFixedRotationAdjustments(
+ Resources resources, @Surface.Rotation int rotation) {
+ FixedRotationAdjustments fixedRotationAdjustments =
+ setFixedRotationAdjustments(resources, rotation);
+ resources.overrideDisplayAdjustments(
+ buildOverrideRotationAdjustments(fixedRotationAdjustments));
+ return fixedRotationAdjustments;
+ }
+
+ private static FixedRotationAdjustments setFixedRotationAdjustments(Resources resources,
+ @Surface.Rotation int rotation) {
+ final FixedRotationAdjustments fixedRotationAdjustments =
+ new FixedRotationAdjustments(rotation, APP_WIDTH, APP_HEIGHT,
+ DisplayCutout.NO_CUTOUT);
+ resources.getDisplayAdjustments().setFixedRotationAdjustments(fixedRotationAdjustments);
+ return fixedRotationAdjustments;
+ }
+
+ private static Consumer<DisplayAdjustments> buildOverrideRotationAdjustments(
+ FixedRotationAdjustments fixedRotationAdjustments) {
+ return consumedDisplayAdjustments
+ -> consumedDisplayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments);
+ }
+}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index b7bf8ab75799..43d56626ed1a 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1219,6 +1219,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/DragState.java"
},
+ "-681380736": {
+ "message": "Sandbox max bounds for uid %s to bounds %s due to letterboxing from mismatch with parent bounds? %s size compat mode %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_CONFIGURATION",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
"-677449371": {
"message": "moveTaskToRootTask: moving task=%d to rootTaskId=%d toTop=%b",
"level": "DEBUG",
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index 83dca53b1542..2f08db1b7d0a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -107,7 +107,7 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
+ .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
repetitions = 5)
}
}
diff --git a/packages/Connectivity/service/Android.bp b/packages/Connectivity/service/Android.bp
index e65b7b423bdc..2fb9f72feab7 100644
--- a/packages/Connectivity/service/Android.bp
+++ b/packages/Connectivity/service/Android.bp
@@ -50,12 +50,11 @@ cc_library_shared {
}
java_library {
- name: "service-connectivity",
+ name: "service-connectivity-pre-jarjar",
srcs: [
+ ":framework-connectivity-shared-srcs",
":connectivity-service-srcs",
],
- installable: true,
- jarjar_rules: "jarjar-rules.txt",
libs: [
"android.net.ipsec.ike",
"services.core",
@@ -73,3 +72,16 @@ java_library {
"com.android.tethering",
],
}
+
+java_library {
+ name: "service-connectivity",
+ installable: true,
+ static_libs:[
+ "service-connectivity-pre-jarjar",
+ ],
+ jarjar_rules: "jarjar-rules.txt",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.tethering",
+ ],
+}
diff --git a/packages/Connectivity/service/jarjar-rules.txt b/packages/Connectivity/service/jarjar-rules.txt
index d8205bf780fd..d8c60a428ef6 100644
--- a/packages/Connectivity/service/jarjar-rules.txt
+++ b/packages/Connectivity/service/jarjar-rules.txt
@@ -1,2 +1,13 @@
rule com.android.net.module.util.** com.android.connectivity.net-utils.@1
-rule com.android.modules.utils.** com.android.connectivity.modules-utils.@1 \ No newline at end of file
+rule com.android.modules.utils.** com.android.connectivity.modules-utils.@1
+
+# internal util classes
+# Exclude AsyncChannel. TODO: remove AsyncChannel usage in ConnectivityService
+rule com.android.internal.util.AsyncChannel* @0
+# Exclude LocationPermissionChecker. This is going to be moved to libs/net
+rule com.android.internal.util.LocationPermissionChecker* @0
+rule android.util.LocalLog* com.android.connectivity.util.LocalLog@1
+# android.util.IndentingPrintWriter* should use a different package name from
+# the one in com.android.internal.util
+rule android.util.IndentingPrintWriter* android.connectivity.util.IndentingPrintWriter@1
+rule com.android.internal.util.** com.android.connectivity.util.@1
diff --git a/packages/CtsShim/build/shim/AndroidManifest.xml b/packages/CtsShim/build/shim/AndroidManifest.xml
index 7d2626d50083..1ffe56c0c76a 100644
--- a/packages/CtsShim/build/shim/AndroidManifest.xml
+++ b/packages/CtsShim/build/shim/AndroidManifest.xml
@@ -53,6 +53,9 @@
</intent-filter>
</activity>
+ <!-- The stub shared library for package visibility test -->
+ <library android:name="com.android.cts.ctsshim.shared_library" />
+
</application>
</manifest>
diff --git a/packages/SystemUI/res/drawable/qs_footer_action_chip_background.xml b/packages/SystemUI/res/drawable/qs_footer_action_chip_background.xml
new file mode 100644
index 000000000000..34675ab891c1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_footer_action_chip_background.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 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.
+ -->
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:insetTop="@dimen/qs_footer_action_inset"
+ android:insetBottom="@dimen/qs_footer_action_inset">
+ <ripple
+ android:color="?android:attr/colorControlHighlight"
+ android:height="44dp">
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <solid android:color="@android:color/white"/>
+ <corners android:radius="@dimen/screenshot_button_corner_radius"/>
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="rectangle">
+ <stroke android:width="1dp" android:color="@color/qs_footer_action_border"/>
+ <solid android:color="@android:color/transparent"/>
+ <corners android:radius="@dimen/screenshot_button_corner_radius"/>
+ </shape>
+ </item>
+ </ripple>
+</inset> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_footer_action_chip_background_borderless.xml b/packages/SystemUI/res/drawable/qs_footer_action_chip_background_borderless.xml
new file mode 100644
index 000000000000..596ed711dd43
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_footer_action_chip_background_borderless.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 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.
+ -->
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:insetTop="@dimen/qs_footer_action_inset"
+ android:insetBottom="@dimen/qs_footer_action_inset">
+ <ripple
+ android:color="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <solid android:color="@android:color/white"/>
+ <corners android:radius="@dimen/screenshot_button_corner_radius"/>
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="rectangle">
+ <solid android:color="@android:color/transparent"/>
+ <corners android:radius="@dimen/screenshot_button_corner_radius"/>
+ </shape>
+ </item>
+ </ripple>
+</inset> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index 93dd1a12f147..02179722b35c 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -89,7 +89,7 @@
android:background="?android:attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:clipToPadding="false"
- android:contentDescription="@string/accessibility_quick_settings_edit"
+ android:contentDescription="@string/accessibility_quick_settings_power_menu"
android:focusable="true"
android:padding="@dimen/qs_footer_icon_padding"
android:src="@*android:drawable/ic_lock_power_off"
diff --git a/packages/SystemUI/res/layout/qs_footer_impl_two_lines.xml b/packages/SystemUI/res/layout/qs_footer_impl_two_lines.xml
new file mode 100644
index 000000000000..726e69f6e50c
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_footer_impl_two_lines.xml
@@ -0,0 +1,159 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+** Copyright 2021, 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.
+-->
+
+<!-- Extends FrameLayout -->
+<com.android.systemui.qs.QSFooterView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/qs_footer"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/qs_footer_height"
+ android:layout_marginStart="@dimen/qs_footer_margin"
+ android:layout_marginEnd="@dimen/qs_footer_margin"
+ android:background="@android:color/transparent"
+ android:baselineAligned="false"
+ android:clickable="false"
+ android:clipChildren="false"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:layout_gravity="center_vertical">
+
+ <TextView
+ android:id="@+id/build"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:paddingStart="@dimen/qs_tile_margin_horizontal"
+ android:paddingEnd="4dp"
+ android:layout_weight="1"
+ android:clickable="true"
+ android:ellipsize="marquee"
+ android:focusable="true"
+ android:gravity="center_vertical"
+ android:singleLine="true"
+ android:textAppearance="@style/TextAppearance.QS.Status"
+ android:visibility="gone" />
+
+ <com.android.systemui.qs.PageIndicator
+ android:id="@+id/footer_page_indicator"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_vertical"
+ android:visibility="gone" />
+
+ <View
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/qs_footer_actions_container"
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:gravity="center_vertical">
+
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
+ android:id="@android:id/edit"
+ android:layout_width="0dp"
+ android:layout_height="@dimen/qs_footer_action_button_size"
+ android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
+ android:layout_weight="1"
+ android:background="@drawable/qs_footer_action_chip_background"
+ android:clickable="true"
+ android:clipToPadding="false"
+ android:contentDescription="@string/accessibility_quick_settings_edit"
+ android:focusable="true"
+ android:padding="@dimen/qs_footer_icon_padding"
+ android:src="@*android:drawable/ic_mode_edit"
+ android:tint="?android:attr/colorForeground" />
+
+ <com.android.systemui.statusbar.phone.MultiUserSwitch
+ android:id="@+id/multi_user_switch"
+ android:layout_width="0dp"
+ android:layout_height="@dimen/qs_footer_action_button_size"
+ android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
+ android:layout_weight="1"
+ android:background="@drawable/qs_footer_action_chip_background"
+ android:focusable="true">
+
+ <ImageView
+ android:id="@+id/multi_user_avatar"
+ android:layout_width="@dimen/multi_user_avatar_expanded_size"
+ android:layout_height="@dimen/multi_user_avatar_expanded_size"
+ android:layout_gravity="center"
+ android:scaleType="centerInside" />
+ </com.android.systemui.statusbar.phone.MultiUserSwitch>
+
+ <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
+ android:id="@+id/settings_button_container"
+ android:layout_width="0dp"
+ android:layout_height="@dimen/qs_footer_action_button_size"
+ android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
+ android:background="@drawable/qs_footer_action_chip_background"
+ android:layout_weight="1"
+ android:clipChildren="false"
+ android:clipToPadding="false">
+
+ <com.android.systemui.statusbar.phone.SettingsButton
+ android:id="@+id/settings_button"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/qs_footer_action_button_size"
+ android:layout_gravity="center"
+ android:contentDescription="@string/accessibility_quick_settings_settings"
+ android:background="@drawable/qs_footer_action_chip_background_borderless"
+ android:padding="@dimen/qs_footer_icon_padding"
+ android:scaleType="centerInside"
+ android:src="@drawable/ic_settings"
+ android:tint="?android:attr/colorForeground" />
+
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
+ android:id="@+id/tuner_icon"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingStart="36dp"
+ android:paddingEnd="4dp"
+ android:src="@drawable/tuner"
+ android:tint="?android:attr/textColorTertiary"
+ android:visibility="invisible" />
+
+ </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
+
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
+ android:id="@+id/pm_lite"
+ android:layout_width="0dp"
+ android:layout_height="@dimen/qs_footer_action_button_size"
+ android:layout_weight="1"
+ android:background="@drawable/qs_footer_action_chip_background"
+ android:clickable="true"
+ android:clipToPadding="false"
+ android:focusable="true"
+ android:padding="@dimen/qs_footer_icon_padding"
+ android:src="@*android:drawable/ic_lock_power_off"
+ android:contentDescription="@string/accessibility_quick_settings_power_menu"
+ android:tint="?android:attr/colorForeground" />
+
+ </LinearLayout>
+ </LinearLayout>
+
+</com.android.systemui.qs.QSFooterView>
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 77f17439c487..d29cf872fd26 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -42,7 +42,10 @@
android:background="@android:color/transparent"
android:focusable="true"
android:accessibilityTraversalBefore="@android:id/edit">
- <include layout="@layout/qs_footer_impl" />
+ <ViewStub
+ android:id="@+id/qs_footer_stub"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
<include layout="@layout/qs_media_divider"
android:id="@+id/divider"/>
</com.android.systemui.qs.QSPanel>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index c36f7cd630a9..424172458b80 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -36,6 +36,7 @@
<color name="qs_user_detail_icon_muted">#FFFFFFFF</color> <!-- not so muted after all -->
<color name="qs_tile_disabled_color">#9E9E9E</color> <!-- 38% black -->
<color name="qs_customize_decoration">@color/GM2_grey_300</color>
+ <color name="qs_footer_action_border">#2E312C</color>
<!-- The color of the background in the separated list of the Global Actions menu -->
<color name="global_actions_separated_background">#F5F5F5</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index b75a0bc1525a..da58a3442e6f 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -107,7 +107,7 @@
<!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
<string name="quick_settings_tiles_stock" translatable="false">
- wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,screenrecord,reverse,reduce_brightness,cameratoggle,mictoggle,controls
+ wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,screenrecord,reverse,reduce_brightness,cameratoggle,mictoggle,controls,alarm
</string>
<!-- The tiles to display in QuickSettings -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 392eb496031a..94214d503004 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -415,6 +415,9 @@
<!-- The size of each of the icon buttons in the QS footer -->
<dimen name="qs_footer_action_button_size">@dimen/qs_footer_height</dimen>
+ <!-- (48dp - 44dp) / 2 -->
+ <dimen name="qs_footer_action_inset">2dp</dimen>
+
<!-- Margins on each side of QS Footer -->
<dimen name="qs_footer_margin">2dp</dimen>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index e5518928c98c..8cd5757247e2 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -46,4 +46,6 @@
<bool name="flag_navigation_bar_overlay">false</bool>
<bool name="flag_pm_lite">false</bool>
+
+ <bool name="flag_alarm_tile">false</bool>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 783f80c2ee02..fcda80dd5f15 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2312,6 +2312,9 @@
<!-- accessibility label for button to edit quick settings [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_edit">Edit order of settings.</string>
+ <!-- accessibility label for button to open power menu [CHAR LIMIT=NONE] -->
+ <string name="accessibility_quick_settings_power_menu">Power menu</string>
+
<!-- accessibility label for paging indicator in quick settings [CHAR LIMITi=NONE] -->
<string name="accessibility_quick_settings_page">Page <xliff:g name="current_page" example="1">%1$d</xliff:g> of <xliff:g name="num_pages" example="2">%2$d</xliff:g></string>
@@ -2858,4 +2861,7 @@
<string name="qs_remove_labels" translatable="false"></string>
<string name="qs_tile_label_fontFamily" translatable="false">@*android:string/config_headlineFontFamily</string>
+
+ <!-- Secondary label for alarm tile when there is no next alarm information [CHAR LIMIT=20] -->
+ <string name="qs_alarm_tile_no_alarm">No alarm set</string>
</resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index a4054bea1167..69e6ed043172 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -2077,8 +2077,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
boolean shouldListenForUdfps() {
return shouldListenForFingerprint()
&& !mBouncer
- && mStatusBarState != StatusBarState.SHADE_LOCKED
- && mStatusBarState != StatusBarState.FULLSCREEN_USER_SWITCHER
&& mStrongAuthTracker.hasUserAuthenticatedSinceBoot();
}
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 3852b24fe4b3..fba34e0b8ad3 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -367,7 +367,7 @@ public class SwipeHelper implements Gefingerpoken {
}
/**
- * @param view The view to be dismissed
+ * @param animView The view to be dismissed
* @param velocity The desired pixels/second speed at which the view should move
* @param endAction The action to perform at the end
* @param delay The delay after which we should start
@@ -477,12 +477,8 @@ public class SwipeHelper implements Gefingerpoken {
public void snapChild(final View animView, final float targetLeft, float velocity) {
final boolean canBeDismissed = mCallback.canChildBeDismissed(animView);
- AnimatorUpdateListener updateListener = new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- onTranslationUpdate(animView, (float) animation.getAnimatedValue(), canBeDismissed);
- }
- };
+ AnimatorUpdateListener updateListener = animation -> onTranslationUpdate(animView,
+ (float) animation.getAnimatedValue(), canBeDismissed);
Animator anim = getViewTranslationAnimator(animView, targetLeft, updateListener);
if (anim == null) {
@@ -501,8 +497,6 @@ public class SwipeHelper implements Gefingerpoken {
mSnappingChild = false;
if (!wasCancelled) {
updateSwipeProgressFromOffset(animView, canBeDismissed);
- onChildSnappedBack(animView, targetLeft);
- mCallback.onChildSnappedBack(animView, targetLeft);
resetSwipeState();
}
}
@@ -513,6 +507,7 @@ public class SwipeHelper implements Gefingerpoken {
mFlingAnimationUtils.apply(anim, getTranslation(animView), targetLeft, velocity,
maxDistance);
anim.start();
+ mCallback.onChildSnappedBack(animView, targetLeft);
}
/**
@@ -594,13 +589,12 @@ public class SwipeHelper implements Gefingerpoken {
if (!mIsSwiping && !mMenuRowIntercepting) {
if (mCallback.getChildAtPosition(ev) != null) {
-
// We are dragging directly over a card, make sure that we also catch the gesture
// even if nobody else wants the touch event.
+ mTouchedView = mCallback.getChildAtPosition(ev);
onInterceptTouchEvent(ev);
return true;
} else {
-
// We are not doing anything, make sure the long press callback
// is not still ticking like a bomb waiting to go off.
cancelLongPress();
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index e85cafaf47ac..d3168c8d2a05 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -170,7 +170,9 @@ public class AssistManager {
boolean visible = false;
if (mView != null) {
visible = mView.isShowing();
- mWindowManager.removeView(mView);
+ if (mView.isAttachedToWindow()) {
+ mWindowManager.removeView(mView);
+ }
}
mView = (AssistOrbContainer) LayoutInflater.from(mContext).inflate(
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 1d789ca15dfb..4b6a8f639cc4 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -451,6 +451,7 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
mStatusBar.addExpansionChangedListener(mStatusBarExpansionListener);
mStatusBarStateController.addCallback(mStatusBarStateListener);
+ mStatusBarStateListener.onStateChanged(mStatusBarStateController.getState());
mWindowManager.addView(mView, computeLayoutParams(animation));
mView.setOnTouchListener(mOnTouchListener);
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java
index 64576a97ddb2..f7fe14a8e841 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java
@@ -22,7 +22,6 @@ import static com.android.systemui.classifier.FalsingModule.DOUBLE_TAP_TOUCH_SLO
import android.view.MotionEvent;
import java.util.List;
-import java.util.Queue;
import javax.inject.Inject;
import javax.inject.Named;
@@ -49,8 +48,7 @@ public class DoubleTapClassifier extends FalsingClassifier {
@Override
Result calculateFalsingResult(double historyPenalty, double historyConfidence) {
List<MotionEvent> secondTapEvents = getRecentMotionEvents();
- Queue<? extends List<MotionEvent>> historicalEvents = getHistoricalEvents();
- List<MotionEvent> firstTapEvents = historicalEvents.peek();
+ List<MotionEvent> firstTapEvents = getPriorMotionEvents();
StringBuilder reason = new StringBuilder();
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java
index dbfeacfa91c4..f40045b0f08e 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java
@@ -21,7 +21,6 @@ import android.view.MotionEvent;
import com.android.systemui.util.sensors.ProximitySensor;
import java.util.List;
-import java.util.Queue;
/**
* Base class for rules that determine False touches.
@@ -40,8 +39,8 @@ public abstract class FalsingClassifier {
return mDataProvider.getRecentMotionEvents();
}
- Queue<? extends List<MotionEvent>> getHistoricalEvents() {
- return mDataProvider.getHistoricalMotionEvents();
+ List<MotionEvent> getPriorMotionEvents() {
+ return mDataProvider.getPriorMotionEvents();
}
MotionEvent getFirstMotionEvent() {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
index 4bacc1598490..336f13f117d3 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
@@ -23,13 +23,9 @@ import android.view.MotionEvent.PointerProperties;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.util.time.SystemClock;
import java.util.ArrayList;
-import java.util.Deque;
-import java.util.LinkedList;
import java.util.List;
-import java.util.Queue;
import javax.inject.Inject;
@@ -40,24 +36,23 @@ import javax.inject.Inject;
public class FalsingDataProvider {
private static final long MOTION_EVENT_AGE_MS = 1000;
- private static final long EXTENDED_MOTION_EVENT_AGE_MS = 30 * 1000;
private static final float THREE_HUNDRED_SIXTY_DEG = (float) (2 * Math.PI);
private final int mWidthPixels;
private final int mHeightPixels;
private final BatteryController mBatteryController;
- private final SystemClock mSystemClock;
private final float mXdpi;
private final float mYdpi;
private final List<SessionListener> mSessionListeners = new ArrayList<>();
private final List<MotionEventListener> mMotionEventListeners = new ArrayList<>();
- private final List<GestureCompleteListener> mGestuerCompleteListeners = new ArrayList<>();
+ private final List<GestureCompleteListener> mGestureCompleteListeners = new ArrayList<>();
private @Classifier.InteractionType int mInteractionType;
- private final Deque<TimeLimitedMotionEventBuffer> mExtendedMotionEvents = new LinkedList<>();
private TimeLimitedMotionEventBuffer mRecentMotionEvents =
new TimeLimitedMotionEventBuffer(MOTION_EVENT_AGE_MS);
+ private List<MotionEvent> mPriorMotionEvents;
+
private boolean mDirty = true;
private float mAngle = 0;
@@ -66,14 +61,12 @@ public class FalsingDataProvider {
private boolean mJustUnlockedWithFace;
@Inject
- public FalsingDataProvider(DisplayMetrics displayMetrics, BatteryController batteryController,
- SystemClock systemClock) {
+ public FalsingDataProvider(DisplayMetrics displayMetrics, BatteryController batteryController) {
mXdpi = displayMetrics.xdpi;
mYdpi = displayMetrics.ydpi;
mWidthPixels = displayMetrics.widthPixels;
mHeightPixels = displayMetrics.heightPixels;
mBatteryController = batteryController;
- mSystemClock = systemClock;
FalsingClassifier.logInfo("xdpi, ydpi: " + getXdpi() + ", " + getYdpi());
FalsingClassifier.logInfo("width, height: " + getWidthPixels() + ", " + getHeightPixels());
@@ -111,10 +104,10 @@ public class FalsingDataProvider {
private void completePriorGesture() {
if (!mRecentMotionEvents.isEmpty()) {
- mGestuerCompleteListeners.forEach(listener -> listener.onGestureComplete(
+ mGestureCompleteListeners.forEach(listener -> listener.onGestureComplete(
mRecentMotionEvents.get(mRecentMotionEvents.size() - 1).getEventTime()));
- mExtendedMotionEvents.addFirst(mRecentMotionEvents);
+ mPriorMotionEvents = mRecentMotionEvents;
}
}
@@ -140,14 +133,8 @@ public class FalsingDataProvider {
return mRecentMotionEvents;
}
- /** Returns recent gestures, exclusive of the most recent gesture. Newer gestures come first. */
- public Queue<? extends List<MotionEvent>> getHistoricalMotionEvents() {
- long nowMs = mSystemClock.uptimeMillis();
-
- mExtendedMotionEvents.removeIf(
- motionEvents -> motionEvents.isFullyExpired(nowMs - EXTENDED_MOTION_EVENT_AGE_MS));
-
- return mExtendedMotionEvents;
+ public List<MotionEvent> getPriorMotionEvents() {
+ return mPriorMotionEvents;
}
/**
@@ -344,12 +331,12 @@ public class FalsingDataProvider {
/** Register a {@link GestureCompleteListener}. */
public void addGestureCompleteListener(GestureCompleteListener listener) {
- mGestuerCompleteListeners.add(listener);
+ mGestureCompleteListeners.add(listener);
}
/** Unregister a {@link GestureCompleteListener}. */
public void removeGestureCompleteListener(GestureCompleteListener listener) {
- mGestuerCompleteListeners.remove(listener);
+ mGestureCompleteListeners.remove(listener);
}
void onSessionStarted() {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/TimeLimitedMotionEventBuffer.java b/packages/SystemUI/src/com/android/systemui/classifier/TimeLimitedMotionEventBuffer.java
index 7969b4e83ac0..e5da38936593 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/TimeLimitedMotionEventBuffer.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/TimeLimitedMotionEventBuffer.java
@@ -42,18 +42,6 @@ public class TimeLimitedMotionEventBuffer implements List<MotionEvent> {
mMotionEvents = new LinkedList<>();
}
- /**
- * Returns true if the most recent event in the buffer is past the expiration time.
- *
- * This method does not mutate the underlying data. This method does imply that, if the supplied
- * expiration time is old enough and a new {@link MotionEvent} gets added to the buffer, all
- * prior events would be removed.
- */
- public boolean isFullyExpired(long expirationMs) {
- return mMotionEvents.isEmpty()
- || mMotionEvents.getLast().getEventTime() <= expirationMs;
- }
-
private void ejectOldEvents() {
if (mMotionEvents.isEmpty()) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index d85b10167697..1f67276bfbae 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -79,7 +79,6 @@ import android.transition.AutoTransition;
import android.transition.TransitionManager;
import android.transition.TransitionSet;
import android.util.ArraySet;
-import android.util.FeatureFlagUtils;
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.IWindowManager;
@@ -114,7 +113,6 @@ import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.EmergencyAffordanceManager;
-import com.android.internal.util.ScreenRecordHelper;
import com.android.internal.util.ScreenshotHelper;
import com.android.internal.view.RotationPolicy;
import com.android.internal.widget.LockPatternUtils;
@@ -242,7 +240,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private final boolean mShowSilentToggle;
private final EmergencyAffordanceManager mEmergencyAffordanceManager;
private final ScreenshotHelper mScreenshotHelper;
- private final ScreenRecordHelper mScreenRecordHelper;
private final ActivityStarter mActivityStarter;
private final SysuiColorExtractor mSysuiColorExtractor;
private final IStatusBarService mStatusBarService;
@@ -378,7 +375,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mEmergencyAffordanceManager = new EmergencyAffordanceManager(context);
mScreenshotHelper = new ScreenshotHelper(context);
- mScreenRecordHelper = new ScreenRecordHelper(context);
mConfigurationController.addCallback(this);
@@ -979,7 +975,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
@VisibleForTesting
- class ScreenshotAction extends SinglePressAction implements LongPressAction {
+ class ScreenshotAction extends SinglePressAction {
final String KEY_SYSTEM_NAV_2BUTTONS = "system_nav_2buttons";
public ScreenshotAction() {
@@ -1024,18 +1020,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
return NAV_BAR_MODE_2BUTTON == mContext.getResources().getInteger(
com.android.internal.R.integer.config_navBarInteractionMode);
}
-
-
- @Override
- public boolean onLongPress() {
- if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SCREENRECORD_LONG_PRESS)) {
- mUiEventLogger.log(GlobalActionsEvent.GA_SCREENSHOT_LONG_PRESS);
- mScreenRecordHelper.launchRecordPrompt();
- } else {
- onPress();
- }
- return true;
- }
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 088743cd0f94..9e3be69c414e 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -609,20 +609,21 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
if (mVocab != null) {
app = mVocab.getOrDefault(mPackageName, -1);
}
- // Check if we are within the tightest bounds beyond which
- // we would not need to run the ML model.
- boolean withinRange = x < mMLEnableWidth + mLeftInset
- || x >= (mDisplaySize.x - mMLEnableWidth - mRightInset);
- if (!withinRange) {
+
+ // Denotes whether we should proceed with the gesture. Even if it is false, we may want to
+ // log it assuming it is not invalid due to exclusion.
+ boolean withinRange = x < mEdgeWidthLeft + mLeftInset
+ || x >= (mDisplaySize.x - mEdgeWidthRight - mRightInset);
+ if (withinRange) {
int results = -1;
- if (mUseMLModel && (results = getBackGesturePredictionsCategory(x, y, app)) != -1) {
- withinRange = results == 1;
- } else {
- // Denotes whether we should proceed with the gesture.
- // Even if it is false, we may want to log it assuming
- // it is not invalid due to exclusion.
- withinRange = x < mEdgeWidthLeft + mLeftInset
- || x >= (mDisplaySize.x - mEdgeWidthRight - mRightInset);
+
+ // Check if we are within the tightest bounds beyond which we would not need to run the
+ // ML model
+ boolean withinMinRange = x < mMLEnableWidth + mLeftInset
+ || x >= (mDisplaySize.x - mMLEnableWidth - mRightInset);
+ if (!withinMinRange && mUseMLModel
+ && (results = getBackGesturePredictionsCategory(x, y, app)) != -1) {
+ withinRange = (results == 1);
}
}
@@ -726,7 +727,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
mGestureLog.removeFirst();
}
mGestureLog.addLast(String.format(
- "Gesture [%d,alw=%B,%B, %B,%B,disp=%s,wl=%d,il=%d,wr=%d,ir=%d,excl=%s]",
+ "Gesture [%d,alw=%B,%B,%B,%B,disp=%s,wl=%d,il=%d,wr=%d,ir=%d,excl=%s]",
System.currentTimeMillis(), mAllowGesture, mIsOnLeftEdge, mIsBackGestureAllowed,
QuickStepContract.isBackGestureDisabled(mSysUiFlags), mDisplaySize,
mEdgeWidthLeft, mLeftInset, mEdgeWidthRight, mRightInset, mExcludeRegion));
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
index 7e20be6826dc..3d8784b29e4c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
@@ -94,7 +94,7 @@ public class QSFooterView extends FrameLayout {
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mEdit = findViewById(android.R.id.edit);
+ mEdit = requireViewById(android.R.id.edit);
mPageIndicator = findViewById(R.id.footer_page_indicator);
@@ -104,14 +104,15 @@ public class QSFooterView extends FrameLayout {
mMultiUserSwitch = findViewById(R.id.multi_user_switch);
mMultiUserAvatar = mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
- mActionsContainer = findViewById(R.id.qs_footer_actions_container);
+ mActionsContainer = requireViewById(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
- ((RippleDrawable) mSettingsButton.getBackground()).setForceSoftware(true);
-
+ if (mSettingsButton.getBackground() instanceof RippleDrawable) {
+ ((RippleDrawable) mSettingsButton.getBackground()).setForceSoftware(true);
+ }
updateResources();
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
@@ -143,7 +144,7 @@ public class QSFooterView extends FrameLayout {
int defSpace = mContext.getResources().getDimensionPixelOffset(R.dimen.default_gear_space);
mSettingsCogAnimator = new Builder()
- .addFloat(mSettingsContainer, "translationX",
+ .addFloat(mSettingsButton, "translationX",
isLayoutRtl() ? (remaining - defSpace) : -(remaining - defSpace), 0)
.addFloat(mSettingsButton, "rotation", -120, 0)
.build();
@@ -173,12 +174,15 @@ public class QSFooterView extends FrameLayout {
@Nullable
private TouchAnimator createFooterAnimator() {
- return new TouchAnimator.Builder()
+ TouchAnimator.Builder builder = new TouchAnimator.Builder()
.addFloat(mActionsContainer, "alpha", 0, 1)
- .addFloat(mEditContainer, "alpha", 0, 1)
.addFloat(mPageIndicator, "alpha", 0, 1)
- .setStartDelay(0.9f)
- .build();
+ .addFloat(mBuildText, "alpha", 0, 1)
+ .setStartDelay(0.9f);
+ if (mEditContainer != null) {
+ builder.addFloat(mEditContainer, "alpha", 0, 1);
+ }
+ return builder.build();
}
/** */
@@ -273,8 +277,10 @@ public class QSFooterView extends FrameLayout {
mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
isTunerEnabled ? View.VISIBLE : View.INVISIBLE);
final boolean isDemo = UserManager.isDeviceInDemoMode(mContext);
- mMultiUserSwitch.setVisibility(showUserSwitcher() ? View.VISIBLE : View.INVISIBLE);
- mEditContainer.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
+ mMultiUserSwitch.setVisibility(showUserSwitcher() ? View.VISIBLE : View.GONE);
+ if (mEditContainer != null) {
+ mEditContainer.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
+ }
mSettingsButton.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
mBuildText.setVisibility(mExpanded && mShouldShowBuildText ? View.VISIBLE : View.GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
index 2bea72cc0c7e..f1f4e16206a1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
@@ -153,8 +153,8 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme
@Override
protected void onViewAttached() {
- if (mShowPMLiteButton) {
- mPowerMenuLite.setVisibility(View.VISIBLE);
+ if (!mShowPMLiteButton) {
+ mPowerMenuLite.setVisibility(View.GONE);
}
mView.addOnLayoutChangeListener(
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 7657dcead583..ff9b9120c6e1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -32,6 +32,7 @@ import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewStub;
import android.widget.LinearLayout;
import com.android.internal.logging.UiEventLogger;
@@ -127,8 +128,21 @@ public class QSPanel extends LinearLayout implements Tunable {
}
+ protected void inflateQSFooter(boolean newFooter) {
+ ViewStub stub = findViewById(R.id.qs_footer_stub);
+ if (stub != null) {
+ stub.setLayoutResource(
+ newFooter ? R.layout.qs_footer_impl_two_lines : R.layout.qs_footer_impl);
+ stub.inflate();
+ mFooter = findViewById(R.id.qs_footer);
+ }
+ }
+
void initialize(boolean sideLabels) {
mSideLabels = sideLabels;
+
+ inflateQSFooter(sideLabels);
+
mRegularTileLayout = createRegularTileLayout();
mTileLayout = mRegularTileLayout;
@@ -344,7 +358,6 @@ public class QSPanel extends LinearLayout implements Tunable {
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mFooter = findViewById(R.id.qs_footer);
mDivider = findViewById(R.id.divider);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 9b66b59c06df..f51d7ef381ee 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -55,6 +55,11 @@ public class QuickQSPanel extends QSPanel {
applyBottomMargin((View) mRegularTileLayout);
}
+ @Override
+ protected void inflateQSFooter(boolean newFooter) {
+ // No footer
+ }
+
private void applyBottomMargin(View view) {
int margin = getResources().getDimensionPixelSize(R.dimen.qs_header_tile_margin_bottom);
MarginLayoutParams layoutParams = (MarginLayoutParams) view.getLayoutParams();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 6983b38489f6..9e5fe732cd0c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -30,6 +30,7 @@ import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.qs.tiles.AirplaneModeTile;
+import com.android.systemui.qs.tiles.AlarmTile;
import com.android.systemui.qs.tiles.BatterySaverTile;
import com.android.systemui.qs.tiles.BluetoothTile;
import com.android.systemui.qs.tiles.CameraToggleTile;
@@ -91,6 +92,7 @@ public class QSFactoryImpl implements QSFactory {
private final Provider<CameraToggleTile> mCameraToggleTileProvider;
private final Provider<MicrophoneToggleTile> mMicrophoneToggleTileProvider;
private final Provider<DeviceControlsTile> mDeviceControlsTileProvider;
+ private final Provider<AlarmTile> mAlarmTileProvider;
private final Lazy<QSHost> mQsHostLazy;
private final Provider<CustomTile.Builder> mCustomTileBuilderProvider;
@@ -126,7 +128,8 @@ public class QSFactoryImpl implements QSFactory {
Provider<ReduceBrightColorsTile> reduceBrightColorsTileProvider,
Provider<CameraToggleTile> cameraToggleTileProvider,
Provider<MicrophoneToggleTile> microphoneToggleTileProvider,
- Provider<DeviceControlsTile> deviceControlsTileProvider) {
+ Provider<DeviceControlsTile> deviceControlsTileProvider,
+ Provider<AlarmTile> alarmTileProvider) {
mQsHostLazy = qsHostLazy;
mCustomTileBuilderProvider = customTileBuilderProvider;
@@ -157,6 +160,7 @@ public class QSFactoryImpl implements QSFactory {
mCameraToggleTileProvider = cameraToggleTileProvider;
mMicrophoneToggleTileProvider = microphoneToggleTileProvider;
mDeviceControlsTileProvider = deviceControlsTileProvider;
+ mAlarmTileProvider = alarmTileProvider;
}
public QSTile createTile(String tileSpec) {
@@ -218,6 +222,8 @@ public class QSFactoryImpl implements QSFactory {
return mMicrophoneToggleTileProvider.get();
case "controls":
return mDeviceControlsTileProvider.get();
+ case "alarm":
+ return mAlarmTileProvider.get();
}
// Custom tiles
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
new file mode 100644
index 000000000000..dede627cd4dc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
@@ -0,0 +1,117 @@
+package com.android.systemui.qs.tiles
+
+import android.app.AlarmManager
+import android.app.AlarmManager.AlarmClockInfo
+import android.content.Intent
+import android.os.Handler
+import android.os.Looper
+import android.provider.AlarmClock
+import android.service.quicksettings.Tile
+import android.text.TextUtils
+import android.text.format.DateFormat
+import androidx.annotation.VisibleForTesting
+import com.android.internal.logging.MetricsLogger
+import com.android.systemui.R
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.statusbar.policy.NextAlarmController
+import java.util.Locale
+import javax.inject.Inject
+
+class AlarmTile @Inject constructor(
+ host: QSHost,
+ @Background backgroundLooper: Looper,
+ @Main mainHandler: Handler,
+ metricsLogger: MetricsLogger,
+ statusBarStateController: StatusBarStateController,
+ activityStarter: ActivityStarter,
+ qsLogger: QSLogger,
+ private val featureFlags: FeatureFlags,
+ private val userTracker: UserTracker,
+ nextAlarmController: NextAlarmController
+) : QSTileImpl<QSTile.State>(
+ host,
+ backgroundLooper,
+ mainHandler,
+ metricsLogger,
+ statusBarStateController,
+ activityStarter,
+ qsLogger
+) {
+
+ private var lastAlarmInfo: AlarmManager.AlarmClockInfo? = null
+ private val icon = ResourceIcon.get(R.drawable.ic_alarm)
+ @VisibleForTesting
+ internal val defaultIntent = Intent(AlarmClock.ACTION_SET_ALARM)
+ private val callback = NextAlarmController.NextAlarmChangeCallback { nextAlarm ->
+ lastAlarmInfo = nextAlarm
+ refreshState()
+ }
+
+ init {
+ nextAlarmController.observe(this, callback)
+ }
+
+ override fun isAvailable(): Boolean {
+ return featureFlags.isAlarmTileAvailable
+ }
+
+ override fun newTileState(): QSTile.State {
+ return QSTile.State().apply {
+ handlesLongClick = false
+ }
+ }
+
+ private fun startDefaultSetAlarm() {
+ mActivityStarter.postStartActivityDismissingKeyguard(defaultIntent, 0)
+ }
+
+ override fun handleClick() {
+ lastAlarmInfo?.showIntent?.let {
+ mActivityStarter.postStartActivityDismissingKeyguard(it)
+ } ?: startDefaultSetAlarm()
+ }
+
+ override fun handleUpdateState(state: QSTile.State, arg: Any?) {
+ state.icon = icon
+ state.label = tileLabel
+ lastAlarmInfo?.let {
+ state.secondaryLabel = formatNextAlarm(it)
+ state.state = Tile.STATE_ACTIVE
+ } ?: run {
+ state.secondaryLabel = mContext.getString(R.string.qs_alarm_tile_no_alarm)
+ state.state = Tile.STATE_INACTIVE
+ }
+ state.contentDescription = TextUtils.concat(state.label, ", ", state.secondaryLabel)
+ }
+
+ override fun getTileLabel(): CharSequence {
+ return mContext.getString(R.string.status_bar_alarm)
+ }
+
+ private fun formatNextAlarm(info: AlarmClockInfo): String {
+ val skeleton = if (use24HourFormat()) "EHm" else "Ehma"
+ val pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton)
+ return DateFormat.format(pattern, info.triggerTime).toString()
+ }
+
+ private fun use24HourFormat(): Boolean {
+ return DateFormat.is24HourFormat(mContext, userTracker.userId)
+ }
+
+ override fun getMetricsCategory(): Int {
+ return 0
+ }
+
+ override fun getLongClickIntent(): Intent? {
+ return null
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
index c066619d049a..bb8c36776d57 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
@@ -136,7 +136,7 @@ public class CropView extends View {
case MotionEvent.ACTION_UP:
if (mCurrentDraggingBoundary != CropBoundary.NONE) {
// Commit the delta to the stored crop values.
- commitDeltas();
+ commitDeltas(mCurrentDraggingBoundary);
updateListener(event);
}
}
@@ -184,12 +184,12 @@ public class CropView extends View {
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- commitDeltas();
+ commitDeltas(boundary);
}
@Override
public void onAnimationCancel(Animator animation) {
- commitDeltas();
+ commitDeltas(boundary);
}
});
animator.setFloatValues(0f, 1f);
@@ -228,11 +228,14 @@ public class CropView extends View {
mCropInteractionListener = listener;
}
- private void commitDeltas() {
- mTopCrop += mTopDelta;
- mBottomCrop += mBottomDelta;
- mTopDelta = 0;
- mBottomDelta = 0;
+ private void commitDeltas(CropBoundary boundary) {
+ if (boundary == CropBoundary.TOP) {
+ mTopCrop += mTopDelta;
+ mTopDelta = 0;
+ } else if (boundary == CropBoundary.BOTTOM) {
+ mBottomCrop += mBottomDelta;
+ mBottomDelta = 0;
+ }
}
private void updateListener(MotionEvent event) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
index 4dc846e0e95d..db997053af23 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
@@ -339,14 +339,22 @@ public class LongScreenshotActivity extends Activity {
}
@Override
- public void onComplete(ImageTileSet imageTileSet) {
+ public void onComplete(ImageTileSet imageTileSet, int pageSize) {
Log.i(TAG, "Got tiles " + imageTileSet.getWidth() + " x "
+ imageTileSet.getHeight());
mPreview.setImageDrawable(imageTileSet.getDrawable());
updateCropLocation();
mMagnifierView.setDrawable(imageTileSet.getDrawable(),
imageTileSet.getWidth(), imageTileSet.getHeight());
- mCropView.animateBoundaryTo(CropView.CropBoundary.BOTTOM, 0.5f);
+ // Original boundaries go from the image tile set's y=0 to y=pageSize, so
+ // we animate to that as a starting crop position.
+ float topFraction = Math.max(0,
+ -imageTileSet.getTop() / (float) imageTileSet.getHeight());
+ float bottomFraction = Math.min(1f,
+ 1 - (imageTileSet.getBottom() - pageSize)
+ / (float) imageTileSet.getHeight());
+ mCropView.animateBoundaryTo(CropView.CropBoundary.TOP, topFraction);
+ mCropView.animateBoundaryTo(CropView.CropBoundary.BOTTOM, bottomFraction);
mBackgroundExecutor.execute(() -> saveCacheBitmap(imageTileSet));
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
index b62e2c34ed6e..34094cd81120 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
@@ -171,7 +171,7 @@ public class ScrollCaptureController {
if (mImageTileSet.isEmpty()) {
mCaptureCallback.onError();
} else {
- mCaptureCallback.onComplete(mImageTileSet);
+ mCaptureCallback.onComplete(mImageTileSet, session.getPageHeight());
}
}
@@ -179,7 +179,7 @@ public class ScrollCaptureController {
* Callback for image capture completion or error.
*/
public interface ScrollCaptureCallback {
- void onComplete(ImageTileSet imageTileSet);
+ void onComplete(ImageTileSet imageTileSet, int pageHeight);
void onError();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
index 1d59257c9c4f..c8e0d60ce304 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -90,4 +90,8 @@ public class FeatureFlags {
public boolean isPMLiteEnabled() {
return mFlagReader.isEnabled(R.bool.flag_pm_lite);
}
+
+ public boolean isAlarmTileAvailable() {
+ return mFlagReader.isEnabled(R.bool.flag_alarm_tile);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 3496581b1b8a..e1199077efb9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -16,9 +16,6 @@
package com.android.systemui.statusbar;
-import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN_REVERSE;
-import static com.android.systemui.statusbar.phone.NotificationIconContainer.IconState.NO_VALUE;
-
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -34,7 +31,6 @@ import android.view.WindowInsets;
import android.view.accessibility.AccessibilityNodeInfo;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -55,8 +51,6 @@ import com.android.systemui.statusbar.phone.NotificationIconContainer;
public class NotificationShelf extends ActivatableNotificationView implements
View.OnLayoutChangeListener, StateListener {
- private static final boolean USE_ANIMATIONS_WHEN_OPENING =
- SystemProperties.getBoolean("debug.icon_opening_animations", true);
private static final boolean ICON_ANMATIONS_WHILE_SCROLLING
= SystemProperties.getBoolean("debug.icon_scroll_animations", true);
private static final int TAG_CONTINUOUS_CLIPPING = R.id.continuous_clipping_tag;
@@ -174,18 +168,9 @@ public class NotificationShelf extends ActivatableNotificationView implements
float viewEnd = lastViewState.yTranslation + lastViewState.height;
viewState.copyFrom(lastViewState);
viewState.height = getIntrinsicHeight();
-
viewState.yTranslation = Math.max(Math.min(viewEnd, maxShelfEnd) - viewState.height,
getFullyClosedTranslation());
viewState.zTranslation = ambientState.getBaseZHeight();
- // For the small display size, it's not enough to make the icon not covered by
- // the top cutout so the denominator add the height of cutout.
- // Totally, (getIntrinsicHeight() * 2 + mCutoutHeight) should be smaller then
- // mAmbientState.getTopPadding().
- float openedAmount = (viewState.yTranslation - getFullyClosedTranslation())
- / (getIntrinsicHeight() * 2 + mCutoutHeight);
- openedAmount = Math.min(1.0f, openedAmount);
- viewState.openedAmount = openedAmount;
viewState.clipTopAmount = 0;
viewState.alpha = 1;
viewState.belowSpeedBump = mHostLayoutController.getSpeedBumpIndex() == 0;
@@ -220,11 +205,6 @@ public class NotificationShelf extends ActivatableNotificationView implements
View lastChild = mAmbientState.getLastVisibleBackgroundChild();
mNotGoneIndex = -1;
float interpolationStart = mMaxLayoutHeight - getIntrinsicHeight() * 2;
- float expandAmount = 0.0f;
- if (shelfStart >= interpolationStart) {
- expandAmount = (shelfStart - interpolationStart) / getIntrinsicHeight();
- expandAmount = Math.min(1.0f, expandAmount);
- }
// find the first view that doesn't overlap with the shelf
int notGoneIndex = 0;
int colorOfViewBeforeLast = NO_COLOR;
@@ -239,7 +219,6 @@ public class NotificationShelf extends ActivatableNotificationView implements
boolean scrollingFast = currentScrollVelocity > mScrollFastThreshold
|| (mAmbientState.isExpansionChanging()
&& Math.abs(mAmbientState.getExpandingVelocity()) > mScrollFastThreshold);
- boolean scrolling = currentScrollVelocity > 0;
boolean expandingAnimated = mAmbientState.isExpansionChanging()
&& !mAmbientState.isPanelTracking();
int baseZHeight = mAmbientState.getBaseZHeight();
@@ -249,8 +228,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
ActivatableNotificationView previousAnv = null;
for (int i = 0; i < mHostLayoutController.getChildCount(); i++) {
- ExpandableView child = (ExpandableView) mHostLayoutController.getChildAt(i);
-
+ ExpandableView child = mHostLayoutController.getChildAt(i);
if (!child.needsClippingToShelf() || child.getVisibility() == GONE) {
continue;
}
@@ -268,9 +246,8 @@ public class NotificationShelf extends ActivatableNotificationView implements
int clipTop = updateNotificationClipHeight(child, notificationClipEnd, notGoneIndex);
clipTopAmount = Math.max(clipTop, clipTopAmount);
-
- float inShelfAmount = updateShelfTransformation(child, expandAmount, scrolling,
- scrollingFast, expandingAnimated, isLastChild);
+ final float inShelfAmount = updateShelfTransformation(child, scrollingFast,
+ expandingAnimated, isLastChild);
// If the current row is an ExpandableNotificationRow, update its color, roundedness,
// and icon state.
if (child instanceof ExpandableNotificationRow) {
@@ -498,56 +475,40 @@ public class NotificationShelf extends ActivatableNotificationView implements
/**
* @return the amount how much this notification is in the shelf
*/
- private float updateShelfTransformation(ExpandableView view, float expandAmount,
- boolean scrolling, boolean scrollingFast, boolean expandingAnimated,
- boolean isLastChild) {
- StatusBarIconView icon = view.getShelfIcon();
- NotificationIconContainer.IconState iconState = getIconState(icon);
+ private float updateShelfTransformation(ExpandableView view, boolean scrollingFast,
+ boolean expandingAnimated, boolean isLastChild) {
// Let calculate how much the view is in the shelf
float viewStart = view.getTranslationY();
int fullHeight = view.getActualHeight() + mPaddingBetweenElements;
float iconTransformStart = calculateIconTransformationStart(view);
- float transformDistance = getIntrinsicHeight() * 1.5f;
- transformDistance *= NotificationUtils.interpolate(1.f, 1.5f, expandAmount);
- transformDistance = Math.min(transformDistance, fullHeight);
-
// Let's make sure the transform distance is
// at most to the icon (relevant for conversations)
- transformDistance = Math.min(viewStart + fullHeight - iconTransformStart,
- transformDistance);
+ float transformDistance = Math.min(
+ viewStart + fullHeight - iconTransformStart,
+ getIntrinsicHeight());
if (isLastChild) {
fullHeight = Math.min(fullHeight, view.getMinHeight() - getIntrinsicHeight());
- transformDistance = Math.min(transformDistance, view.getMinHeight()
- - getIntrinsicHeight());
+ transformDistance = Math.min(
+ transformDistance,
+ view.getMinHeight() - getIntrinsicHeight());
}
float viewEnd = viewStart + fullHeight;
- handleCustomTransformHeight(view, expandingAnimated, iconState);
-
- float fullTransitionAmount;
- float iconTransitionAmount;
- float contentTransformationAmount;
+ float fullTransitionAmount = 0.0f;
+ float iconTransitionAmount = 0.0f;
float shelfStart = getTranslationY();
- boolean fullyInOrOut = true;
- if (viewEnd >= shelfStart && (!mAmbientState.isUnlockHintRunning() || view.isInShelf())
+
+ if (viewEnd >= shelfStart
+ && (!mAmbientState.isUnlockHintRunning() || view.isInShelf())
&& (mAmbientState.isShadeExpanded()
|| (!view.isPinned() && !view.isHeadsUpAnimatingAway()))) {
- if (viewStart < shelfStart) {
- if (iconState != null && iconState.hasCustomTransformHeight()) {
- fullHeight = iconState.customTransformHeight;
- transformDistance = iconState.customTransformHeight;
- }
+ if (viewStart < shelfStart) {
float fullAmount = (shelfStart - viewStart) / fullHeight;
fullAmount = Math.min(1.0f, fullAmount);
- float interpolatedAmount = Interpolators.ACCELERATE_DECELERATE.getInterpolation(
- fullAmount);
- interpolatedAmount = NotificationUtils.interpolate(
- interpolatedAmount, fullAmount, expandAmount);
- fullTransitionAmount = 1.0f - interpolatedAmount;
-
+ fullTransitionAmount = 1.0f - fullAmount;
if (isLastChild) {
// Reduce icon transform distance to completely fade in shelf icon
// by the time the notification icon fades out, and vice versa
@@ -558,37 +519,14 @@ public class NotificationShelf extends ActivatableNotificationView implements
}
iconTransitionAmount = MathUtils.constrain(iconTransitionAmount, 0.0f, 1.0f);
iconTransitionAmount = 1.0f - iconTransitionAmount;
- fullyInOrOut = false;
} else {
+ // Fully in shelf.
fullTransitionAmount = 1.0f;
iconTransitionAmount = 1.0f;
}
-
- // Transforming the content
- contentTransformationAmount = (shelfStart - viewStart) / transformDistance;
- contentTransformationAmount = Math.min(1.0f, contentTransformationAmount);
- contentTransformationAmount = 1.0f - contentTransformationAmount;
- } else {
- fullTransitionAmount = 0.0f;
- iconTransitionAmount = 0.0f;
- contentTransformationAmount = 0.0f;
- }
- if (iconState != null && fullyInOrOut && !expandingAnimated && iconState.isLastExpandIcon) {
- iconState.isLastExpandIcon = false;
- iconState.customTransformHeight = NO_VALUE;
}
-
- // Update the content transformation amount
- if (view.isAboveShelf() || view.showingPulsing()
- || (!isLastChild && iconState != null && !iconState.translateContent)) {
- contentTransformationAmount = 0.0f;
- }
- view.setContentTransformationAmount(contentTransformationAmount, isLastChild);
-
- // Update the positioning of the icon
- updateIconPositioning(view, iconTransitionAmount, fullTransitionAmount,
- transformDistance, scrolling, scrollingFast, expandingAnimated, isLastChild);
-
+ updateIconPositioning(view, iconTransitionAmount,
+ scrollingFast, expandingAnimated, isLastChild);
return fullTransitionAmount;
}
@@ -607,87 +545,31 @@ public class NotificationShelf extends ActivatableNotificationView implements
return start;
}
- private void handleCustomTransformHeight(ExpandableView view, boolean expandingAnimated,
- NotificationIconContainer.IconState iconState) {
- if (iconState != null && expandingAnimated && mAmbientState.getScrollY() == 0
- && !mAmbientState.isOnKeyguard() && !iconState.isLastExpandIcon) {
- // We are expanding animated. Because we switch to a linear interpolation in this case,
- // the last icon may be stuck in between the shelf position and the notification
- // position, which looks pretty bad. We therefore optimize this case by applying a
- // shorter transition such that the icon is either fully in the notification or we clamp
- // it into the shelf if it's close enough.
- // We need to persist this, since after the expansion, the behavior should still be the
- // same.
- float position = mAmbientState.getIntrinsicPadding()
- + mHostLayoutController.getPositionInLinearLayout(view);
- int maxShelfStart = mMaxLayoutHeight - getIntrinsicHeight();
- if (position < maxShelfStart && position + view.getIntrinsicHeight() >= maxShelfStart
- && view.getTranslationY() < position) {
- iconState.isLastExpandIcon = true;
- iconState.customTransformHeight = NO_VALUE;
- // Let's check if we're close enough to snap into the shelf
- boolean forceInShelf = mMaxLayoutHeight - getIntrinsicHeight() - position
- < getIntrinsicHeight();
- if (!forceInShelf) {
- // We are overlapping the shelf but not enough, so the icon needs to be
- // repositioned
- iconState.customTransformHeight = (int) (mMaxLayoutHeight
- - getIntrinsicHeight() - position);
- }
- }
- }
- }
-
private void updateIconPositioning(ExpandableView view, float iconTransitionAmount,
- float fullTransitionAmount, float iconTransformDistance, boolean scrolling,
boolean scrollingFast, boolean expandingAnimated, boolean isLastChild) {
StatusBarIconView icon = view.getShelfIcon();
NotificationIconContainer.IconState iconState = getIconState(icon);
if (iconState == null) {
return;
}
- boolean forceInShelf =
- iconState.isLastExpandIcon && !iconState.hasCustomTransformHeight();
boolean clampInShelf = iconTransitionAmount > 0.5f || isTargetClipped(view);
float clampedAmount = clampInShelf ? 1.0f : 0.0f;
if (iconTransitionAmount == clampedAmount) {
- iconState.noAnimations = (scrollingFast || expandingAnimated) && !forceInShelf;
- iconState.useFullTransitionAmount = iconState.noAnimations
- || (!ICON_ANMATIONS_WHILE_SCROLLING && iconTransitionAmount == 0.0f
- && scrolling);
- iconState.useLinearTransitionAmount = !ICON_ANMATIONS_WHILE_SCROLLING
- && iconTransitionAmount == 0.0f && !mAmbientState.isExpansionChanging();
- iconState.translateContent = mMaxLayoutHeight - getTranslationY()
- - getIntrinsicHeight() > 0;
- }
- if (!forceInShelf && (scrollingFast || (expandingAnimated
- && iconState.useFullTransitionAmount && !ViewState.isAnimatingY(icon)))) {
+ iconState.noAnimations = (scrollingFast || expandingAnimated) && !isLastChild;
+ }
+ if (!isLastChild
+ && (scrollingFast || (expandingAnimated && !ViewState.isAnimatingY(icon)))) {
iconState.cancelAnimations(icon);
- iconState.useFullTransitionAmount = true;
iconState.noAnimations = true;
}
- if (iconState.hasCustomTransformHeight()) {
- iconState.useFullTransitionAmount = true;
- }
- if (iconState.isLastExpandIcon) {
- iconState.translateContent = false;
- }
float transitionAmount;
if (mAmbientState.isHiddenAtAll() && !view.isInShelf()) {
transitionAmount = mAmbientState.isFullyHidden() ? 1 : 0;
- } else if (isLastChild || !USE_ANIMATIONS_WHEN_OPENING
- || iconState.useFullTransitionAmount
- || iconState.useLinearTransitionAmount) {
- transitionAmount = iconTransitionAmount;
} else {
transitionAmount = iconTransitionAmount;
iconState.needsCannedAnimation = iconState.clampedAppearAmount != clampedAmount
&& !mNoAnimationsInThisFrame;
}
- iconState.iconAppearAmount = !USE_ANIMATIONS_WHEN_OPENING
- || iconState.useFullTransitionAmount
- ? fullTransitionAmount
- : transitionAmount;
iconState.clampedAppearAmount = clampedAmount;
setIconTransformationAmount(view, transitionAmount, isLastChild);
}
@@ -706,8 +588,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
return endOfTarget >= getTranslationY() - mPaddingBetweenElements;
}
- private void setIconTransformationAmount(ExpandableView view,
- float transitionAmount,
+ private void setIconTransformationAmount(ExpandableView view, float transitionAmount,
boolean isLastChild) {
if (!(view instanceof ExpandableNotificationRow)) {
return;
@@ -718,32 +599,30 @@ public class NotificationShelf extends ActivatableNotificationView implements
if (iconState == null) {
return;
}
- iconState.hidden = transitionAmount == 0.0f && !iconState.isAnimating(icon);
- boolean isAppearing = row.isDrawingAppearAnimation() && !row.isInShelf();
- if (isAppearing) {
- iconState.hidden = true;
- iconState.iconAppearAmount = 0.0f;
- }
iconState.alpha = transitionAmount;
+ boolean isAppearing = row.isDrawingAppearAnimation() && !row.isInShelf();
+ iconState.hidden = isAppearing
+ || (view instanceof ExpandableNotificationRow
+ && ((ExpandableNotificationRow) view).isLowPriority()
+ && mShelfIcons.hasMaxNumDot())
+ || (transitionAmount == 0.0f && !iconState.isAnimating(icon))
+ || row.isAboveShelf()
+ || row.showingPulsing()
+ || (!row.isInShelf() && isLastChild)
+ || row.getTranslationZ() > mAmbientState.getBaseZHeight();
+ iconState.iconAppearAmount = iconState.hidden? 0f : transitionAmount;
+
// Fade in icons at shelf start
// This is important for conversation icons, which are badged and need x reset
iconState.xTranslation = mShelfIcons.getActualPaddingStart();
- boolean stayingInShelf = row.isInShelf() && !row.isTransformingIntoShelf();
+ final boolean stayingInShelf = row.isInShelf() && !row.isTransformingIntoShelf();
if (stayingInShelf) {
iconState.iconAppearAmount = 1.0f;
iconState.alpha = 1.0f;
- iconState.scaleX = 1.0f;
- iconState.scaleY = 1.0f;
iconState.hidden = false;
}
- if (row.isAboveShelf()
- || row.showingPulsing()
- || (!row.isInShelf() && (isLastChild && row.areGutsExposed()
- || row.getTranslationZ() > mAmbientState.getBaseZHeight()))) {
- iconState.hidden = true;
- }
int backgroundColor = getBackgroundColorWithoutTint();
int shelfColor = icon.getContrastedStaticDrawableColor(backgroundColor);
if (row.isShowingIcon() && shelfColor != StatusBarIconView.NO_COLOR) {
@@ -819,42 +698,6 @@ public class NotificationShelf extends ActivatableNotificationView implements
return ret;
}
- private void setOpenedAmount(float openedAmount) {
- mNoAnimationsInThisFrame = openedAmount == 1.0f && mOpenedAmount == 0.0f;
- mOpenedAmount = openedAmount;
- if (!mAmbientState.isPanelFullWidth() || mAmbientState.isDozing()) {
- // We don't do a transformation at all, lets just assume we are fully opened
- openedAmount = 1.0f;
- }
- int start = mRelativeOffset;
- if (isLayoutRtl()) {
- start = getWidth() - start - mCollapsedIcons.getWidth();
- }
- int width = (int) NotificationUtils.interpolate(
- start + mCollapsedIcons.getFinalTranslationX(),
- mShelfIcons.getWidth(),
- FAST_OUT_SLOW_IN_REVERSE.getInterpolation(openedAmount));
- mShelfIcons.setActualLayoutWidth(width);
- boolean hasOverflow = mCollapsedIcons.hasOverflow();
- int collapsedPadding = mCollapsedIcons.getPaddingEnd();
- if (!hasOverflow) {
- // we have to ensure that adding the low priority notification won't lead to an
- // overflow
- collapsedPadding -= mCollapsedIcons.getNoOverflowExtraPadding();
- } else {
- // Partial overflow padding will fill enough space to add extra dots
- collapsedPadding -= mCollapsedIcons.getPartialOverflowExtraPadding();
- }
- float padding = NotificationUtils.interpolate(collapsedPadding,
- mShelfIcons.getPaddingEnd(),
- openedAmount);
- mShelfIcons.setActualPaddingEnd(padding);
- float paddingStart = NotificationUtils.interpolate(start,
- mShelfIcons.getPaddingStart(), openedAmount);
- mShelfIcons.setActualPaddingStart(paddingStart);
- mShelfIcons.setOpenedAmount(openedAmount);
- }
-
public void setMaxLayoutHeight(int maxLayoutHeight) {
mMaxLayoutHeight = maxLayoutHeight;
}
@@ -947,7 +790,6 @@ public class NotificationShelf extends ActivatableNotificationView implements
}
private class ShelfState extends ExpandableViewState {
- private float openedAmount;
private boolean hasItemsInStableShelf;
@Override
@@ -957,7 +799,6 @@ public class NotificationShelf extends ActivatableNotificationView implements
}
super.applyToView(view);
- setOpenedAmount(openedAmount);
updateAppearance();
setHasItemsInStableShelf(hasItemsInStableShelf);
mShelfIcons.setAnimationsEnabled(mAnimationsEnabled);
@@ -970,7 +811,6 @@ public class NotificationShelf extends ActivatableNotificationView implements
}
super.animateTo(child, properties);
- setOpenedAmount(openedAmount);
updateAppearance();
setHasItemsInStableShelf(hasItemsInStableShelf);
mShelfIcons.setAnimationsEnabled(mAnimationsEnabled);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
index 004cf9968a77..b0d41f155713 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
@@ -218,13 +218,18 @@ class ConversationNotificationManager @Inject constructor(
})
}
+ private fun ConversationState.shouldIncrementUnread(newBuilder: Notification.Builder) =
+ if (notification.flags and Notification.FLAG_ONLY_ALERT_ONCE != 0) {
+ false
+ } else {
+ val oldBuilder = Notification.Builder.recoverBuilder(context, notification)
+ Notification.areStyledNotificationsVisiblyDifferent(oldBuilder, newBuilder)
+ }
+
fun getUnreadCount(entry: NotificationEntry, recoveredBuilder: Notification.Builder): Int =
states.compute(entry.key) { _, state ->
val newCount = state?.run {
- val old = Notification.Builder.recoverBuilder(context, notification)
- val increment = Notification
- .areStyledNotificationsVisiblyDifferent(old, recoveredBuilder)
- if (increment) unreadCount + 1 else unreadCount
+ if (shouldIncrementUnread(recoveredBuilder)) unreadCount + 1 else unreadCount
} ?: 1
ConversationState(newCount, entry.sbn.notification)
}!!.unreadCount
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
index 18806effc545..5a3f48cec290 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
@@ -99,17 +99,11 @@ public class HighPriorityProvider {
private boolean hasHighPriorityCharacteristics(NotificationEntry entry) {
return !hasUserSetImportance(entry)
- && (isImportantOngoing(entry)
- || entry.getSbn().getNotification().hasMediaSession()
+ && (entry.getSbn().getNotification().hasMediaSession()
|| isPeopleNotification(entry)
|| isMessagingStyle(entry));
}
- private boolean isImportantOngoing(NotificationEntry entry) {
- return entry.getSbn().getNotification().isForegroundService()
- && entry.getRanking().getImportance() >= NotificationManager.IMPORTANCE_LOW;
- }
-
private boolean isMessagingStyle(NotificationEntry entry) {
return Notification.MessagingStyle.class.equals(
entry.getSbn().getNotification().getNotificationStyle());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index ba03d01b20b0..73e080423d40 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -69,7 +69,6 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable {
private float mContentTranslation;
protected boolean mLastInSection;
protected boolean mFirstInSection;
- boolean mIsBeingSwiped;
public ExpandableView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -174,14 +173,6 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable {
return false;
}
- public void setIsBeingSwiped(boolean swiped) {
- mIsBeingSwiped = swiped;
- }
-
- public boolean isBeingSwiped() {
- return mIsBeingSwiped;
- }
-
public boolean isHeadsUpAnimatingAway() {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
index 45f5b3136b9b..b1ac12e84fb3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.stack;
import android.content.res.Resources;
import android.util.MathUtils;
+import android.view.View;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
@@ -47,6 +48,10 @@ public class NotificationRoundnessManager {
private ExpandableNotificationRow mTrackedHeadsUp;
private float mAppearFraction;
+ private ExpandableView mSwipedView = null;
+ private ExpandableView mViewBeforeSwipedView = null;
+ private ExpandableView mViewAfterSwipedView = null;
+
@Inject
NotificationRoundnessManager(
KeyguardBypassController keyguardBypassController,
@@ -68,6 +73,11 @@ public class NotificationRoundnessManager {
boolean updateViewWithoutCallback(ExpandableView view,
boolean animate) {
+ if (view == null
+ || view == mViewBeforeSwipedView
+ || view == mViewAfterSwipedView) {
+ return false;
+ }
float topRoundness = getRoundness(view, true /* top */);
float bottomRoundness = getRoundness(view, false /* top */);
boolean topChanged = view.setTopRoundness(topRoundness, animate);
@@ -105,9 +115,60 @@ public class NotificationRoundnessManager {
return false;
}
+ void setViewsAffectedBySwipe(
+ ExpandableView viewBefore,
+ ExpandableView viewSwiped,
+ ExpandableView viewAfter,
+ boolean cornerAnimationsEnabled) {
+ if (!cornerAnimationsEnabled) {
+ return;
+ }
+ final boolean animate = true;
+
+ ExpandableView oldViewBefore = mViewBeforeSwipedView;
+ mViewBeforeSwipedView = viewBefore;
+ if (oldViewBefore != null) {
+ final float bottomRoundness = getRoundness(oldViewBefore, false /* top */);
+ oldViewBefore.setBottomRoundness(bottomRoundness, animate);
+ }
+ if (viewBefore != null) {
+ viewBefore.setBottomRoundness(1f, animate);
+ }
+
+ ExpandableView oldSwipedview = mSwipedView;
+ mSwipedView = viewSwiped;
+ if (oldSwipedview != null) {
+ final float bottomRoundness = getRoundness(oldSwipedview, false /* top */);
+ final float topRoundness = getRoundness(oldSwipedview, true /* top */);
+ oldSwipedview.setTopRoundness(topRoundness, animate);
+ oldSwipedview.setBottomRoundness(bottomRoundness, animate);
+ }
+ if (viewSwiped != null) {
+ viewSwiped.setTopRoundness(1f, animate);
+ viewSwiped.setBottomRoundness(1f, animate);
+ }
+
+ ExpandableView oldViewAfter = mViewAfterSwipedView;
+ mViewAfterSwipedView = viewAfter;
+ if (oldViewAfter != null) {
+ final float topRoundness = getRoundness(oldViewAfter, true /* top */);
+ oldViewAfter.setTopRoundness(topRoundness, animate);
+ }
+ if (viewAfter != null) {
+ viewAfter.setTopRoundness(1f, animate);
+ }
+ }
+
private float getRoundness(ExpandableView view, boolean top) {
+ if (view == null) {
+ return 0f;
+ }
+ if (view == mViewBeforeSwipedView
+ || view == mSwipedView
+ || view == mViewAfterSwipedView) {
+ return 1f;
+ }
if ((view.isPinned()
- || view.isBeingSwiped()
|| (view.isHeadsUpAnimatingAway()) && !mExpanded)) {
return 1.0f;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 417ff5e9aed0..970efd5cbfe2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -1025,8 +1025,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
boolean clip = clipStart > start && clipStart < end
|| clipEnd >= start && clipEnd <= end;
clip &= !(first && mScrollAdapter.isScrolledToTop());
- child.setDistanceToTopRoundness(clip ? Math.max(start - clipStart, 0)
- : ExpandableView.NO_ROUNDNESS);
+ child.setDistanceToTopRoundness(ExpandableView.NO_ROUNDNESS);
first = false;
}
}
@@ -2292,9 +2291,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
ExpandableView child = (ExpandableView) getChildAt(i);
if (child.getVisibility() != View.GONE
&& !(child instanceof StackScrollerDecorView)
- && child != mShelf
- && (mSwipeHelper.getSwipedView() != child
- || !child.getResources().getBoolean(R.bool.flag_notif_updates))) {
+ && child != mShelf) {
children.add(child);
}
}
@@ -4993,28 +4990,48 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mSwipedOutViews.add(v);
}
- void onSwipeBegin(View v) {
- if (v instanceof ExpandableView) {
- ExpandableView ev = (ExpandableView) v;
- ev.setIsBeingSwiped(true);
- mController.getNoticationRoundessManager()
- .updateViewWithoutCallback(ev, true /* animate */);
+ void onSwipeBegin(View viewSwiped) {
+ if (!(viewSwiped instanceof ExpandableNotificationRow)) {
+ return;
}
- requestDisallowInterceptTouchEvent(true);
+ final int indexOfSwipedView = indexOfChild(viewSwiped);
+ if (indexOfSwipedView < 0) {
+ return;
+ }
+ mSectionsManager.updateFirstAndLastViewsForAllSections(
+ mSections, getChildrenWithBackground());
+ View viewBefore = null;
+ if (indexOfSwipedView > 0) {
+ viewBefore = getChildAt(indexOfSwipedView - 1);
+ if (mSectionsManager.beginsSection(viewSwiped, viewBefore)) {
+ viewBefore = null;
+ }
+ }
+ View viewAfter = null;
+ if (indexOfSwipedView < getChildCount()) {
+ viewAfter = getChildAt(indexOfSwipedView + 1);
+ if (mSectionsManager.beginsSection(viewAfter, viewSwiped)) {
+ viewAfter = null;
+ }
+ }
+ mController.getNoticationRoundessManager()
+ .setViewsAffectedBySwipe((ExpandableView) viewBefore,
+ (ExpandableView) viewSwiped,
+ (ExpandableView) viewAfter,
+ getResources().getBoolean(R.bool.flag_notif_updates));
+
updateFirstAndLastBackgroundViews();
+ requestDisallowInterceptTouchEvent(true);
updateContinuousShadowDrawing();
updateContinuousBackgroundDrawing();
requestChildrenUpdate();
}
- void onSwipeEnd(View v) {
- if (v instanceof ExpandableView) {
- ExpandableView ev = (ExpandableView) v;
- ev.setIsBeingSwiped(false);
- mController.getNoticationRoundessManager()
- .updateViewWithoutCallback(ev, true /* animate */);
- }
+ void onSwipeEnd() {
updateFirstAndLastBackgroundViews();
+ mController.getNoticationRoundessManager()
+ .setViewsAffectedBySwipe(null, null, null,
+ getResources().getBoolean(R.bool.flag_notif_updates));
}
void setTopHeadsUpEntry(NotificationEntry topEntry) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 399702869d70..7baad1cc265e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -399,7 +399,7 @@ public class NotificationStackScrollLayoutController {
if (mView.getDismissAllInProgress()) {
return;
}
- mView.onSwipeEnd(view);
+ mView.onSwipeEnd();
if (view instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
if (row.isHeadsUp()) {
@@ -459,7 +459,7 @@ public class NotificationStackScrollLayoutController {
@Override
public void onChildSnappedBack(View animView, float targetLeft) {
- mView.onSwipeEnd(animView);
+ mView.onSwipeEnd();
if (animView instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) animView;
if (row.isPinned() && !canChildBeDismissed(row)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 9561851ab28b..89b97aef6283 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -155,7 +155,6 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
private int mCannedAnimationStartIndex = -1;
private int mSpeedBumpIndex = -1;
private int mIconSize;
- private float mOpenedAmount = 0.0f;
private boolean mDisallowNextAnimation;
private boolean mAnimationsEnabled = true;
private ArrayMap<String, ArrayList<StatusBarIcon>> mReplacingIcons;
@@ -349,6 +348,10 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
}
}
+ public boolean hasMaxNumDot() {
+ return mNumDots >= MAX_DOTS;
+ }
+
private boolean areAnimationsEnabled(StatusBarIconView icon) {
return mAnimationsEnabled || icon == mIsolatedIcon;
}
@@ -391,7 +394,6 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
float overflowStart = getMaxOverflowStart();
mVisualOverflowStart = 0;
mFirstVisibleIconState = null;
- boolean hasAmbient = mSpeedBumpIndex != -1 && mSpeedBumpIndex < getChildCount();
for (int i = 0; i < childCount; i++) {
View view = getChildAt(i);
IconState iconState = mIconStates.get(view);
@@ -410,10 +412,9 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
float drawingScale = mOnLockScreen && view instanceof StatusBarIconView
? ((StatusBarIconView) view).getIconScaleIncreased()
: 1f;
- if (mOpenedAmount != 0.0f) {
- noOverflowAfter = noOverflowAfter && !hasAmbient && !forceOverflow;
- }
- iconState.visibleState = StatusBarIconView.STATE_ICON;
+ iconState.visibleState = iconState.hidden
+ ? StatusBarIconView.STATE_HIDDEN
+ : StatusBarIconView.STATE_ICON;
boolean isOverflowing =
(translationX > (noOverflowAfter ? layoutEnd - mIconSize
@@ -598,10 +599,6 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
mSpeedBumpIndex = speedBumpIndex;
}
- public void setOpenedAmount(float expandAmount) {
- mOpenedAmount = expandAmount;
- }
-
public boolean hasOverflow() {
return mNumDots > 0;
}
@@ -706,13 +703,8 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
public boolean justAdded = true;
private boolean justReplaced;
public boolean needsCannedAnimation;
- public boolean useFullTransitionAmount;
- public boolean useLinearTransitionAmount;
- public boolean translateContent;
public int iconColor = StatusBarIconView.NO_COLOR;
public boolean noAnimations;
- public boolean isLastExpandIcon;
- public int customTransformHeight = NO_VALUE;
private final View mView;
private final Consumer<Property> mCannedAnimationEndListener;
@@ -734,8 +726,15 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
StatusBarIconView icon = (StatusBarIconView) view;
boolean animate = false;
AnimationProperties animationProperties = null;
- boolean animationsAllowed = areAnimationsEnabled(icon) && !mDisallowNextAnimation
- && !noAnimations;
+ final boolean isLowPriorityIconChange =
+ (visibleState == StatusBarIconView.STATE_HIDDEN
+ && icon.getVisibleState() == StatusBarIconView.STATE_DOT)
+ || (visibleState == StatusBarIconView.STATE_DOT
+ && icon.getVisibleState() == StatusBarIconView.STATE_HIDDEN);
+ final boolean animationsAllowed = areAnimationsEnabled(icon)
+ && !mDisallowNextAnimation
+ && !noAnimations
+ && !isLowPriorityIconChange;
if (animationsAllowed) {
if (justAdded || justReplaced) {
super.applyToView(icon);
@@ -822,10 +821,6 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
needsCannedAnimation = false;
}
- public boolean hasCustomTransformHeight() {
- return isLastExpandIcon && customTransformHeight != NO_VALUE;
- }
-
@Override
public void initFrom(View view) {
super.initFrom(view);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index eb95d1653e84..b9d8d27b8971 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -793,36 +793,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
@Test
- public void testStartUdfpsServiceOnShadeLocked() {
- // GIVEN
- // - bouncer isn't showing
- // - user has authenticated since boot
- setKeyguardBouncerVisibility(false /* isVisible */);
- when(mStrongAuthTracker.hasUserAuthenticatedSinceBoot()).thenReturn(true);
-
- // WHEN the status bar state changes to SHADE_LOCKED
- mStatusBarStateListener.onStateChanged(StatusBarState.SHADE_LOCKED);
-
- // THEN we shouldn't listen for udfps
- assertThat(mKeyguardUpdateMonitor.shouldListenForUdfps()).isEqualTo(false);
- }
-
- @Test
- public void testStartUdfpsServiceOnFullscreenUserSwitcher() {
- // GIVEN
- // - bouncer isn't showing
- // - user has authenticated since boot
- setKeyguardBouncerVisibility(false /* isVisible */);
- when(mStrongAuthTracker.hasUserAuthenticatedSinceBoot()).thenReturn(true);
-
- // WHEN the status bar state changes to FULLSCREEN_USER_SWITCHER
- mStatusBarStateListener.onStateChanged(StatusBarState.FULLSCREEN_USER_SWITCHER);
-
- // THEN we shouldn't listen for udfps
- assertThat(mKeyguardUpdateMonitor.shouldListenForUdfps()).isEqualTo(false);
- }
-
- @Test
public void testStartUdfpsServiceNoAuthenticationSinceLastBoot() {
// GIVEN
// - bouncer isn't showing
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java
index 472ed7a22fe3..ca0a4aa2b04d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java
@@ -21,7 +21,6 @@ import static com.android.systemui.classifier.Classifier.UNLOCK;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
-import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.utils.leaks.FakeBatteryController;
import com.android.systemui.utils.leaks.LeakCheckedTest;
@@ -45,8 +44,7 @@ public class ClassifierTest extends LeakCheckedTest {
displayMetrics.widthPixels = 1000;
displayMetrics.heightPixels = 1000;
mFakeBatteryController = new FakeBatteryController(getLeakCheck());
- mDataProvider = new FalsingDataProvider(displayMetrics, mFakeBatteryController,
- new FakeSystemClock());
+ mDataProvider = new FalsingDataProvider(displayMetrics, mFakeBatteryController);
mDataProvider.setInteractionType(UNLOCK);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java
index 17c2700c5bda..4e1742497d90 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java
@@ -19,7 +19,6 @@ package com.android.systemui.classifier;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.anyList;
-import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;
import android.testing.AndroidTestingRunner;
@@ -35,8 +34,6 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
-import java.util.Deque;
-import java.util.LinkedList;
import java.util.List;
@SmallTest
@@ -47,7 +44,7 @@ public class DoubleTapClassifierTest extends ClassifierTest {
private static final long DOUBLE_TAP_TIMEOUT_MS = 100;
private List<MotionEvent> mMotionEvents = new ArrayList<>();
- private final Deque<List<MotionEvent>> mHistoricalMotionEvents = new LinkedList<>();
+ private List<MotionEvent> mPriorMotionEvents = new ArrayList<>();
@Mock
private FalsingDataProvider mDataProvider;
@@ -64,7 +61,7 @@ public class DoubleTapClassifierTest extends ClassifierTest {
MockitoAnnotations.initMocks(this);
mClassifier = new DoubleTapClassifier(mDataProvider, mSingleTapClassifier, TOUCH_SLOP,
DOUBLE_TAP_TIMEOUT_MS);
- doReturn(mHistoricalMotionEvents).when(mDataProvider).getHistoricalMotionEvents();
+ when(mDataProvider.getPriorMotionEvents()).thenReturn(mPriorMotionEvents);
}
@After
@@ -177,9 +174,8 @@ public class DoubleTapClassifierTest extends ClassifierTest {
}
private void archiveMotionEvents() {
- mHistoricalMotionEvents.addFirst(mMotionEvents);
- doReturn(mHistoricalMotionEvents).when(mDataProvider).getHistoricalMotionEvents();
+ mPriorMotionEvents = mMotionEvents;
+ when(mDataProvider.getPriorMotionEvents()).thenReturn(mPriorMotionEvents);
mMotionEvents = new ArrayList<>();
-
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
index be0cc97a2f0f..ebdda67a90d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
@@ -26,7 +26,6 @@ import android.view.MotionEvent;
import androidx.test.filters.SmallTest;
-import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.utils.leaks.FakeBatteryController;
import org.junit.After;
@@ -52,8 +51,7 @@ public class FalsingDataProviderTest extends ClassifierTest {
displayMetrics.ydpi = 100;
displayMetrics.widthPixels = 1000;
displayMetrics.heightPixels = 1000;
- mDataProvider = new FalsingDataProvider(displayMetrics, mFakeBatteryController,
- new FakeSystemClock());
+ mDataProvider = new FalsingDataProvider(displayMetrics, mFakeBatteryController);
}
@After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/TimeLimitedMotionEventBufferTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/TimeLimitedMotionEventBufferTest.java
index 6e312594a2e4..901196f8bbba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/TimeLimitedMotionEventBufferTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/TimeLimitedMotionEventBufferTest.java
@@ -89,20 +89,4 @@ public class TimeLimitedMotionEventBufferTest extends SysuiTestCase {
assertThat(mBuffer.get(0), is(eventC));
assertThat(mBuffer.get(1), is(eventD));
}
-
- @Test
- public void testFullyExpired() {
- MotionEvent eventA = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
- MotionEvent eventB = MotionEvent.obtain(0, 1, MotionEvent.ACTION_MOVE, 0, 0, 0);
- MotionEvent eventC = MotionEvent.obtain(0, 2, MotionEvent.ACTION_MOVE, 0, 0, 0);
- MotionEvent eventD = MotionEvent.obtain(0, 3, MotionEvent.ACTION_UP, 0, 0, 0);
-
- mBuffer.add(eventA);
- mBuffer.add(eventB);
- mBuffer.add(eventC);
- mBuffer.add(eventD);
-
- assertThat(mBuffer.isFullyExpired(2), is(false));
- assertThat(mBuffer.isFullyExpired(6), is(true));
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
index 1062fae52e7a..eedf09936b4d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
@@ -46,16 +46,12 @@ import android.service.dreams.IDreamManager;
import android.telephony.TelephonyManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.util.FeatureFlagUtils;
import android.view.IWindowManager;
import android.view.View;
import android.view.WindowManagerPolicyConstants;
import android.widget.FrameLayout;
import androidx.test.filters.SmallTest;
-import androidx.test.uiautomator.By;
-import androidx.test.uiautomator.UiObject2;
-import androidx.test.uiautomator.Until;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.logging.MetricsLogger;
@@ -251,22 +247,6 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
}
@Test
- public void testShouldLogScreenshotLongPress() {
- FeatureFlagUtils.setEnabled(mContext, FeatureFlagUtils.SCREENRECORD_LONG_PRESS, true);
- GlobalActionsDialog.ScreenshotAction screenshotAction =
- mGlobalActionsDialog.makeScreenshotActionForTesting();
- screenshotAction.onLongPress();
- verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_SCREENSHOT_LONG_PRESS);
-
- // Dismiss ScreenRecordDialog opened by the long press above.
- final UiObject2 cancelButton = getUiDevice().wait(
- Until.findObject(By.text(CANCEL_BUTTON)), UI_TIMEOUT_MILLIS);
- if (cancelButton != null) {
- cancelButton.click();
- }
- }
-
- @Test
public void testShouldShowScreenshot() {
mContext.getOrCreateTestableResources().addOverride(
com.android.internal.R.integer.config_navBarInteractionMode,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt
new file mode 100644
index 000000000000..a9d10e9e6872
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt
@@ -0,0 +1,147 @@
+package com.android.systemui.qs.tiles
+
+import android.app.AlarmManager
+import android.app.PendingIntent
+import android.os.Handler
+import android.service.quicksettings.Tile
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.statusbar.policy.NextAlarmController
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.eq
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class AlarmTileTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var qsHost: QSHost
+ @Mock
+ private lateinit var metricsLogger: MetricsLogger
+ @Mock
+ private lateinit var statusBarStateController: StatusBarStateController
+ @Mock
+ private lateinit var activityStarter: ActivityStarter
+ @Mock
+ private lateinit var qsLogger: QSLogger
+ @Mock
+ private lateinit var featureFlags: FeatureFlags
+ @Mock
+ private lateinit var userTracker: UserTracker
+ @Mock
+ private lateinit var nextAlarmController: NextAlarmController
+ @Mock
+ private lateinit var uiEventLogger: UiEventLogger
+ @Mock
+ private lateinit var pendingIntent: PendingIntent
+ @Captor
+ private lateinit var callbackCaptor: ArgumentCaptor<NextAlarmController.NextAlarmChangeCallback>
+
+ private lateinit var testableLooper: TestableLooper
+ private lateinit var tile: AlarmTile
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ testableLooper = TestableLooper.get(this)
+
+ `when`(qsHost.context).thenReturn(mContext)
+ `when`(qsHost.uiEventLogger).thenReturn(uiEventLogger)
+
+ tile = AlarmTile(
+ qsHost,
+ testableLooper.looper,
+ Handler(testableLooper.looper),
+ metricsLogger,
+ statusBarStateController,
+ activityStarter,
+ qsLogger,
+ featureFlags,
+ userTracker,
+ nextAlarmController
+ )
+
+ verify(nextAlarmController).observe(eq(tile), capture(callbackCaptor))
+ tile.refreshState()
+ testableLooper.processAllMessages()
+ }
+
+ @Test
+ fun testNotAvailableFeatureFlag() {
+ `when`(featureFlags.isAlarmTileAvailable).thenReturn(false)
+ assertThat(tile.isAvailable).isFalse()
+ }
+
+ @Test
+ fun testAvailableFeatureFlag() {
+ `when`(featureFlags.isAlarmTileAvailable).thenReturn(true)
+ assertThat(tile.isAvailable).isTrue()
+ }
+
+ @Test
+ fun testDoesntHandleLongClick() {
+ assertThat(tile.state.handlesLongClick).isFalse()
+ }
+
+ @Test
+ fun testInactiveByDefault() {
+ assertThat(tile.state.state).isEqualTo(Tile.STATE_INACTIVE)
+ }
+
+ @Test
+ fun testInactiveAfterNullNextAlarm() {
+ callbackCaptor.value.onNextAlarmChanged(null)
+
+ testableLooper.processAllMessages()
+ assertThat(tile.state.state).isEqualTo(Tile.STATE_INACTIVE)
+ }
+
+ @Test
+ fun testActivityStartedWhenNullNextAlarm() {
+ callbackCaptor.value.onNextAlarmChanged(null)
+ tile.click()
+
+ testableLooper.processAllMessages()
+ verify(activityStarter).postStartActivityDismissingKeyguard(tile.defaultIntent, 0)
+ }
+
+ @Test
+ fun testActiveAfterNextAlarm() {
+ val alarmInfo = AlarmManager.AlarmClockInfo(1L, pendingIntent)
+ callbackCaptor.value.onNextAlarmChanged(alarmInfo)
+
+ testableLooper.processAllMessages()
+ assertThat(tile.state.state).isEqualTo(Tile.STATE_ACTIVE)
+ }
+
+ @Test
+ fun testActivityStartedWhenNextAlarm() {
+ val alarmInfo = AlarmManager.AlarmClockInfo(1L, pendingIntent)
+ callbackCaptor.value.onNextAlarmChanged(alarmInfo)
+ tile.click()
+
+ testableLooper.processAllMessages()
+ verify(activityStarter).postStartActivityDismissingKeyguard(pendingIntent)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java
index 14877eec9a83..30708a7cb2fe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java
@@ -127,25 +127,7 @@ public class HighPriorityProviderTest extends SysuiTestCase {
.getPeopleNotificationType(entry))
.thenReturn(TYPE_NON_PERSON);
- // THEN it has high priority
- assertTrue(mHighPriorityProvider.isHighPriority(entry));
- }
-
- @Test
- public void minImportanceForeground() {
- // GIVEN notification is low importance and is associated with a foreground service
- final Notification notification = mock(Notification.class);
- when(notification.isForegroundService()).thenReturn(true);
-
- final NotificationEntry entry = new NotificationEntryBuilder()
- .setNotification(notification)
- .setImportance(IMPORTANCE_MIN)
- .build();
- when(mPeopleNotificationIdentifier
- .getPeopleNotificationType(entry))
- .thenReturn(TYPE_NON_PERSON);
-
- // THEN it does NOT have high priority
+ // THEN it has low priority
assertFalse(mHighPriorityProvider.isHighPriority(entry));
}
@@ -155,7 +137,6 @@ public class HighPriorityProviderTest extends SysuiTestCase {
// to less than IMPORTANCE_DEFAULT (ie: IMPORTANCE_LOW or IMPORTANCE_MIN)
final Notification notification = new Notification.Builder(mContext, "test")
.setStyle(new Notification.MessagingStyle(""))
- .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
.build();
final NotificationChannel channel = new NotificationChannel("a", "a",
IMPORTANCE_LOW);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 9a5e4ca0068a..07b473caa9cc 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3614,11 +3614,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
// pendingIntent => NetworkRequestInfo map.
// This method assumes that every non-null PendingIntent maps to exactly 1 NetworkRequestInfo.
private NetworkRequestInfo findExistingNetworkRequestInfo(PendingIntent pendingIntent) {
- Intent intent = pendingIntent.getIntent();
for (Map.Entry<NetworkRequest, NetworkRequestInfo> entry : mNetworkRequests.entrySet()) {
PendingIntent existingPendingIntent = entry.getValue().mPendingIntent;
if (existingPendingIntent != null &&
- existingPendingIntent.getIntent().filterEquals(intent)) {
+ existingPendingIntent.intentFilterEquals(pendingIntent)) {
return entry.getValue();
}
}
@@ -3661,6 +3660,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
}
+ // If this NRI has a satisfier already, it is replacing an older request that
+ // has been removed. Track it.
+ final NetworkRequest activeRequest = nri.getActiveRequest();
+ if (null != activeRequest) {
+ // If there is an active request, then for sure there is a satisfier.
+ nri.getSatisfier().addRequest(activeRequest);
+ }
}
rematchAllNetworksAndRequests();
@@ -5281,14 +5287,26 @@ public class ConnectivityService extends IConnectivityManager.Stub
ensureAllNetworkRequestsHaveType(r);
mRequests = initializeRequests(r);
mNetworkRequestForCallback = nri.getNetworkRequestForCallback();
- // Note here that the satisfier may have corresponded to an old request, that
- // this code doesn't try to take over. While it is a small discrepancy in the
- // structure of these requests, it will be fixed by the next rematch and it's
- // not as bad as having an NRI not storing its real satisfier.
- // Fixing this discrepancy would require figuring out in the copying code what
- // is the new request satisfied by this, which is a bit complex and not very
- // useful as no code is using it until rematch fixes it.
- mSatisfier = nri.mSatisfier;
+ final NetworkAgentInfo satisfier = nri.getSatisfier();
+ if (null != satisfier) {
+ // If the old NRI was satisfied by an NAI, then it may have had an active request.
+ // The active request is necessary to figure out what callbacks to send, in
+ // particular then a network updates its capabilities.
+ // As this code creates a new NRI with a new set of requests, figure out which of
+ // the list of requests should be the active request. It is always the first
+ // request of the list that can be satisfied by the satisfier since the order of
+ // requests is a priority order.
+ // Note even in the presence of a satisfier there may not be an active request,
+ // when the satisfier is the no-service network.
+ NetworkRequest activeRequest = null;
+ for (final NetworkRequest candidate : r) {
+ if (candidate.canBeSatisfiedBy(satisfier.networkCapabilities)) {
+ activeRequest = candidate;
+ break;
+ }
+ }
+ setSatisfier(satisfier, activeRequest);
+ }
mMessenger = nri.mMessenger;
mBinder = nri.mBinder;
mPid = nri.mPid;
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 978bd643f9b7..a9904ba0de91 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -964,14 +964,21 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
remove(r.binder);
}
}
- if (events.contains(TelephonyCallback.EVENT_CALL_STATE_CHANGED)) {
+ if (events.contains(TelephonyCallback.EVENT_LEGACY_CALL_STATE_CHANGED)) {
try {
- r.callback.onCallStateChanged(mCallState[phoneId],
+ r.callback.onLegacyCallStateChanged(mCallState[phoneId],
getCallIncomingNumber(r, phoneId));
} catch (RemoteException ex) {
remove(r.binder);
}
}
+ if (events.contains(TelephonyCallback.EVENT_CALL_STATE_CHANGED)) {
+ try {
+ r.callback.onCallStateChanged(mCallState[phoneId]);
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
+ }
if (events.contains(TelephonyCallback.EVENT_DATA_CONNECTION_STATE_CHANGED)) {
try {
r.callback.onDataConnectionStateChanged(mDataConnectionState[phoneId],
@@ -1306,13 +1313,24 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
synchronized (mRecords) {
for (Record r : mRecords) {
- if (r.matchTelephonyCallbackEvent(TelephonyCallback.EVENT_CALL_STATE_CHANGED)
+ if (r.matchTelephonyCallbackEvent(TelephonyCallback.EVENT_LEGACY_CALL_STATE_CHANGED)
&& (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) {
try {
// Ensure the listener has read call log permission; if they do not return
// an empty phone number.
+ // This is ONLY for legacy onCallStateChanged in PhoneStateListener.
String phoneNumberOrEmpty = r.canReadCallLog() ? phoneNumber : "";
- r.callback.onCallStateChanged(state, phoneNumberOrEmpty);
+ r.callback.onLegacyCallStateChanged(state, phoneNumberOrEmpty);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+
+ if (r.matchTelephonyCallbackEvent(TelephonyCallback.EVENT_CALL_STATE_CHANGED)
+ && (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) {
+ try {
+ // The new callback does NOT provide the phone number.
+ r.callback.onCallStateChanged(state);
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
@@ -1341,12 +1359,25 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mCallState[phoneId] = state;
mCallIncomingNumber[phoneId] = incomingNumber;
for (Record r : mRecords) {
- if (r.matchTelephonyCallbackEvent(TelephonyCallback.EVENT_CALL_STATE_CHANGED)
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_LEGACY_CALL_STATE_CHANGED)
&& (r.subId == subId)
&& (r.subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) {
try {
+ // Only the legacy PhoneStateListener receives the phone number.
String incomingNumberOrEmpty = getCallIncomingNumber(r, phoneId);
- r.callback.onCallStateChanged(state, incomingNumberOrEmpty);
+ r.callback.onLegacyCallStateChanged(state, incomingNumberOrEmpty);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ if (r.matchTelephonyCallbackEvent(TelephonyCallback.EVENT_CALL_STATE_CHANGED)
+ && (r.subId == subId)
+ && (r.subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) {
+ try {
+ // The phone number is not included in the new call state changed
+ // listener.
+ r.callback.onCallStateChanged(state);
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 4a12ff6932de..6614e06aba8c 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -5276,8 +5276,12 @@ public class AppOpsService extends IAppOpsService.Stub {
pw.println(" Limit output to data associated with the given package name.");
pw.println(" --attributionTag [attributionTag]");
pw.println(" Limit output to data associated with the given attribution tag.");
+ pw.println(" --include-discrete [n]");
+ pw.println(" Include discrete ops limited to n per dimension. Use zero for no limit.");
pw.println(" --watchers");
pw.println(" Only output the watcher sections.");
+ pw.println(" --history");
+ pw.println(" Only output history.");
}
private void dumpStatesLocked(@NonNull PrintWriter pw, @Nullable String filterAttributionTag,
@@ -5412,10 +5416,12 @@ public class AppOpsService extends IAppOpsService.Stub {
boolean dumpWatchers = false;
// TODO ntmyren: Remove the dumpHistory and dumpFilter
boolean dumpHistory = false;
+ boolean includeDiscreteOps = false;
+ int nDiscreteOps = 10;
@HistoricalOpsRequestFilter int dumpFilter = 0;
if (args != null) {
- for (int i=0; i<args.length; i++) {
+ for (int i = 0; i < args.length; i++) {
String arg = args[i];
if ("-h".equals(arg)) {
dumpHelp(pw);
@@ -5473,7 +5479,22 @@ public class AppOpsService extends IAppOpsService.Stub {
}
} else if ("--watchers".equals(arg)) {
dumpWatchers = true;
- } else if (arg.length() > 0 && arg.charAt(0) == '-'){
+ } else if ("--include-discrete".equals(arg)) {
+ i++;
+ if (i >= args.length) {
+ pw.println("No argument for --include-discrete option");
+ return;
+ }
+ try {
+ nDiscreteOps = Integer.valueOf(args[i]);
+ } catch (NumberFormatException e) {
+ pw.println("Wrong parameter: " + args[i]);
+ return;
+ }
+ includeDiscreteOps = true;
+ } else if ("--history".equals(arg)) {
+ dumpHistory = true;
+ } else if (arg.length() > 0 && arg.charAt(0) == '-') {
pw.println("Unknown option: " + arg);
return;
} else {
@@ -5483,6 +5504,8 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
+ final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+ final Date date = new Date();
synchronized (this) {
pw.println("Current AppOps Service state:");
if (!dumpHistory && !dumpWatchers) {
@@ -5492,8 +5515,6 @@ public class AppOpsService extends IAppOpsService.Stub {
final long now = System.currentTimeMillis();
final long nowElapsed = SystemClock.elapsedRealtime();
final long nowUptime = SystemClock.uptimeMillis();
- final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
- final Date date = new Date();
boolean needSep = false;
if (dumpFilter == 0 && dumpMode < 0 && mProfileOwners != null && !dumpWatchers
&& !dumpHistory) {
@@ -5961,6 +5982,11 @@ public class AppOpsService extends IAppOpsService.Stub {
mHistoricalRegistry.dump(" ", pw, dumpUid, dumpPackage, dumpAttributionTag, dumpOp,
dumpFilter);
}
+ if (includeDiscreteOps) {
+ pw.println("Discrete accesses: ");
+ mHistoricalRegistry.dumpDiscreteData(pw, dumpUid, dumpPackage, dumpAttributionTag,
+ dumpFilter, dumpOp, sdf, date, " ", nDiscreteOps);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/appop/DiscreteRegistry.java b/services/core/java/com/android/server/appop/DiscreteRegistry.java
index a99d90883f87..ed62abc7d773 100644
--- a/services/core/java/com/android/server/appop/DiscreteRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteRegistry.java
@@ -23,9 +23,13 @@ import static android.app.AppOpsManager.FILTER_BY_UID;
import static android.app.AppOpsManager.OP_CAMERA;
import static android.app.AppOpsManager.OP_COARSE_LOCATION;
import static android.app.AppOpsManager.OP_FINE_LOCATION;
+import static android.app.AppOpsManager.OP_FLAGS_ALL;
import static android.app.AppOpsManager.OP_FLAG_SELF;
import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED;
+import static android.app.AppOpsManager.OP_NONE;
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
+import static android.app.AppOpsManager.flagsToString;
+import static android.app.AppOpsManager.getUidStateName;
import static java.lang.Math.max;
@@ -45,15 +49,20 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.XmlUtils;
+import libcore.util.EmptyArray;
+
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.Date;
import java.util.List;
/**
@@ -237,6 +246,23 @@ final class DiscreteRegistry {
}
}
+ void dump(@NonNull PrintWriter pw, int uidFilter, @Nullable String packageNameFilter,
+ @Nullable String attributionTagFilter,
+ @AppOpsManager.HistoricalOpsRequestFilter int filter, int dumpOp,
+ @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix,
+ int nDiscreteOps) {
+ DiscreteOps discreteOps = new DiscreteOps();
+ synchronized (mOnDiskLock) {
+ writeAndClearAccessHistory();
+ String[] opNamesFilter = dumpOp == OP_NONE ? EmptyArray.STRING
+ : new String[]{AppOpsManager.opToPublicName(dumpOp)};
+ readDiscreteOpsFromDisk(discreteOps, 0, Instant.now().toEpochMilli(), filter,
+ uidFilter, packageNameFilter, opNamesFilter, attributionTagFilter,
+ OP_FLAGS_ALL);
+ }
+ discreteOps.dump(pw, sdf, date, prefix, nDiscreteOps);
+ }
+
public static boolean isDiscreteOp(int op, int uid, @AppOpsManager.OpFlags int flags) {
if (!isDiscreteOp(op)) {
return false;
@@ -306,6 +332,18 @@ final class DiscreteRegistry {
stream.close();
}
+ private void dump(@NonNull PrintWriter pw, @NonNull SimpleDateFormat sdf,
+ @NonNull Date date, @NonNull String prefix, int nDiscreteOps) {
+ int nUids = mUids.size();
+ for (int i = 0; i < nUids; i++) {
+ pw.print(prefix);
+ pw.print("Uid: ");
+ pw.print(mUids.keyAt(i));
+ pw.println();
+ mUids.valueAt(i).dump(pw, sdf, date, prefix + " ", nDiscreteOps);
+ }
+ }
+
private DiscreteUidOps getOrCreateDiscreteUidOps(int uid) {
DiscreteUidOps result = mUids.get(uid);
if (result == null) {
@@ -395,6 +433,18 @@ final class DiscreteRegistry {
}
}
+ private void dump(@NonNull PrintWriter pw, @NonNull SimpleDateFormat sdf,
+ @NonNull Date date, @NonNull String prefix, int nDiscreteOps) {
+ int nPackages = mPackages.size();
+ for (int i = 0; i < nPackages; i++) {
+ pw.print(prefix);
+ pw.print("Package: ");
+ pw.print(mPackages.keyAt(i));
+ pw.println();
+ mPackages.valueAt(i).dump(pw, sdf, date, prefix + " ", nDiscreteOps);
+ }
+ }
+
void deserialize(TypedXmlPullParser parser, long beginTimeMillis,
long endTimeMillis, @AppOpsManager.HistoricalOpsRequestFilter int filter,
@Nullable String packageNameFilter,
@@ -458,6 +508,17 @@ final class DiscreteRegistry {
}
}
+ private void dump(@NonNull PrintWriter pw, @NonNull SimpleDateFormat sdf,
+ @NonNull Date date, @NonNull String prefix, int nDiscreteOps) {
+ int nOps = mPackageOps.size();
+ for (int i = 0; i < nOps; i++) {
+ pw.print(prefix);
+ pw.print(AppOpsManager.opToName(mPackageOps.keyAt(i)));
+ pw.println();
+ mPackageOps.valueAt(i).dump(pw, sdf, date, prefix + " ", nDiscreteOps);
+ }
+ }
+
void deserialize(TypedXmlPullParser parser, long beginTimeMillis, long endTimeMillis,
@AppOpsManager.HistoricalOpsRequestFilter int filter,
@Nullable String[] opNamesFilter, @Nullable String attributionTagFilter,
@@ -535,6 +596,24 @@ final class DiscreteRegistry {
}
}
+ private void dump(@NonNull PrintWriter pw, @NonNull SimpleDateFormat sdf,
+ @NonNull Date date, @NonNull String prefix, int nDiscreteOps) {
+ int nAttributions = mAttributedOps.size();
+ for (int i = 0; i < nAttributions; i++) {
+ pw.print(prefix);
+ pw.print("Attribution: ");
+ pw.print(mAttributedOps.keyAt(i));
+ pw.println();
+ List<DiscreteOpEvent> ops = mAttributedOps.valueAt(i);
+ int nOps = ops.size();
+ int first = nDiscreteOps < 1 ? 0 : max(0, nOps - nDiscreteOps);
+ for (int j = first; j < nOps; j++) {
+ ops.get(j).dump(pw, sdf, date, prefix + " ");
+
+ }
+ }
+ }
+
void serialize(TypedXmlSerializer out) throws Exception {
int nAttributions = mAttributedOps.size();
for (int i = 0; i < nAttributions; i++) {
@@ -609,6 +688,24 @@ final class DiscreteRegistry {
mOpFlag = opFlag;
}
+ private void dump(@NonNull PrintWriter pw, @NonNull SimpleDateFormat sdf,
+ @NonNull Date date, @NonNull String prefix) {
+ pw.print(prefix);
+ pw.print("Access [");
+ pw.print(getUidStateName(mUidState));
+ pw.print("-");
+ pw.print(flagsToString(mOpFlag));
+ pw.print("] at ");
+ date.setTime(mNoteTime);
+ pw.print(sdf.format(date));
+ if (mNoteDuration != -1) {
+ pw.print(" for ");
+ pw.print(mNoteDuration);
+ pw.print(" milliseconds ");
+ }
+ pw.println();
+ }
+
private void serialize(TypedXmlSerializer out) throws Exception {
out.attributeLong(null, ATTR_NOTE_TIME, mNoteTime);
if (mNoteDuration != -1) {
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index 0fcf5ee35e11..22d628b8e789 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -349,6 +349,15 @@ final class HistoricalRegistry {
}
}
+ void dumpDiscreteData(@NonNull PrintWriter pw, int uidFilter,
+ @Nullable String packageNameFilter, @Nullable String attributionTagFilter,
+ @HistoricalOpsRequestFilter int filter, int dumpOp,
+ @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix,
+ int nDiscreteOps) {
+ mDiscreteRegistry.dump(pw, uidFilter, packageNameFilter, attributionTagFilter, filter,
+ dumpOp, sdf, date, prefix, nDiscreteOps);
+ }
+
@HistoricalMode int getMode() {
synchronized (mInMemoryLock) {
return mMode;
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 7dfecd56eaf5..5020917f8eb1 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -60,6 +60,7 @@ import android.util.SparseArray;
import android.view.autofill.AutofillManagerInternal;
import android.widget.Toast;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -955,9 +956,10 @@ public class ClipboardService extends SystemService {
mPm.getApplicationInfoAsUser(callingPackage, 0, userId));
String message;
if (sourceAppLabel != null) {
- message = callingAppLabel + " pasted from " + sourceAppLabel;
+ message = getContext().getString(
+ R.string.pasted_from_app, callingAppLabel, sourceAppLabel);
} else {
- message = callingAppLabel + " pasted from clipboard";
+ message = getContext().getString(R.string.pasted_from_clipboard, callingAppLabel);
}
Slog.i(TAG, message);
Binder.withCleanCallingIdentity(() ->
diff --git a/services/core/java/com/android/server/display/WifiDisplayAdapter.java b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
index d2baaf2228a1..0ba191c0762f 100644
--- a/services/core/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
@@ -91,6 +91,10 @@ final class WifiDisplayAdapter extends DisplayAdapter {
private boolean mPendingStatusChangeBroadcast;
+ private static final String[] RECEIVER_PERMISSIONS_FOR_BROADCAST = {
+ android.Manifest.permission.ACCESS_FINE_LOCATION,
+ };
+
// Called with SyncRoot lock held.
public WifiDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
Context context, Handler handler, Listener listener,
@@ -432,7 +436,8 @@ final class WifiDisplayAdapter extends DisplayAdapter {
}
// Send protected broadcast about wifi display status to registered receivers.
- getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
+ getContext().createContextAsUser(UserHandle.ALL, 0)
+ .sendBroadcastWithMultiplePermissions(intent, RECEIVER_PERMISSIONS_FOR_BROADCAST);
}
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
diff --git a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
index 02b09b15b464..73ecbe0432b7 100644
--- a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
+++ b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
@@ -20,6 +20,7 @@ import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
import android.hardware.hdmi.IHdmiControlCallback;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
/**
* Feature action that performs one touch play against TV/Display device. This action is initiated
@@ -40,13 +41,15 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction {
// standby mode, and do not accept the command until their power status becomes 'ON'.
// For a workaround, we send <Give Device Power Status> commands periodically to make sure
// the device switches its status to 'ON'. Then we send additional <Active Source>.
- private static final int STATE_WAITING_FOR_REPORT_POWER_STATUS = 1;
+ @VisibleForTesting
+ static final int STATE_WAITING_FOR_REPORT_POWER_STATUS = 1;
// The maximum number of times we send <Give Device Power Status> before we give up.
// We wait up to RESPONSE_TIMEOUT_MS * LOOP_COUNTER_MAX = 20 seconds.
private static final int LOOP_COUNTER_MAX = 10;
private final int mTargetAddress;
+ private final boolean mIsCec20;
private int mPowerStatusCounter = 0;
@@ -65,8 +68,19 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction {
private OneTouchPlayAction(HdmiCecLocalDevice localDevice, int targetAddress,
IHdmiControlCallback callback) {
+ this(localDevice, targetAddress, callback,
+ localDevice.getDeviceInfo().getCecVersion()
+ >= HdmiControlManager.HDMI_CEC_VERSION_2_0
+ && localDevice.mService.getHdmiCecNetwork().getCecDeviceInfo(
+ targetAddress).getCecVersion() >= HdmiControlManager.HDMI_CEC_VERSION_2_0);
+ }
+
+ @VisibleForTesting
+ OneTouchPlayAction(HdmiCecLocalDevice localDevice, int targetAddress,
+ IHdmiControlCallback callback, boolean isCec20) {
super(localDevice, callback);
mTargetAddress = targetAddress;
+ mIsCec20 = isCec20;
}
@Override
@@ -74,6 +88,9 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction {
// Because only source device can create this action, it's safe to cast.
mSource = source();
sendCommand(HdmiCecMessageBuilder.buildTextViewOn(getSourceAddress(), mTargetAddress));
+ boolean targetOnBefore = localDevice().mService.getHdmiCecNetwork()
+ .getCecDeviceInfo(mTargetAddress).getDevicePowerStatus()
+ == HdmiControlManager.POWER_STATUS_ON;
broadcastActiveSource();
// If the device is not an audio system itself, request the connected audio system to
// turn on.
@@ -81,7 +98,20 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction {
sendCommand(HdmiCecMessageBuilder.buildSystemAudioModeRequest(getSourceAddress(),
Constants.ADDR_AUDIO_SYSTEM, getSourcePath(), true));
}
- queryDevicePowerStatus();
+ int targetPowerStatus = localDevice().mService.getHdmiCecNetwork()
+ .getCecDeviceInfo(mTargetAddress).getDevicePowerStatus();
+ if (!mIsCec20 || targetPowerStatus == HdmiControlManager.POWER_STATUS_UNKNOWN) {
+ queryDevicePowerStatus();
+ } else if (targetPowerStatus == HdmiControlManager.POWER_STATUS_ON) {
+ if (!targetOnBefore) {
+ // Suppress 2nd <Active Source> message if the target device was already on when
+ // the 1st one was sent.
+ broadcastActiveSource();
+ }
+ finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
+ return true;
+ }
+ mState = STATE_WAITING_FOR_REPORT_POWER_STATUS;
addTimer(mState, HdmiConfig.TIMEOUT_MS);
return true;
}
@@ -101,7 +131,6 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction {
}
private void queryDevicePowerStatus() {
- mState = STATE_WAITING_FOR_REPORT_POWER_STATUS;
sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(),
mTargetAddress));
}
diff --git a/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java b/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
index dbd8dd9eff3b..aa3e5795b684 100644
--- a/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
+++ b/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
@@ -82,7 +82,7 @@ public class SystemEmergencyHelper extends EmergencyHelper {
TelephonyCallback.CallStateListener{
@Override
- public void onCallStateChanged(int state, String incomingNumber) {
+ public void onCallStateChanged(int state) {
if (state == TelephonyManager.CALL_STATE_IDLE) {
if (mIsInEmergencyCall) {
mEmergencyCallEndRealtimeMs = SystemClock.elapsedRealtime();
diff --git a/services/core/java/com/android/server/locksettings/TEST_MAPPING b/services/core/java/com/android/server/locksettings/TEST_MAPPING
index c1cba5f7f22d..4a216ca90f9b 100644
--- a/services/core/java/com/android/server/locksettings/TEST_MAPPING
+++ b/services/core/java/com/android/server/locksettings/TEST_MAPPING
@@ -10,6 +10,17 @@
"exclude-annotation": "android.platform.test.annotations.FlakyTest"
}
]
+ },
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.locksettings."
+ },
+ {
+ "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ }
+ ]
}
]
}
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index d3a56c6f67c0..044e186140a4 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -70,6 +70,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.IInterface;
import android.os.ParcelFileDescriptor;
+import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -114,6 +115,7 @@ public class LauncherAppsService extends SystemService {
@Override
public void onStart() {
publishBinderService(Context.LAUNCHER_APPS_SERVICE, mLauncherAppsImpl);
+ mLauncherAppsImpl.registerLoadingProgressForIncrementalApps();
}
static class BroadcastCookie {
@@ -1184,6 +1186,30 @@ public class LauncherAppsService extends SystemService {
mCallbackHandler.post(r);
}
+ /**
+ * Check all installed apps and if a package is installed via Incremental and not fully
+ * loaded, register loading progress listener.
+ */
+ void registerLoadingProgressForIncrementalApps() {
+ final PackageManagerInternal pmInt =
+ LocalServices.getService(PackageManagerInternal.class);
+ final List<UserHandle> users = mUm.getUserProfiles();
+ if (users == null) {
+ return;
+ }
+ for (UserHandle user : users) {
+ pmInt.forEachInstalledPackage(pkg -> {
+ final String packageName = pkg.getPackageName();
+ if (pmInt.getIncrementalStatesInfo(packageName, Process.myUid(),
+ user.getIdentifier()).isLoading()) {
+ pmInt.registerInstalledLoadingProgressCallback(packageName,
+ new PackageLoadingProgressCallback(packageName, user),
+ user.getIdentifier());
+ }
+ }, user.getIdentifier());
+ }
+ }
+
public static class ShortcutChangeHandler implements LauncherApps.ShortcutChangeCallback {
private final UserManagerInternal mUserManagerInternal;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index bc991634fb07..a1da241c6642 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4482,6 +4482,10 @@ public class PackageManagerService extends IPackageManager.Stub
break;
}
+ case DumpState.DUMP_PREFERRED:
+ mSettings.dumpPreferred(pw, dumpState, packageName);
+ break;
+
case DumpState.DUMP_PREFERRED_XML:
{
pw.flush();
@@ -24141,11 +24145,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED)) {
- // TODO: This cannot be moved to ComputerEngine since some variables with collections
- // types in IntentResolver such as mTypeToFilter do not have a copy of `F[]`.
- synchronized (mLock) {
- mSettings.dumpPreferred(pw, dumpState, packageName);
- }
+ dump(DumpState.DUMP_PREFERRED, fd, pw, dumpState);
}
if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED_XML)) {
diff --git a/services/core/java/com/android/server/power/FaceDownDetector.java b/services/core/java/com/android/server/power/FaceDownDetector.java
index 2442079bec8b..fe9663aaabe5 100644
--- a/services/core/java/com/android/server/power/FaceDownDetector.java
+++ b/services/core/java/com/android/server/power/FaceDownDetector.java
@@ -30,6 +30,7 @@ import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Handler;
import android.os.Looper;
+import android.os.PowerManager;
import android.os.SystemClock;
import android.provider.DeviceConfig;
import android.util.Slog;
@@ -66,7 +67,7 @@ public class FaceDownDetector implements SensorEventListener {
private static final float MOVING_AVERAGE_WEIGHT = 0.5f;
/** DeviceConfig flag name, if {@code true}, enables Face Down features. */
- private static final String KEY_FEATURE_ENABLED = "enable_flip_to_screen_off";
+ static final String KEY_FEATURE_ENABLED = "enable_flip_to_screen_off";
/** Default value in absence of {@link DeviceConfig} override. */
private static final boolean DEFAULT_FEATURE_ENABLED = true;
@@ -139,6 +140,7 @@ public class FaceDownDetector implements SensorEventListener {
new ExponentialMovingAverage(MOVING_AVERAGE_WEIGHT);
private boolean mFaceDown = false;
+ private boolean mInteractive = false;
private boolean mActive = false;
private float mPrevAcceleration = 0;
@@ -149,62 +151,64 @@ public class FaceDownDetector implements SensorEventListener {
private final Handler mHandler;
private final Runnable mUserActivityRunnable;
+ private final BroadcastReceiver mScreenReceiver;
+
+ private Context mContext;
public FaceDownDetector(@NonNull Consumer<Boolean> onFlip) {
mOnFlip = Objects.requireNonNull(onFlip);
mHandler = new Handler(Looper.getMainLooper());
+ mScreenReceiver = new ScreenStateReceiver();
mUserActivityRunnable = () -> {
if (mFaceDown) {
exitFaceDown(USER_INTERACTION, SystemClock.uptimeMillis() - mLastFlipTime);
- checkAndUpdateActiveState(false);
+ updateActiveState();
}
};
}
/** Initializes the FaceDownDetector and all necessary listeners. */
public void systemReady(Context context) {
+ mContext = context;
mSensorManager = context.getSystemService(SensorManager.class);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
readValuesFromDeviceConfig();
- checkAndUpdateActiveState(true);
DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_ATTENTION_MANAGER_SERVICE,
ActivityThread.currentApplication().getMainExecutor(),
(properties) -> onDeviceConfigChange(properties.getKeyset()));
+ updateActiveState();
+ }
+
+ private void registerScreenReceiver(Context context) {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
- context.registerReceiver(new ScreenStateReceiver(), intentFilter);
+ context.registerReceiver(mScreenReceiver, intentFilter);
}
/**
* Sets the active state of the detector. If false, we will not process accelerometer changes.
*/
- private void checkAndUpdateActiveState(boolean active) {
- if (mIsEnabled && mActive != active) {
- final long currentTime = SystemClock.uptimeMillis();
- // Don't make active if there was recently a user interaction while face down.
- if (active && mPreviousResultType == USER_INTERACTION
- && currentTime - mPreviousResultTime < mUserInteractionBackoffMillis) {
- return;
- }
- if (DEBUG) Slog.d(TAG, "Update active - " + active);
- mActive = active;
- if (!active) {
- if (mFaceDown && mPreviousResultTime != USER_INTERACTION) {
- mPreviousResultType = SCREEN_OFF_RESULT;
- mPreviousResultTime = currentTime;
+ private void updateActiveState() {
+ final long currentTime = SystemClock.uptimeMillis();
+ final boolean sawRecentInteraction = mPreviousResultType == USER_INTERACTION
+ && currentTime - mPreviousResultTime < mUserInteractionBackoffMillis;
+ final boolean shouldBeActive = mInteractive && mIsEnabled && !sawRecentInteraction;
+ if (mActive != shouldBeActive) {
+ if (shouldBeActive) {
+ mSensorManager.registerListener(
+ this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
+ if (mPreviousResultType == SCREEN_OFF_RESULT) {
+ logScreenOff();
}
+ } else {
mSensorManager.unregisterListener(this);
mFaceDown = false;
mOnFlip.accept(false);
- } else {
- if (mPreviousResultType == SCREEN_OFF_RESULT) {
- logScreenOff();
- }
- mSensorManager.registerListener(
- this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
}
+ mActive = shouldBeActive;
+ if (DEBUG) Slog.d(TAG, "Update active - " + shouldBeActive);
}
}
@@ -389,6 +393,7 @@ public class FaceDownDetector implements SensorEventListener {
case KEY_TIME_THRESHOLD_MILLIS:
case KEY_FEATURE_ENABLED:
readValuesFromDeviceConfig();
+ updateActiveState();
return;
default:
Slog.i(TAG, "Ignoring change on " + key);
@@ -401,8 +406,18 @@ public class FaceDownDetector implements SensorEventListener {
mZAccelerationThreshold = getZAccelerationThreshold();
mZAccelerationThresholdLenient = mZAccelerationThreshold + 1.0f;
mTimeThreshold = getTimeThreshold();
- mIsEnabled = isEnabled();
mUserInteractionBackoffMillis = getUserInteractionBackoffMillis();
+ final boolean oldEnabled = mIsEnabled;
+ mIsEnabled = isEnabled();
+ if (oldEnabled != mIsEnabled) {
+ if (!mIsEnabled) {
+ mContext.unregisterReceiver(mScreenReceiver);
+ mInteractive = false;
+ } else {
+ registerScreenReceiver(mContext);
+ mInteractive = mContext.getSystemService(PowerManager.class).isInteractive();
+ }
+ }
Slog.i(TAG, "readValuesFromDeviceConfig():"
+ "\nmAccelerationThreshold=" + mAccelerationThreshold
@@ -423,9 +438,11 @@ public class FaceDownDetector implements SensorEventListener {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
- checkAndUpdateActiveState(false);
+ mInteractive = false;
+ updateActiveState();
} else if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) {
- checkAndUpdateActiveState(true);
+ mInteractive = true;
+ updateActiveState();
}
}
}
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 6bc9978a0731..15429f455d6e 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -979,7 +979,7 @@ public class VcnGatewayConnection extends StateMachine {
// IkeSessionCallback.onClosedExceptionally(), which calls sessionClosed()
if (exception != null) {
mGatewayStatusCallback.onGatewayConnectionError(
- mConnectionConfig.getRequiredUnderlyingCapabilities(),
+ mConnectionConfig.getExposedCapabilities(),
VCN_ERROR_CODE_INTERNAL_ERROR,
RuntimeException.class.getName(),
"Received "
@@ -1016,7 +1016,7 @@ public class VcnGatewayConnection extends StateMachine {
}
mGatewayStatusCallback.onGatewayConnectionError(
- mConnectionConfig.getRequiredUnderlyingCapabilities(),
+ mConnectionConfig.getExposedCapabilities(),
errorCode,
exceptionClass,
exceptionMessage);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 5446a39fad86..2e94ca15c9d8 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
@@ -86,6 +87,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
import static android.content.pm.ActivityInfo.isFixedOrientationPortrait;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.res.Configuration.EMPTY;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -205,6 +207,7 @@ import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.ACTIVITY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -278,6 +281,7 @@ import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
import android.os.storage.StorageManager;
+import android.permission.PermissionManager;
import android.service.dreams.DreamActivity;
import android.service.dreams.DreamManagerInternal;
import android.service.voice.IVoiceInteractionSession;
@@ -6955,6 +6959,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// layout traversals.
mConfigurationSeq = Math.max(++mConfigurationSeq, 1);
getResolvedOverrideConfiguration().seq = mConfigurationSeq;
+
+ // Sandbox max bounds by setting it to the app bounds, if activity is letterboxed or in
+ // size compat mode.
+ if (providesMaxBounds()) {
+ if (DEBUG_CONFIGURATION) {
+ ProtoLog.d(WM_DEBUG_CONFIGURATION, "Sandbox max bounds for uid %s to bounds %s "
+ + "due to letterboxing from mismatch with parent bounds? %s size compat "
+ + "mode %s", getUid(),
+ resolvedConfig.windowConfiguration.getBounds(), !matchParentBounds(),
+ inSizeCompatMode());
+ }
+ resolvedConfig.windowConfiguration
+ .setMaxBounds(resolvedConfig.windowConfiguration.getBounds());
+ }
}
/**
@@ -7296,6 +7314,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return super.getBounds();
}
+ @Override
+ public boolean providesMaxBounds() {
+ // System and SystemUI should always be able to access the physical display bounds,
+ // so do not provide it with the overridden maximum bounds.
+ // TODO(b/179179513) check WindowState#mOwnerCanAddInternalSystemWindow instead
+ if (getUid() == SYSTEM_UID || PermissionManager.checkPermission(INTERNAL_SYSTEM_WINDOW,
+ getPid(), info.applicationInfo.uid) == PERMISSION_GRANTED) {
+ return false;
+ }
+ // Max bounds should be sandboxed where an activity is letterboxed (activity bounds will be
+ // smaller than task bounds) or put in size compat mode.
+ return !matchParentBounds() || inSizeCompatMode();
+ }
+
@VisibleForTesting
@Override
Rect getAnimationBounds(int appRootTaskClipMode) {
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 62a00802896f..8fbe1775fd19 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -367,8 +367,7 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
* Returns {@code true} if this {@link ConfigurationContainer} provides the maximum bounds to
* its child {@link ConfigurationContainer}s. Returns {@code false}, otherwise.
* <p>
- * The maximum bounds is how large a window can be expanded. Currently only
- * {@link DisplayContent} and {@link DisplayArea} effect this property.
+ * The maximum bounds is how large a window can be expanded.
* </p>
*/
protected boolean providesMaxBounds() {
diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp
index 42360d82afe9..8f12b2e5c132 100644
--- a/services/incremental/BinderIncrementalService.cpp
+++ b/services/incremental/BinderIncrementalService.cpp
@@ -348,6 +348,12 @@ binder::Status BinderIncrementalService::unregisterStorageHealthListener(int32_t
return ok();
}
+binder::Status BinderIncrementalService::getMetrics(int32_t storageId,
+ android::os::PersistableBundle* _aidl_return) {
+ mImpl.getMetrics(storageId, _aidl_return);
+ return ok();
+}
+
} // namespace android::os::incremental
jlong Incremental_IncrementalService_Start(JNIEnv* env) {
diff --git a/services/incremental/BinderIncrementalService.h b/services/incremental/BinderIncrementalService.h
index 740c542f9759..ebb23dc25bac 100644
--- a/services/incremental/BinderIncrementalService.h
+++ b/services/incremental/BinderIncrementalService.h
@@ -18,6 +18,7 @@
#include <binder/BinderService.h>
#include <binder/IServiceManager.h>
+#include <binder/PersistableBundle.h>
#include <jni.h>
#include "IncrementalService.h"
@@ -97,6 +98,8 @@ public:
const ::android::os::incremental::StorageHealthCheckParams& healthCheckParams,
const ::android::sp<IStorageHealthListener>& healthListener, bool* _aidl_return) final;
binder::Status unregisterStorageHealthListener(int32_t storageId) final;
+ binder::Status getMetrics(int32_t storageId,
+ android::os::PersistableBundle* _aidl_return) final;
private:
android::incremental::IncrementalService mImpl;
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index ce6e6ab1e29c..1fcc2843bd43 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -2118,6 +2118,29 @@ bool IncrementalService::removeTimedJobs(TimedQueueWrapper& timedQueue, MountId
return true;
}
+void IncrementalService::getMetrics(StorageId storageId, android::os::PersistableBundle* result) {
+ const auto duration = getMillsSinceOldestPendingRead(storageId);
+ if (duration >= 0) {
+ const auto kMetricsMillisSinceOldestPendingRead =
+ os::incremental::BnIncrementalService::METRICS_MILLIS_SINCE_OLDEST_PENDING_READ();
+ result->putLong(String16(kMetricsMillisSinceOldestPendingRead.data()), duration);
+ }
+}
+
+long IncrementalService::getMillsSinceOldestPendingRead(StorageId storageId) {
+ std::unique_lock l(mLock);
+ const auto ifs = getIfsLocked(storageId);
+ if (!ifs) {
+ LOG(ERROR) << "getMillsSinceOldestPendingRead failed, invalid storageId: " << storageId;
+ return -EINVAL;
+ }
+ if (!ifs->dataLoaderStub) {
+ LOG(ERROR) << "getMillsSinceOldestPendingRead failed, no data loader: " << storageId;
+ return -EINVAL;
+ }
+ return ifs->dataLoaderStub->elapsedMsSinceOldestPendingRead();
+}
+
IncrementalService::DataLoaderStub::DataLoaderStub(IncrementalService& service, MountId id,
DataLoaderParamsParcel&& params,
FileSystemControlParcel&& control,
@@ -2516,9 +2539,7 @@ void IncrementalService::DataLoaderStub::updateHealthStatus(bool baseline) {
std::max(1000ms,
std::chrono::milliseconds(mHealthCheckParams.unhealthyMonitoringMs));
- const auto kernelDeltaUs = kernelTsUs - mHealthBase.kernelTsUs;
- const auto userTs = mHealthBase.userTs + std::chrono::microseconds(kernelDeltaUs);
- const auto delta = std::chrono::duration_cast<std::chrono::milliseconds>(now - userTs);
+ const auto delta = elapsedMsSinceKernelTs(now, kernelTsUs);
Milliseconds checkBackAfter;
if (delta + kTolerance < blockedTimeout) {
@@ -2550,6 +2571,13 @@ void IncrementalService::DataLoaderStub::updateHealthStatus(bool baseline) {
fsmStep();
}
+Milliseconds IncrementalService::DataLoaderStub::elapsedMsSinceKernelTs(TimePoint now,
+ BootClockTsUs kernelTsUs) {
+ const auto kernelDeltaUs = kernelTsUs - mHealthBase.kernelTsUs;
+ const auto userTs = mHealthBase.userTs + std::chrono::microseconds(kernelDeltaUs);
+ return std::chrono::duration_cast<Milliseconds>(now - userTs);
+}
+
const incfs::UniqueControl& IncrementalService::DataLoaderStub::initializeHealthControl() {
if (mHealthPath.empty()) {
resetHealthControl();
@@ -2581,16 +2609,15 @@ BootClockTsUs IncrementalService::DataLoaderStub::getOldestPendingReadTs() {
if (mService.mIncFs->waitForPendingReads(control, 0ms, &mLastPendingReads) !=
android::incfs::WaitResult::HaveData ||
mLastPendingReads.empty()) {
+ // Clear previous pending reads
+ mLastPendingReads.clear();
return result;
}
LOG(DEBUG) << id() << ": pendingReads: " << control.pendingReads() << ", "
<< mLastPendingReads.size() << ": " << mLastPendingReads.front().bootClockTsUs;
- for (auto&& pendingRead : mLastPendingReads) {
- result = std::min(result, pendingRead.bootClockTsUs);
- }
- return result;
+ return getOldestTsFromLastPendingReads();
}
void IncrementalService::DataLoaderStub::registerForPendingReads() {
@@ -2612,6 +2639,22 @@ void IncrementalService::DataLoaderStub::registerForPendingReads() {
mService.mLooper->wake();
}
+BootClockTsUs IncrementalService::DataLoaderStub::getOldestTsFromLastPendingReads() {
+ auto result = kMaxBootClockTsUs;
+ for (auto&& pendingRead : mLastPendingReads) {
+ result = std::min(result, pendingRead.bootClockTsUs);
+ }
+ return result;
+}
+
+long IncrementalService::DataLoaderStub::elapsedMsSinceOldestPendingRead() {
+ const auto oldestPendingReadKernelTs = getOldestTsFromLastPendingReads();
+ if (oldestPendingReadKernelTs == kMaxBootClockTsUs) {
+ return 0;
+ }
+ return elapsedMsSinceKernelTs(Clock::now(), oldestPendingReadKernelTs).count();
+}
+
void IncrementalService::DataLoaderStub::unregisterFromPendingReads() {
const auto pendingReadsFd = mHealthControl.pendingReads();
if (pendingReadsFd < 0) {
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index d8f2c91a971c..14e5a7734172 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -20,12 +20,14 @@
#include <android/content/pm/DataLoaderParamsParcel.h>
#include <android/content/pm/FileSystemControlParcel.h>
#include <android/content/pm/IDataLoaderStatusListener.h>
+#include <android/os/incremental/BnIncrementalService.h>
#include <android/os/incremental/BnIncrementalServiceConnector.h>
#include <android/os/incremental/BnStorageHealthListener.h>
#include <android/os/incremental/BnStorageLoadingProgressListener.h>
#include <android/os/incremental/PerUidReadTimeouts.h>
#include <android/os/incremental/StorageHealthCheckParams.h>
#include <binder/IAppOpsCallback.h>
+#include <binder/PersistableBundle.h>
#include <utils/String16.h>
#include <utils/StrongPointer.h>
#include <ziparchive/zip_archive.h>
@@ -181,6 +183,8 @@ public:
bool extractNativeLibs);
bool waitForNativeBinariesExtraction(StorageId storage);
+ void getMetrics(int32_t storageId, android::os::PersistableBundle* _aidl_return);
+
class AppOpsListener : public android::BnAppOpsCallback {
public:
AppOpsListener(IncrementalService& incrementalService, std::string packageName)
@@ -229,6 +233,7 @@ private:
const content::pm::DataLoaderParamsParcel& params() const { return mParams; }
void setHealthListener(StorageHealthCheckParams&& healthCheckParams,
const StorageHealthListener* healthListener);
+ long elapsedMsSinceOldestPendingRead();
private:
binder::Status onStatusChanged(MountId mount, int newStatus) final;
@@ -259,6 +264,8 @@ private:
void resetHealthControl();
BootClockTsUs getOldestPendingReadTs();
+ BootClockTsUs getOldestTsFromLastPendingReads();
+ Milliseconds elapsedMsSinceKernelTs(TimePoint now, BootClockTsUs kernelTsUs);
Milliseconds updateBindDelay();
@@ -424,6 +431,7 @@ private:
bool removeTimedJobs(TimedQueueWrapper& timedQueue, MountId id);
bool updateLoadingProgress(int32_t storageId,
const StorageLoadingProgressListener& progressListener);
+ long getMillsSinceOldestPendingRead(StorageId storage);
private:
const std::unique_ptr<VoldServiceWrapper> mVold;
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index b00a84fcd003..5236983c83ff 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -21,6 +21,7 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <utils/Log.h>
+#include <utils/String16.h>
#include <chrono>
#include <future>
@@ -701,6 +702,18 @@ public:
mDataLoaderManager->getDataLoaderSuccess();
}
+ void checkMillisSinceOldestPendingRead(int storageId, long expected) {
+ android::os::PersistableBundle result{};
+ mIncrementalService->getMetrics(storageId, &result);
+ int64_t value = -1;
+ ASSERT_TRUE(result.getLong(String16(BnIncrementalService::
+ METRICS_MILLIS_SINCE_OLDEST_PENDING_READ()
+ .c_str()),
+ &value));
+ ASSERT_EQ(expected, value);
+ ASSERT_EQ(1, (int)result.size());
+ }
+
protected:
NiceMock<MockVoldService>* mVold = nullptr;
NiceMock<MockIncFs>* mIncFs = nullptr;
@@ -995,6 +1008,7 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderUnhealthyStorage) {
ASSERT_NE(nullptr, mLooper->mCallbackData);
ASSERT_EQ(storageId, listener->mStorageId);
ASSERT_EQ(IStorageHealthListener::HEALTH_STATUS_OK, listener->mStatus);
+ checkMillisSinceOldestPendingRead(storageId, 0);
// Looper/epoll callback.
mIncFs->waitForPendingReadsSuccess(kFirstTimestampUs);
@@ -1020,6 +1034,8 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderUnhealthyStorage) {
ASSERT_EQ(nullptr, mLooper->mCallbackData);
ASSERT_EQ(storageId, listener->mStorageId);
ASSERT_EQ(IStorageHealthListener::HEALTH_STATUS_BLOCKED, listener->mStatus);
+ checkMillisSinceOldestPendingRead(storageId, params.blockedTimeoutMs);
+
// Timed callback present.
ASSERT_EQ(storageId, mTimedQueue->mId);
ASSERT_GE(mTimedQueue->mAfter, 1000ms);
@@ -1035,6 +1051,8 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderUnhealthyStorage) {
ASSERT_EQ(nullptr, mLooper->mCallbackData);
ASSERT_EQ(storageId, listener->mStorageId);
ASSERT_EQ(IStorageHealthListener::HEALTH_STATUS_UNHEALTHY, listener->mStatus);
+ checkMillisSinceOldestPendingRead(storageId, params.unhealthyTimeoutMs);
+
// Timed callback present.
ASSERT_EQ(storageId, mTimedQueue->mId);
ASSERT_GE(mTimedQueue->mAfter, unhealthyMonitoring);
@@ -1050,6 +1068,8 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderUnhealthyStorage) {
ASSERT_EQ(nullptr, mLooper->mCallbackData);
ASSERT_EQ(storageId, listener->mStorageId);
ASSERT_EQ(IStorageHealthListener::HEALTH_STATUS_UNHEALTHY, listener->mStatus);
+ checkMillisSinceOldestPendingRead(storageId, params.unhealthyTimeoutMs);
+
// Timed callback present.
ASSERT_EQ(storageId, mTimedQueue->mId);
ASSERT_GE(mTimedQueue->mAfter, unhealthyMonitoring);
@@ -1065,6 +1085,7 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderUnhealthyStorage) {
ASSERT_NE(nullptr, mLooper->mCallbackData);
ASSERT_EQ(storageId, listener->mStorageId);
ASSERT_EQ(IStorageHealthListener::HEALTH_STATUS_OK, listener->mStatus);
+ checkMillisSinceOldestPendingRead(storageId, 0);
}
TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccess) {
@@ -1581,4 +1602,52 @@ TEST_F(IncrementalServiceTest, testPerUidTimeoutsSuccess) {
ASSERT_EQ(mTimedQueue->mAfter, Milliseconds());
}
+TEST_F(IncrementalServiceTest, testInvalidMetricsQuery) {
+ const auto invalidStorageId = 100;
+ android::os::PersistableBundle result{};
+ mIncrementalService->getMetrics(invalidStorageId, &result);
+ int64_t expected = -1, value = -1;
+ ASSERT_FALSE(
+ result.getLong(String16(BnIncrementalService::METRICS_MILLIS_SINCE_OLDEST_PENDING_READ()
+ .c_str()),
+ &value));
+ ASSERT_EQ(expected, value);
+ ASSERT_TRUE(result.empty());
+}
+
+TEST_F(IncrementalServiceTest, testNoMetrics) {
+ mVold->setIncFsMountOptionsSuccess();
+ TemporaryDir tempDir;
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
+ ASSERT_GE(storageId, 0);
+ android::os::PersistableBundle result{};
+ mIncrementalService->getMetrics(storageId, &result);
+ int64_t expected = -1, value = -1;
+ ASSERT_FALSE(
+ result.getLong(String16(BnIncrementalService::METRICS_MILLIS_SINCE_OLDEST_PENDING_READ()
+ .c_str()),
+ &value));
+ ASSERT_EQ(expected, value);
+ ASSERT_EQ(0, (int)result.size());
+}
+
+TEST_F(IncrementalServiceTest, testInvalidMetricsKeys) {
+ mVold->setIncFsMountOptionsSuccess();
+ TemporaryDir tempDir;
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
+ ASSERT_GE(storageId, 0);
+ ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
+ {}, {}));
+ android::os::PersistableBundle result{};
+ mIncrementalService->getMetrics(storageId, &result);
+ int64_t expected = -1, value = -1;
+ ASSERT_FALSE(result.getLong(String16("invalid"), &value));
+ ASSERT_EQ(expected, value);
+ ASSERT_EQ(1, (int)result.size());
+}
+
} // namespace android::os::incremental
diff --git a/services/tests/servicestests/src/com/android/server/power/FaceDownDetectorTest.java b/services/tests/mockingservicestests/src/com/android/server/power/FaceDownDetectorTest.java
index ef20ee7e6ecd..8ecb07158564 100644
--- a/services/tests/servicestests/src/com/android/server/power/FaceDownDetectorTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/power/FaceDownDetectorTest.java
@@ -16,59 +16,74 @@
package com.android.server.power;
+import static android.provider.DeviceConfig.NAMESPACE_ATTENTION_MANAGER_SERVICE;
+
+import static com.android.server.power.FaceDownDetector.KEY_FEATURE_ENABLED;
+
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.doReturn;
+
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorManager;
+import android.os.PowerManager;
+import android.provider.DeviceConfig;
import android.testing.TestableContext;
import androidx.test.platform.app.InstrumentationRegistry;
-import com.android.server.display.TestUtils;
+import com.android.server.testables.TestableDeviceConfig;
import org.junit.Before;
import org.junit.ClassRule;
+import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
import java.time.Duration;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
public class FaceDownDetectorTest {
@ClassRule
public static final TestableContext sContext = new TestableContext(
InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
+ @Rule
+ public TestableDeviceConfig.TestableDeviceConfigRule
+ mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule();
- private final FaceDownDetector mFaceDownDetector =
- new FaceDownDetector(this::onFlip);
+ private final FaceDownDetector mFaceDownDetector = new FaceDownDetector(this::onFlip);
@Mock private SensorManager mSensorManager;
+ @Mock private PowerManager mPowerManager;
- private long mCurrentTime;
- private int mOnFaceDownCalls = 0;
- private int mOnFaceDownExitCalls = 0;
+ private Duration mCurrentTime;
+ private int mOnFaceDownCalls;
+ private int mOnFaceDownExitCalls;
@Before
- public void setup() {
+ public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
sContext.addMockSystemService(SensorManager.class, mSensorManager);
- mCurrentTime = 0;
+ sContext.addMockSystemService(PowerManager.class, mPowerManager);
+ doReturn(true).when(mPowerManager).isInteractive();
+ DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+ KEY_FEATURE_ENABLED, "true", false);
+ mCurrentTime = Duration.ZERO;
+ mOnFaceDownCalls = 0;
+ mOnFaceDownExitCalls = 0;
}
@Test
public void faceDownFor2Seconds_triggersFaceDown() throws Exception {
mFaceDownDetector.systemReady(sContext);
- // Face up
- // Using 0.5 on x to simulate constant acceleration, such as a sloped surface.
- mFaceDownDetector.onSensorChanged(createTestEvent(0.5f, 0.0f, 10.0f));
-
- for (int i = 0; i < 200; i++) {
- advanceTime(Duration.ofMillis(20));
- mFaceDownDetector.onSensorChanged(createTestEvent(0.5f, 0.0f, -10.0f));
- }
+ triggerFaceDown();
assertThat(mOnFaceDownCalls).isEqualTo(1);
assertThat(mOnFaceDownExitCalls).isEqualTo(0);
@@ -111,15 +126,7 @@ public class FaceDownDetectorTest {
public void faceDownFor2Seconds_followedByFaceUp_triggersFaceDownExit() throws Exception {
mFaceDownDetector.systemReady(sContext);
- // Face up
- // Using 0.5 on x to simulate constant acceleration, such as a sloped surface.
- mFaceDownDetector.onSensorChanged(createTestEvent(0.5f, 0.0f, 10.0f));
-
- // Trigger face down
- for (int i = 0; i < 100; i++) {
- advanceTime(Duration.ofMillis(20));
- mFaceDownDetector.onSensorChanged(createTestEvent(0.5f, 0.0f, -10.0f));
- }
+ triggerFaceDown();
// Phone flips
for (int i = 0; i < 10; i++) {
@@ -131,8 +138,71 @@ public class FaceDownDetectorTest {
assertThat(mOnFaceDownExitCalls).isEqualTo(1);
}
+ @Test
+ public void notInteractive_doesNotTriggerFaceDown() throws Exception {
+ doReturn(false).when(mPowerManager).isInteractive();
+ mFaceDownDetector.systemReady(sContext);
+
+ triggerFaceDown();
+
+ assertThat(mOnFaceDownCalls).isEqualTo(0);
+ assertThat(mOnFaceDownExitCalls).isEqualTo(0);
+ }
+
+ @Test
+ public void afterDisablingFeature_doesNotTriggerFaceDown() throws Exception {
+ mFaceDownDetector.systemReady(sContext);
+ setEnabled(false);
+
+ triggerFaceDown();
+
+ assertThat(mOnFaceDownCalls).isEqualTo(0);
+ }
+
+ @Test
+ public void afterReenablingWhileNonInteractive_doesNotTriggerFaceDown() throws Exception {
+ mFaceDownDetector.systemReady(sContext);
+ setEnabled(false);
+
+ doReturn(false).when(mPowerManager).isInteractive();
+ setEnabled(true);
+
+ triggerFaceDown();
+
+ assertThat(mOnFaceDownCalls).isEqualTo(0);
+ }
+
+ @Test
+ public void afterReenablingWhileInteractive_doesTriggerFaceDown() throws Exception {
+ mFaceDownDetector.systemReady(sContext);
+ setEnabled(false);
+
+ setEnabled(true);
+
+ triggerFaceDown();
+
+ assertThat(mOnFaceDownCalls).isEqualTo(1);
+ }
+
+ private void triggerFaceDown() throws Exception {
+ // Face up
+ // Using 0.5 on x to simulate constant acceleration, such as a sloped surface.
+ mFaceDownDetector.onSensorChanged(createTestEvent(0.5f, 0.0f, 10.0f));
+
+ for (int i = 0; i < 200; i++) {
+ advanceTime(Duration.ofMillis(20));
+ mFaceDownDetector.onSensorChanged(createTestEvent(0.5f, 0.0f, -10.0f));
+ }
+ }
+
+ private void setEnabled(Boolean enabled) throws Exception {
+ DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+ KEY_FEATURE_ENABLED, enabled.toString(), false);
+ waitForListenerToHandle();
+ }
+
private void advanceTime(Duration duration) {
- mCurrentTime += duration.toNanos();
+ mCurrentTime = mCurrentTime.plus(duration);
}
/**
@@ -146,12 +216,11 @@ public class FaceDownDetectorTest {
SensorEvent.class.getDeclaredConstructor(int.class);
constructor.setAccessible(true);
final SensorEvent event = constructor.newInstance(3);
- event.sensor =
- TestUtils.createSensor(Sensor.TYPE_ACCELEROMETER, Sensor.STRING_TYPE_ACCELEROMETER);
+ event.sensor = createSensor(Sensor.TYPE_ACCELEROMETER, Sensor.STRING_TYPE_ACCELEROMETER);
event.values[0] = x;
event.values[1] = y;
event.values[2] = gravity;
- event.timestamp = mCurrentTime;
+ event.timestamp = mCurrentTime.toNanos();
return event;
}
@@ -162,4 +231,25 @@ public class FaceDownDetectorTest {
mOnFaceDownExitCalls++;
}
}
+
+ private Sensor createSensor(int type, String strType) throws Exception {
+ Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor();
+ constr.setAccessible(true);
+ Sensor sensor = constr.newInstance();
+ Method setter = Sensor.class.getDeclaredMethod("setType", Integer.TYPE);
+ setter.setAccessible(true);
+ setter.invoke(sensor, type);
+ if (strType != null) {
+ Field f = sensor.getClass().getDeclaredField("mStringType");
+ f.setAccessible(true);
+ f.set(sensor, strType);
+ }
+ return sensor;
+ }
+
+ private void waitForListenerToHandle() throws Exception {
+ final CountDownLatch latch = new CountDownLatch(1);
+ sContext.getMainExecutor().execute(latch::countDown);
+ assertThat(latch.await(5, TimeUnit.SECONDS)).isTrue();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 915392e6eb80..1a6bad8b29cf 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -61,6 +61,12 @@ import java.util.concurrent.TimeUnit;
/** Tests for {@link HdmiCecLocalDevicePlayback} class. */
public class HdmiCecLocalDevicePlaybackTest {
+ private static final int PORT_1 = 1;
+ private static final HdmiDeviceInfo INFO_TV = new HdmiDeviceInfo(
+ ADDR_TV, 0x0000, PORT_1, HdmiDeviceInfo.DEVICE_TV,
+ 0x1234, "TV",
+ HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
+
private HdmiControlService mHdmiControlService;
private HdmiCecController mHdmiCecController;
private HdmiCecLocalDevicePlayback mHdmiCecLocalDevicePlayback;
@@ -159,6 +165,7 @@ public class HdmiCecLocalDevicePlaybackTest {
mNativeWrapper.setPhysicalAddress(mPlaybackPhysicalAddress);
mTestLooper.dispatchAll();
mPlaybackLogicalAddress = mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress();
+ mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV);
mNativeWrapper.clearResultMessages();
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
new file mode 100644
index 000000000000..907cf3eb1f70
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
@@ -0,0 +1,420 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import static com.android.server.hdmi.Constants.ADDR_TV;
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+import static com.android.server.hdmi.OneTouchPlayAction.STATE_WAITING_FOR_REPORT_POWER_STATUS;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.media.AudioManager;
+import android.os.Handler;
+import android.os.IPowerManager;
+import android.os.IThermalService;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.test.TestLooper;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.hdmi.HdmiCecFeatureAction.ActionTimer;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+
+/** Tests for {@link OneTouchPlayAction} */
+@SmallTest
+@RunWith(JUnit4.class)
+public class OneTouchPlayActionTest {
+ private static final byte[] POWER_ON = new byte[]{HdmiControlManager.POWER_STATUS_ON};
+ private static final byte[] POWER_TRANSIENT_TO_ON =
+ new byte[]{HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON};
+
+ private static final int PORT_1 = 1;
+ private static final HdmiDeviceInfo INFO_TV = new HdmiDeviceInfo(
+ ADDR_TV, 0x0000, PORT_1, HdmiDeviceInfo.DEVICE_TV,
+ 0x1234, "TV",
+ HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
+
+ private Context mContextSpy;
+ private HdmiControlService mHdmiControlService;
+ private FakeNativeWrapper mNativeWrapper;
+
+ private TestLooper mTestLooper = new TestLooper();
+ private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+ private int mPhysicalAddress;
+
+ @Mock
+ private IPowerManager mIPowerManagerMock;
+ @Mock
+ private IThermalService mIThermalServiceMock;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
+
+ PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
+ when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(mIPowerManagerMock.isInteractive()).thenReturn(true);
+
+ mHdmiControlService = new HdmiControlService(mContextSpy) {
+ @Override
+ AudioManager getAudioManager() {
+ return new AudioManager() {
+ @Override
+ public void setWiredDeviceConnectionState(
+ int type, int state, String address, String name) {
+ // Do nothing.
+ }
+ };
+ }
+
+ @Override
+ void wakeUp() {
+ }
+
+ @Override
+ boolean isPowerStandby() {
+ return false;
+ }
+
+ @Override
+ protected PowerManager getPowerManager() {
+ return powerManager;
+ }
+
+ @Override
+ protected void writeStringSystemProperty(String key, String value) {
+ // do nothing
+ }
+ };
+
+ Looper looper = mTestLooper.getLooper();
+ mHdmiControlService.setIoLooper(looper);
+ mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(mContextSpy));
+ mNativeWrapper = new FakeNativeWrapper();
+ HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper(
+ this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
+ mHdmiControlService.setCecController(hdmiCecController);
+ mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
+ mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
+ mHdmiControlService.initService();
+ mPhysicalAddress = 0x2000;
+ mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
+ mTestLooper.dispatchAll();
+ mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV);
+ }
+
+ private OneTouchPlayAction createOneTouchPlayAction(HdmiCecLocalDevicePlayback device,
+ TestActionTimer actionTimer, TestCallback callback, boolean isCec20) {
+ OneTouchPlayAction action = new OneTouchPlayAction(device, ADDR_TV, callback, isCec20);
+ action.setActionTimer(actionTimer);
+ return action;
+ }
+
+ @Test
+ public void succeedAfterGettingPowerStatusOn_Cec14b() {
+ HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback(
+ mHdmiControlService);
+ playbackDevice.init();
+ mLocalDevices.add(playbackDevice);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mTestLooper.dispatchAll();
+
+ TestActionTimer actionTimer = new TestActionTimer();
+ TestCallback callback = new TestCallback();
+ OneTouchPlayAction action = createOneTouchPlayAction(playbackDevice, actionTimer, callback,
+ false);
+ playbackDevice.addAndStartAction(action);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
+ playbackDevice.mAddress, mPhysicalAddress);
+ HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(playbackDevice.mAddress,
+ ADDR_TV);
+ HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder
+ .buildGiveDevicePowerStatus(playbackDevice.mAddress, ADDR_TV);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
+ assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
+ mNativeWrapper.clearResultMessages();
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAITING_FOR_REPORT_POWER_STATUS);
+ HdmiCecMessage reportPowerStatusOn = new HdmiCecMessage(
+ ADDR_TV, playbackDevice.mAddress, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_ON);
+ action.processCommand(reportPowerStatusOn);
+ mTestLooper.dispatchAll();
+
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(textViewOn);
+ assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus);
+ assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
+ }
+
+ @Test
+ public void succeedAfterGettingTransientPowerStatus_Cec14b() {
+ HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback(
+ mHdmiControlService);
+ playbackDevice.init();
+ mLocalDevices.add(playbackDevice);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mTestLooper.dispatchAll();
+
+ TestActionTimer actionTimer = new TestActionTimer();
+ TestCallback callback = new TestCallback();
+ OneTouchPlayAction action = createOneTouchPlayAction(playbackDevice, actionTimer, callback,
+ false);
+ playbackDevice.addAndStartAction(action);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
+ playbackDevice.mAddress, mPhysicalAddress);
+ HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(playbackDevice.mAddress,
+ ADDR_TV);
+ HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder
+ .buildGiveDevicePowerStatus(playbackDevice.mAddress, ADDR_TV);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
+ assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
+ mNativeWrapper.clearResultMessages();
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAITING_FOR_REPORT_POWER_STATUS);
+ HdmiCecMessage reportPowerStatusTransientToOn = new HdmiCecMessage(
+ ADDR_TV, playbackDevice.mAddress, Constants.MESSAGE_REPORT_POWER_STATUS,
+ POWER_TRANSIENT_TO_ON);
+ action.processCommand(reportPowerStatusTransientToOn);
+ action.handleTimerEvent(STATE_WAITING_FOR_REPORT_POWER_STATUS);
+ mTestLooper.dispatchAll();
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
+ mNativeWrapper.clearResultMessages();
+
+ HdmiCecMessage reportPowerStatusOn = new HdmiCecMessage(
+ ADDR_TV, playbackDevice.mAddress, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_ON);
+ action.processCommand(reportPowerStatusOn);
+ mTestLooper.dispatchAll();
+
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(textViewOn);
+ assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus);
+ assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
+ }
+
+ @Test
+ public void timeOut_Cec14b() {
+ HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback(
+ mHdmiControlService);
+ playbackDevice.init();
+ mLocalDevices.add(playbackDevice);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mTestLooper.dispatchAll();
+
+ TestActionTimer actionTimer = new TestActionTimer();
+ TestCallback callback = new TestCallback();
+ OneTouchPlayAction action = createOneTouchPlayAction(playbackDevice, actionTimer, callback,
+ false);
+ playbackDevice.addAndStartAction(action);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
+ playbackDevice.mAddress, mPhysicalAddress);
+ HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(playbackDevice.mAddress,
+ ADDR_TV);
+ HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder
+ .buildGiveDevicePowerStatus(playbackDevice.mAddress, ADDR_TV);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
+ assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
+ mNativeWrapper.clearResultMessages();
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAITING_FOR_REPORT_POWER_STATUS);
+ for (int i = 0; i < 10; ++i) {
+ action.handleTimerEvent(STATE_WAITING_FOR_REPORT_POWER_STATUS);
+ mTestLooper.dispatchAll();
+ }
+
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(textViewOn);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(activeSource);
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
+ action.handleTimerEvent(STATE_WAITING_FOR_REPORT_POWER_STATUS);
+ assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_TIMEOUT);
+ }
+
+ @Test
+ public void succeedIfPowerStatusOn_Cec20() {
+ HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback(
+ mHdmiControlService);
+ playbackDevice.init();
+ mLocalDevices.add(playbackDevice);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mHdmiControlService.getHdmiCecNetwork().updateDevicePowerStatus(ADDR_TV,
+ HdmiControlManager.POWER_STATUS_ON);
+ mTestLooper.dispatchAll();
+
+ TestActionTimer actionTimer = new TestActionTimer();
+ TestCallback callback = new TestCallback();
+ OneTouchPlayAction action = createOneTouchPlayAction(playbackDevice, actionTimer, callback,
+ true);
+ playbackDevice.addAndStartAction(action);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
+ playbackDevice.mAddress, mPhysicalAddress);
+ HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(playbackDevice.mAddress,
+ ADDR_TV);
+ HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder
+ .buildGiveDevicePowerStatus(playbackDevice.mAddress, ADDR_TV);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
+ assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus);
+ assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
+ }
+
+ @Test
+ public void succeedIfPowerStatusUnknown_Cec20() {
+ HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback(
+ mHdmiControlService);
+ playbackDevice.init();
+ mLocalDevices.add(playbackDevice);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mHdmiControlService.getHdmiCecNetwork().updateDevicePowerStatus(ADDR_TV,
+ HdmiControlManager.POWER_STATUS_UNKNOWN);
+ mTestLooper.dispatchAll();
+
+ TestActionTimer actionTimer = new TestActionTimer();
+ TestCallback callback = new TestCallback();
+ OneTouchPlayAction action = createOneTouchPlayAction(playbackDevice, actionTimer, callback,
+ true);
+ playbackDevice.addAndStartAction(action);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
+ playbackDevice.mAddress, mPhysicalAddress);
+ HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(playbackDevice.mAddress,
+ ADDR_TV);
+ HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder
+ .buildGiveDevicePowerStatus(playbackDevice.mAddress, ADDR_TV);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
+ assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
+ mNativeWrapper.clearResultMessages();
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAITING_FOR_REPORT_POWER_STATUS);
+ HdmiCecMessage reportPowerStatusOn = new HdmiCecMessage(
+ ADDR_TV, playbackDevice.mAddress, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_ON);
+ action.processCommand(reportPowerStatusOn);
+ mTestLooper.dispatchAll();
+
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(textViewOn);
+ assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus);
+ assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
+ }
+
+ @Test
+ public void succeedIfPowerStatusStandby_Cec20() {
+ HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback(
+ mHdmiControlService);
+ playbackDevice.init();
+ mLocalDevices.add(playbackDevice);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mHdmiControlService.getHdmiCecNetwork().updateDevicePowerStatus(ADDR_TV,
+ HdmiControlManager.POWER_STATUS_STANDBY);
+ mTestLooper.dispatchAll();
+
+ TestActionTimer actionTimer = new TestActionTimer();
+ TestCallback callback = new TestCallback();
+ OneTouchPlayAction action = createOneTouchPlayAction(playbackDevice, actionTimer, callback,
+ true);
+ playbackDevice.addAndStartAction(action);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
+ playbackDevice.mAddress, mPhysicalAddress);
+ HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(playbackDevice.mAddress,
+ ADDR_TV);
+ HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder
+ .buildGiveDevicePowerStatus(playbackDevice.mAddress, ADDR_TV);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
+ assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus);
+ mNativeWrapper.clearResultMessages();
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAITING_FOR_REPORT_POWER_STATUS);
+ HdmiCecMessage reportPowerStatusOn = new HdmiCecMessage(
+ ADDR_TV, playbackDevice.mAddress, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_ON);
+ action.processCommand(reportPowerStatusOn);
+ mTestLooper.dispatchAll();
+
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(textViewOn);
+ assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus);
+ assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
+ }
+
+ private static class TestActionTimer implements ActionTimer {
+ private int mState;
+
+ @Override
+ public void sendTimerMessage(int state, long delayMillis) {
+ mState = state;
+ }
+
+ @Override
+ public void clearTimerMessage() {
+ }
+
+ private int getState() {
+ return mState;
+ }
+ }
+
+ private static class TestCallback extends IHdmiControlCallback.Stub {
+ private final ArrayList<Integer> mCallbackResult = new ArrayList<Integer>();
+
+ @Override
+ public void onComplete(int result) {
+ mCallbackResult.add(result);
+ }
+
+ private int getResult() {
+ assertThat(mCallbackResult.size()).isEqualTo(1);
+ return mCallbackResult.get(0);
+ }
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 2f1d7eb404ad..e21e8bac38b9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -40,6 +40,8 @@ import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
import static com.android.server.wm.Task.ActivityState.STOPPED;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -117,18 +119,19 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testKeepBoundsWhenChangingFromFreeformToFullscreen() {
removeGlobalMinSizeRestriction();
- // create freeform display and a freeform app
+ // Create landscape freeform display and a freeform app.
DisplayContent display = new TestDisplayContent.Builder(mAtm, 2000, 1000)
.setCanRotate(false)
.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM).build();
setUpApp(display);
- // Put app window into freeform and then make it a compat app.
+ // Put app window into portrait freeform and then make it a compat app.
final Rect bounds = new Rect(100, 100, 400, 600);
mTask.setBounds(bounds);
-
prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
assertEquals(bounds, mActivity.getBounds());
+ // Activity is not yet in size compat mode; it is filling the freeform window.
+ assertMaxBoundsInheritDisplayAreaBounds();
// The activity should be able to accept negative x position [-150, 100 - 150, 600].
final int dx = bounds.left + bounds.width() / 2;
@@ -137,7 +140,7 @@ public class SizeCompatTests extends WindowTestsBase {
final int density = mActivity.getConfiguration().densityDpi;
- // change display configuration to fullscreen
+ // Change display configuration to fullscreen.
Configuration c = new Configuration(display.getRequestedOverrideConfiguration());
c.windowConfiguration.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
display.onRequestedOverrideConfigurationChanged(c);
@@ -147,6 +150,8 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(bounds.width(), mActivity.getBounds().width());
assertEquals(bounds.height(), mActivity.getBounds().height());
assertEquals(density, mActivity.getConfiguration().densityDpi);
+ // Size compat mode is sandboxed at the activity level.
+ assertActivityMaxBoundsSandboxed();
}
@Test
@@ -172,6 +177,12 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(appBounds.height(), appBounds.width() * aspectRatio, 0.5f /* delta */);
// The decor height should be a part of the effective bounds.
assertEquals(mActivity.getBounds().height(), appBounds.height() + notchHeight);
+ // Activity max bounds should be sandboxed; activity is letterboxed due to aspect ratio.
+ assertActivityMaxBoundsSandboxed();
+ // Activity max bounds ignore notch, since an app can be shown past the notch (although app
+ // is currently limited by the notch).
+ assertThat(mActivity.getWindowConfiguration().getMaxBounds().height())
+ .isEqualTo(displayBounds.height());
mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
assertFitted();
@@ -181,9 +192,17 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(appBounds.width(), appBounds.height() * aspectRatio, 0.5f /* delta */);
// The notch is no longer on top.
assertEquals(appBounds, mActivity.getBounds());
+ // Activity max bounds are sandboxed.
+ assertActivityMaxBoundsSandboxed();
mActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
assertFitted();
+ // Activity max bounds should be sandboxed; activity is letterboxed due to aspect ratio.
+ assertActivityMaxBoundsSandboxed();
+ // Activity max bounds ignore notch, since an app can be shown past the notch (although app
+ // is currently limited by the notch).
+ assertThat(mActivity.getWindowConfiguration().getMaxBounds().height())
+ .isEqualTo(displayBounds.height());
}
@Test
@@ -206,6 +225,9 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(originalBounds.width(), mActivity.getBounds().width());
assertEquals(originalBounds.height(), mActivity.getBounds().height());
assertEquals(originalDpi, mActivity.getConfiguration().densityDpi);
+ // Activity is sandboxed; it is in size compat mode since it is not resizable and has a
+ // max aspect ratio.
+ assertActivityMaxBoundsSandboxed();
assertScaled();
}
@@ -213,11 +235,13 @@ public class SizeCompatTests extends WindowTestsBase {
public void testFixedScreenBoundsWhenDisplaySizeChanged() {
setUpDisplaySizeWithApp(1000, 2500);
prepareUnresizable(mActivity, -1f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
+ final DisplayContent display = mActivity.mDisplayContent;
assertFitted();
+ // Activity inherits bounds from TaskDisplayArea, since not sandboxed.
+ assertMaxBoundsInheritDisplayAreaBounds();
final Rect origBounds = new Rect(mActivity.getBounds());
final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
- final DisplayContent display = mActivity.mDisplayContent;
// Change the size of current display.
resizeDisplay(display, 1000, 2000);
@@ -234,6 +258,8 @@ public class SizeCompatTests extends WindowTestsBase {
// The position of configuration bounds should be the same as compat bounds.
assertEquals(mActivity.getBounds().left, currentBounds.left);
assertEquals(mActivity.getBounds().top, currentBounds.top);
+ // Activity is sandboxed to the offset size compat bounds.
+ assertActivityMaxBoundsSandboxed();
// Change display size to a different orientation
resizeDisplay(display, 2000, 1000);
@@ -242,6 +268,8 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(origBounds.height(), currentBounds.height());
assertEquals(ORIENTATION_LANDSCAPE, display.getConfiguration().orientation);
assertEquals(Configuration.ORIENTATION_PORTRAIT, mActivity.getConfiguration().orientation);
+ // Activity is sandboxed to the offset size compat bounds.
+ assertActivityMaxBoundsSandboxed();
// The previous resize operation doesn't consider the rotation change after size changed.
// These setups apply the requested orientation to rotation as real case that the top fixed
@@ -261,6 +289,8 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(origBounds.height(), currentBounds.height());
assertEquals(offsetX, currentBounds.left);
assertScaled();
+ // Activity is sandboxed due to size compat mode.
+ assertActivityMaxBoundsSandboxed();
}
@Test
@@ -276,6 +306,8 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(bounds.width(), bounds.height() * maxAspect, 0.0001f /* delta */);
// The position should be horizontal centered.
assertEquals((displayWidth - bounds.width()) / 2, bounds.left);
+ // Activity max bounds should be sandboxed since it is letterboxed.
+ assertActivityMaxBoundsSandboxed();
mActivity.mDisplayContent.setImeLayeringTarget(addWindowToActivity(mActivity));
// Make sure IME cannot attach to the app, otherwise IME window will also be shifted.
@@ -287,6 +319,8 @@ public class SizeCompatTests extends WindowTestsBase {
// It should keep non-attachable because the resolved bounds will be computed according to
// the aspect ratio that won't match its parent bounds.
assertFalse(mActivity.mDisplayContent.isImeAttachedToApp());
+ // Activity max bounds should be sandboxed since it is letterboxed.
+ assertActivityMaxBoundsSandboxed();
}
@Test
@@ -312,14 +346,13 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
- public void testMoveToDifferentOrientDisplay() {
+ public void testMoveToDifferentOrientationDisplay() {
setUpDisplaySizeWithApp(1000, 2500);
prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
assertFitted();
- final Rect configBounds = mActivity.getWindowConfiguration().getBounds();
- final int origWidth = configBounds.width();
- final int origHeight = configBounds.height();
+ final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
+ final Rect originalBounds = new Rect(mActivity.getWindowConfiguration().getBounds());
final int notchHeight = 100;
final DisplayContent newDisplay = new TestDisplayContent.Builder(mAtm, 2000, 1000)
@@ -328,37 +361,45 @@ public class SizeCompatTests extends WindowTestsBase {
// Move the non-resizable activity to the new display.
mTask.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */);
// The configuration bounds [820, 0 - 1820, 2500] should keep the same.
- assertEquals(origWidth, configBounds.width());
- assertEquals(origHeight, configBounds.height());
+ assertEquals(originalBounds.width(), currentBounds.width());
+ assertEquals(originalBounds.height(), currentBounds.height());
assertScaled();
+ // Activity max bounds are sandboxed due to size compat mode on the new display.
+ assertActivityMaxBoundsSandboxed();
final Rect newDisplayBounds = newDisplay.getWindowConfiguration().getBounds();
// The scaled bounds should exclude notch area (1000 - 100 == 360 * 2500 / 1000 = 900).
assertEquals(newDisplayBounds.height() - notchHeight,
- (int) ((float) mActivity.getBounds().width() * origHeight / origWidth));
+ (int) ((float) mActivity.getBounds().width() * originalBounds.height()
+ / originalBounds.width()));
// Recompute the natural configuration in the new display.
mActivity.clearSizeCompatMode();
mActivity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
// Because the display cannot rotate, the portrait activity will fit the short side of
// display with keeping portrait bounds [200, 0 - 700, 1000] in center.
- assertEquals(newDisplayBounds.height(), configBounds.height());
- assertEquals(configBounds.height() * newDisplayBounds.height() / newDisplayBounds.width(),
- configBounds.width());
+ assertEquals(newDisplayBounds.height(), currentBounds.height());
+ assertEquals(currentBounds.height() * newDisplayBounds.height() / newDisplayBounds.width(),
+ currentBounds.width());
assertFitted();
// The appBounds should be [200, 100 - 700, 1000].
final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds();
- assertEquals(configBounds.width(), appBounds.width());
- assertEquals(configBounds.height() - notchHeight, appBounds.height());
+ assertEquals(currentBounds.width(), appBounds.width());
+ assertEquals(currentBounds.height() - notchHeight, appBounds.height());
+ // Activity max bounds are sandboxed due to letterboxing from orientation mismatch with
+ // display.
+ assertActivityMaxBoundsSandboxed();
}
@Test
- public void testFixedOrientRotateCutoutDisplay() {
+ public void testFixedOrientationRotateCutoutDisplay() {
// Create a display with a notch/cutout
final int notchHeight = 60;
- setUpApp(new TestDisplayContent.Builder(mAtm, 1000, 2500)
+ final int width = 1000;
+ setUpApp(new TestDisplayContent.Builder(mAtm, width, 2500)
.setNotch(notchHeight).build());
- // Bounds=[0, 0 - 1000, 1460], AppBounds=[0, 60 - 1000, 1460].
+ // Bounds=[0, 0 - 1000, 1400], AppBounds=[0, 60 - 1000, 1460].
+ final float maxAspect = 1.4f;
prepareUnresizable(mActivity, 1.4f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
@@ -366,6 +407,11 @@ public class SizeCompatTests extends WindowTestsBase {
final Rect origBounds = new Rect(currentBounds);
final Rect origAppBounds = new Rect(appBounds);
+ // Activity is sandboxed, and bounds include the area consumed by the notch.
+ assertActivityMaxBoundsSandboxed();
+ assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds().height())
+ .isEqualTo(Math.round(width * maxAspect) + notchHeight);
+
// Although the activity is fixed orientation, force rotate the display.
rotateDisplay(mActivity.mDisplayContent, ROTATION_270);
assertEquals(ROTATION_270, mTask.getWindowConfiguration().getRotation());
@@ -381,10 +427,13 @@ public class SizeCompatTests extends WindowTestsBase {
// The position in configuration should be global coordinates.
assertEquals(mActivity.getBounds().left, currentBounds.left);
assertEquals(mActivity.getBounds().top, currentBounds.top);
+
+ // Activity max bounds are sandboxed due to size compat mode.
+ assertActivityMaxBoundsSandboxed();
}
@Test
- public void testFixedAspOrientChangeOrient() {
+ public void testFixedAspectRatioOrientationChangeOrientation() {
setUpDisplaySizeWithApp(1000, 2500);
final float maxAspect = 1.4f;
@@ -396,6 +445,8 @@ public class SizeCompatTests extends WindowTestsBase {
final Rect originalAppBounds = new Rect(mActivity.getWindowConfiguration().getAppBounds());
assertEquals((int) (originalBounds.width() * maxAspect), originalBounds.height());
+ // Activity is sandboxed due to fixed aspect ratio.
+ assertActivityMaxBoundsSandboxed();
// Change the fixed orientation.
mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
@@ -407,6 +458,8 @@ public class SizeCompatTests extends WindowTestsBase {
mActivity.getWindowConfiguration().getAppBounds().height());
assertEquals(originalAppBounds.height(),
mActivity.getWindowConfiguration().getAppBounds().width());
+ // Activity is sandboxed due to fixed aspect ratio.
+ assertActivityMaxBoundsSandboxed();
}
@Test
@@ -455,6 +508,8 @@ public class SizeCompatTests extends WindowTestsBase {
// restarted and the override configuration won't be cleared.
verify(mActivity, never()).restartProcessIfVisible();
assertScaled();
+ // Activity max bounds are sandboxed due to size compat mode, even if is not visible.
+ assertActivityMaxBoundsSandboxed();
// Change display density
display.mBaseDisplayDensity = (int) (0.7f * display.mBaseDisplayDensity);
@@ -568,12 +623,16 @@ public class SizeCompatTests extends WindowTestsBase {
// in multi-window mode.
mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
assertFalse(activity.shouldCreateCompatDisplayInsets());
+ // Activity should not be sandboxed.
+ assertMaxBoundsInheritDisplayAreaBounds();
// The non-resizable activity should not be size compat because the display support
// changing windowing mode from fullscreen to freeform.
mTask.mDisplayContent.setDisplayWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
assertFalse(activity.shouldCreateCompatDisplayInsets());
+ // Activity should not be sandboxed.
+ assertMaxBoundsInheritDisplayAreaBounds();
}
@Test
@@ -637,6 +696,9 @@ public class SizeCompatTests extends WindowTestsBase {
// be transparent.
assertFalse(displayPolicy.isFullyTransparentAllowed(w, TYPE_STATUS_BAR));
+ // Activity is sandboxed.
+ assertActivityMaxBoundsSandboxed();
+
// Make the activity fill the display.
prepareUnresizable(mActivity, 10 /* maxAspect */, SCREEN_ORIENTATION_LANDSCAPE);
w.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
@@ -646,6 +708,7 @@ public class SizeCompatTests extends WindowTestsBase {
// The letterbox should only cover the notch area, so status bar can be transparent.
assertEquals(new Rect(notchHeight, 0, 0, 0), mActivity.getLetterboxInsets());
assertTrue(displayPolicy.isFullyTransparentAllowed(w, TYPE_STATUS_BAR));
+ assertActivityMaxBoundsSandboxed();
}
@Test
@@ -668,6 +731,7 @@ public class SizeCompatTests extends WindowTestsBase {
// App should launch in fixed orientation letterbox.
assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
+ assertActivityMaxBoundsSandboxed();
// Activity bounds should be 700x1400 with the ratio as the display.
assertEquals(displayBounds.height(), activityBounds.height());
@@ -789,6 +853,7 @@ public class SizeCompatTests extends WindowTestsBase {
assertScaled();
assertEquals(activityBounds.width(), newActivityBounds.width());
assertEquals(activityBounds.height(), newActivityBounds.height());
+ assertActivityMaxBoundsSandboxed();
}
@Test
@@ -800,29 +865,29 @@ public class SizeCompatTests extends WindowTestsBase {
// Portrait fixed app without max aspect.
prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
- Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
- Rect activityBounds = new Rect(mActivity.getBounds());
-
// App should launch in fullscreen.
assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
- assertEquals(displayBounds, activityBounds);
+ // Activity inherits max bounds from TaskDisplayArea.
+ assertMaxBoundsInheritDisplayAreaBounds();
// Rotate display to landscape.
rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
- displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
- activityBounds = new Rect(mActivity.getBounds());
- assertTrue(displayBounds.width() > displayBounds.height());
+ final Rect rotatedDisplayBounds = new Rect(mActivity.mDisplayContent.getBounds());
+ final Rect rotatedActivityBounds = new Rect(mActivity.getBounds());
+ assertTrue(rotatedDisplayBounds.width() > rotatedDisplayBounds.height());
// App should be in size compat.
assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertScaled();
+ assertThat(mActivity.inSizeCompatMode()).isTrue();
+ assertActivityMaxBoundsSandboxed();
// App bounds should be 700x1400 with the ratio as the display.
- assertEquals(displayBounds.height(), activityBounds.height());
- assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
- activityBounds.width());
+ assertEquals(rotatedDisplayBounds.height(), rotatedActivityBounds.height());
+ assertEquals(rotatedDisplayBounds.height() * rotatedDisplayBounds.height()
+ / rotatedDisplayBounds.width(), rotatedActivityBounds.width());
}
@Test
@@ -859,6 +924,7 @@ public class SizeCompatTests extends WindowTestsBase {
// has 700x1400 bounds with the ratio as the display.
assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(newActivity.inSizeCompatMode());
+ assertActivityMaxBoundsSandboxed();
assertEquals(taskBounds, displayBounds);
assertEquals(displayBounds.height(), newActivityBounds.height());
assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
@@ -899,6 +965,11 @@ public class SizeCompatTests extends WindowTestsBase {
// Task bounds should fill parent bounds.
assertEquals(displayBounds, taskBounds);
+ // Prior and new activity max bounds are sandboxed due to letterbox.
+ assertThat(newActivity.getConfiguration().windowConfiguration.getMaxBounds())
+ .isEqualTo(newActivityBounds);
+ assertActivityMaxBoundsSandboxed();
+
// Activity bounds should be (1400 / 1.3 = 1076)x1400 with the app requested ratio.
assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(newActivity.inSizeCompatMode());
@@ -927,6 +998,9 @@ public class SizeCompatTests extends WindowTestsBase {
// App should be in size compat.
assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertScaled();
+ assertThat(mActivity.inSizeCompatMode()).isTrue();
+ // Activity max bounds are sandboxed due to size compat mode.
+ assertActivityMaxBoundsSandboxed();
final Rect activityBounds = new Rect(mActivity.getBounds());
mTask.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */);
@@ -936,6 +1010,8 @@ public class SizeCompatTests extends WindowTestsBase {
assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertScaled();
assertEquals(activityBounds, mActivity.getBounds());
+ // Activity max bounds are sandboxed due to size compat.
+ assertActivityMaxBoundsSandboxed();
}
@Test
@@ -951,6 +1027,7 @@ public class SizeCompatTests extends WindowTestsBase {
// In fixed orientation letterbox
assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
+ assertActivityMaxBoundsSandboxed();
// Rotate display to portrait.
rotateDisplay(display, ROTATION_90);
@@ -958,13 +1035,15 @@ public class SizeCompatTests extends WindowTestsBase {
// App should be in size compat.
assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertScaled();
+ assertActivityMaxBoundsSandboxed();
// Rotate display to landscape.
rotateDisplay(display, ROTATION_180);
- // In Task letterbox
+ // In activity letterbox
assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
+ assertActivityMaxBoundsSandboxed();
}
@Test
@@ -982,20 +1061,23 @@ public class SizeCompatTests extends WindowTestsBase {
// In fixed orientation letterbox
assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
+ assertActivityMaxBoundsSandboxed();
- // Rotate display to portrait.
+ // Rotate display to landscape.
rotateDisplay(display, ROTATION_90);
// App should be in size compat.
assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertScaled();
+ assertActivityMaxBoundsSandboxed();
- // Rotate display to landscape.
+ // Rotate display to portrait.
rotateDisplay(display, ROTATION_180);
// In fixed orientation letterbox
assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
+ assertActivityMaxBoundsSandboxed();
}
@Test
@@ -1012,12 +1094,18 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(ORIENTATION_LANDSCAPE, display.getConfiguration().orientation);
assertEquals(2800, displayBounds.width());
assertEquals(1400, displayBounds.height());
- taskDisplayArea.setBounds(0, 0, 2400, 1000);
+ Rect displayAreaBounds = new Rect(0, 0, 2400, 1000);
+ taskDisplayArea.setBounds(displayAreaBounds);
final Rect activityBounds = new Rect(mActivity.getBounds());
assertFalse(mActivity.inSizeCompatMode());
assertEquals(2400, activityBounds.width());
assertEquals(1000, activityBounds.height());
+ // Task and activity maximum bounds inherit from TaskDisplayArea bounds.
+ assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
+ .isEqualTo(displayAreaBounds);
+ assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
+ .isEqualTo(displayAreaBounds);
}
@Test
@@ -1042,6 +1130,7 @@ public class SizeCompatTests extends WindowTestsBase {
assertScaled();
assertEquals(originalBounds,
mActivity.getConfiguration().windowConfiguration.getBounds());
+ assertActivityMaxBoundsSandboxed();
// Recompute the natural configuration of the non-resizable activity and the split screen.
mActivity.clearSizeCompatMode();
@@ -1053,12 +1142,13 @@ public class SizeCompatTests extends WindowTestsBase {
addWindowToActivity(mActivity);
mActivity.mRootWindowContainer.performSurfacePlacement();
- // Split screen is also in portrait [1000,1400], so activty should be in fixed orientation
+ // Split screen is also in portrait [1000,1400], so activity should be in fixed orientation
// letterbox.
assertEquals(ORIENTATION_PORTRAIT, mTask.getConfiguration().orientation);
assertEquals(ORIENTATION_LANDSCAPE, mActivity.getConfiguration().orientation);
assertFitted();
assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+ assertActivityMaxBoundsSandboxed();
// Letterbox should fill the gap between the split screen and the letterboxed activity.
final Rect primarySplitBounds = new Rect(organizer.mPrimary.getBounds());
@@ -1165,6 +1255,22 @@ public class SizeCompatTests extends WindowTestsBase {
assertFalse(mActivity.hasSizeCompatBounds());
}
+ /** Asserts the activity max bounds inherit from the TaskDisplayArea. */
+ private void assertMaxBoundsInheritDisplayAreaBounds() {
+ assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
+ .isEqualTo(mTask.getDisplayArea().getBounds());
+ }
+
+ /**
+ * Asserts activity-level letterbox or size compat mode size compat mode, so activity max
+ * bounds are sandboxed.
+ */
+ private void assertActivityMaxBoundsSandboxed() {
+ // Activity max bounds are sandboxed due to size compat mode.
+ assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
+ .isEqualTo(mActivity.getWindowConfiguration().getBounds());
+ }
+
static Configuration rotateDisplay(DisplayContent display, int rotation) {
final Configuration c = new Configuration();
display.getDisplayRotation().setRotation(rotation);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index ae85ceb72958..cac69657f5cc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -136,6 +136,7 @@ class TestDisplayContent extends DisplayContent {
final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
mInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
final TestDisplayContent newDisplay = createInternal(display);
+
// disable the normal system decorations
final DisplayPolicy displayPolicy = newDisplay.getDisplayPolicy();
spyOn(displayPolicy);
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 20e4b88ff13b..6985b360c9cc 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -17,25 +17,12 @@
package com.android.server.wm.flicker.rotation
import android.platform.test.annotations.Presubmit
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.endRotation
-import com.android.server.wm.flicker.focusDoesNotChange
import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -66,24 +53,6 @@ class ChangeAppRotationTest(
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- @Presubmit
- @Test
- fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
- testSpec.config.endRotation, allStates = false)
-
- @Presubmit
- @Test
fun screenshotLayerBecomesInvisible() {
testSpec.assertLayers {
this.isVisible(testApp.getPackage())
@@ -94,49 +63,6 @@ class ChangeAppRotationTest(
}
}
- @FlakyTest(bugId = 140855415)
- @Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
- @FlakyTest(bugId = 140855415)
- @Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
- @FlakyTest(bugId = 140855415)
- @Test
- fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(
- testSpec.config.startRotation, testSpec.config.endRotation)
-
- @FlakyTest(bugId = 140855415)
- @Test
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(
- testSpec.config.startRotation, testSpec.config.endRotation)
-
- @FlakyTest(bugId = 140855415)
- @Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
-
- @FlakyTest(bugId = 140855415)
- @Test
- fun appLayerRotates_StartingPos() {
- testSpec.assertLayersStart {
- this.coversExactly(startingPos, testApp.getPackage())
- }
- }
-
- @FlakyTest(bugId = 140855415)
- @Test
- fun appLayerRotates_EndingPos() {
- testSpec.assertLayersEnd {
- this.coversExactly(endingPos, testApp.getPackage())
- }
- }
-
- @FlakyTest(bugId = 151179149)
- @Test
- fun focusDoesNotChange() = testSpec.focusDoesNotChange()
-
companion object {
private const val SCREENSHOT_LAYER = "RotationLayer"
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
index c391112a53ec..9d78eb86539a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
@@ -17,17 +17,30 @@
package com.android.server.wm.flicker.rotation
import android.app.Instrumentation
+import android.platform.test.annotations.Presubmit
+import androidx.test.filters.FlakyTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
+import com.android.server.wm.flicker.focusDoesNotChange
import com.android.server.wm.flicker.helpers.StandardAppHelper
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.noUncoveredRegions
import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import org.junit.Test
abstract class RotationTransition(protected val testSpec: FlickerTestParameter) {
protected abstract val testApp: StandardAppHelper
@@ -63,4 +76,82 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter)
transition(testSpec.config)
}
}
+
+ @Presubmit
+ @Test
+ open fun navBarWindowIsAlwaysVisible() {
+ testSpec.navBarWindowIsAlwaysVisible()
+ }
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ open fun navBarLayerIsAlwaysVisible() {
+ testSpec.navBarLayerIsAlwaysVisible()
+ }
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ open fun navBarLayerRotatesAndScales() {
+ testSpec.navBarLayerRotatesAndScales(
+ testSpec.config.startRotation, testSpec.config.endRotation)
+ }
+
+ @Presubmit
+ @Test
+ open fun statusBarWindowIsAlwaysVisible() {
+ testSpec.statusBarWindowIsAlwaysVisible()
+ }
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ open fun statusBarLayerIsAlwaysVisible() {
+ testSpec.statusBarLayerIsAlwaysVisible()
+ }
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ open fun statusBarLayerRotatesScales() {
+ testSpec.statusBarLayerRotatesScales(
+ testSpec.config.startRotation, testSpec.config.endRotation)
+ }
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ open fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ @Presubmit
+ @Test
+ open fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ }
+
+ @Presubmit
+ @Test
+ open fun noUncoveredRegions() {
+ testSpec.noUncoveredRegions(testSpec.config.startRotation,
+ testSpec.config.endRotation, allStates = false)
+ }
+
+ @FlakyTest(bugId = 151179149)
+ @Test
+ open fun focusDoesNotChange() {
+ testSpec.focusDoesNotChange()
+ }
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ open fun appLayerRotates_StartingPos() {
+ testSpec.assertLayersStart {
+ this.coversExactly(startingPos, testApp.getPackage())
+ }
+ }
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ open fun appLayerRotates_EndingPos() {
+ testSpec.assertLayersEnd {
+ this.coversExactly(endingPos, testApp.getPackage())
+ }
+ }
} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index fc5bcc7eef1b..45d3006b9481 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -24,21 +24,9 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.appWindowAlwaysVisibleOnTop
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.endRotation
-import com.android.server.wm.flicker.focusDoesNotChange
import com.android.server.wm.flicker.helpers.SeamlessRotationAppHelper
import com.android.server.wm.flicker.layerAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -71,54 +59,25 @@ class SeamlessAppRotationTest(
}
}
- @Presubmit
- @Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- @Presubmit
- @Test
- fun appWindowAlwaysVisibleOnTop() = testSpec.appWindowAlwaysVisibleOnTop(testApp.`package`)
-
- @Presubmit
- @Test
- fun layerAlwaysVisible() = testSpec.layerAlwaysVisible(testApp.`package`)
-
@FlakyTest(bugId = 140855415)
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
@FlakyTest(bugId = 140855415)
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
- @FlakyTest(bugId = 140855415)
- @Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
- @FlakyTest(bugId = 140855415)
- @Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+ override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
@FlakyTest(bugId = 147659548)
@Test
- fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
- testSpec.config.endRotation, allStates = false)
-
- @FlakyTest
- @Test
- fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(
- testSpec.config.startRotation, testSpec.config.endRotation)
+ override fun noUncoveredRegions() = super.noUncoveredRegions()
- @FlakyTest
+ @Presubmit
@Test
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(
- testSpec.config.startRotation, testSpec.config.endRotation)
+ fun appWindowAlwaysVisibleOnTop() = testSpec.appWindowAlwaysVisibleOnTop(testApp.`package`)
- @FlakyTest
+ @Presubmit
@Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+ fun layerAlwaysVisible() = testSpec.layerAlwaysVisible(testApp.`package`)
@FlakyTest(bugId = 147659548)
@Test
@@ -128,10 +87,6 @@ class SeamlessAppRotationTest(
}
}
- @FlakyTest(bugId = 151179149)
- @Test
- fun focusDoesNotChange() = testSpec.focusDoesNotChange()
-
companion object {
private val testFactory = FlickerTestParameterFactory.getInstance()
diff --git a/tests/net/Android.bp b/tests/net/Android.bp
index 81224957b2c7..7f0318a135dc 100644
--- a/tests/net/Android.bp
+++ b/tests/net/Android.bp
@@ -70,7 +70,7 @@ android_test {
"mockito-target-minus-junit4",
"net-tests-utils",
"platform-test-annotations",
- "service-connectivity",
+ "service-connectivity-pre-jarjar",
"services.core",
"services.net",
],
diff --git a/tests/net/TEST_MAPPING b/tests/net/TEST_MAPPING
index 89fc6ea2c47b..d659688700d3 100644
--- a/tests/net/TEST_MAPPING
+++ b/tests/net/TEST_MAPPING
@@ -9,6 +9,23 @@
"name": "FrameworksNetDeflakeTest"
}
],
+ "auto-postsubmit": [
+ // Test tag for automotive targets. These are only running in postsubmit so as to harden the
+ // automotive targets to avoid introducing additional test flake and build time. The plan for
+ // presubmit testing for auto is to augment the existing tests to cover auto use cases as well.
+ // Additionally, this tag is used in targeted test suites to limit resource usage on the test
+ // infra during the hardening phase.
+ // TODO: this tag to be removed once the above is no longer an issue.
+ {
+ "name": "FrameworksNetTests"
+ },
+ {
+ "name": "FrameworksNetIntegrationTests"
+ },
+ {
+ "name": "FrameworksNetDeflakeTest"
+ }
+ ],
"imports": [
{
"path": "cts/tests/tests/net"
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index cc1ac61c2ab9..2c6a21ad7570 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -72,6 +72,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE;
import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
import static android.net.NetworkCapabilities.NET_CAPABILITY_RCS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_SUPL;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P;
@@ -5574,7 +5575,7 @@ public class ConnectivityServiceTest {
reset(mStatsManager);
// Temp metered change shouldn't update ifaces
- mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED);
+ mCellNetworkAgent.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
waitForIdle();
verify(mStatsManager, never()).notifyNetworkStatus(eq(Arrays.asList(onlyCell)),
any(List.class), eq(MOBILE_IFNAME), any(List.class));
@@ -10652,7 +10653,7 @@ public class ConnectivityServiceTest {
null,
null);
- // default NCs will be unregistered in tearDown
+ // default callbacks will be unregistered in tearDown
}
/**
@@ -10709,7 +10710,7 @@ public class ConnectivityServiceTest {
null,
mService.mNoServiceNetwork.network());
- // default NCs will be unregistered in tearDown
+ // default callbacks will be unregistered in tearDown
}
/**
@@ -10768,7 +10769,7 @@ public class ConnectivityServiceTest {
null,
mService.mNoServiceNetwork.network());
- // default NCs will be unregistered in tearDown
+ // default callbacks will be unregistered in tearDown
}
/**
@@ -10827,7 +10828,28 @@ public class ConnectivityServiceTest {
null,
mService.mNoServiceNetwork.network());
- // default NCs will be unregistered in tearDown
+ // default callbacks will be unregistered in tearDown
+ }
+
+ @Test
+ public void testCapabilityWithOemNetworkPreference() throws Exception {
+ @OemNetworkPreferences.OemNetworkPreference final int networkPref =
+ OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY;
+ setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest(networkPref);
+ registerDefaultNetworkCallbacks();
+
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true);
+
+ mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+
+ mCellNetworkAgent.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
+ mSystemDefaultNetworkCallback.expectCapabilitiesThat(mCellNetworkAgent, nc ->
+ nc.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
+ mDefaultNetworkCallback.expectCapabilitiesThat(mCellNetworkAgent, nc ->
+ nc.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
+
+ // default callbacks will be unregistered in tearDown
}
@Test
diff --git a/tests/vcn/assets/client-end-cert.pem b/tests/vcn/assets/client-end-cert.pem
new file mode 100644
index 000000000000..e82da85c50ab
--- /dev/null
+++ b/tests/vcn/assets/client-end-cert.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDaDCCAlCgAwIBAgIIcorRI3n29E4wDQYJKoZIhvcNAQELBQAwQTELMAkGA1UE
+BhMCVVMxEDAOBgNVBAoTB0FuZHJvaWQxIDAeBgNVBAMTF3R3by5jYS50ZXN0LmFu
+ZHJvaWQubmV0MB4XDTIwMDQxNDA1MDM0OVoXDTIzMDQxNDA1MDM0OVowRTELMAkG
+A1UEBhMCVVMxEDAOBgNVBAoTB0FuZHJvaWQxJDAiBgNVBAMTG2NsaWVudC50ZXN0
+LmlrZS5hbmRyb2lkLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+AK/cK+sIaiQlJYvy5+Dq70sJbgR7PO1uS2qkLRP7Wb3z5SNvz94nQvZRrFn1AFIE
+CpfESh5kUF6gJe7t7NR3mpQ98iEosCRBMDJT8qB+EeHiL4wkrmCE9sYMTyvaApRc
+6Qzozn/9kKma7Qpj/25AvoPluTERqhZ6AQ77BJeb6FNOAoO1Aoe9GJuB1xmRxjRw
+D0mwusL+ciQ/7uKlsFP5VO5XqACcohXSerzO8jcD9necBvka3SDepqqzn1K0NPRC
+25fMmS5kSjddKtKOif7w2NI3OpVsmP3kHv66If73VURsy0lgXPYyKkq8lAMrtmXG
+R7svFGPbEl+Swkpr3b+dzF8CAwEAAaNgMF4wHwYDVR0jBBgwFoAUcqSu1uRYT/DL
+bLoDNUz38nGvCKQwJgYDVR0RBB8wHYIbY2xpZW50LnRlc3QuaWtlLmFuZHJvaWQu
+bmV0MBMGA1UdJQQMMAoGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUAA4IBAQCa53tK
+I9RM9/MutZ5KNG2Gfs2cqaPyv8ZRhs90HDWZhkFVu7prywJAxOd2hxxHPsvgurio
+4bKAxnT4EXevgz5YoCbj2TPIL9TdFYh59zZ97XXMxk+SRdypgF70M6ETqKPs3hDP
+ZRMMoHvvYaqaPvp4StSBX9A44gSyjHxVYJkrjDZ0uffKg5lFL5IPvqfdmSRSpGab
+SyGTP4OLTy0QiNV3pBsJGdl0h5BzuTPR9OTl4xgeqqBQy2bDjmfJBuiYyCSCkPi7
+T3ohDYCymhuSkuktHPNG1aKllUJaw0tuZuNydlgdAveXPYfM36uvK0sfd9qr9pAy
+rmkYV2MAWguFeckh
+-----END CERTIFICATE----- \ No newline at end of file
diff --git a/tests/vcn/assets/client-private-key.key b/tests/vcn/assets/client-private-key.key
new file mode 100644
index 000000000000..22736e98e030
--- /dev/null
+++ b/tests/vcn/assets/client-private-key.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCv3CvrCGokJSWL
+8ufg6u9LCW4EezztbktqpC0T+1m98+Ujb8/eJ0L2UaxZ9QBSBAqXxEoeZFBeoCXu
+7ezUd5qUPfIhKLAkQTAyU/KgfhHh4i+MJK5ghPbGDE8r2gKUXOkM6M5//ZCpmu0K
+Y/9uQL6D5bkxEaoWegEO+wSXm+hTTgKDtQKHvRibgdcZkcY0cA9JsLrC/nIkP+7i
+pbBT+VTuV6gAnKIV0nq8zvI3A/Z3nAb5Gt0g3qaqs59StDT0QtuXzJkuZEo3XSrS
+jon+8NjSNzqVbJj95B7+uiH+91VEbMtJYFz2MipKvJQDK7Zlxke7LxRj2xJfksJK
+a92/ncxfAgMBAAECggEAQztaMvW5lm35J8LKsWs/5qEJRX9T8LWs8W0oqq36Riub
+G2wgvR6ndAIPcSjAYZqX7iOl7m6NZ0+0kN63HxdGqovwKIskpAekBGmhpYftED1n
+zh0r6UyMB3UnQ22KdOv8UOokIDxxdNX8728BdUYdT9Ggdkj5jLRB+VcwD0IUlNvo
+zzTpURV9HEd87uiLqd4AAHXSI0lIHI5U43z24HI/J6/YbYHT3Rlh6CIa/LuwO6vL
+gFkgqg0/oy6yJtjrHtzNVA67F0UaH62hR4YFgbC0d955SJnDidWOv/0j2DMpfdCc
+9kFAcPwUSyykvUSLnGIKWSG4D+6gzIeAeUx4oO7kMQKBgQDVNRkX8AGTHyLg+NXf
+spUWWcodwVioXl30Q7h6+4bt8OI61UbhQ7wX61wvJ1cySpa2KOYa2UdagQVhGhhL
+ADu363R77uXF/jZgzVfmjjyJ2nfDqRgHWRTlSkuq/jCOQCz7VIPHRZg5WL/9D4ms
+TAqMjpzqeMfFZI+w4/+xpcJIuQKBgQDTKBy+ZuerWrVT9icWKvLU58o5EVj/2yFy
+GJvKm+wRAAX2WzjNnR4HVd4DmMREVz1BPYby0j5gqjvtDsxYYu39+NT7JvMioLLK
+QPj+7k5geYgNqVgCxB1vP89RhY2X1RLrN9sTXOodgFPeXOQWNYITkGp3eQpx4nTJ
++K/al3oB1wKBgAjnc8nVIyuyxDEjE0OJYMKTM2a0uXAmqMPXxC+Wq5bqVXhhidlE
+i+lv0eTCPtkB1nN7F8kNQ/aaps/cWCFhvBy9P5shagUvzbOTP9WIIS0cq53HRRKh
+fMbqqGhWv05hjb9dUzeSR341n6cA7B3++v3Nwu3j52vt/DZF/1q68nc5AoGAS0SU
+ImbKE/GsizZGLoe2sZ/CHN+LKwCwhlwxRGKaHmE0vuE7eUeVSaYZEo0lAPtb8WJ+
+NRYueASWgeTxgFwbW5mUScZTirdfo+rPFwhZVdhcYApKPgosN9i2DOgfVcz1BnWN
+mPRY25U/0BaqkyQVruWeneG+kGPZn5kPDktKiVcCgYEAkzwU9vCGhm7ZVALvx/zR
+wARz2zsL9ImBc0P4DK1ld8g90FEnHrEgeI9JEwz0zFHOCMLwlk7kG0Xev7vfjZ7G
+xSqtQYOH33Qp6rtBOgdt8hSyDFvakvDl6bqhAw52gelO3MTpAB1+ZsfZ5gFx13Jf
+idNFcaIrC52PtZIH7QCzdDY=
+-----END PRIVATE KEY----- \ No newline at end of file
diff --git a/tests/vcn/java/android/net/vcn/VcnControlPlaneIkeConfigTest.java b/tests/vcn/java/android/net/vcn/VcnControlPlaneIkeConfigTest.java
index 36f5e41462e8..2333718d0cab 100644
--- a/tests/vcn/java/android/net/vcn/VcnControlPlaneIkeConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnControlPlaneIkeConfigTest.java
@@ -99,6 +99,13 @@ public class VcnControlPlaneIkeConfigTest {
}
@Test
+ public void testPersistableBundle() {
+ final VcnControlPlaneIkeConfig config = buildTestConfig();
+
+ assertEquals(config, new VcnControlPlaneIkeConfig(config.toPersistableBundle()));
+ }
+
+ @Test
public void testConstructConfigWithoutIkeParams() {
try {
new VcnControlPlaneIkeConfig(null, CHILD_PARAMS);
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java
new file mode 100644
index 000000000000..546d957d417e
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2021 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 android.net.vcn.persistablebundleutils;
+
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+import static android.telephony.TelephonyManager.APPTYPE_USIM;
+
+import static org.junit.Assert.assertEquals;
+
+import android.net.InetAddresses;
+import android.net.eap.EapSessionConfig;
+import android.net.ipsec.ike.IkeFqdnIdentification;
+import android.net.ipsec.ike.IkeSessionParams;
+import android.os.PersistableBundle;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.org.bouncycastle.util.io.pem.PemObject;
+import com.android.internal.org.bouncycastle.util.io.pem.PemReader;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.nio.charset.StandardCharsets;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPrivateKey;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class IkeSessionParamsUtilsTest {
+ private static IkeSessionParams.Builder createBuilderMinimum() {
+ final InetAddress serverAddress = InetAddresses.parseNumericAddress("192.0.2.100");
+
+ return new IkeSessionParams.Builder()
+ .setServerHostname(serverAddress.getHostAddress())
+ .addSaProposal(SaProposalUtilsTest.buildTestIkeSaProposal())
+ .setLocalIdentification(new IkeFqdnIdentification("client.test.android.net"))
+ .setRemoteIdentification(new IkeFqdnIdentification("server.test.android.net"))
+ .setAuthPsk("psk".getBytes());
+ }
+
+ private static void verifyPersistableBundleEncodeDecodeIsLossless(IkeSessionParams params) {
+ final PersistableBundle bundle = IkeSessionParamsUtils.toPersistableBundle(params);
+ final IkeSessionParams result = IkeSessionParamsUtils.fromPersistableBundle(bundle);
+
+ assertEquals(result, params);
+ }
+
+ @Test
+ public void testEncodeRecodeParamsWithLifetimes() throws Exception {
+ final int hardLifetime = (int) TimeUnit.HOURS.toSeconds(20L);
+ final int softLifetime = (int) TimeUnit.HOURS.toSeconds(10L);
+ final IkeSessionParams params =
+ createBuilderMinimum().setLifetimeSeconds(hardLifetime, softLifetime).build();
+ verifyPersistableBundleEncodeDecodeIsLossless(params);
+ }
+
+ @Test
+ public void testEncodeRecodeParamsWithDpdDelay() throws Exception {
+ final int dpdDelay = (int) TimeUnit.MINUTES.toSeconds(10L);
+ final IkeSessionParams params = createBuilderMinimum().setDpdDelaySeconds(dpdDelay).build();
+
+ verifyPersistableBundleEncodeDecodeIsLossless(params);
+ }
+
+ @Test
+ public void testEncodeRecodeParamsWithNattKeepalive() throws Exception {
+ final int nattKeepAliveDelay = (int) TimeUnit.MINUTES.toSeconds(5L);
+ final IkeSessionParams params =
+ createBuilderMinimum().setNattKeepAliveDelaySeconds(nattKeepAliveDelay).build();
+
+ verifyPersistableBundleEncodeDecodeIsLossless(params);
+ }
+
+ @Test
+ public void testEncodeRecodeParamsWithRetransmissionTimeouts() throws Exception {
+ final int[] retransmissionTimeout = new int[] {500, 500, 500, 500, 500, 500};
+ final IkeSessionParams params =
+ createBuilderMinimum()
+ .setRetransmissionTimeoutsMillis(retransmissionTimeout)
+ .build();
+
+ verifyPersistableBundleEncodeDecodeIsLossless(params);
+ }
+
+ @Test
+ public void testEncodeRecodeParamsWithConfigRequests() throws Exception {
+ final Inet4Address ipv4Address =
+ (Inet4Address) InetAddresses.parseNumericAddress("192.0.2.100");
+ final Inet6Address ipv6Address =
+ (Inet6Address) InetAddresses.parseNumericAddress("2001:db8::1");
+
+ final IkeSessionParams params =
+ createBuilderMinimum()
+ .addPcscfServerRequest(AF_INET)
+ .addPcscfServerRequest(AF_INET6)
+ .addPcscfServerRequest(ipv4Address)
+ .addPcscfServerRequest(ipv6Address)
+ .build();
+ verifyPersistableBundleEncodeDecodeIsLossless(params);
+ }
+
+ @Test
+ public void testEncodeRecodeParamsWithAuthPsk() throws Exception {
+ final IkeSessionParams params = createBuilderMinimum().setAuthPsk("psk".getBytes()).build();
+ verifyPersistableBundleEncodeDecodeIsLossless(params);
+ }
+
+ @Test
+ public void testEncodeRecodeParamsWithIkeOptions() throws Exception {
+ final IkeSessionParams params =
+ createBuilderMinimum()
+ .addIkeOption(IkeSessionParams.IKE_OPTION_ACCEPT_ANY_REMOTE_ID)
+ .addIkeOption(IkeSessionParams.IKE_OPTION_MOBIKE)
+ .build();
+ verifyPersistableBundleEncodeDecodeIsLossless(params);
+ }
+
+ private static InputStream openAssetsFile(String fileName) throws Exception {
+ return InstrumentationRegistry.getContext().getResources().getAssets().open(fileName);
+ }
+
+ private static X509Certificate createCertFromPemFile(String fileName) throws Exception {
+ final CertificateFactory factory = CertificateFactory.getInstance("X.509");
+ return (X509Certificate) factory.generateCertificate(openAssetsFile(fileName));
+ }
+
+ private static RSAPrivateKey createRsaPrivateKeyFromKeyFile(String fileName) throws Exception {
+ final PemObject pemObject =
+ new PemReader(new InputStreamReader(openAssetsFile(fileName))).readPemObject();
+ return (RSAPrivateKey) CertUtils.privateKeyFromByteArray(pemObject.getContent());
+ }
+
+ @Test
+ public void testEncodeRecodeParamsWithDigitalSignAuth() throws Exception {
+ final X509Certificate serverCaCert = createCertFromPemFile("self-signed-ca.pem");
+ final X509Certificate clientEndCert = createCertFromPemFile("client-end-cert.pem");
+ final RSAPrivateKey clientPrivateKey =
+ createRsaPrivateKeyFromKeyFile("client-private-key.key");
+
+ final IkeSessionParams params =
+ createBuilderMinimum()
+ .setAuthDigitalSignature(serverCaCert, clientEndCert, clientPrivateKey)
+ .build();
+ verifyPersistableBundleEncodeDecodeIsLossless(params);
+ }
+
+ @Test
+ public void testEncodeRecodeParamsWithEapAuth() throws Exception {
+ final X509Certificate serverCaCert = createCertFromPemFile("self-signed-ca.pem");
+
+ final byte[] eapId = "test@android.net".getBytes(StandardCharsets.US_ASCII);
+ final int subId = 1;
+ final EapSessionConfig eapConfig =
+ new EapSessionConfig.Builder()
+ .setEapIdentity(eapId)
+ .setEapSimConfig(subId, APPTYPE_USIM)
+ .setEapAkaConfig(subId, APPTYPE_USIM)
+ .build();
+
+ final IkeSessionParams params =
+ createBuilderMinimum().setAuthEap(serverCaCert, eapConfig).build();
+ verifyPersistableBundleEncodeDecodeIsLossless(params);
+ }
+}
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java
index 8ae8692b4f75..664044a9e7d4 100644
--- a/tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java
@@ -32,21 +32,25 @@ import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class SaProposalUtilsTest {
+ /** Package private so that IkeSessionParamsUtilsTest can use it */
+ static IkeSaProposal buildTestIkeSaProposal() {
+ return new IkeSaProposal.Builder()
+ .addEncryptionAlgorithm(
+ SaProposal.ENCRYPTION_ALGORITHM_3DES, SaProposal.KEY_LEN_UNUSED)
+ .addEncryptionAlgorithm(
+ SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, SaProposal.KEY_LEN_AES_128)
+ .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96)
+ .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128)
+ .addPseudorandomFunction(SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC)
+ .addPseudorandomFunction(SaProposal.PSEUDORANDOM_FUNCTION_SHA2_256)
+ .addDhGroup(SaProposal.DH_GROUP_1024_BIT_MODP)
+ .addDhGroup(SaProposal.DH_GROUP_3072_BIT_MODP)
+ .build();
+ }
+
@Test
public void testPersistableBundleEncodeDecodeIsLosslessIkeProposal() throws Exception {
- final IkeSaProposal proposal =
- new IkeSaProposal.Builder()
- .addEncryptionAlgorithm(
- SaProposal.ENCRYPTION_ALGORITHM_3DES, SaProposal.KEY_LEN_UNUSED)
- .addEncryptionAlgorithm(
- SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, SaProposal.KEY_LEN_AES_128)
- .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96)
- .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128)
- .addPseudorandomFunction(SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC)
- .addPseudorandomFunction(SaProposal.PSEUDORANDOM_FUNCTION_SHA2_256)
- .addDhGroup(SaProposal.DH_GROUP_1024_BIT_MODP)
- .addDhGroup(SaProposal.DH_GROUP_3072_BIT_MODP)
- .build();
+ final IkeSaProposal proposal = buildTestIkeSaProposal();
final PersistableBundle bundle = IkeSaProposalUtils.toPersistableBundle(proposal);
final SaProposal resultProposal = IkeSaProposalUtils.fromPersistableBundle(bundle);
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index 69b2fb135a8d..4bf849111a5d 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -241,7 +241,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
verify(mGatewayStatusCallback)
.onGatewayConnectionError(
- eq(mConfig.getRequiredUnderlyingCapabilities()),
+ eq(mConfig.getExposedCapabilities()),
eq(VCN_ERROR_CODE_INTERNAL_ERROR),
any(),
any());
@@ -275,10 +275,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
verify(mGatewayStatusCallback)
.onGatewayConnectionError(
- eq(mConfig.getRequiredUnderlyingCapabilities()),
- eq(expectedErrorType),
- any(),
- any());
+ eq(mConfig.getExposedCapabilities()), eq(expectedErrorType), any(), any());
}
@Test