summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/current.txt25
-rw-r--r--core/api/system-current.txt1
-rw-r--r--core/api/test-current.txt5
-rw-r--r--core/java/android/app/Activity.java33
-rw-r--r--core/java/android/app/Dialog.java19
-rw-r--r--core/java/android/app/StatusBarManager.java2
-rw-r--r--core/java/android/content/AttributionSource.java46
-rw-r--r--core/java/android/hardware/CameraStreamStats.java13
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java88
-rw-r--r--core/java/android/hardware/camera2/CameraDevice.java30
-rw-r--r--core/java/android/hardware/camera2/CameraExtensionCharacteristics.java144
-rw-r--r--core/java/android/hardware/camera2/CameraExtensionSession.java26
-rw-r--r--core/java/android/hardware/camera2/CameraMetadata.java113
-rw-r--r--core/java/android/hardware/camera2/extension/ICaptureProcessorImpl.aidl3
-rw-r--r--core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl2
-rw-r--r--core/java/android/hardware/camera2/extension/IPreviewImageProcessorImpl.aidl4
-rw-r--r--core/java/android/hardware/camera2/extension/IProcessResultImpl.aidl24
-rw-r--r--core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java7
-rw-r--r--core/java/android/hardware/camera2/impl/CameraDeviceImpl.java4
-rw-r--r--core/java/android/hardware/camera2/impl/CameraExtensionForwardProcessor.java7
-rw-r--r--core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java7
-rw-r--r--core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java182
-rw-r--r--core/java/android/hardware/camera2/impl/CameraMetadataNative.java18
-rw-r--r--core/java/android/hardware/camera2/params/MandatoryStreamCombination.java316
-rw-r--r--core/java/android/hardware/camera2/params/OutputConfiguration.java94
-rw-r--r--core/java/android/hardware/lights/Light.java6
-rw-r--r--core/java/android/inputmethodservice/NavigationBarController.java4
-rw-r--r--core/java/android/inputmethodservice/navigationbar/NavigationBarView.java2
-rw-r--r--core/java/android/net/INetworkPolicyManager.aidl4
-rw-r--r--core/java/android/net/NetworkPolicyManager.java15
-rw-r--r--core/java/android/provider/Settings.java6
-rw-r--r--core/java/android/service/games/IGameSessionController.aidl2
-rw-r--r--core/java/android/view/OnBackInvokedDispatcher.java63
-rw-r--r--core/java/android/view/ViewRootImpl.java61
-rw-r--r--core/java/android/widget/TextView.java10
-rw-r--r--core/java/android/window/ProxyOnBackInvokedDispatcher.java37
-rw-r--r--core/java/android/window/WindowOnBackInvokedDispatcher.java44
-rw-r--r--core/res/res/values/colors.xml3
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--data/etc/privapp-permissions-platform.xml1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java82
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/IBackAnimation.aidl45
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java332
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java364
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java46
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java33
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java171
-rw-r--r--media/java/android/media/ImageReader.java62
-rw-r--r--packages/ConnectivityT/service/Android.bp5
-rw-r--r--packages/ConnectivityT/service/src/com/android/server/net/CookieTagMapKey.java33
-rw-r--r--packages/ConnectivityT/service/src/com/android/server/net/CookieTagMapValue.java37
-rw-r--r--packages/ConnectivityT/service/src/com/android/server/net/StatsMapKey.java46
-rw-r--r--packages/ConnectivityT/service/src/com/android/server/net/StatsMapValue.java46
-rw-r--r--packages/ConnectivityT/service/src/com/android/server/net/UidStatsMapKey.java33
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java1
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java12
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java3
-rw-r--r--packages/Shell/AndroidManifest.xml1
-rw-r--r--packages/SystemUI/res/layout/qs_tile_label.xml8
-rw-r--r--packages/SystemUI/res/values/config.xml2
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java8
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/AutoMarqueeTextView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/SafeMarqueeTextView.kt44
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java17
-rw-r--r--packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java128
-rw-r--r--proto/src/camera.proto2
-rw-r--r--services/core/java/com/android/server/adb/AdbService.java5
-rw-r--r--services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java4
-rw-r--r--services/core/java/com/android/server/apphibernation/AppHibernationService.java55
-rw-r--r--services/core/java/com/android/server/apphibernation/UserLevelState.java4
-rw-r--r--services/core/java/com/android/server/camera/CameraServiceProxy.java4
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java184
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java2
-rw-r--r--services/core/java/com/android/server/sensorprivacy/CameraPrivacyLightController.java125
-rw-r--r--services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java4
-rw-r--r--services/core/java/com/android/server/wm/TrustedOverlayHost.java (renamed from services/core/java/com/android/server/wm/OverlayHost.java)12
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java10
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java11
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java12
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java7
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java248
-rw-r--r--services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java44
-rw-r--r--services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java2
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java3
-rw-r--r--telephony/java/android/service/euicc/EuiccService.java7
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java67
-rw-r--r--tests/SilkFX/AndroidManifest.xml10
-rw-r--r--tests/SilkFX/res/drawable/background_blur_drawable.xml20
-rw-r--r--tests/SilkFX/res/drawable/blur_activity_background_drawable_white.xml20
-rw-r--r--tests/SilkFX/res/layout/activity_background_blur.xml173
-rw-r--r--tests/SilkFX/res/values/style.xml31
-rw-r--r--tests/SilkFX/src/com/android/test/silkfx/Main.kt8
-rw-r--r--tests/SilkFX/src/com/android/test/silkfx/materials/BackgroundBlurActivity.kt189
108 files changed, 3673 insertions, 769 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index a9f1d4513de4..12546d81353a 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -17342,12 +17342,14 @@ package android.hardware.camera2 {
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_RECOMMENDED_TEN_BIT_DYNAMIC_RANGE_PROFILE;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Float> SCALER_AVAILABLE_MAX_DIGITAL_ZOOM;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> SCALER_AVAILABLE_ROTATE_AND_CROP_MODES;
+ field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> SCALER_AVAILABLE_STREAM_USE_CASES;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SCALER_CROPPING_TYPE;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Size> SCALER_DEFAULT_SECURE_IMAGE_SIZE;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_MAXIMUM_RESOLUTION_STREAM_COMBINATIONS;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_STREAM_COMBINATIONS;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_TEN_BIT_OUTPUT_STREAM_COMBINATIONS;
+ field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_USE_CASE_STREAM_COMBINATIONS;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MultiResolutionStreamConfigurationMap> SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.StreamConfigurationMap> SCALER_STREAM_CONFIGURATION_MAP;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.StreamConfigurationMap> SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION;
@@ -17441,6 +17443,8 @@ package android.hardware.camera2 {
}
public final class CameraExtensionCharacteristics {
+ method @NonNull public java.util.Set<android.hardware.camera2.CaptureRequest.Key> getAvailableCaptureRequestKeys(int);
+ method @NonNull public java.util.Set<android.hardware.camera2.CaptureResult.Key> getAvailableCaptureResultKeys(int);
method @Nullable public android.util.Range<java.lang.Long> getEstimatedCaptureLatencyRangeMillis(int, @NonNull android.util.Size, int);
method @NonNull public <T> java.util.List<android.util.Size> getExtensionSupportedSizes(int, @NonNull Class<T>);
method @NonNull public java.util.List<android.util.Size> getExtensionSupportedSizes(int, int);
@@ -17465,6 +17469,7 @@ package android.hardware.camera2 {
ctor public CameraExtensionSession.ExtensionCaptureCallback();
method public void onCaptureFailed(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest);
method public void onCaptureProcessStarted(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest);
+ method public void onCaptureResultAvailable(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest, @NonNull android.hardware.camera2.TotalCaptureResult);
method public void onCaptureSequenceAborted(@NonNull android.hardware.camera2.CameraExtensionSession, int);
method public void onCaptureSequenceCompleted(@NonNull android.hardware.camera2.CameraExtensionSession, int);
method public void onCaptureStarted(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest, long);
@@ -17676,9 +17681,16 @@ package android.hardware.camera2 {
field public static final int REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS = 5; // 0x5
field public static final int REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING = 17; // 0x11
field public static final int REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA = 13; // 0xd
+ field public static final int REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE = 19; // 0x13
field public static final int REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA = 14; // 0xe
field public static final int REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR = 16; // 0x10
field public static final int REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING = 7; // 0x7
+ field public static final int SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT = 0; // 0x0
+ field public static final int SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW = 1; // 0x1
+ field public static final int SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL = 4; // 0x4
+ field public static final int SCALER_AVAILABLE_STREAM_USE_CASES_STILL_CAPTURE = 2; // 0x2
+ field public static final int SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL = 5; // 0x5
+ field public static final int SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_RECORD = 3; // 0x3
field public static final int SCALER_CROPPING_TYPE_CENTER_ONLY = 0; // 0x0
field public static final int SCALER_CROPPING_TYPE_FREEFORM = 1; // 0x1
field public static final int SCALER_ROTATE_AND_CROP_180 = 2; // 0x2
@@ -18086,6 +18098,7 @@ package android.hardware.camera2.params {
method public int get10BitFormat();
method @NonNull public java.util.List<android.util.Size> getAvailableSizes();
method public int getFormat();
+ method public int getStreamUseCase();
method public boolean is10BitCapable();
method public boolean isInput();
method public boolean isMaximumSize();
@@ -18142,6 +18155,7 @@ package android.hardware.camera2.params {
method public void enableSurfaceSharing();
method public int getDynamicRangeProfile();
method public int getMaxSharedSurfaceCount();
+ method public int getStreamUseCase();
method @Nullable public android.view.Surface getSurface();
method public int getSurfaceGroupId();
method @NonNull public java.util.List<android.view.Surface> getSurfaces();
@@ -18149,6 +18163,7 @@ package android.hardware.camera2.params {
method public void removeSurface(@NonNull android.view.Surface);
method public void setDynamicRangeProfile(int);
method public void setPhysicalCameraId(@Nullable String);
+ method public void setStreamUseCase(int);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> CREATOR;
field public static final int SURFACE_GROUP_ID_NONE = -1; // 0xffffffff
@@ -43049,7 +43064,8 @@ package android.telephony {
method public void setSubscriptionOverrideCongested(int, boolean, @NonNull int[], long);
method public void setSubscriptionOverrideUnmetered(int, boolean, long);
method public void setSubscriptionOverrideUnmetered(int, boolean, @NonNull int[], long);
- method public void setSubscriptionPlans(int, @NonNull java.util.List<android.telephony.SubscriptionPlan>);
+ method @Deprecated public void setSubscriptionPlans(int, @NonNull java.util.List<android.telephony.SubscriptionPlan>);
+ method public void setSubscriptionPlans(int, @NonNull java.util.List<android.telephony.SubscriptionPlan>, long);
method @RequiresPermission("android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS") public void switchToSubscription(int, @NonNull android.app.PendingIntent);
field public static final String ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED = "android.telephony.action.DEFAULT_SMS_SUBSCRIPTION_CHANGED";
field public static final String ACTION_DEFAULT_SUBSCRIPTION_CHANGED = "android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED";
@@ -48934,10 +48950,9 @@ package android.view {
method public default void onBackInvoked();
}
- public abstract class OnBackInvokedDispatcher {
- ctor public OnBackInvokedDispatcher();
- method public abstract void registerOnBackInvokedCallback(@NonNull android.view.OnBackInvokedCallback, int);
- method public abstract void unregisterOnBackInvokedCallback(@NonNull android.view.OnBackInvokedCallback);
+ public interface OnBackInvokedDispatcher {
+ method public void registerOnBackInvokedCallback(@NonNull android.view.OnBackInvokedCallback, @IntRange(from=0) int);
+ method public void unregisterOnBackInvokedCallback(@NonNull android.view.OnBackInvokedCallback);
field public static final int PRIORITY_DEFAULT = 0; // 0x0
field public static final int PRIORITY_OVERLAY = 1000000; // 0xf4240
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index ef55d50b7677..3dad1a987494 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -11143,6 +11143,7 @@ package android.service.euicc {
field public static final String EXTRA_RESOLUTION_CONFIRMATION_CODE_RETRIED = "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE_RETRIED";
field public static final String EXTRA_RESOLUTION_CONSENT = "android.service.euicc.extra.RESOLUTION_CONSENT";
field public static final String EXTRA_RESOLUTION_PORT_INDEX = "android.service.euicc.extra.RESOLUTION_PORT_INDEX";
+ field public static final String EXTRA_RESOLUTION_SUBSCRIPTION_ID = "android.service.euicc.extra.RESOLUTION_SUBSCRIPTION_ID";
field public static final String EXTRA_RESOLUTION_USE_PORT_INDEX = "android.service.euicc.extra.RESOLUTION_USE_PORT_INDEX";
field public static final String EXTRA_RESOLVABLE_ERRORS = "android.service.euicc.extra.RESOLVABLE_ERRORS";
field public static final int RESOLVABLE_ERROR_CONFIRMATION_CODE = 1; // 0x1
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index e27fc3480276..7b72374f2dce 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -670,6 +670,7 @@ package android.content {
ctor public AttributionSource(int, @Nullable String, @Nullable String);
ctor public AttributionSource(int, @Nullable String, @Nullable String, @NonNull android.os.IBinder);
ctor public AttributionSource(int, @Nullable String, @Nullable String, @Nullable java.util.Set<java.lang.String>, @Nullable android.content.AttributionSource);
+ method public void enforceCallingPid();
}
public final class AutofillOptions implements android.os.Parcelable {
@@ -2801,6 +2802,10 @@ package android.view {
field public static final int FLAG_IS_ACCESSIBILITY_EVENT = 2048; // 0x800
}
+ public interface OnBackInvokedDispatcher {
+ field public static final long DISPATCH_BACK_INVOCATION_AHEAD_OF_TIME = 195946584L; // 0xbade858L
+ }
+
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) public @interface RemotableViewMethod {
method public abstract String asyncImpl() default "";
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 8935022639e4..db865ced6a67 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -115,6 +115,7 @@ import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
+import android.view.OnBackInvokedCallback;
import android.view.OnBackInvokedDispatcher;
import android.view.OnBackInvokedDispatcherOwner;
import android.view.RemoteAnimationDefinition;
@@ -144,6 +145,7 @@ import android.widget.AdapterView;
import android.widget.Toast;
import android.widget.Toolbar;
import android.window.SplashScreen;
+import android.window.WindowOnBackInvokedDispatcher;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -791,6 +793,7 @@ public class Activity extends ContextThemeWrapper
private static final int LOG_AM_ON_ACTIVITY_RESULT_CALLED = 30062;
private static final int LOG_AM_ON_TOP_RESUMED_GAINED_CALLED = 30064;
private static final int LOG_AM_ON_TOP_RESUMED_LOST_CALLED = 30065;
+ private OnBackInvokedCallback mDefaultBackCallback;
/**
* After {@link Build.VERSION_CODES#TIRAMISU},
@@ -1617,7 +1620,16 @@ public class Activity extends ContextThemeWrapper
}
mRestoredFromBundle = savedInstanceState != null;
mCalled = true;
-
+ if (!WindowOnBackInvokedDispatcher.shouldUseLegacyBack()) {
+ // Add onBackPressed as default back behavior.
+ mDefaultBackCallback = new OnBackInvokedCallback() {
+ @Override
+ public void onBackInvoked() {
+ navigateBack();
+ }
+ };
+ getOnBackInvokedDispatcher().registerSystemOnBackInvokedCallback(mDefaultBackCallback);
+ }
}
/**
@@ -2653,6 +2665,10 @@ public class Activity extends ContextThemeWrapper
if (mUiTranslationController != null) {
mUiTranslationController.onActivityDestroyed();
}
+
+ if (mDefaultBackCallback != null) {
+ getOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(mDefaultBackCallback);
+ }
}
/**
@@ -3773,10 +3789,13 @@ public class Activity extends ContextThemeWrapper
* @see KeyEvent
*/
public boolean onKeyUp(int keyCode, KeyEvent event) {
- if (getApplicationInfo().targetSdkVersion
- >= Build.VERSION_CODES.ECLAIR) {
- if (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking()
- && !event.isCanceled()) {
+ int sdkVersion = getApplicationInfo().targetSdkVersion;
+ if (sdkVersion >= Build.VERSION_CODES.ECLAIR) {
+ if (keyCode == KeyEvent.KEYCODE_BACK
+ && event.isTracking()
+ && !event.isCanceled()
+ && mDefaultBackCallback == null) {
+ // Using legacy back handling.
onBackPressed();
return true;
}
@@ -3841,6 +3860,10 @@ public class Activity extends ContextThemeWrapper
if (!fragmentManager.isStateSaved() && fragmentManager.popBackStackImmediate()) {
return;
}
+ navigateBack();
+ }
+
+ private void navigateBack() {
if (!isTaskRoot()) {
// If the activity is not the root of the task, allow finish to proceed normally.
finishAfterTransition();
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 4b42ddc383b2..208477588885 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -52,6 +52,7 @@ import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
+import android.view.OnBackInvokedCallback;
import android.view.OnBackInvokedDispatcher;
import android.view.OnBackInvokedDispatcherOwner;
import android.view.SearchEvent;
@@ -62,6 +63,7 @@ import android.view.ViewGroup.LayoutParams;
import android.view.Window;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
+import android.window.WindowOnBackInvokedDispatcher;
import com.android.internal.R;
import com.android.internal.app.WindowDecorActionBar;
@@ -156,6 +158,7 @@ public class Dialog implements DialogInterface, Window.Callback,
/** A {@link Runnable} to run instead of dismissing when {@link #dismiss()} is called. */
private Runnable mDismissOverride;
+ private OnBackInvokedCallback mDefaultBackCallback;
/**
* Creates a dialog window that uses the default dialog theme.
@@ -453,6 +456,16 @@ public class Dialog implements DialogInterface, Window.Callback,
*/
protected void onStart() {
if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(true);
+ if (mContext != null && !WindowOnBackInvokedDispatcher.shouldUseLegacyBack()) {
+ // Add onBackPressed as default back behavior.
+ mDefaultBackCallback = new OnBackInvokedCallback() {
+ @Override
+ public void onBackInvoked() {
+ onBackPressed();
+ }
+ };
+ getOnBackInvokedDispatcher().registerSystemOnBackInvokedCallback(mDefaultBackCallback);
+ }
}
/**
@@ -460,6 +473,9 @@ public class Dialog implements DialogInterface, Window.Callback,
*/
protected void onStop() {
if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false);
+ if (mDefaultBackCallback != null) {
+ getOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(mDefaultBackCallback);
+ }
}
private static final String DIALOG_SHOWING_TAG = "android:dialogShowing";
@@ -685,7 +701,8 @@ public class Dialog implements DialogInterface, Window.Callback,
public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE)
&& event.isTracking()
- && !event.isCanceled()) {
+ && !event.isCanceled()
+ && WindowOnBackInvokedDispatcher.shouldUseLegacyBack()) {
onBackPressed();
return true;
}
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 8fcb07f578e8..da1ba5265c4a 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -180,6 +180,8 @@ public class StatusBarManager {
public static final int NAVIGATION_HINT_BACK_ALT = 1 << 0;
/** @hide */
public static final int NAVIGATION_HINT_IME_SHOWN = 1 << 1;
+ /** @hide */
+ public static final int NAVIGATION_HINT_IME_SWITCHER_SHOWN = 1 << 2;
/** @hide */
public static final int WINDOW_STATUS_BAR = 1;
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index 157e709a67f0..3f2fa2188d24 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -155,8 +155,8 @@ public final class AttributionSource implements Parcelable {
this(AttributionSourceState.CREATOR.createFromParcel(in));
// Since we just unpacked this object as part of it transiting a Binder
- // call, this is the perfect time to enforce that its UID can be trusted
- enforceCallingUid();
+ // call, this is the perfect time to enforce that its UID and PID can be trusted
+ enforceCallingUidAndPid();
}
/** @hide */
@@ -259,13 +259,24 @@ public final class AttributionSource implements Parcelable {
}
/**
+ * If you are handling an IPC and you don't trust the caller you need to validate whether the
+ * attribution source is one for the calling app to prevent the caller to pass you a source from
+ * another app without including themselves in the attribution chain.
+ *
+ * @throws SecurityException if the attribution source cannot be trusted to be from the caller.
+ */
+ private void enforceCallingUidAndPid() {
+ enforceCallingUid();
+ enforceCallingPid();
+ }
+
+ /**
* If you are handling an IPC and you don't trust the caller you need to validate
* whether the attribution source is one for the calling app to prevent the caller
* to pass you a source from another app without including themselves in the
* attribution chain.
*
- * @throws SecurityException if the attribution source cannot be trusted to be
- * from the caller.
+ * @throws SecurityException if the attribution source cannot be trusted to be from the caller.
*/
public void enforceCallingUid() {
if (!checkCallingUid()) {
@@ -294,6 +305,33 @@ public final class AttributionSource implements Parcelable {
return true;
}
+ /**
+ * Validate that the pid being claimed for the calling app is not spoofed
+ *
+ * @throws SecurityException if the attribution source cannot be trusted to be from the caller.
+ * @hide
+ */
+ @TestApi
+ public void enforceCallingPid() {
+ if (!checkCallingPid()) {
+ throw new SecurityException("Calling pid: " + Binder.getCallingPid()
+ + " doesn't match source pid: " + mAttributionSourceState.pid);
+ }
+ }
+
+ /**
+ * Validate that the pid being claimed for the calling app is not spoofed
+ *
+ * @return if the attribution source cannot be trusted to be from the caller.
+ */
+ private boolean checkCallingPid() {
+ final int callingPid = Binder.getCallingPid();
+ if (mAttributionSourceState.pid != -1 && callingPid != mAttributionSourceState.pid) {
+ return false;
+ }
+ return true;
+ }
+
@Override
public String toString() {
if (Build.IS_DEBUGGABLE) {
diff --git a/core/java/android/hardware/CameraStreamStats.java b/core/java/android/hardware/CameraStreamStats.java
index ed22de8dd594..85890c1912c5 100644
--- a/core/java/android/hardware/CameraStreamStats.java
+++ b/core/java/android/hardware/CameraStreamStats.java
@@ -16,6 +16,7 @@
package android.hardware;
import android.hardware.camera2.params.DynamicRangeProfiles;
+import android.hardware.camera2.CameraMetadata;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
@@ -47,6 +48,7 @@ public class CameraStreamStats implements Parcelable {
private float[] mHistogramBins;
private long[] mHistogramCounts;
private int mDynamicRangeProfile;
+ private int mStreamUseCase;
private static final String TAG = "CameraStreamStats";
@@ -63,11 +65,13 @@ public class CameraStreamStats implements Parcelable {
mMaxAppBuffers = 0;
mHistogramType = HISTOGRAM_TYPE_UNKNOWN;
mDynamicRangeProfile = DynamicRangeProfiles.STANDARD;
+ mStreamUseCase = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT;
}
public CameraStreamStats(int width, int height, int format,
int dataSpace, long usage, long requestCount, long errorCount,
- int startLatencyMs, int maxHalBuffers, int maxAppBuffers, int dynamicRangeProfile) {
+ int startLatencyMs, int maxHalBuffers, int maxAppBuffers, int dynamicRangeProfile,
+ int streamUseCase) {
mWidth = width;
mHeight = height;
mFormat = format;
@@ -80,6 +84,7 @@ public class CameraStreamStats implements Parcelable {
mMaxAppBuffers = maxAppBuffers;
mHistogramType = HISTOGRAM_TYPE_UNKNOWN;
mDynamicRangeProfile = dynamicRangeProfile;
+ mStreamUseCase = streamUseCase;
}
public static final @android.annotation.NonNull Parcelable.Creator<CameraStreamStats> CREATOR =
@@ -126,6 +131,7 @@ public class CameraStreamStats implements Parcelable {
dest.writeFloatArray(mHistogramBins);
dest.writeLongArray(mHistogramCounts);
dest.writeInt(mDynamicRangeProfile);
+ dest.writeInt(mStreamUseCase);
}
public void readFromParcel(Parcel in) {
@@ -143,6 +149,7 @@ public class CameraStreamStats implements Parcelable {
mHistogramBins = in.createFloatArray();
mHistogramCounts = in.createLongArray();
mDynamicRangeProfile = in.readInt();
+ mStreamUseCase = in.readInt();
}
public int getWidth() {
@@ -200,4 +207,8 @@ public class CameraStreamStats implements Parcelable {
public int getDynamicRangeProfile() {
return mDynamicRangeProfile;
}
+
+ public int getStreamUseCase() {
+ return mStreamUseCase;
+ }
}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index d2dc314585d6..a29bffe6ea93 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -657,7 +657,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
*
* @throws IllegalArgumentException if metadataClass is not a subclass of CameraMetadata
*/
- private <TKey> List<TKey>
+ <TKey> List<TKey>
getAvailableKeyList(Class<?> metadataClass, Class<TKey> keyClass, int[] filterTags,
boolean includeSynthetic) {
@@ -2214,6 +2214,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR ULTRA_HIGH_RESOLUTION_SENSOR}</li>
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING REMOSAIC_REPROCESSING}</li>
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT DYNAMIC_RANGE_TEN_BIT}</li>
+ * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE STREAM_USE_CASE}</li>
* </ul>
*
* <p>This key is available on all devices.</p>
@@ -2238,6 +2239,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* @see #REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR
* @see #REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING
* @see #REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT
+ * @see #REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE
*/
@PublicKey
@NonNull
@@ -3475,6 +3477,90 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
new Key<Boolean>("android.scaler.multiResolutionStreamSupported", boolean.class);
/**
+ * <p>The stream use cases supported by this camera device.</p>
+ * <p>The stream use case indicates the purpose of a particular camera stream from
+ * the end-user perspective. Some examples of camera use cases are: preview stream for
+ * live viewfinder shown to the user, still capture for generating high quality photo
+ * capture, video record for encoding the camera output for the purpose of future playback,
+ * and video call for live realtime video conferencing.</p>
+ * <p>With this flag, the camera device can optimize the image processing pipeline
+ * parameters, such as tuning, sensor mode, and ISP settings, indepedent of
+ * the properties of the immediate camera output surface. For example, if the output
+ * surface is a SurfaceTexture, the stream use case flag can be used to indicate whether
+ * the camera frames eventually go to display, video encoder,
+ * still image capture, or all of them combined.</p>
+ * <p>The application sets the use case of a camera stream by calling
+ * {@link android.hardware.camera2.params.OutputConfiguration#setStreamUseCase }.</p>
+ * <p>A camera device with
+ * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE }
+ * capability must support the following stream use cases:</p>
+ * <ul>
+ * <li>DEFAULT</li>
+ * <li>PREVIEW</li>
+ * <li>STILL_CAPTURE</li>
+ * <li>VIDEO_RECORD</li>
+ * <li>PREVIEW_VIDEO_STILL</li>
+ * <li>VIDEO_CALL</li>
+ * </ul>
+ * <p>The guaranteed stream combinations related to stream use case for a camera device with
+ * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE }
+ * capability is documented in the camera device
+ * {@link android.hardware.camera2.CameraDevice#createCaptureSession guideline}. The
+ * application is strongly recommended to use one of the guaranteed stream combintations.
+ * If the application creates a session with a stream combination not in the guaranteed
+ * list, or with mixed DEFAULT and non-DEFAULT use cases within the same session,
+ * the camera device may ignore some stream use cases due to hardware constraints
+ * and implementation details.</p>
+ * <p>For stream combinations not covered by the stream use case mandatory lists, such as
+ * reprocessable session, constrained high speed session, or RAW stream combinations, the
+ * application should leave stream use cases within the session as DEFAULT.</p>
+ * <p><b>Possible values:</b></p>
+ * <ul>
+ * <li>{@link #SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT DEFAULT}</li>
+ * <li>{@link #SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW PREVIEW}</li>
+ * <li>{@link #SCALER_AVAILABLE_STREAM_USE_CASES_STILL_CAPTURE STILL_CAPTURE}</li>
+ * <li>{@link #SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_RECORD VIDEO_RECORD}</li>
+ * <li>{@link #SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL PREVIEW_VIDEO_STILL}</li>
+ * <li>{@link #SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL VIDEO_CALL}</li>
+ * </ul>
+ *
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * @see #SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT
+ * @see #SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW
+ * @see #SCALER_AVAILABLE_STREAM_USE_CASES_STILL_CAPTURE
+ * @see #SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_RECORD
+ * @see #SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL
+ * @see #SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL
+ */
+ @PublicKey
+ @NonNull
+ public static final Key<int[]> SCALER_AVAILABLE_STREAM_USE_CASES =
+ new Key<int[]>("android.scaler.availableStreamUseCases", int[].class);
+
+ /**
+ * <p>An array of mandatory stream combinations with stream use cases.
+ * This is an app-readable conversion of the mandatory stream combination
+ * {@link android.hardware.camera2.CameraDevice#createCaptureSession tables} with
+ * each stream's use case being set.</p>
+ * <p>The array of
+ * {@link android.hardware.camera2.params.MandatoryStreamCombination combinations} is
+ * generated according to the documented
+ * {@link android.hardware.camera2.CameraDevice#createCaptureSession guideline} for a
+ * camera device with
+ * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE }
+ * capability.
+ * The mandatory stream combination array will be {@code null} in case the device doesn't
+ * have {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE }
+ * capability.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ */
+ @PublicKey
+ @NonNull
+ @SyntheticKey
+ public static final Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_USE_CASE_STREAM_COMBINATIONS =
+ new Key<android.hardware.camera2.params.MandatoryStreamCombination[]>("android.scaler.mandatoryUseCaseStreamCombinations", android.hardware.camera2.params.MandatoryStreamCombination[].class);
+
+ /**
* <p>The area of the image sensor which corresponds to active pixels after any geometric
* distortion correction has been applied.</p>
* <p>This is the rectangle representing the size of the active region of the sensor (i.e.
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 47eb79d07469..1a42eaf541ca 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -404,7 +404,10 @@ public abstract class CameraDevice implements AutoCloseable {
* (output format)/(surface type), or if the extension is not
* supported, or if any of the output configurations select
* a dynamic range different from
- * {@link android.hardware.camera2.params.DynamicRangeProfiles#STANDARD}
+ * {@link android.hardware.camera2.params.DynamicRangeProfiles#STANDARD},
+ * or if any of the output configurations sets a stream use
+ * case different from {@link
+ * android.hardware.camera2.CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT}.
* @see CameraExtensionCharacteristics#getSupportedExtensions
* @see CameraExtensionCharacteristics#getExtensionSupportedSizes
*/
@@ -855,6 +858,31 @@ public abstract class CameraDevice implements AutoCloseable {
* will cause a capture session initialization failure.
* </p>
*
+ * <p>Devices with the STREAM_USE_CASE capability ({@link
+ * CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES} includes {@link
+ * CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE}) support below additional
+ * stream combinations:
+ *
+ * <table>
+ * <tr><th colspan="10">STREAM_USE_CASE capability additional guaranteed configurations</th></tr>
+ * <tr><th colspan="3" id="rb">Target 1</th><th colspan="3" id="rb">Target 2</th><th colspan="3" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr>
+ * <tr><th>Type</th><th id="rb">Max size</th><th>Usecase</th><th>Type</th><th id="rb">Max size</th><th>Usecase</th><th>Type</th><th id="rb">Max size</th><th>Usecase</th> </tr>
+ * <tr> <td>{@code YUV / PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td colspan="3" id="rb"></td> <td colspan="3" id="rb"></td> <td>Simple preview or in-app image processing</td> </tr>
+ * <tr> <td>{@code YUV / PRIV}</td><td id="rb">{@code RECORD}</td><td id="rb">{@code VIDEO_RECORD}</td> <td colspan="3" id="rb"></td> <td colspan="3" id="rb"></td> <td>Simple video recording or in-app video processing</td> </tr>
+ * <tr> <td>{@code YUV / JPEG}</td><td id="rb">{@code MAXIMUM}</td><td id="rb">{@code STILL_CAPTURE}</td> <td colspan="3" id="rb"></td> <td colspan="3" id="rb"></td> <td>Simple JPEG or YUV still image capture</td> </tr>
+ * <tr> <td>{@code YUV / PRIV}</td><td id="rb">{@code s1440p}</td><td id="rb">{@code PREVIEW_VIDEO_STILL}</td> <td colspan="3" id="rb"></td> <td colspan="3" id="rb"></td> <td>Multi-purpose stream for preview, video and still image capture</td> </tr>
+ * <tr> <td>{@code YUV / PRIV}</td><td id="rb">{@code s1440p}</td><td id="rb">{@code VIDEO_CALL}</td> <td colspan="3" id="rb"></td> <td colspan="3" id="rb"></td> <td>Simple video call</td> </tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV / JPEG}</td><td id="rb">{@code MAXIMUM}</td><td id="rb">{@code STILL_CAPTURE}</td> <td colspan="3" id="rb"></td> <td>Preview with JPEG or YUV still image capture</td> </tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV / PRIV}</td><td id="rb">{@code RECORD}</td><td id="rb">{@code VIDEO_RECORD}</td> <td colspan="3" id="rb"></td> <td>Preview with video recording or in-app video processing</td> </tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td colspan="3" id="rb"></td> <td>Preview with in-application image processing</td> </tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV / PRIV}</td><td id="rb">{@code s1440p}</td><td id="rb">{@code VIDEO_CALL}</td> <td colspan="3" id="rb"></td> <td>Preview with video call</td> </tr>
+ * <tr> <td>{@code YUV / PRIV}</td><td id="rb">{@code s1440p}</td><td id="rb">{@code PREVIEW_VIDEO_STILL}</td> <td>{@code YUV / JPEG}</td><td id="rb">{@code MAXIMUM}</td><td id="rb">{@code STILL_CAPTURE}</td> <td colspan="3" id="rb"></td> <td>Multi-purpose stream with JPEG or YUV still capture</td> </tr>
+ * <tr> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code STILL_CAPTURE}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM}</td><td id="rb">{@code STILL_CAPTURE}</td> <td colspan="3" id="rb"></td> <td>YUV and JPEG concurrent still image capture (for testing)</td> </tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV / PRIV}</td><td id="rb">{@code RECORD}</td><td id="rb">{@code VIDEO_RECORD}</td> <td>{@code YUV / JPEG}</td><td id="rb">{@code RECORD}</td><td id="rb">{@code STILL_CAPTURE}</td> <td>Preview, video record and JPEG or YUV video snapshot</td> </tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV / JPEG}</td><td id="rb">{@code MAXIMUM}</td><td id="rb">{@code STILL_CAPTURE}</td> <td>Preview, in-application image processing, and JPEG or YUV still image capture</td> </tr>
+ * </table><br>
+ * </p>
+ *
* <p>Since the capabilities of camera devices vary greatly, a given camera device may support
* target combinations with sizes outside of these guarantees, but this can only be tested for
* by calling {@link #isSessionConfigurationSupported} or attempting to create a session with
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index 5c636c7fe2f0..aa98f1fdf9bc 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -30,6 +30,7 @@ import android.hardware.camera2.extension.IInitializeSessionCallback;
import android.hardware.camera2.extension.IPreviewExtenderImpl;
import android.hardware.camera2.extension.LatencyRange;
import android.hardware.camera2.extension.SizeList;
+import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.params.ExtensionSessionConfiguration;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.os.ConditionVariable;
@@ -49,6 +50,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@@ -785,8 +787,8 @@ public final class CameraExtensionCharacteristics {
if (latencyRange != null) {
return new Range(latencyRange.min, latencyRange.max);
}
- }
- } catch (RemoteException e) {
+ }
+ } catch (RemoteException e) {
Log.e(TAG, "Failed to query the extension capture latency! Extension service does"
+ " not respond!");
} finally {
@@ -795,4 +797,142 @@ public final class CameraExtensionCharacteristics {
return null;
}
+
+ /**
+ * Returns the set of keys supported by a {@link CaptureRequest} submitted in a
+ * {@link CameraExtensionSession} with a given extension type.
+ *
+ * <p>The set returned is not modifiable, so any attempts to modify it will throw
+ * a {@code UnsupportedOperationException}.</p>
+ *
+ * @param extension the extension type
+ *
+ * @return non-modifiable set of capture keys supported by camera extension session initialized
+ * with the given extension type.
+ * @throws IllegalArgumentException in case of unsupported extension.
+ */
+ @NonNull
+ public Set<CaptureRequest.Key> getAvailableCaptureRequestKeys(@Extension int extension) {
+ long clientId = registerClient(mContext);
+ if (clientId < 0) {
+ throw new IllegalArgumentException("Unsupported extensions");
+ }
+
+ HashSet<CaptureRequest.Key> ret = new HashSet<>();
+
+ try {
+ if (!isExtensionSupported(mCameraId, extension, mChars)) {
+ throw new IllegalArgumentException("Unsupported extension");
+ }
+ Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
+ initializeExtension(extension);
+ extenders.second.onInit(mCameraId, mChars.getNativeMetadata());
+ extenders.second.init(mCameraId, mChars.getNativeMetadata());
+ CameraMetadataNative captureRequestMeta =
+ extenders.second.getAvailableCaptureRequestKeys();
+
+ if (captureRequestMeta != null) {
+ int[] requestKeys = captureRequestMeta.get(
+ CameraCharacteristics.REQUEST_AVAILABLE_REQUEST_KEYS);
+ if (requestKeys == null) {
+ throw new AssertionError("android.request.availableRequestKeys must be non-null"
+ + " in the characteristics");
+ }
+ CameraCharacteristics requestChars = new CameraCharacteristics(captureRequestMeta);
+
+ Object crKey = CaptureRequest.Key.class;
+ Class<CaptureRequest.Key<?>> crKeyTyped = (Class<CaptureRequest.Key<?>>)crKey;
+
+ ret.addAll(requestChars.getAvailableKeyList(CaptureRequest.class, crKeyTyped,
+ requestKeys, /*includeSynthetic*/ false));
+ }
+
+ // Jpeg quality and orientation must always be supported
+ if (!ret.contains(CaptureRequest.JPEG_QUALITY)) {
+ ret.add(CaptureRequest.JPEG_QUALITY);
+ }
+ if (!ret.contains(CaptureRequest.JPEG_ORIENTATION)) {
+ ret.add(CaptureRequest.JPEG_ORIENTATION);
+ }
+ extenders.second.onDeInit();
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Failed to query the available capture request keys!");
+ } finally {
+ unregisterClient(clientId);
+ }
+
+ return Collections.unmodifiableSet(ret);
+ }
+
+ /**
+ * Returns the set of keys supported by a {@link CaptureResult} passed as an argument to
+ * {@link CameraExtensionSession.ExtensionCaptureCallback#onCaptureResultAvailable}.
+ *
+ * <p>The set returned is not modifiable, so any attempts to modify it will throw
+ * a {@code UnsupportedOperationException}.</p>
+ *
+ * <p>In case the set is empty, then the extension is not able to support any capture results
+ * and the {@link CameraExtensionSession.ExtensionCaptureCallback#onCaptureResultAvailable}
+ * callback will not be fired.</p>
+ *
+ * @param extension the extension type
+ *
+ * @return non-modifiable set of capture result keys supported by camera extension session
+ * initialized with the given extension type.
+ * @throws IllegalArgumentException in case of unsupported extension.
+ */
+ @NonNull
+ public Set<CaptureResult.Key> getAvailableCaptureResultKeys(@Extension int extension) {
+ long clientId = registerClient(mContext);
+ if (clientId < 0) {
+ throw new IllegalArgumentException("Unsupported extensions");
+ }
+
+ HashSet<CaptureResult.Key> ret = new HashSet<>();
+ try {
+ if (!isExtensionSupported(mCameraId, extension, mChars)) {
+ throw new IllegalArgumentException("Unsupported extension");
+ }
+
+ Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
+ initializeExtension(extension);
+ extenders.second.onInit(mCameraId, mChars.getNativeMetadata());
+ extenders.second.init(mCameraId, mChars.getNativeMetadata());
+ CameraMetadataNative captureResultMeta =
+ extenders.second.getAvailableCaptureResultKeys();
+
+ if (captureResultMeta != null) {
+ int[] resultKeys = captureResultMeta.get(
+ CameraCharacteristics.REQUEST_AVAILABLE_RESULT_KEYS);
+ if (resultKeys == null) {
+ throw new AssertionError("android.request.availableResultKeys must be non-null "
+ + "in the characteristics");
+ }
+ CameraCharacteristics resultChars = new CameraCharacteristics(captureResultMeta);
+ Object crKey = CaptureResult.Key.class;
+ Class<CaptureResult.Key<?>> crKeyTyped = (Class<CaptureResult.Key<?>>)crKey;
+
+ ret.addAll(resultChars.getAvailableKeyList(CaptureResult.class, crKeyTyped,
+ resultKeys, /*includeSynthetic*/ false));
+
+ // Jpeg quality, orientation and sensor timestamp must always be supported
+ if (!ret.contains(CaptureResult.JPEG_QUALITY)) {
+ ret.add(CaptureResult.JPEG_QUALITY);
+ }
+ if (!ret.contains(CaptureResult.JPEG_ORIENTATION)) {
+ ret.add(CaptureResult.JPEG_ORIENTATION);
+ }
+ if (!ret.contains(CaptureResult.SENSOR_TIMESTAMP)) {
+ ret.add(CaptureResult.SENSOR_TIMESTAMP);
+ }
+ }
+ extenders.second.onDeInit();
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Failed to query the available capture result keys!");
+ } finally {
+ unregisterClient(clientId);
+ }
+
+ return Collections.unmodifiableSet(ret);
+ }
}
diff --git a/core/java/android/hardware/camera2/CameraExtensionSession.java b/core/java/android/hardware/camera2/CameraExtensionSession.java
index 5892f682dd49..ee3441fc16f1 100644
--- a/core/java/android/hardware/camera2/CameraExtensionSession.java
+++ b/core/java/android/hardware/camera2/CameraExtensionSession.java
@@ -172,6 +172,32 @@ public abstract class CameraExtensionSession implements AutoCloseable {
int sequenceId) {
// default empty implementation
}
+
+ /**
+ * This method is called when an image capture has fully completed and all the
+ * result metadata is available.
+ *
+ * <p>This callback will only be called in case
+ * {@link CameraExtensionCharacteristics#getAvailableCaptureResultKeys} returns a valid
+ * non-empty list.</p>
+ *
+ * <p>The default implementation of this method does nothing.</p>
+ *
+ * @param session The session received during
+ * {@link StateCallback#onConfigured(CameraExtensionSession)}
+ * @param request The request that was given to the CameraDevice
+ * @param result The total output metadata from the capture, which only includes the
+ * capture result keys advertised as supported in
+ * {@link CameraExtensionCharacteristics#getAvailableCaptureResultKeys}.
+ *
+ * @see #capture
+ * @see #setRepeatingRequest
+ * @see CameraExtensionCharacteristics#getAvailableCaptureResultKeys
+ */
+ public void onCaptureResultAvailable(@NonNull CameraExtensionSession session,
+ @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
+ // default empty implementation
+ }
}
/**
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 803684da6ddb..95238ee4e6a6 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -1210,6 +1210,36 @@ public abstract class CameraMetadata<TKey> {
*/
public static final int REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT = 18;
+ /**
+ * <p>The camera device supports selecting a per-stream use case via
+ * {@link android.hardware.camera2.params.OutputConfiguration#setStreamUseCase }
+ * so that the device can optimize camera pipeline parameters such as tuning, sensor
+ * mode, or ISP settings for a specific user scenario.
+ * Some sample usages of this capability are:
+ * * Distinguish high quality YUV captures from a regular YUV stream where
+ * the image quality may not be as good as the JPEG stream, or
+ * * Use one stream to serve multiple purposes: viewfinder, video recording and
+ * still capture. This is common with applications that wish to apply edits equally
+ * to preview, saved images, and saved videos.</p>
+ * <p>This capability requires the camera device to support the following
+ * stream use cases:
+ * * DEFAULT for backward compatibility where the application doesn't set
+ * a stream use case
+ * * PREVIEW for live viewfinder and in-app image analysis
+ * * STILL_CAPTURE for still photo capture
+ * * VIDEO_RECORD for recording video clips
+ * * PREVIEW_VIDEO_STILL for one single stream used for viewfinder, video
+ * recording, and still capture.
+ * * VIDEO_CALL for long running video calls</p>
+ * <p>{@link android.hardware.camera2.CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES }
+ * lists all of the supported stream use cases.</p>
+ * <p>Refer to {@link android.hardware.camera2.CameraDevice#createCaptureSession } for the
+ * mandatory stream combinations involving stream use cases, which can also be queried
+ * via {@link android.hardware.camera2.params.MandatoryStreamCombination }.</p>
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+ */
+ public static final int REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE = 19;
+
//
// Enumeration values for CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP
//
@@ -1336,6 +1366,89 @@ public abstract class CameraMetadata<TKey> {
public static final int SCALER_CROPPING_TYPE_FREEFORM = 1;
//
+ // Enumeration values for CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES
+ //
+
+ /**
+ * <p>Default stream use case.</p>
+ * <p>This use case is the same as when the application doesn't set any use case for
+ * the stream. The camera device uses the properties of the output target, such as
+ * format, dataSpace, or surface class type, to optimize the image processing pipeline.</p>
+ * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES
+ */
+ public static final int SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT = 0x0;
+
+ /**
+ * <p>Live stream shown to the user.</p>
+ * <p>Optimized for performance and usability as a viewfinder, but not necessarily for
+ * image quality. The output is not meant to be persisted as saved images or video.</p>
+ * <p>No stall if android.control.<em> are set to FAST; may have stall if android.control.</em>
+ * are set to HIGH_QUALITY. This use case has the same behavior as the default
+ * SurfaceView and SurfaceTexture targets. Additionally, this use case can be used for
+ * in-app image analysis.</p>
+ * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES
+ */
+ public static final int SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW = 0x1;
+
+ /**
+ * <p>Still photo capture.</p>
+ * <p>Optimized for high-quality high-resolution capture, and not expected to maintain
+ * preview-like frame rates.</p>
+ * <p>The stream may have stalls regardless of whether android.control.* is HIGH_QUALITY.
+ * This use case has the same behavior as the default JPEG and RAW related formats.</p>
+ * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES
+ */
+ public static final int SCALER_AVAILABLE_STREAM_USE_CASES_STILL_CAPTURE = 0x2;
+
+ /**
+ * <p>Recording video clips.</p>
+ * <p>Optimized for high-quality video capture, including high-quality image stabilization
+ * if supported by the device and enabled by the application. As a result, may produce
+ * output frames with a substantial lag from real time, to allow for highest-quality
+ * stabilization or other processing. As such, such an output is not suitable for drawing
+ * to screen directly, and is expected to be persisted to disk or similar for later
+ * playback or processing. Only streams that set the VIDEO_RECORD use case are guaranteed
+ * to have video stabilization applied when the video stabilization control is set
+ * to ON, as opposed to PREVIEW_STABILIZATION.</p>
+ * <p>This use case has the same behavior as the default MediaRecorder and MediaCodec
+ * targets.</p>
+ * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES
+ */
+ public static final int SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_RECORD = 0x3;
+
+ /**
+ * <p>One single stream used for combined purposes of preview, video, and still capture.</p>
+ * <p>For such multi-purpose streams, the camera device aims to make the best tradeoff
+ * between the individual use cases. For example, the STILL_CAPTURE use case by itself
+ * may have stalls for achieving best image quality. But if combined with PREVIEW and
+ * VIDEO_RECORD, the camera device needs to trade off the additional image processing
+ * for speed so that preview and video recording aren't slowed down.</p>
+ * <p>Similarly, VIDEO_RECORD may produce frames with a substantial lag, but
+ * PREVIEW_VIDEO_STILL must have minimal output delay. This means that to enable video
+ * stabilization with this use case, the device must support and the app must select the
+ * PREVIEW_STABILIZATION mode for video stabilization.</p>
+ * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES
+ */
+ public static final int SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL = 0x4;
+
+ /**
+ * <p>Long-running video call optimized for both power efficienty and video quality.</p>
+ * <p>The camera sensor may run in a lower-resolution mode to reduce power consumption
+ * at the cost of some image and digital zoom quality. Unlike VIDEO_RECORD, VIDEO_CALL
+ * outputs are expected to work in dark conditions, so are usually accompanied with
+ * variable frame rate settings to allow sufficient exposure time in low light.</p>
+ * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES
+ */
+ public static final int SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL = 0x5;
+
+ /**
+ * <p>Vendor defined use cases. These depend on the vendor implementation.</p>
+ * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES
+ * @hide
+ */
+ public static final int SCALER_AVAILABLE_STREAM_USE_CASES_VENDOR_START = 0x10000;
+
+ //
// Enumeration values for CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT
//
diff --git a/core/java/android/hardware/camera2/extension/ICaptureProcessorImpl.aidl b/core/java/android/hardware/camera2/extension/ICaptureProcessorImpl.aidl
index 022b084f613b..3c5f5ff63a34 100644
--- a/core/java/android/hardware/camera2/extension/ICaptureProcessorImpl.aidl
+++ b/core/java/android/hardware/camera2/extension/ICaptureProcessorImpl.aidl
@@ -17,6 +17,7 @@ package android.hardware.camera2.extension;
import android.view.Surface;
import android.hardware.camera2.extension.CaptureBundle;
+import android.hardware.camera2.extension.IProcessResultImpl;
import android.hardware.camera2.extension.Size;
/** @hide */
@@ -25,5 +26,5 @@ interface ICaptureProcessorImpl
void onOutputSurface(in Surface surface, int imageFormat);
void onResolutionUpdate(in Size size);
void onImageFormatUpdate(int imageFormat);
- void process(in List<CaptureBundle> capturelist);
+ void process(in List<CaptureBundle> capturelist, in IProcessResultImpl resultCallback);
}
diff --git a/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl b/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl
index 3ebf63793b79..a8a7866e5ca4 100644
--- a/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl
+++ b/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl
@@ -39,4 +39,6 @@ interface IImageCaptureExtenderImpl
int getMaxCaptureStage();
@nullable List<SizeList> getSupportedResolutions();
LatencyRange getEstimatedCaptureLatencyRange(in Size outputSize);
+ CameraMetadataNative getAvailableCaptureRequestKeys();
+ CameraMetadataNative getAvailableCaptureResultKeys();
}
diff --git a/core/java/android/hardware/camera2/extension/IPreviewImageProcessorImpl.aidl b/core/java/android/hardware/camera2/extension/IPreviewImageProcessorImpl.aidl
index f7e4023c5067..ecd098be9d3b 100644
--- a/core/java/android/hardware/camera2/extension/IPreviewImageProcessorImpl.aidl
+++ b/core/java/android/hardware/camera2/extension/IPreviewImageProcessorImpl.aidl
@@ -17,6 +17,7 @@ package android.hardware.camera2.extension;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.view.Surface;
+import android.hardware.camera2.extension.IProcessResultImpl;
import android.hardware.camera2.extension.ParcelImage;
import android.hardware.camera2.extension.Size;
@@ -26,5 +27,6 @@ interface IPreviewImageProcessorImpl
void onOutputSurface(in Surface surface, int imageFormat);
void onResolutionUpdate(in Size size);
void onImageFormatUpdate(int imageFormat);
- void process(in ParcelImage image, in CameraMetadataNative result, int sequenceId);
+ void process(in ParcelImage image, in CameraMetadataNative result, int sequenceId,
+ in IProcessResultImpl resultCallback);
}
diff --git a/core/java/android/hardware/camera2/extension/IProcessResultImpl.aidl b/core/java/android/hardware/camera2/extension/IProcessResultImpl.aidl
new file mode 100644
index 000000000000..4114edb37a8c
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/IProcessResultImpl.aidl
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2022, 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.hardware.camera2.extension;
+
+import android.hardware.camera2.impl.CameraMetadataNative;
+
+/** @hide */
+interface IProcessResultImpl
+{
+ void onCaptureCompleted(long shutterTimestamp, in CameraMetadataNative results);
+}
diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
index 9d2c901ed049..cd392ced2081 100644
--- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
@@ -105,7 +105,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
@RequiresPermission(android.Manifest.permission.CAMERA)
public static CameraAdvancedExtensionSessionImpl createCameraAdvancedExtensionSession(
@NonNull CameraDevice cameraDevice, @NonNull Context ctx,
- @NonNull ExtensionSessionConfiguration config)
+ @NonNull ExtensionSessionConfiguration config, int sessionId)
throws CameraAccessException, RemoteException {
long clientId = CameraExtensionCharacteristics.registerClient(ctx);
if (clientId < 0) {
@@ -135,6 +135,11 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
throw new IllegalArgumentException("Unsupported dynamic range profile: " +
c.getDynamicRangeProfile());
}
+ if (c.getStreamUseCase() !=
+ CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT) {
+ throw new IllegalArgumentException("Unsupported stream use case: " +
+ c.getStreamUseCase());
+ }
}
int suitableSurfaceCount = 0;
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 9b19fc4d3ef2..3cb0c93d8409 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -2495,10 +2495,10 @@ public class CameraDeviceImpl extends CameraDevice
if (CameraExtensionCharacteristics.areAdvancedExtensionsSupported()) {
mCurrentAdvancedExtensionSession =
CameraAdvancedExtensionSessionImpl.createCameraAdvancedExtensionSession(
- this, mContext, extensionConfiguration);
+ this, mContext, extensionConfiguration, mNextSessionId++);
} else {
mCurrentExtensionSession = CameraExtensionSessionImpl.createCameraExtensionSession(
- this, mContext, extensionConfiguration);
+ this, mContext, extensionConfiguration, mNextSessionId++);
}
} catch (RemoteException e) {
throw new CameraAccessException(CameraAccessException.CAMERA_ERROR);
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionForwardProcessor.java b/core/java/android/hardware/camera2/impl/CameraExtensionForwardProcessor.java
index bf4593260a70..d148d87fc9d8 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionForwardProcessor.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionForwardProcessor.java
@@ -19,6 +19,7 @@ package android.hardware.camera2.impl;
import android.annotation.SuppressLint;
import android.hardware.camera2.CameraExtensionCharacteristics;
import android.hardware.camera2.extension.IPreviewImageProcessorImpl;
+import android.hardware.camera2.extension.IProcessResultImpl;
import android.hardware.camera2.extension.ParcelImage;
import android.hardware.camera2.TotalCaptureResult;
import android.media.Image;
@@ -114,12 +115,12 @@ public class CameraExtensionForwardProcessor {
}
}
- public void process(ParcelImage image, TotalCaptureResult totalCaptureResult)
- throws RemoteException {
+ public void process(ParcelImage image, TotalCaptureResult totalCaptureResult,
+ IProcessResultImpl resultCallback) throws RemoteException {
if ((mIntermediateSurface != null) && (mIntermediateSurface.isValid()) &&
!mOutputAbandoned) {
mProcessor.process(image, totalCaptureResult.getNativeMetadata(),
- totalCaptureResult.getSequenceId());
+ totalCaptureResult.getSequenceId(), resultCallback);
}
}
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java b/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
index 425f22c31306..1514a2be5de8 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
@@ -24,6 +24,7 @@ import android.graphics.ImageFormat;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.extension.CaptureBundle;
import android.hardware.camera2.extension.ICaptureProcessorImpl;
+import android.hardware.camera2.extension.IProcessResultImpl;
import android.media.Image;
import android.media.Image.Plane;
import android.media.ImageReader;
@@ -183,11 +184,13 @@ public class CameraExtensionJpegProcessor implements ICaptureProcessorImpl {
int cropLeft, int cropTop, int cropRight, int cropBottom,
int rot90);
- public void process(List<CaptureBundle> captureBundle) throws RemoteException {
+ @Override
+ public void process(List<CaptureBundle> captureBundle, IProcessResultImpl captureCallback)
+ throws RemoteException {
JpegParameters jpegParams = getJpegParameters(captureBundle);
try {
mJpegParameters.add(jpegParams);
- mProcessor.process(captureBundle);
+ mProcessor.process(captureBundle, captureCallback);
} catch (Exception e) {
mJpegParameters.remove(jpegParams);
throw e;
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
index c8ecfd0bdea9..a50db57e48cd 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
@@ -36,6 +36,7 @@ import android.hardware.camera2.extension.ICaptureProcessorImpl;
import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
import android.hardware.camera2.extension.IInitializeSessionCallback;
import android.hardware.camera2.extension.IPreviewExtenderImpl;
+import android.hardware.camera2.extension.IProcessResultImpl;
import android.hardware.camera2.extension.IRequestUpdateProcessorImpl;
import android.hardware.camera2.extension.ParcelImage;
import android.hardware.camera2.TotalCaptureResult;
@@ -67,6 +68,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.Executor;
public final class CameraExtensionSessionImpl extends CameraExtensionSession {
@@ -83,6 +85,10 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
private final StateCallback mCallbacks;
private final List<Size> mSupportedPreviewSizes;
private final InitializeSessionHandler mInitializeHandler;
+ private final int mSessionId;
+ private final Set<CaptureRequest.Key> mSupportedRequestKeys;
+ private final Set<CaptureResult.Key> mSupportedResultKeys;
+ private boolean mCaptureResultsSupported;
private CameraCaptureSession mCaptureSession = null;
private Surface mCameraRepeatingSurface, mClientRepeatingRequestSurface;
@@ -121,7 +127,8 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
public static CameraExtensionSessionImpl createCameraExtensionSession(
@NonNull CameraDevice cameraDevice,
@NonNull Context ctx,
- @NonNull ExtensionSessionConfiguration config)
+ @NonNull ExtensionSessionConfiguration config,
+ int sessionId)
throws CameraAccessException, RemoteException {
long clientId = CameraExtensionCharacteristics.registerClient(ctx);
if (clientId < 0) {
@@ -151,6 +158,11 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
throw new IllegalArgumentException("Unsupported dynamic range profile: " +
c.getDynamicRangeProfile());
}
+ if (c.getStreamUseCase() !=
+ CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT) {
+ throw new IllegalArgumentException("Unsupported stream use case: " +
+ c.getStreamUseCase());
+ }
}
Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
@@ -197,7 +209,10 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
repeatingRequestSurface,
burstCaptureSurface,
config.getStateCallback(),
- config.getExecutor());
+ config.getExecutor(),
+ sessionId,
+ extensionChars.getAvailableCaptureRequestKeys(config.getExtension()),
+ extensionChars.getAvailableCaptureResultKeys(config.getExtension()));
session.initialize();
@@ -212,7 +227,10 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
@Nullable Surface repeatingRequestSurface,
@Nullable Surface burstCaptureSurface,
@NonNull StateCallback callback,
- @NonNull Executor executor) {
+ @NonNull Executor executor,
+ int sessionId,
+ @NonNull Set<CaptureRequest.Key> requestKeys,
+ @Nullable Set<CaptureResult.Key> resultKeys) {
mExtensionClientId = extensionClientId;
mImageExtender = imageExtender;
mPreviewExtender = previewExtender;
@@ -227,6 +245,10 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
mHandler = new Handler(mHandlerThread.getLooper());
mInitialized = false;
mInitializeHandler = new InitializeSessionHandler();
+ mSessionId = sessionId;
+ mSupportedRequestKeys = requestKeys;
+ mSupportedResultKeys = resultKeys;
+ mCaptureResultsSupported = !resultKeys.isEmpty();
}
private void initializeRepeatingRequestPipeline() throws RemoteException {
@@ -483,7 +505,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
return captureStageList;
}
- private static List<CaptureRequest> createBurstRequest(CameraDevice cameraDevice,
+ private List<CaptureRequest> createBurstRequest(CameraDevice cameraDevice,
List<CaptureStageImpl> captureStageList, CaptureRequest clientRequest,
Surface target, int captureTemplate, Map<CaptureRequest, Integer> captureMap) {
CaptureRequest.Builder requestBuilder;
@@ -495,16 +517,13 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
return null;
}
- // This will override the extension capture stage jpeg parameters with the user set
- // jpeg quality and rotation. This will guarantee that client configured jpeg
- // parameters always have highest priority.
- Integer jpegRotation = clientRequest.get(CaptureRequest.JPEG_ORIENTATION);
- if (jpegRotation != null) {
- captureStage.parameters.set(CaptureRequest.JPEG_ORIENTATION, jpegRotation);
- }
- Byte jpegQuality = clientRequest.get(CaptureRequest.JPEG_QUALITY);
- if (jpegQuality != null) {
- captureStage.parameters.set(CaptureRequest.JPEG_QUALITY, jpegQuality);
+ // This will guarantee that client configured
+ // parameters always have the highest priority.
+ for (CaptureRequest.Key requestKey : mSupportedRequestKeys){
+ Object value = clientRequest.get(requestKey);
+ if (value != null) {
+ captureStage.parameters.set(requestKey, value);
+ }
}
requestBuilder.addTarget(target);
@@ -517,10 +536,9 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
return ret;
}
- private static CaptureRequest createRequest(CameraDevice cameraDevice,
- List<CaptureStageImpl> captureStageList,
- Surface target,
- int captureTemplate) throws CameraAccessException {
+ private CaptureRequest createRequest(CameraDevice cameraDevice,
+ List<CaptureStageImpl> captureStageList, Surface target, int captureTemplate,
+ CaptureRequest clientRequest) throws CameraAccessException {
CaptureRequest.Builder requestBuilder;
requestBuilder = cameraDevice.createCaptureRequest(captureTemplate);
if (target != null) {
@@ -528,14 +546,35 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
}
CaptureRequest ret = requestBuilder.build();
+ CameraMetadataNative nativeMeta = ret.getNativeMetadata();
for (CaptureStageImpl captureStage : captureStageList) {
if (captureStage != null) {
- CameraMetadataNative.update(ret.getNativeMetadata(), captureStage.parameters);
+ CameraMetadataNative.update(nativeMeta, captureStage.parameters);
+ }
+ }
+
+ if (clientRequest != null) {
+ // This will guarantee that client configured
+ // parameters always have the highest priority.
+ for (CaptureRequest.Key requestKey : mSupportedRequestKeys) {
+ Object value = clientRequest.get(requestKey);
+ if (value != null) {
+ nativeMeta.set(requestKey, value);
+ }
}
}
+
return ret;
}
+ private CaptureRequest createRequest(CameraDevice cameraDevice,
+ List<CaptureStageImpl> captureStageList,
+ Surface target,
+ int captureTemplate) throws CameraAccessException {
+ return createRequest(cameraDevice, captureStageList, target, captureTemplate,
+ /*clientRequest*/ null);
+ }
+
@Override
public int capture(@NonNull CaptureRequest request,
@NonNull Executor executor,
@@ -629,12 +668,17 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
}
private int setRepeatingRequest(CaptureStageImpl captureStage,
- CameraCaptureSession.CaptureCallback requestHandler)
+ CameraCaptureSession.CaptureCallback requestHandler) throws CameraAccessException {
+ return setRepeatingRequest(captureStage, requestHandler, /*clientRequest*/ null);
+ }
+
+ private int setRepeatingRequest(CaptureStageImpl captureStage,
+ CameraCaptureSession.CaptureCallback requestHandler, CaptureRequest clientRequest)
throws CameraAccessException {
ArrayList<CaptureStageImpl> captureStageList = new ArrayList<>();
captureStageList.add(captureStage);
- CaptureRequest repeatingRequest = createRequest(mCameraDevice,
- captureStageList, mCameraRepeatingSurface, CameraDevice.TEMPLATE_PREVIEW);
+ CaptureRequest repeatingRequest = createRequest(mCameraDevice, captureStageList,
+ mCameraRepeatingSurface, CameraDevice.TEMPLATE_PREVIEW, clientRequest);
return mCaptureSession.setSingleRepeatingRequest(repeatingRequest,
new CameraExtensionUtils.HandlerExecutor(mHandler), requestHandler);
}
@@ -843,6 +887,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
private ImageCallback mImageCallback = null;
private boolean mCaptureFailed = false;
+ private CaptureResultHandler mCaptureResultHandler = null;
public BurstRequestHandler(@NonNull CaptureRequest request, @NonNull Executor executor,
@NonNull ExtensionCaptureCallback callbacks,
@@ -963,20 +1008,18 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
Long timestamp = result.get(CaptureResult.SENSOR_TIMESTAMP);
if (timestamp != null) {
+ if (mCaptureResultsSupported && (mCaptureResultHandler == null)) {
+ mCaptureResultHandler = new CaptureResultHandler(mClientRequest, mExecutor,
+ mCallbacks, result.getSessionId());
+ }
if (mImageProcessor != null) {
if (mCapturePendingMap.indexOfKey(timestamp) >= 0) {
Image img = mCapturePendingMap.get(timestamp).first;
- mCaptureStageMap.put(stageId,
- new Pair<>(img,
- result));
+ mCaptureStageMap.put(stageId, new Pair<>(img, result));
checkAndFireBurstProcessing();
} else {
- mCapturePendingMap.put(timestamp,
- new Pair<>(null,
- stageId));
- mCaptureStageMap.put(stageId,
- new Pair<>(null,
- result));
+ mCapturePendingMap.put(timestamp, new Pair<>(null, stageId));
+ mCaptureStageMap.put(stageId, new Pair<>(null, result));
}
} else {
mCaptureRequestMap.clear();
@@ -986,6 +1029,18 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
() -> mCallbacks
.onCaptureProcessStarted(CameraExtensionSessionImpl.this,
mClientRequest));
+
+ if (mCaptureResultHandler != null) {
+ CameraMetadataNative captureResults = new CameraMetadataNative();
+ for (CaptureResult.Key key : mSupportedResultKeys) {
+ Object value = result.get(key);
+ if (value != null) {
+ captureResults.set(key, value);
+ }
+ }
+ mCaptureResultHandler.onCaptureCompleted(timestamp,
+ captureResults);
+ }
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -1013,7 +1068,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
List<CaptureBundle> captureList = initializeParcelable(mCaptureStageMap,
jpegOrientation, jpegQuality);
try {
- mImageProcessor.process(captureList);
+ mImageProcessor.process(captureList, mCaptureResultHandler);
} catch (RemoteException e) {
Log.e(TAG, "Failed to process multi-frame request! Extension service "
+ "does not respond!");
@@ -1228,6 +1283,43 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
}
}
+ private class CaptureResultHandler extends IProcessResultImpl.Stub {
+ private final Executor mExecutor;
+ private final ExtensionCaptureCallback mCallbacks;
+ private final CaptureRequest mClientRequest;
+ private final int mRequestId;
+
+ public CaptureResultHandler(@NonNull CaptureRequest clientRequest,
+ @NonNull Executor executor, @NonNull ExtensionCaptureCallback listener,
+ int requestId) {
+ mClientRequest = clientRequest;
+ mExecutor = executor;
+ mCallbacks = listener;
+ mRequestId = requestId;
+ }
+
+ @Override
+ public void onCaptureCompleted(long shutterTimestamp, CameraMetadataNative result) {
+ if (result == null) {
+ Log.e(TAG,"Invalid capture result!");
+ return;
+ }
+
+ result.set(CaptureResult.SENSOR_TIMESTAMP, shutterTimestamp);
+ TotalCaptureResult totalResult = new TotalCaptureResult(mCameraDevice.getId(), result,
+ mClientRequest, mRequestId, shutterTimestamp, new ArrayList<CaptureResult>(),
+ mSessionId, new PhysicalCaptureResultInfo[0]);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(
+ () -> mCallbacks.onCaptureResultAvailable(CameraExtensionSessionImpl.this,
+ mClientRequest, totalResult));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
// This handler can operate in two modes:
// 1) Using valid client callbacks, which means camera buffers will be propagated the
// registered output surfaces and clients will be notified accordingly.
@@ -1242,6 +1334,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
private OnImageAvailableListener mImageCallback = null;
private LongSparseArray<Pair<Image, TotalCaptureResult>> mPendingResultMap =
new LongSparseArray<>();
+ private CaptureResultHandler mCaptureResultHandler = null;
private boolean mRequestUpdatedNeeded = false;
@@ -1375,6 +1468,11 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
synchronized (mInterfaceLock) {
final Long timestamp = result.get(CaptureResult.SENSOR_TIMESTAMP);
if (timestamp != null) {
+ if (mCaptureResultsSupported && mClientNotificationsEnabled &&
+ (mCaptureResultHandler == null)) {
+ mCaptureResultHandler = new CaptureResultHandler(mClientRequest, mExecutor,
+ mCallbacks, result.getSessionId());
+ }
if (mPreviewProcessorType ==
IPreviewExtenderImpl.PROCESSOR_TYPE_REQUEST_UPDATE_ONLY) {
CaptureStageImpl captureStage = null;
@@ -1387,7 +1485,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
}
if (captureStage != null) {
try {
- setRepeatingRequest(captureStage, this);
+ setRepeatingRequest(captureStage, this, request);
mRequestUpdatedNeeded = true;
} catch (IllegalStateException e) {
// This is possible in case the camera device closes and the
@@ -1406,7 +1504,8 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
ParcelImage parcelImage = initializeParcelImage(
mPendingResultMap.get(timestamp).first);
try {
- mPreviewImageProcessor.process(parcelImage, result);
+ mPreviewImageProcessor.process(parcelImage, result,
+ mCaptureResultHandler);
} catch (RemoteException e) {
processStatus = false;
Log.e(TAG, "Extension service does not respond during " +
@@ -1444,6 +1543,19 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
.onCaptureProcessStarted(
CameraExtensionSessionImpl.this,
mClientRequest));
+ if ((mCaptureResultHandler != null) && (mPreviewProcessorType !=
+ IPreviewExtenderImpl.PROCESSOR_TYPE_IMAGE_PROCESSOR)) {
+ CameraMetadataNative captureResults =
+ new CameraMetadataNative();
+ for (CaptureResult.Key key : mSupportedResultKeys) {
+ Object value = result.get(key);
+ if (value != null) {
+ captureResults.set(key, value);
+ }
+ }
+ mCaptureResultHandler.onCaptureCompleted(timestamp,
+ captureResults);
+ }
} else {
mExecutor.execute(
() -> mCallbacks
@@ -1587,7 +1699,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
ParcelImage parcelImage = initializeParcelImage(img);
try {
mPreviewImageProcessor.process(parcelImage,
- mPendingResultMap.get(timestamp).second);
+ mPendingResultMap.get(timestamp).second, mCaptureResultHandler);
} catch (RemoteException e) {
processStatus = false;
Log.e(TAG, "Extension service does not respond during " +
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 0f8bdf64e132..9b67633fe72b 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -333,6 +333,7 @@ public class CameraMetadataNative implements Parcelable {
private static final int MANDATORY_STREAM_CONFIGURATIONS_MAX_RESOLUTION = 1;
private static final int MANDATORY_STREAM_CONFIGURATIONS_CONCURRENT = 2;
private static final int MANDATORY_STREAM_CONFIGURATIONS_10BIT = 3;
+ private static final int MANDATORY_STREAM_CONFIGURATIONS_USE_CASE = 4;
private static String translateLocationProviderToProcess(final String provider) {
if (provider == null) {
@@ -700,6 +701,16 @@ public class CameraMetadataNative implements Parcelable {
});
sGetCommandMap.put(
+ CameraCharacteristics.SCALER_MANDATORY_USE_CASE_STREAM_COMBINATIONS.getNativeKey(),
+ new GetCommand() {
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+ return (T) metadata.getMandatoryUseCaseStreamCombinations();
+ }
+ });
+
+ sGetCommandMap.put(
CameraCharacteristics.CONTROL_MAX_REGIONS_AE.getNativeKey(), new GetCommand() {
@Override
@SuppressWarnings("unchecked")
@@ -1413,6 +1424,9 @@ public class CameraMetadataNative implements Parcelable {
case MANDATORY_STREAM_CONFIGURATIONS_10BIT:
combs = build.getAvailableMandatory10BitStreamCombinations();
break;
+ case MANDATORY_STREAM_CONFIGURATIONS_USE_CASE:
+ combs = build.getAvailableMandatoryStreamUseCaseCombinations();
+ break;
default:
combs = build.getAvailableMandatoryStreamCombinations();
}
@@ -1446,6 +1460,10 @@ public class CameraMetadataNative implements Parcelable {
return getMandatoryStreamCombinationsHelper(MANDATORY_STREAM_CONFIGURATIONS_DEFAULT);
}
+ private MandatoryStreamCombination[] getMandatoryUseCaseStreamCombinations() {
+ return getMandatoryStreamCombinationsHelper(MANDATORY_STREAM_CONFIGURATIONS_USE_CASE);
+ }
+
private StreamConfigurationMap getStreamConfigurationMap() {
StreamConfiguration[] configurations = getBase(
CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
index 32c15da4a909..0d93c985b793 100644
--- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
+++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
@@ -26,6 +26,9 @@ import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.params.OutputConfiguration.StreamUseCase;
+import android.hardware.camera2.params.StreamConfigurationMap;
import android.hardware.camera2.utils.HashCodeHelpers;
import android.media.CamcorderProfile;
import android.util.Log;
@@ -63,6 +66,7 @@ public final class MandatoryStreamCombination {
private final boolean mIsUltraHighResolution;
private final boolean mIsMaximumSize;
private final boolean mIs10BitCapable;
+ private final int mStreamUseCase;
/**
* Create a new {@link MandatoryStreamInformation}.
@@ -141,6 +145,30 @@ public final class MandatoryStreamCombination {
public MandatoryStreamInformation(@NonNull List<Size> availableSizes, @Format int format,
boolean isMaximumSize, boolean isInput, boolean isUltraHighResolution,
boolean is10BitCapable) {
+ this(availableSizes, format, isMaximumSize, isInput, isUltraHighResolution,
+ is10BitCapable, CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT);
+ }
+
+ /**
+ * Create a new {@link MandatoryStreamInformation}.
+ *
+ * @param availableSizes List of possible stream sizes.
+ * @param format Image format.
+ * @param isMaximumSize Whether this is a maximum size stream.
+ * @param isInput Flag indicating whether this stream is input.
+ * @param isUltraHighResolution Flag indicating whether this is a ultra-high resolution
+ * stream.
+ * @param is10BitCapable Flag indicating whether this stream is able to support 10-bit
+ * @param streamUseCase The stream use case.
+ *
+ * @throws IllegalArgumentException
+ * if sizes is empty or if the format was not user-defined in
+ * ImageFormat/PixelFormat.
+ * @hide
+ */
+ public MandatoryStreamInformation(@NonNull List<Size> availableSizes, @Format int format,
+ boolean isMaximumSize, boolean isInput, boolean isUltraHighResolution,
+ boolean is10BitCapable, @StreamUseCase int streamUseCase) {
if (availableSizes.isEmpty()) {
throw new IllegalArgumentException("No available sizes");
}
@@ -150,6 +178,7 @@ public final class MandatoryStreamCombination {
mIsInput = isInput;
mIsUltraHighResolution = isUltraHighResolution;
mIs10BitCapable = is10BitCapable;
+ mStreamUseCase = streamUseCase;
}
/**
@@ -272,6 +301,20 @@ public final class MandatoryStreamCombination {
}
/**
+ * Retrieve the mandatory stream use case.
+ *
+ * <p>If this {@link MandatoryStreamInformation} is part of a mandatory stream
+ * combination for stream use cases, the return value will be a non-DEFAULT value.
+ * For {@link MandatoryStreamInformation} belonging to other mandatory stream
+ * combinations, the return value will be DEFAULT. </p>
+ *
+ * @return the integer stream use case.
+ */
+ public @StreamUseCase int getStreamUseCase() {
+ return mStreamUseCase;
+ }
+
+ /**
* Check if this {@link MandatoryStreamInformation} is equal to another
* {@link MandatoryStreamInformation}.
*
@@ -292,6 +335,7 @@ public final class MandatoryStreamCombination {
final MandatoryStreamInformation other = (MandatoryStreamInformation) obj;
if ((mFormat != other.mFormat) || (mIsInput != other.mIsInput) ||
(mIsUltraHighResolution != other.mIsUltraHighResolution) ||
+ (mStreamUseCase != other.mStreamUseCase) ||
(mAvailableSizes.size() != other.mAvailableSizes.size())) {
return false;
}
@@ -308,7 +352,8 @@ public final class MandatoryStreamCombination {
@Override
public int hashCode() {
return HashCodeHelpers.hashCode(mFormat, Boolean.hashCode(mIsInput),
- Boolean.hashCode(mIsUltraHighResolution), mAvailableSizes.hashCode());
+ Boolean.hashCode(mIsUltraHighResolution), mAvailableSizes.hashCode(),
+ mStreamUseCase);
}
}
@@ -316,6 +361,21 @@ public final class MandatoryStreamCombination {
private final boolean mIsReprocessable;
private final ArrayList<MandatoryStreamInformation> mStreamsInformation =
new ArrayList<MandatoryStreamInformation>();
+
+ /**
+ * Short hand for stream use cases
+ */
+ private static final int STREAM_USE_CASE_PREVIEW =
+ CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW;
+ private static final int STREAM_USE_CASE_STILL_CAPTURE =
+ CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_STILL_CAPTURE;
+ private static final int STREAM_USE_CASE_RECORD =
+ CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_RECORD;
+ private static final int STREAM_USE_CASE_PREVIEW_VIDEO_STILL =
+ CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL;
+ private static final int STREAM_USE_CASE_VIDEO_CALL =
+ CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL;
+
/**
* Create a new {@link MandatoryStreamCombination}.
*
@@ -411,15 +471,15 @@ public final class MandatoryStreamCombination {
private static final class StreamTemplate {
public int mFormat;
public SizeThreshold mSizeThreshold;
- public boolean mIsInput;
+ public int mStreamUseCase;
public StreamTemplate(int format, SizeThreshold sizeThreshold) {
- this(format, sizeThreshold, /*isInput*/false);
+ this(format, sizeThreshold, CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT);
}
public StreamTemplate(@Format int format, @NonNull SizeThreshold sizeThreshold,
- boolean isInput) {
+ @StreamUseCase int streamUseCase) {
mFormat = format;
mSizeThreshold = sizeThreshold;
- mIsInput = isInput;
+ mStreamUseCase = streamUseCase;
}
}
@@ -1034,6 +1094,174 @@ public final class MandatoryStreamCombination {
"High-resolution recording with video snapshot"),
};
+ private static StreamCombinationTemplate sStreamUseCaseCombinations[] = {
+ // Single stream combinations
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW) },
+ "Simple preview"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW) },
+ "Simple in-application image processing"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD,
+ STREAM_USE_CASE_RECORD) },
+ "Simple video recording"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD,
+ STREAM_USE_CASE_RECORD) },
+ "Simple in-application video processing"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM,
+ STREAM_USE_CASE_STILL_CAPTURE) },
+ "Simple JPEG still capture"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM,
+ STREAM_USE_CASE_STILL_CAPTURE) },
+ "Simple YUV still capture"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p,
+ STREAM_USE_CASE_PREVIEW_VIDEO_STILL) },
+ "Multi-purpose stream for preview, video and still capture"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p,
+ STREAM_USE_CASE_PREVIEW_VIDEO_STILL) },
+ "Multi-purpose YUV stream for preview, video and still capture"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p,
+ STREAM_USE_CASE_VIDEO_CALL) },
+ "Simple video call"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p,
+ STREAM_USE_CASE_VIDEO_CALL) },
+ "Simple YUV video call"),
+
+ // 2-stream combinations
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM,
+ STREAM_USE_CASE_STILL_CAPTURE)},
+ "Preview with JPEG still image capture"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM,
+ STREAM_USE_CASE_STILL_CAPTURE)},
+ "Preview with YUV still image capture"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD,
+ STREAM_USE_CASE_RECORD)},
+ "Preview with video recording"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD,
+ STREAM_USE_CASE_RECORD)},
+ "Preview with in-application video processing"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW)},
+ "Preview with in-application image processing"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p,
+ STREAM_USE_CASE_VIDEO_CALL)},
+ "Preview with video call"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p,
+ STREAM_USE_CASE_VIDEO_CALL)},
+ "Preview with YUV video call"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p,
+ STREAM_USE_CASE_PREVIEW_VIDEO_STILL),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM,
+ STREAM_USE_CASE_STILL_CAPTURE)},
+ "Multi-purpose stream with JPEG still capture"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p,
+ STREAM_USE_CASE_PREVIEW_VIDEO_STILL),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM,
+ STREAM_USE_CASE_STILL_CAPTURE)},
+ "Multi-purpose stream with YUV still capture"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p,
+ STREAM_USE_CASE_PREVIEW_VIDEO_STILL),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM,
+ STREAM_USE_CASE_STILL_CAPTURE)},
+ "Multi-purpose YUV stream with JPEG still capture"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p,
+ STREAM_USE_CASE_PREVIEW_VIDEO_STILL),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM,
+ STREAM_USE_CASE_STILL_CAPTURE)},
+ "Multi-purpose YUV stream with YUV still capture"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_STILL_CAPTURE),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM,
+ STREAM_USE_CASE_STILL_CAPTURE)},
+ "YUV and JPEG concurrent still image capture (for testing)"),
+
+ // 3-stream combinations
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD,
+ STREAM_USE_CASE_RECORD),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.RECORD,
+ STREAM_USE_CASE_STILL_CAPTURE)},
+ "Preview, video record and JPEG video snapshot"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD,
+ STREAM_USE_CASE_RECORD),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD,
+ STREAM_USE_CASE_STILL_CAPTURE)},
+ "Preview, video record and YUV video snapshot"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD,
+ STREAM_USE_CASE_RECORD),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.RECORD,
+ STREAM_USE_CASE_STILL_CAPTURE)},
+ "Preview, in-application video processing and JPEG video snapshot"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD,
+ STREAM_USE_CASE_RECORD),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD,
+ STREAM_USE_CASE_STILL_CAPTURE)},
+ "Preview, in-application video processing and YUV video snapshot"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM,
+ STREAM_USE_CASE_STILL_CAPTURE)},
+ "Preview, in-application image processing, and JPEG still image capture"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM,
+ STREAM_USE_CASE_STILL_CAPTURE)},
+ "Preview, in-application image processing, and YUV still image capture"),
+ };
+
/**
* Helper builder class to generate a list of available mandatory stream combinations.
* @hide
@@ -1153,6 +1381,76 @@ public final class MandatoryStreamCombination {
}
/**
+ * Retrieve a list of all available mandatory stream combinations with stream use cases.
+ * when the camera device has {@link
+ * CameraMetdata.REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE} capability.
+ *
+ * @return a non-modifiable list of supported mandatory stream combinations with stream
+ * use cases. Null in case the device doesn't have {@link
+ * CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE}
+ * capability.
+ */
+ public @NonNull List<MandatoryStreamCombination>
+ getAvailableMandatoryStreamUseCaseCombinations() {
+ if (!isCapabilitySupported(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE)) {
+ return null;
+ }
+
+ HashMap<Pair<SizeThreshold, Integer>, List<Size>> availableSizes =
+ enumerateAvailableSizes();
+ if (availableSizes == null) {
+ Log.e(TAG, "Available size enumeration failed!");
+ return null;
+ }
+
+ ArrayList<MandatoryStreamCombination> availableStreamCombinations = new ArrayList<>();
+ availableStreamCombinations.ensureCapacity(sStreamUseCaseCombinations.length);
+ for (StreamCombinationTemplate combTemplate : sStreamUseCaseCombinations) {
+ ArrayList<MandatoryStreamInformation> streamsInfo =
+ new ArrayList<MandatoryStreamInformation>();
+ streamsInfo.ensureCapacity(combTemplate.mStreamTemplates.length);
+
+ for (StreamTemplate template : combTemplate.mStreamTemplates) {
+ List<Size> sizes = null;
+ Pair<SizeThreshold, Integer> pair;
+ pair = new Pair<SizeThreshold, Integer>(template.mSizeThreshold,
+ new Integer(template.mFormat));
+ sizes = availableSizes.get(pair);
+
+ MandatoryStreamInformation streamInfo;
+ boolean isMaximumSize =
+ (template.mSizeThreshold == SizeThreshold.MAXIMUM);
+ try {
+ streamInfo = new MandatoryStreamInformation(sizes, template.mFormat,
+ isMaximumSize, /*isInput*/false, /*isUltraHighResolution*/false,
+ /*is10BitCapable*/ false, template.mStreamUseCase);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "No available sizes found for format: " + template.mFormat +
+ " size threshold: " + template.mSizeThreshold + " combination: " +
+ combTemplate.mDescription);
+ return null;
+ }
+ streamsInfo.add(streamInfo);
+ }
+
+ MandatoryStreamCombination streamCombination;
+ try {
+ streamCombination = new MandatoryStreamCombination(streamsInfo,
+ combTemplate.mDescription, /*isReprocessable*/ false);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "No stream information for mandatory combination: "
+ + combTemplate.mDescription);
+ return null;
+ }
+
+ availableStreamCombinations.add(streamCombination);
+ }
+
+ return Collections.unmodifiableList(availableStreamCombinations);
+ }
+
+ /**
* Retrieve a list of all available mandatory concurrent stream combinations.
* This method should only be called for devices which are listed in combinations returned
* by CameraManager.getConcurrentCameraIds.
@@ -1632,6 +1930,8 @@ public final class MandatoryStreamCombination {
Size recordingMaxSize = new Size(0, 0);
Size previewMaxSize = new Size(0, 0);
Size vgaSize = new Size(640, 480);
+ Size s720pSize = new Size(1280, 720);
+ Size s1440pSize = new Size(1920, 1440);
// For external camera, or hidden physical camera, CamcorderProfile may not be
// available, so get maximum recording size using stream configuration map.
if (isExternalCamera() || mIsHiddenPhysicalCamera) {
@@ -1682,6 +1982,12 @@ public final class MandatoryStreamCombination {
pair = new Pair<SizeThreshold, Integer>(SizeThreshold.MAXIMUM, intFormat);
availableSizes.put(pair, Arrays.asList(sizes));
+
+ pair = new Pair<SizeThreshold, Integer>(SizeThreshold.s720p, intFormat);
+ availableSizes.put(pair, getSizesWithinBound(sizes, s720pSize));
+
+ pair = new Pair<SizeThreshold, Integer>(SizeThreshold.s1440p, intFormat);
+ availableSizes.put(pair, getSizesWithinBound(sizes, s1440pSize));
}
return availableSizes;
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index f2b881ba7758..d8295c962154 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -159,6 +159,17 @@ public final class OutputConfiguration implements Parcelable {
CameraMetadata.SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION})
public @interface SensorPixelMode {};
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"STREAM_USE_CASE_"}, value =
+ {CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT,
+ CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW,
+ CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_STILL_CAPTURE,
+ CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_RECORD,
+ CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL,
+ CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL})
+ public @interface StreamUseCase {};
+
/**
* Create a new {@link OutputConfiguration} instance with a {@link Surface}.
*
@@ -355,6 +366,7 @@ public final class OutputConfiguration implements Parcelable {
mIsMultiResolution = false;
mSensorPixelModesUsed = new ArrayList<Integer>();
mDynamicRangeProfile = DynamicRangeProfiles.STANDARD;
+ mStreamUseCase = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT;
}
/**
@@ -453,6 +465,7 @@ public final class OutputConfiguration implements Parcelable {
mIsMultiResolution = false;
mSensorPixelModesUsed = new ArrayList<Integer>();
mDynamicRangeProfile = DynamicRangeProfiles.STANDARD;
+ mStreamUseCase = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT;
}
/**
@@ -730,6 +743,73 @@ public final class OutputConfiguration implements Parcelable {
}
/**
+ * Set stream use case for this OutputConfiguration
+ *
+ * <p>Stream use case is used to describe the purpose of the stream, whether it's for live
+ * preview, still image capture, video recording, or their combinations. This flag is useful
+ * for scenarios where the immediate consumer target isn't sufficient to indicate the stream's
+ * usage.</p>
+ *
+ * <p>The main difference beteween stream use case and capture intent is that the former
+ * enables the camera device to optimize camera hardware and software pipelines based on user
+ * scenarios for each stream, whereas the latter is mainly a hint to camera to decide
+ * optimal 3A strategy that's applicable to the whole session. The camera device carries out
+ * configurations such as selecting tuning parameters, choosing camera sensor mode, and
+ * constructing image processing pipeline based on the streams's use cases. Capture intents are
+ * then used to fine tune 3A behaviors such as adjusting AE/AF convergence speed, and capture
+ * intents may change during the lifetime of a session. For example, for a session with a
+ * PREVIEW_VIDEO_STILL use case stream and a STILL_CAPTURE use case stream, the capture intents
+ * may be PREVIEW with fast 3A convergence speed and flash metering with automatic control for
+ * live preview, STILL_CAPTURE with best 3A parameters for still photo capture, or VIDEO_RECORD
+ * with slower 3A convergence speed for better video playback experience.</p>
+ *
+ * <p>The supported stream use cases supported by a camera device can be queried by
+ * {@link android.hardware.camera2.CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES}.</p>
+ *
+ * <p>The mandatory stream combinations involving stream use cases can be found at {@link
+ * android.hardware.camera2.CameraDevice#createCaptureSession}, as well as queried via
+ * {@link android.hardware.camera2.params.MandatoryStreamCombination}. The application is
+ * strongly recommended to select one of the guaranteed stream combinations where all streams'
+ * use cases are set to non-DEFAULT values. If the application chooses a stream combination
+ * not in the mandatory list, the camera device may ignore some use case flags due to
+ * hardware constraints or implementation details.</p>
+ *
+ * <p>This function must be called before {@link CameraDevice#createCaptureSession} or {@link
+ * CameraDevice#createCaptureSessionByOutputConfigurations}. Calling this function after
+ * {@link CameraDevice#createCaptureSession} or
+ * {@link CameraDevice#createCaptureSessionByOutputConfigurations} has no effect to the camera
+ * session.</p>
+ *
+ * @param streamUseCase The stream use case to be set.
+ *
+ * @throws IllegalArgumentException If the streamUseCase isn't within the range of valid
+ * values.
+ */
+ public void setStreamUseCase(@StreamUseCase int streamUseCase) {
+ // Verify that the value is in range
+ int maxUseCaseValue = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL;
+ if (streamUseCase > maxUseCaseValue &&
+ streamUseCase < CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VENDOR_START) {
+ throw new IllegalArgumentException("Not a valid stream use case value " +
+ streamUseCase);
+ }
+
+ mStreamUseCase = streamUseCase;
+ }
+
+ /**
+ * Get the current stream use case
+ *
+ * <p>If no {@link #setStreamUseCase} is called first, this function returns
+ * {@link CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT DEFAULT}.</p>
+ *
+ * @return the currently set stream use case
+ */
+ public int getStreamUseCase() {
+ return mStreamUseCase;
+ }
+
+ /**
* Create a new {@link OutputConfiguration} instance with another {@link OutputConfiguration}
* instance.
*
@@ -756,6 +836,7 @@ public final class OutputConfiguration implements Parcelable {
this.mIsMultiResolution = other.mIsMultiResolution;
this.mSensorPixelModesUsed = other.mSensorPixelModesUsed;
this.mDynamicRangeProfile = other.mDynamicRangeProfile;
+ this.mStreamUseCase = other.mStreamUseCase;
}
/**
@@ -774,6 +855,8 @@ public final class OutputConfiguration implements Parcelable {
String physicalCameraId = source.readString();
boolean isMultiResolutionOutput = source.readInt() == 1;
int[] sensorPixelModesUsed = source.createIntArray();
+ int streamUseCase = source.readInt();
+
checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
int dynamicRangeProfile = source.readInt();
DynamicRangeProfiles.checkProfileValue(dynamicRangeProfile);
@@ -801,6 +884,7 @@ public final class OutputConfiguration implements Parcelable {
mIsMultiResolution = isMultiResolutionOutput;
mSensorPixelModesUsed = convertIntArrayToIntegerList(sensorPixelModesUsed);
mDynamicRangeProfile = dynamicRangeProfile;
+ mStreamUseCase = streamUseCase;
}
/**
@@ -917,6 +1001,7 @@ public final class OutputConfiguration implements Parcelable {
// writeList doesn't seem to work well with Integer list.
dest.writeIntArray(convertIntegerToIntList(mSensorPixelModesUsed));
dest.writeInt(mDynamicRangeProfile);
+ dest.writeInt(mStreamUseCase);
}
/**
@@ -947,7 +1032,8 @@ public final class OutputConfiguration implements Parcelable {
mConfiguredDataspace != other.mConfiguredDataspace ||
mConfiguredGenerationId != other.mConfiguredGenerationId ||
!Objects.equals(mPhysicalCameraId, other.mPhysicalCameraId) ||
- mIsMultiResolution != other.mIsMultiResolution)
+ mIsMultiResolution != other.mIsMultiResolution ||
+ mStreamUseCase != other.mStreamUseCase)
return false;
if (mSensorPixelModesUsed.size() != other.mSensorPixelModesUsed.size()) {
return false;
@@ -985,7 +1071,7 @@ public final class OutputConfiguration implements Parcelable {
mSurfaceGroupId, mSurfaceType, mIsShared ? 1 : 0,
mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode(),
- mDynamicRangeProfile);
+ mDynamicRangeProfile, mStreamUseCase);
}
return HashCodeHelpers.hashCode(
@@ -994,7 +1080,7 @@ public final class OutputConfiguration implements Parcelable {
mConfiguredDataspace, mSurfaceGroupId, mIsShared ? 1 : 0,
mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode(),
- mDynamicRangeProfile);
+ mDynamicRangeProfile, mStreamUseCase);
}
private static final String TAG = "OutputConfiguration";
@@ -1028,4 +1114,6 @@ public final class OutputConfiguration implements Parcelable {
private ArrayList<Integer> mSensorPixelModesUsed;
// Dynamic range profile
private int mDynamicRangeProfile;
+ // Stream use case
+ private int mStreamUseCase;
}
diff --git a/core/java/android/hardware/lights/Light.java b/core/java/android/hardware/lights/Light.java
index dbe7a418b0fc..c3113799ad45 100644
--- a/core/java/android/hardware/lights/Light.java
+++ b/core/java/android/hardware/lights/Light.java
@@ -38,6 +38,12 @@ public final class Light implements Parcelable {
/** Type for lights that indicate microphone usage */
public static final int LIGHT_TYPE_MICROPHONE = 8;
+ /** Type for lights that indicate camera usage
+ *
+ * @hide
+ */
+ public static final int LIGHT_TYPE_CAMERA = 9;
+
// These enum values start from 10001 to avoid collision with expanding of HAL light types.
/**
* Type for lights that indicate a monochrome color LED light.
diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java
index 83fc7276eabc..c3bb381bb740 100644
--- a/core/java/android/inputmethodservice/NavigationBarController.java
+++ b/core/java/android/inputmethodservice/NavigationBarController.java
@@ -219,7 +219,7 @@ final class NavigationBarController {
// TODO(b/213337792): Set NAVIGATION_HINT_IME_SHOWN only when necessary.
final int hints = StatusBarManager.NAVIGATION_HINT_BACK_ALT
| (mShouldShowImeSwitcherWhenImeIsShown
- ? StatusBarManager.NAVIGATION_HINT_IME_SHOWN
+ ? StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN
: 0);
navigationBarView.setNavigationIconHints(hints);
}
@@ -470,7 +470,7 @@ final class NavigationBarController {
}
final int hints = StatusBarManager.NAVIGATION_HINT_BACK_ALT
| (shouldShowImeSwitcherWhenImeIsShown
- ? StatusBarManager.NAVIGATION_HINT_IME_SHOWN : 0);
+ ? StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN : 0);
navigationBarView.setNavigationIconHints(hints);
}
diff --git a/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java b/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java
index 42847784dd2b..a2d71054c65d 100644
--- a/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java
+++ b/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java
@@ -270,7 +270,7 @@ public final class NavigationBarView extends FrameLayout {
// Update IME button visibility, a11y and rotate button always overrides the appearance
final boolean imeSwitcherVisible =
- (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0;
+ (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN) != 0;
getImeSwitchButton().setVisibility(imeSwitcherVisible ? View.VISIBLE : View.INVISIBLE);
getBackButton().setVisibility(View.VISIBLE);
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index 6284f56c8258..dc241066a60a 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -72,9 +72,9 @@ interface INetworkPolicyManager {
SubscriptionPlan getSubscriptionPlan(in NetworkTemplate template);
void notifyStatsProviderWarningOrLimitReached();
SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage);
- void setSubscriptionPlans(int subId, in SubscriptionPlan[] plans, String callingPackage);
+ void setSubscriptionPlans(int subId, in SubscriptionPlan[] plans, long expirationDurationMillis, String callingPackage);
String getSubscriptionPlansOwner(int subId);
- void setSubscriptionOverride(int subId, int overrideMask, int overrideValue, in int[] networkTypes, long timeoutMillis, String callingPackage);
+ void setSubscriptionOverride(int subId, int overrideMask, int overrideValue, in int[] networkTypes, long expirationDurationMillis, String callingPackage);
void factoryReset(String subscriber);
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 9122adfece53..d8f098eb880b 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -482,8 +482,8 @@ public class NetworkPolicyManager {
* @param networkTypes the network types this override applies to. If no
* network types are specified, override values will be ignored.
* {@see TelephonyManager#getAllNetworkTypes()}
- * @param timeoutMillis the timeout after which the requested override will
- * be automatically cleared, or {@code 0} to leave in the
+ * @param expirationDurationMillis the duration after which the requested override
+ * will be automatically cleared, or {@code 0} to leave in the
* requested state until explicitly cleared, or the next reboot,
* whichever happens first
* @param callingPackage the name of the package making the call.
@@ -491,11 +491,11 @@ public class NetworkPolicyManager {
*/
public void setSubscriptionOverride(int subId, @SubscriptionOverrideMask int overrideMask,
@SubscriptionOverrideMask int overrideValue,
- @NonNull @Annotation.NetworkType int[] networkTypes, long timeoutMillis,
+ @NonNull @Annotation.NetworkType int[] networkTypes, long expirationDurationMillis,
@NonNull String callingPackage) {
try {
mService.setSubscriptionOverride(subId, overrideMask, overrideValue, networkTypes,
- timeoutMillis, callingPackage);
+ expirationDurationMillis, callingPackage);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -506,13 +506,16 @@ public class NetworkPolicyManager {
*
* @param subId the subscriber this relationship applies to.
* @param plans the list of plans.
+ * @param expirationDurationMillis the duration after which the subscription plans
+ * will be automatically cleared, or {@code 0} to leave the plans until
+ * explicitly cleared, or the next reboot, whichever happens first
* @param callingPackage the name of the package making the call
* @hide
*/
public void setSubscriptionPlans(int subId, @NonNull SubscriptionPlan[] plans,
- @NonNull String callingPackage) {
+ long expirationDurationMillis, @NonNull String callingPackage) {
try {
- mService.setSubscriptionPlans(subId, plans, callingPackage);
+ mService.setSubscriptionPlans(subId, plans, expirationDurationMillis, callingPackage);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index c4fe1a44d161..8538b7d6be95 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -17322,6 +17322,12 @@ public final class Settings {
"clockwork_long_press_to_assistant_enabled";
/*
+ * Whether the device has Cooldown Mode enabled.
+ * @hide
+ */
+ public static final String COOLDOWN_MODE_ON = "cooldown_mode_on";
+
+ /*
* Whether the device has Wet Mode/ Touch Lock Mode enabled.
* @hide
*/
diff --git a/core/java/android/service/games/IGameSessionController.aidl b/core/java/android/service/games/IGameSessionController.aidl
index 84311dc0aedf..fd994044775f 100644
--- a/core/java/android/service/games/IGameSessionController.aidl
+++ b/core/java/android/service/games/IGameSessionController.aidl
@@ -24,6 +24,6 @@ import com.android.internal.infra.AndroidFuture;
*/
oneway interface IGameSessionController {
void takeScreenshot(int taskId, in AndroidFuture gameScreenshotResultFuture);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)")
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_ACTIVITY)")
void restartGame(in int taskId);
}
diff --git a/core/java/android/view/OnBackInvokedDispatcher.java b/core/java/android/view/OnBackInvokedDispatcher.java
index f3ca531f2a42..5c4ee89bfbd3 100644
--- a/core/java/android/view/OnBackInvokedDispatcher.java
+++ b/core/java/android/view/OnBackInvokedDispatcher.java
@@ -17,8 +17,13 @@
package android.view;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SuppressLint;
+import android.annotation.TestApi;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
import android.os.Build;
import java.lang.annotation.Retention;
@@ -32,13 +37,29 @@ import java.lang.annotation.RetentionPolicy;
* Attribute updates are proactively pushed to the window manager if they change the dispatch
* target (a.k.a. the callback to be invoked next), or its behavior.
*/
-public abstract class OnBackInvokedDispatcher {
+public interface OnBackInvokedDispatcher {
+ /**
+ * Enables dispatching the "back" action via {@link android.view.OnBackInvokedDispatcher}.
+ *
+ * When enabled, the following APIs are no longer invoked:
+ * <ul>
+ * <li> {@link android.app.Activity#onBackPressed}
+ * <li> {@link android.app.Dialog#onBackPressed}
+ * <li> {@link android.view.KeyEvent#KEYCODE_BACK} is no longer dispatched.
+ * </ul>
+ *
+ * @hide
+ */
+ @TestApi
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ long DISPATCH_BACK_INVOCATION_AHEAD_OF_TIME = 195946584L;
/** @hide */
- public static final String TAG = "OnBackInvokedDispatcher";
+ String TAG = "OnBackInvokedDispatcher";
/** @hide */
- public static final boolean DEBUG = Build.isDebuggable();
+ boolean DEBUG = Build.isDebuggable();
/** @hide */
@IntDef({
@@ -46,18 +67,26 @@ public abstract class OnBackInvokedDispatcher {
PRIORITY_OVERLAY,
})
@Retention(RetentionPolicy.SOURCE)
- public @interface Priority{}
+ @interface Priority{}
/**
* Priority level of {@link OnBackInvokedCallback}s for overlays such as menus and
* navigation drawers that should receive back dispatch before non-overlays.
*/
- public static final int PRIORITY_OVERLAY = 1000000;
+ int PRIORITY_OVERLAY = 1000000;
/**
* Default priority level of {@link OnBackInvokedCallback}s.
*/
- public static final int PRIORITY_DEFAULT = 0;
+ int PRIORITY_DEFAULT = 0;
+
+ /**
+ * Priority level of {@link OnBackInvokedCallback}s registered by the system.
+ *
+ * System back animation will play when the callback to receive dispatch has this priority.
+ * @hide
+ */
+ int PRIORITY_SYSTEM = -1;
/**
* Registers a {@link OnBackInvokedCallback}.
@@ -69,10 +98,11 @@ public abstract class OnBackInvokedDispatcher {
* registered, the existing instance (no matter its priority) will be
* unregistered and registered again.
* @param priority The priority of the callback.
+ * @throws {@link IllegalArgumentException} if the priority is negative.
*/
@SuppressLint("SamShouldBeLast")
- public abstract void registerOnBackInvokedCallback(
- @NonNull OnBackInvokedCallback callback, @Priority int priority);
+ void registerOnBackInvokedCallback(
+ @NonNull OnBackInvokedCallback callback, @Priority @IntRange(from = 0) int priority);
/**
* Unregisters a {@link OnBackInvokedCallback}.
@@ -80,5 +110,20 @@ public abstract class OnBackInvokedDispatcher {
* @param callback The callback to be unregistered. Does nothing if the callback has not been
* registered.
*/
- public abstract void unregisterOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback);
+ void unregisterOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback);
+
+ /**
+ * Returns the most prioritized callback to receive back dispatch next.
+ * @hide
+ */
+ @Nullable
+ default OnBackInvokedCallback getTopCallback() {
+ return null;
+ }
+
+ /**
+ * Registers a {@link OnBackInvokedCallback} with system priority.
+ * @hide
+ */
+ default void registerSystemOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback) { }
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3d8653554efd..ee4a934482db 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -317,6 +317,11 @@ public final class ViewRootImpl implements ViewParent,
*/
private final WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher =
new WindowOnBackInvokedDispatcher();
+ /**
+ * Compatibility {@link OnBackInvokedCallback} that dispatches KEYCODE_BACK events
+ * to view root for apps using legacy back behavior.
+ */
+ private OnBackInvokedCallback mCompatOnBackInvokedCallback;
/**
* Callback for notifying about global configuration changes.
@@ -1190,6 +1195,14 @@ public final class ViewRootImpl implements ViewParent,
mTmpFrames.displayFrame, mTempRect2, mTmpFrames.frame);
setFrame(mTmpFrames.frame);
registerBackCallbackOnWindow();
+ if (WindowOnBackInvokedDispatcher.shouldUseLegacyBack()) {
+ // For apps requesting legacy back behavior, we add a compat callback that
+ // dispatches {@link KeyEvent#KEYCODE_BACK} to their root views.
+ // This way from system point of view, these apps are providing custom
+ // {@link OnBackInvokedCallback}s, and will not play system back animations
+ // for them.
+ registerCompatOnBackInvokedCallback();
+ }
if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow);
if (res < WindowManagerGlobal.ADD_OKAY) {
mAttachInfo.mRootView = null;
@@ -5931,7 +5944,7 @@ public final class ViewRootImpl implements ViewParent,
}
}
- private boolean isBack(InputEvent event) {
+ boolean isBack(InputEvent event) {
if (event instanceof KeyEvent) {
return ((KeyEvent) event).getKeyCode() == KeyEvent.KEYCODE_BACK;
} else {
@@ -6471,6 +6484,19 @@ public final class ViewRootImpl implements ViewParent,
return FINISH_NOT_HANDLED;
}
+ if (isBack(event)
+ && mContext != null
+ && !WindowOnBackInvokedDispatcher.shouldUseLegacyBack()) {
+ // Invoke the appropriate {@link OnBackInvokedCallback} if the new back
+ // navigation should be used, and the key event is not handled by anything else.
+ OnBackInvokedCallback topCallback =
+ getOnBackInvokedDispatcher().getTopCallback();
+ if (topCallback != null) {
+ topCallback.onBackInvoked();
+ return FINISH_HANDLED;
+ }
+ }
+
// This dispatch is for windows that don't have a Window.Callback. Otherwise,
// the Window.Callback usually will have already called this (see
// DecorView.superDispatchKeyEvent) leaving this call a no-op.
@@ -8414,6 +8440,7 @@ public final class ViewRootImpl implements ViewParent,
mAdded = false;
}
+ unregisterCompatOnBackInvokedCallback();
mOnBackInvokedDispatcher.detachFromWindow();
WindowManagerGlobal.getInstance().doRemoveView(this);
}
@@ -10782,6 +10809,38 @@ public final class ViewRootImpl implements ViewParent,
mOnBackInvokedDispatcher.attachToWindow(mWindowSession, mWindow);
}
+ private void sendBackKeyEvent(int action) {
+ long when = SystemClock.uptimeMillis();
+ final KeyEvent ev = new KeyEvent(when, when, action,
+ KeyEvent.KEYCODE_BACK, 0 /* repeat */, 0 /* metaState */,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
+ KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
+ InputDevice.SOURCE_KEYBOARD);
+
+ ev.setDisplayId(mContext.getDisplay().getDisplayId());
+ if (mView != null) {
+ mView.dispatchKeyEvent(ev);
+ }
+ }
+
+ private void registerCompatOnBackInvokedCallback() {
+ mCompatOnBackInvokedCallback = new OnBackInvokedCallback() {
+ @Override
+ public void onBackInvoked() {
+ sendBackKeyEvent(KeyEvent.ACTION_DOWN);
+ sendBackKeyEvent(KeyEvent.ACTION_UP);
+ }
+ };
+ mOnBackInvokedDispatcher.registerOnBackInvokedCallback(
+ mCompatOnBackInvokedCallback, OnBackInvokedDispatcher.PRIORITY_DEFAULT);
+ }
+
+ private void unregisterCompatOnBackInvokedCallback() {
+ if (mCompatOnBackInvokedCallback != null) {
+ mOnBackInvokedDispatcher.unregisterOnBackInvokedCallback(mCompatOnBackInvokedCallback);
+ }
+ }
+
@Override
public void setTouchableRegion(Region r) {
if (r != null) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 0fe2ed51beb6..9efa583a894a 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -10821,8 +10821,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
&& mSavedMarqueeModeLayout.getLineWidth(0) > width));
}
+ /**
+ * @hide
+ */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- private void startMarquee() {
+ protected void startMarquee() {
// Do not ellipsize EditText
if (getKeyListener() != null) return;
@@ -10848,7 +10851,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
- private void stopMarquee() {
+ /**
+ * @hide
+ */
+ protected void stopMarquee() {
if (mMarquee != null && !mMarquee.isStopped()) {
mMarquee.stop();
}
diff --git a/core/java/android/window/ProxyOnBackInvokedDispatcher.java b/core/java/android/window/ProxyOnBackInvokedDispatcher.java
index 509bbd4de389..4de977a91d05 100644
--- a/core/java/android/window/ProxyOnBackInvokedDispatcher.java
+++ b/core/java/android/window/ProxyOnBackInvokedDispatcher.java
@@ -42,7 +42,7 @@ import java.util.List;
*
* @hide
*/
-public class ProxyOnBackInvokedDispatcher extends OnBackInvokedDispatcher {
+public class ProxyOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
/**
* List of pair representing an {@link OnBackInvokedCallback} and its associated priority.
@@ -60,14 +60,16 @@ public class ProxyOnBackInvokedDispatcher extends OnBackInvokedDispatcher {
Log.v(TAG, String.format("Pending register %s. Actual=%s", callback,
mActualDispatcherOwner));
}
- synchronized (mLock) {
- mCallbacks.add(Pair.create(callback, priority));
- if (mActualDispatcherOwner != null) {
- mActualDispatcherOwner.getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
- callback, priority);
- }
-
+ if (priority < 0) {
+ throw new IllegalArgumentException("Application registered OnBackInvokedCallback "
+ + "cannot have negative priority. Priority: " + priority);
}
+ registerOnBackInvokedCallbackUnchecked(callback, priority);
+ }
+
+ @Override
+ public void registerSystemOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback) {
+ registerOnBackInvokedCallbackUnchecked(callback, PRIORITY_SYSTEM);
}
@Override
@@ -82,6 +84,17 @@ public class ProxyOnBackInvokedDispatcher extends OnBackInvokedDispatcher {
}
}
+ private void registerOnBackInvokedCallbackUnchecked(
+ @NonNull OnBackInvokedCallback callback, int priority) {
+ synchronized (mLock) {
+ mCallbacks.add(Pair.create(callback, priority));
+ if (mActualDispatcherOwner != null) {
+ mActualDispatcherOwner.getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
+ callback, priority);
+ }
+ }
+ }
+
/**
* Transfers all the pending callbacks to the provided dispatcher.
* <p>
@@ -99,8 +112,12 @@ public class ProxyOnBackInvokedDispatcher extends OnBackInvokedDispatcher {
dispatcher));
}
for (Pair<OnBackInvokedCallback, Integer> callbackPair : mCallbacks) {
- dispatcher.registerOnBackInvokedCallback(callbackPair.first,
- callbackPair.second);
+ int priority = callbackPair.second;
+ if (priority >= 0) {
+ dispatcher.registerOnBackInvokedCallback(callbackPair.first, priority);
+ } else {
+ dispatcher.registerSystemOnBackInvokedCallback(callbackPair.first);
+ }
}
mCallbacks.clear();
}
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 29786046e49c..d37d3b42872f 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -18,8 +18,10 @@ package android.window;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.compat.CompatChanges;
import android.os.Handler;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.util.Log;
import android.view.IWindow;
import android.view.IWindowSession;
@@ -31,7 +33,7 @@ import java.util.HashMap;
import java.util.TreeMap;
/**
- * Provides window based implementation of {@link OnBackInvokedDispatcher}.
+ * Provides window based implementation of {@link android.view.OnBackInvokedDispatcher}.
*
* Callbacks with higher priorities receive back dispatching first.
* Within the same priority, callbacks receive back dispatching in the reverse order
@@ -39,16 +41,19 @@ import java.util.TreeMap;
*
* When the top priority callback is updated, the new callback is propagated to the Window Manager
* if the window the instance is associated with has been attached. It is allowed to register /
- * unregister {@link OnBackInvokedCallback}s before the window is attached, although callbacks
- * will not receive dispatches until window attachment.
+ * unregister {@link android.view.OnBackInvokedCallback}s before the window is attached, although
+ * callbacks will not receive dispatches until window attachment.
*
* @hide
*/
-public class WindowOnBackInvokedDispatcher extends OnBackInvokedDispatcher {
+public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
private IWindowSession mWindowSession;
private IWindow mWindow;
private static final String TAG = "WindowOnBackDispatcher";
private static final boolean DEBUG = false;
+ private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability";
+ private static final boolean IS_BACK_PREDICTABILITY_ENABLED = SystemProperties
+ .getInt(BACK_PREDICTABILITY_PROP, 0) > 0;
/** The currently most prioritized callback. */
@Nullable
@@ -82,6 +87,15 @@ public class WindowOnBackInvokedDispatcher extends OnBackInvokedDispatcher {
@Override
public void registerOnBackInvokedCallback(
@NonNull OnBackInvokedCallback callback, @Priority int priority) {
+ if (priority < 0) {
+ throw new IllegalArgumentException("Application registered OnBackInvokedCallback "
+ + "cannot have negative priority. Priority: " + priority);
+ }
+ registerOnBackInvokedCallbackUnchecked(callback, priority);
+ }
+
+ private void registerOnBackInvokedCallbackUnchecked(
+ @NonNull OnBackInvokedCallback callback, @Priority int priority) {
if (!mOnBackInvokedCallbacks.containsKey(priority)) {
mOnBackInvokedCallbacks.put(priority, new ArrayList<>());
}
@@ -120,6 +134,11 @@ public class WindowOnBackInvokedDispatcher extends OnBackInvokedDispatcher {
}
}
+ @Override
+ public void registerSystemOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback) {
+ registerOnBackInvokedCallbackUnchecked(callback, OnBackInvokedDispatcher.PRIORITY_SYSTEM);
+ }
+
/** Clears all registered callbacks on the instance. */
public void clear() {
mAllCallbacks.clear();
@@ -198,4 +217,21 @@ public class WindowOnBackInvokedDispatcher extends OnBackInvokedDispatcher {
Handler.getMain().post(() -> mCallback.onBackInvoked());
}
}
+
+ @Override
+ public OnBackInvokedCallback getTopCallback() {
+ return mTopCallback == null ? null : mTopCallback.getCallback();
+ }
+
+ /**
+ * Returns if the legacy back behavior should be used.
+ *
+ * Legacy back behavior dispatches KEYCODE_BACK instead of invoking the application registered
+ * {@link android.view.OnBackInvokedCallback}.
+ *
+ */
+ public static boolean shouldUseLegacyBack() {
+ return !CompatChanges.isChangeEnabled(DISPATCH_BACK_INVOCATION_AHEAD_OF_TIME)
+ || !IS_BACK_PREDICTABILITY_ENABLED;
+ }
}
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 59d6005ba193..54325e590347 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -448,4 +448,7 @@
<color name="accessibility_magnification_background">#F50D60</color>
<color name="accessibility_daltonizer_background">#00BCD4</color>
<color name="accessibility_color_inversion_background">#546E7A</color>
+
+ <!-- Color of camera light when camera is in use -->
+ <color name="camera_privacy_light">#FFFFFF</color>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 33efbce15eea..2b25c3eb099b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4722,4 +4722,6 @@
<java-symbol type="bool" name="config_lowPowerStandbySupported" />
<java-symbol type="bool" name="config_lowPowerStandbyEnabledByDefault" />
<java-symbol type="integer" name="config_lowPowerStandbyNonInteractiveTimeout" />
+
+ <java-symbol type="color" name="camera_privacy_light"/>
</resources>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 3badfe4ae15b..83c4024e7867 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -401,6 +401,7 @@ applications that come with the platform
<!-- Permission required for CTS test - TrustTestCases -->
<permission name="android.permission.PROVIDE_TRUST_AGENT" />
<permission name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
+ <permission name="android.permission.TRUST_LISTENER" />
<!-- Permissions required for Incremental CTS tests -->
<permission name="com.android.permission.USE_INSTALLER_V2"/>
<permission name="android.permission.LOADER_USAGE_STATS"/>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
index b310dd638e6c..9a6df23ca971 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
@@ -18,9 +18,12 @@ package com.android.wm.shell.back;
import android.view.MotionEvent;
+import com.android.wm.shell.common.annotations.ExternalThread;
+
/**
- * Interface for SysUI to get access to the Back animation related methods.
+ * Interface for external process to get access to the Back animation related methods.
*/
+@ExternalThread
public interface BackAnimation {
/**
@@ -32,4 +35,11 @@ public interface BackAnimation {
* Sets whether the back gesture is past the trigger threshold or not.
*/
void setTriggerBack(boolean triggerBack);
+
+ /**
+ * Returns a binder that can be passed to an external process to update back animations.
+ */
+ default IBackAnimation createExternalInterface() {
+ return null;
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 229e8ee04982..a5140c3aafff 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.back;
+import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW;
import android.animation.Animator;
@@ -26,6 +27,7 @@ import android.annotation.Nullable;
import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
import android.app.WindowConfiguration;
+import android.content.Context;
import android.graphics.Point;
import android.graphics.PointF;
import android.hardware.HardwareBuffer;
@@ -35,16 +37,18 @@ import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceControl;
import android.window.BackNavigationInfo;
+import android.window.IOnBackInvokedCallback;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.annotations.ShellMainThread;
/**
* Controls the window animation run when a user initiates a back gesture.
*/
-public class BackAnimationController {
+public class BackAnimationController implements RemoteCallable<BackAnimationController> {
private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability";
public static final boolean IS_ENABLED = SystemProperties
@@ -72,18 +76,26 @@ public class BackAnimationController {
private BackNavigationInfo mBackNavigationInfo;
private final SurfaceControl.Transaction mTransaction;
private final IActivityTaskManager mActivityTaskManager;
+ private final Context mContext;
+ @Nullable
+ private IOnBackInvokedCallback mBackToLauncherCallback;
- public BackAnimationController(@ShellMainThread ShellExecutor shellExecutor) {
- this(shellExecutor, new SurfaceControl.Transaction(), ActivityTaskManager.getService());
+ public BackAnimationController(
+ @ShellMainThread ShellExecutor shellExecutor,
+ Context context) {
+ this(shellExecutor, new SurfaceControl.Transaction(), ActivityTaskManager.getService(),
+ context);
}
@VisibleForTesting
BackAnimationController(@NonNull ShellExecutor shellExecutor,
@NonNull SurfaceControl.Transaction transaction,
- @NonNull IActivityTaskManager activityTaskManager) {
+ @NonNull IActivityTaskManager activityTaskManager,
+ Context context) {
mShellExecutor = shellExecutor;
mTransaction = transaction;
mActivityTaskManager = activityTaskManager;
+ mContext = context;
}
public BackAnimation getBackAnimationImpl() {
@@ -92,7 +104,27 @@ public class BackAnimationController {
private final BackAnimation mBackAnimation = new BackAnimationImpl();
+ @Override
+ public Context getContext() {
+ return mContext;
+ }
+
+ @Override
+ public ShellExecutor getRemoteCallExecutor() {
+ return mShellExecutor;
+ }
+
private class BackAnimationImpl implements BackAnimation {
+ private IBackAnimationImpl mBackAnimation;
+
+ @Override
+ public IBackAnimation createExternalInterface() {
+ if (mBackAnimation != null) {
+ mBackAnimation.invalidate();
+ }
+ mBackAnimation = new IBackAnimationImpl(BackAnimationController.this);
+ return mBackAnimation;
+ }
@Override
public void onBackMotion(MotionEvent event) {
@@ -105,6 +137,48 @@ public class BackAnimationController {
}
}
+ private static class IBackAnimationImpl extends IBackAnimation.Stub {
+ private BackAnimationController mController;
+
+ IBackAnimationImpl(BackAnimationController controller) {
+ mController = controller;
+ }
+
+ @Override
+ public void setBackToLauncherCallback(IOnBackInvokedCallback callback) {
+ executeRemoteCallWithTaskPermission(mController, "setBackToLauncherCallback",
+ (controller) -> mController.setBackToLauncherCallback(callback));
+ }
+
+ @Override
+ public void clearBackToLauncherCallback() {
+ executeRemoteCallWithTaskPermission(mController, "clearBackToLauncherCallback",
+ (controller) -> mController.clearBackToLauncherCallback());
+ }
+
+ @Override
+ public void onBackToLauncherAnimationFinished() {
+ executeRemoteCallWithTaskPermission(mController, "onBackToLauncherAnimationFinished",
+ (controller) -> mController.onBackToLauncherAnimationFinished());
+ }
+
+ void invalidate() {
+ mController = null;
+ }
+ }
+
+ private void setBackToLauncherCallback(IOnBackInvokedCallback callback) {
+ mBackToLauncherCallback = callback;
+ }
+
+ private void clearBackToLauncherCallback() {
+ mBackToLauncherCallback = null;
+ }
+
+ private void onBackToLauncherAnimationFinished() {
+ finishAnimation();
+ }
+
/**
* Called when a new motion event needs to be transferred to this
* {@link BackAnimationController}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/IBackAnimation.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/back/IBackAnimation.aidl
new file mode 100644
index 000000000000..6311f879fd45
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/IBackAnimation.aidl
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 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.wm.shell.back;
+
+import android.window.IOnBackInvokedCallback;
+
+/**
+ * Interface for Launcher process to register back invocation callbacks.
+ */
+interface IBackAnimation {
+
+ /**
+ * Sets a {@link IOnBackInvokedCallback} to be invoked when
+ * back navigation has type {@link BackNavigationInfo#TYPE_RETURN_TO_HOME}.
+ */
+ void setBackToLauncherCallback(in IOnBackInvokedCallback callback);
+
+ /**
+ * Clears the previously registered {@link IOnBackInvokedCallback}.
+ */
+ void clearBackToLauncherCallback();
+
+ /**
+ * Notifies Shell that the back to launcher animation has fully finished
+ * (including the transition animation that runs after the finger is lifted).
+ *
+ * At this point the top window leash (if one was created) should be ready to be released.
+ * //TODO: Remove once we play the transition animation through shell transitions.
+ */
+ void onBackToLauncherAnimationFinished();
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index 8f4cfb0a49a4..4d279bc4e927 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -49,7 +49,7 @@ import java.util.function.Consumer;
import java.util.function.Predicate;
/**
- * Controls to show/update restart-activity buttons on Tasks based on whether the foreground
+ * Controller to show/update compat UI components on Tasks based on whether the foreground
* activities are in compatibility mode.
*/
public class CompatUIController implements OnDisplaysChangedListener,
@@ -228,8 +228,7 @@ public class CompatUIController implements OnDisplaysChangedListener,
final CompatUIWindowManager compatUIWindowManager =
createLayout(context, taskInfo, taskListener);
mActiveLayouts.put(taskInfo.taskId, compatUIWindowManager);
- compatUIWindowManager.createLayout(showOnDisplay(taskInfo.displayId),
- taskInfo.topActivityInSizeCompat, taskInfo.cameraCompatControlState);
+ compatUIWindowManager.createLayout(showOnDisplay(taskInfo.displayId), taskInfo);
}
@VisibleForTesting
@@ -254,9 +253,7 @@ public class CompatUIController implements OnDisplaysChangedListener,
if (layout == null) {
return;
}
- layout.updateCompatInfo(taskInfo.configuration, taskListener,
- showOnDisplay(layout.getDisplayId()), taskInfo.topActivityInSizeCompat,
- taskInfo.cameraCompatControlState);
+ layout.updateCompatInfo(taskInfo, taskListener, showOnDisplay(layout.getDisplayId()));
}
private void removeLayout(int taskId) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
index 44526b00bf0d..9c001a37e4b6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
@@ -20,28 +20,16 @@ import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import android.annotation.Nullable;
+import android.app.TaskInfo;
import android.app.TaskInfo.CameraCompatControlState;
import android.content.Context;
import android.content.res.Configuration;
-import android.graphics.PixelFormat;
import android.graphics.Rect;
-import android.os.Binder;
import android.util.Log;
-import android.view.IWindow;
import android.view.LayoutInflater;
-import android.view.SurfaceControl;
-import android.view.SurfaceControlViewHost;
-import android.view.SurfaceSession;
import android.view.View;
-import android.view.WindowManager;
-import android.view.WindowlessWindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.R;
@@ -50,26 +38,19 @@ import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
/**
- * Holds view hierarchy of a root surface and helps to inflate and manage layout for compat
- * controls.
+ * Window manager for the Size Compat restart button and Camera Compat control.
*/
-class CompatUIWindowManager extends WindowlessWindowManager {
+class CompatUIWindowManager extends CompatUIWindowManagerAbstract {
- private static final String TAG = "CompatUIWindowManager";
+ /**
+ * The Compat UI should be the topmost child of the Task in case there can be more than one
+ * child.
+ */
+ private static final int Z_ORDER = Integer.MAX_VALUE;
- private final SyncTransactionQueue mSyncQueue;
private final CompatUIController.CompatUICallback mCallback;
- private final int mDisplayId;
- private final int mTaskId;
- private final Rect mStableBounds;
- private Context mContext;
- private Configuration mTaskConfig;
- private ShellTaskOrganizer.TaskListener mTaskListener;
- private DisplayLayout mDisplayLayout;
-
- // Remember the last reported states in case visibility changes due to keyguard or
- // IME updates.
+ // Remember the last reported states in case visibility changes due to keyguard or IME updates.
@VisibleForTesting
boolean mHasSizeCompat;
@CameraCompatControlState
@@ -82,147 +63,83 @@ class CompatUIWindowManager extends WindowlessWindowManager {
@Nullable
@VisibleForTesting
- CompatUILayout mCompatUILayout;
-
- @Nullable
- private SurfaceControlViewHost mViewHost;
- @Nullable
- private SurfaceControl mLeash;
+ CompatUILayout mLayout;
CompatUIWindowManager(Context context, Configuration taskConfig,
SyncTransactionQueue syncQueue, CompatUIController.CompatUICallback callback,
int taskId, ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout,
- boolean hasShownSizeCompatHint, boolean hasShownCameraCompatHint) {
- super(taskConfig, null /* rootSurface */, null /* hostInputToken */);
- mContext = context;
- mSyncQueue = syncQueue;
+ boolean hasShownSizeCompatHint, boolean hasShownCameraCompatHint) {
+ super(context, taskConfig, syncQueue, taskId, taskListener, displayLayout);
mCallback = callback;
- mTaskConfig = taskConfig;
- mDisplayId = mContext.getDisplayId();
- mTaskId = taskId;
- mTaskListener = taskListener;
- mDisplayLayout = displayLayout;
mShouldShowSizeCompatHint = !hasShownSizeCompatHint;
mShouldShowCameraCompatHint = !hasShownCameraCompatHint;
- mStableBounds = new Rect();
- mDisplayLayout.getStableBounds(mStableBounds);
}
@Override
- public void setConfiguration(Configuration configuration) {
- super.setConfiguration(configuration);
- mContext = mContext.createConfigurationContext(configuration);
+ protected int getZOrder() {
+ return Z_ORDER;
+ }
+
+
+ @Override
+ protected @Nullable View getLayout() {
+ return mLayout;
}
@Override
- protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
- // Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later.
- final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
- .setContainerLayer()
- .setName("CompatUILeash")
- .setHidden(false)
- .setCallsite("CompatUIWindowManager#attachToParentSurface");
- attachToParentSurface(builder);
- mLeash = builder.build();
- b.setParent(mLeash);
+ protected void removeLayout() {
+ mLayout = null;
}
- /** Creates the layout for compat controls. */
- void createLayout(boolean show, boolean hasSizeCompat,
- @CameraCompatControlState int cameraCompatControlState) {
- mHasSizeCompat = hasSizeCompat;
- mCameraCompatControlState = cameraCompatControlState;
- if (!show || mCompatUILayout != null) {
- // Wait until compat controls should be visible.
- return;
- }
+ @Override
+ protected boolean eligibleToShowLayout() {
+ return mHasSizeCompat || shouldShowCameraControl();
+ }
+
+ /**
+ * Updates the internal state with respect to {@code taskInfo} and calls {@link
+ * #createLayout(boolean)}.
+ */
+ void createLayout(boolean canShow, TaskInfo taskInfo) {
+ mHasSizeCompat = taskInfo.topActivityInSizeCompat;
+ mCameraCompatControlState = taskInfo.cameraCompatControlState;
+ createLayout(canShow);
+ }
+
+ @Override
+ protected View createLayout() {
+ mLayout = inflateLayout();
+ mLayout.inject(this);
- initCompatUi();
- updateSurfacePosition();
+ updateVisibilityOfViews();
- if (hasSizeCompat) {
+ if (mHasSizeCompat) {
mCallback.onSizeCompatRestartButtonAppeared(mTaskId);
}
+
+ return mLayout;
}
- private void createLayout(boolean show) {
- createLayout(show, mHasSizeCompat, mCameraCompatControlState);
+ @VisibleForTesting
+ CompatUILayout inflateLayout() {
+ return (CompatUILayout) LayoutInflater.from(mContext).inflate(R.layout.compat_ui_layout,
+ null);
}
- /** Called when compat info changed. */
- void updateCompatInfo(Configuration taskConfig,
- ShellTaskOrganizer.TaskListener taskListener, boolean show, boolean hasSizeCompat,
- @CameraCompatControlState int cameraCompatControlState) {
- final Configuration prevTaskConfig = mTaskConfig;
- final ShellTaskOrganizer.TaskListener prevTaskListener = mTaskListener;
- mTaskConfig = taskConfig;
- mTaskListener = taskListener;
+ @Override
+ public void updateCompatInfo(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener,
+ boolean canShow) {
final boolean prevHasSizeCompat = mHasSizeCompat;
final int prevCameraCompatControlState = mCameraCompatControlState;
- mHasSizeCompat = hasSizeCompat;
- mCameraCompatControlState = cameraCompatControlState;
+ mHasSizeCompat = taskInfo.topActivityInSizeCompat;
+ mCameraCompatControlState = taskInfo.cameraCompatControlState;
- // Update configuration.
- mContext = mContext.createConfigurationContext(taskConfig);
- setConfiguration(taskConfig);
-
- if (mCompatUILayout == null || prevTaskListener != taskListener) {
- // TaskListener changed, recreate the layout for new surface parent.
- release();
- createLayout(show);
- return;
- }
+ super.updateCompatInfo(taskInfo, taskListener, canShow);
if (prevHasSizeCompat != mHasSizeCompat
|| prevCameraCompatControlState != mCameraCompatControlState) {
updateVisibilityOfViews();
}
-
- if (!taskConfig.windowConfiguration.getBounds()
- .equals(prevTaskConfig.windowConfiguration.getBounds())) {
- // Reposition the UI surfaces.
- updateSurfacePosition();
- }
-
- if (taskConfig.getLayoutDirection() != prevTaskConfig.getLayoutDirection()) {
- // Update layout for RTL.
- mCompatUILayout.setLayoutDirection(taskConfig.getLayoutDirection());
- updateSurfacePosition();
- }
-
- }
-
- /** Called when the visibility of the UI should change. */
- void updateVisibility(boolean show) {
- if (mCompatUILayout == null) {
- // Layout may not have been created because it was hidden previously.
- createLayout(show);
- return;
- }
-
- // Hide compat UIs when IME is showing.
- final int newVisibility = show ? View.VISIBLE : View.GONE;
- if (mCompatUILayout.getVisibility() != newVisibility) {
- mCompatUILayout.setVisibility(newVisibility);
- }
- }
-
- /** Called when display layout changed. */
- void updateDisplayLayout(DisplayLayout displayLayout) {
- final Rect prevStableBounds = mStableBounds;
- final Rect curStableBounds = new Rect();
- displayLayout.getStableBounds(curStableBounds);
- mDisplayLayout = displayLayout;
- if (!prevStableBounds.equals(curStableBounds)) {
- // Stable bounds changed, update UI surface positions.
- updateSurfacePosition();
- mStableBounds.set(curStableBounds);
- }
- }
-
- /** Called when it is ready to be placed compat UI surface. */
- void attachToParentSurface(SurfaceControl.Builder b) {
- mTaskListener.attachChildSurfaceToTask(mTaskId, b);
}
/** Called when the restart button is clicked. */
@@ -233,7 +150,7 @@ class CompatUIWindowManager extends WindowlessWindowManager {
/** Called when the camera treatment button is clicked. */
void onCameraTreatmentButtonClicked() {
if (!shouldShowCameraControl()) {
- Log.w(TAG, "Camera compat shouldn't receive clicks in the hidden state.");
+ Log.w(getTag(), "Camera compat shouldn't receive clicks in the hidden state.");
return;
}
// When a camera control is shown, only two states are allowed: "treament applied" and
@@ -244,141 +161,72 @@ class CompatUIWindowManager extends WindowlessWindowManager {
? CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED
: CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
mCallback.onCameraControlStateUpdated(mTaskId, mCameraCompatControlState);
- mCompatUILayout.updateCameraTreatmentButton(mCameraCompatControlState);
+ mLayout.updateCameraTreatmentButton(mCameraCompatControlState);
}
/** Called when the camera dismiss button is clicked. */
void onCameraDismissButtonClicked() {
if (!shouldShowCameraControl()) {
- Log.w(TAG, "Camera compat shouldn't receive clicks in the hidden state.");
+ Log.w(getTag(), "Camera compat shouldn't receive clicks in the hidden state.");
return;
}
mCameraCompatControlState = CAMERA_COMPAT_CONTROL_DISMISSED;
mCallback.onCameraControlStateUpdated(mTaskId, CAMERA_COMPAT_CONTROL_DISMISSED);
- mCompatUILayout.setCameraControlVisibility(/* show= */ false);
+ mLayout.setCameraControlVisibility(/* show= */ false);
}
/** Called when the restart button is long clicked. */
void onRestartButtonLongClicked() {
- if (mCompatUILayout == null) {
+ if (mLayout == null) {
return;
}
- mCompatUILayout.setSizeCompatHintVisibility(/* show= */ true);
+ mLayout.setSizeCompatHintVisibility(/* show= */ true);
}
/** Called when either dismiss or treatment camera buttons is long clicked. */
void onCameraButtonLongClicked() {
- if (mCompatUILayout == null) {
+ if (mLayout == null) {
return;
}
- mCompatUILayout.setCameraCompatHintVisibility(/* show= */ true);
+ mLayout.setCameraCompatHintVisibility(/* show= */ true);
}
- int getDisplayId() {
- return mDisplayId;
- }
-
- int getTaskId() {
- return mTaskId;
- }
-
- /** Releases the surface control and tears down the view hierarchy. */
- void release() {
- // Hiding before releasing to avoid flickering when transitioning to the Home screen.
- mCompatUILayout.setVisibility(View.GONE);
- mCompatUILayout = null;
-
- if (mViewHost != null) {
- mViewHost.release();
- mViewHost = null;
- }
-
- if (mLeash != null) {
- final SurfaceControl leash = mLeash;
- mSyncQueue.runInSync(t -> t.remove(leash));
- mLeash = null;
- }
- }
-
- void relayout() {
- mViewHost.relayout(getWindowLayoutParams());
- updateSurfacePosition();
- }
-
- @VisibleForTesting
- void updateSurfacePosition() {
- if (mCompatUILayout == null || mLeash == null) {
+ @Override
+ protected void updateSurfacePosition(Rect taskBounds, Rect stableBounds) {
+ if (mLayout == null) {
return;
}
-
- // Use stable bounds to prevent controls from overlapping with system bars.
- final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds();
- final Rect stableBounds = new Rect();
- mDisplayLayout.getStableBounds(stableBounds);
- stableBounds.intersect(taskBounds);
-
// Position of the button in the container coordinate.
final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
? stableBounds.left - taskBounds.left
- : stableBounds.right - taskBounds.left - mCompatUILayout.getMeasuredWidth();
+ : stableBounds.right - taskBounds.left - mLayout.getMeasuredWidth();
final int positionY = stableBounds.bottom - taskBounds.top
- - mCompatUILayout.getMeasuredHeight();
+ - mLayout.getMeasuredHeight();
updateSurfacePosition(positionX, positionY);
}
- private int getLayoutDirection() {
- return mContext.getResources().getConfiguration().getLayoutDirection();
- }
-
- private void updateSurfacePosition(int positionX, int positionY) {
- mSyncQueue.runInSync(t -> {
- if (mLeash == null || !mLeash.isValid()) {
- Log.w(TAG, "The leash has been released.");
- return;
- }
- t.setPosition(mLeash, positionX, positionY);
- // The compat UI should be the topmost child of the Task in case there can be more
- // than one children.
- t.setLayer(mLeash, Integer.MAX_VALUE);
- });
- }
-
- /** Inflates {@link CompatUILayout} on to the root surface. */
- private void initCompatUi() {
- if (mViewHost != null) {
- throw new IllegalStateException(
- "A UI has already been created with this window manager.");
- }
-
- // Construction extracted into the separate methods to allow injection for tests.
- mViewHost = createSurfaceViewHost();
- mCompatUILayout = inflateCompatUILayout();
- mCompatUILayout.inject(this);
-
- updateVisibilityOfViews();
-
- mViewHost.setView(mCompatUILayout, getWindowLayoutParams());
- }
-
private void updateVisibilityOfViews() {
+ if (mLayout == null) {
+ return;
+ }
// Size Compat mode restart button.
- mCompatUILayout.setRestartButtonVisibility(mHasSizeCompat);
+ mLayout.setRestartButtonVisibility(mHasSizeCompat);
if (mHasSizeCompat && mShouldShowSizeCompatHint) {
- mCompatUILayout.setSizeCompatHintVisibility(/* show= */ true);
+ mLayout.setSizeCompatHintVisibility(/* show= */ true);
// Only show by default for the first time.
mShouldShowSizeCompatHint = false;
}
// Camera control for stretched issues.
- mCompatUILayout.setCameraControlVisibility(shouldShowCameraControl());
+ mLayout.setCameraControlVisibility(shouldShowCameraControl());
if (shouldShowCameraControl() && mShouldShowCameraCompatHint) {
- mCompatUILayout.setCameraCompatHintVisibility(/* show= */ true);
+ mLayout.setCameraCompatHintVisibility(/* show= */ true);
// Only show by default for the first time.
mShouldShowCameraCompatHint = false;
}
if (shouldShowCameraControl()) {
- mCompatUILayout.updateCameraTreatmentButton(mCameraCompatControlState);
+ mLayout.updateCameraTreatmentButton(mCameraCompatControlState);
}
}
@@ -386,32 +234,4 @@ class CompatUIWindowManager extends WindowlessWindowManager {
return mCameraCompatControlState != CAMERA_COMPAT_CONTROL_HIDDEN
&& mCameraCompatControlState != CAMERA_COMPAT_CONTROL_DISMISSED;
}
-
- @VisibleForTesting
- CompatUILayout inflateCompatUILayout() {
- return (CompatUILayout) LayoutInflater.from(mContext)
- .inflate(R.layout.compat_ui_layout, null);
- }
-
- @VisibleForTesting
- SurfaceControlViewHost createSurfaceViewHost() {
- return new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
- }
-
- /** Gets the layout params. */
- private WindowManager.LayoutParams getWindowLayoutParams() {
- // Measure how big the hint is since its size depends on the text size.
- mCompatUILayout.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
- final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams(
- // Cannot be wrap_content as this determines the actual window size
- mCompatUILayout.getMeasuredWidth(), mCompatUILayout.getMeasuredHeight(),
- TYPE_APPLICATION_OVERLAY,
- FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL,
- PixelFormat.TRANSLUCENT);
- winParams.token = new Binder();
- winParams.setTitle(CompatUILayout.class.getSimpleName() + mTaskId);
- winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
- return winParams;
- }
-
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java
new file mode 100644
index 000000000000..b9a9db1ee800
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2022 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.wm.shell.compatui;
+
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import android.annotation.Nullable;
+import android.app.TaskInfo;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.util.Log;
+import android.view.IWindow;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.SurfaceSession;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowlessWindowManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+/**
+ * A superclass for all Compat UI {@link WindowlessWindowManager}s that holds shared logic and
+ * exposes general API for {@link CompatUIController}.
+ *
+ * <p>Holds view hierarchy of a root surface and helps to inflate and manage layout.
+ */
+abstract class CompatUIWindowManagerAbstract extends WindowlessWindowManager {
+
+ protected final SyncTransactionQueue mSyncQueue;
+ protected final int mDisplayId;
+ protected final int mTaskId;
+
+ protected Context mContext;
+ protected Configuration mTaskConfig;
+ protected ShellTaskOrganizer.TaskListener mTaskListener;
+ protected DisplayLayout mDisplayLayout;
+ protected final Rect mStableBounds;
+
+ /**
+ * Utility class for adding and releasing a View hierarchy for this {@link
+ * WindowlessWindowManager} to {@code mLeash}.
+ */
+ @Nullable
+ protected SurfaceControlViewHost mViewHost;
+
+ /**
+ * A surface leash to position the layout relative to the task, since we can't set position for
+ * the {@code mViewHost} directly.
+ */
+ @Nullable
+ protected SurfaceControl mLeash;
+
+ protected CompatUIWindowManagerAbstract(Context context, Configuration taskConfig,
+ SyncTransactionQueue syncQueue, int taskId,
+ ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout) {
+ super(taskConfig, null /* rootSurface */, null /* hostInputToken */);
+ mContext = context;
+ mSyncQueue = syncQueue;
+ mTaskConfig = taskConfig;
+ mDisplayId = mContext.getDisplayId();
+ mTaskId = taskId;
+ mTaskListener = taskListener;
+ mDisplayLayout = displayLayout;
+ mStableBounds = new Rect();
+ mDisplayLayout.getStableBounds(mStableBounds);
+ }
+
+ /**
+ * Returns the z-order of this window which will be passed to the {@link SurfaceControl} once
+ * {@link #attachToParentSurface} is called.
+ *
+ * <p>See {@link SurfaceControl.Transaction#setLayer}.
+ */
+ protected abstract int getZOrder();
+
+ /** Returns the layout of this window manager. */
+ protected abstract @Nullable View getLayout();
+
+ /**
+ * Inflates and inits the layout of this window manager on to the root surface if both {@code
+ * canShow} and {@link #eligibleToShowLayout} are true.
+ *
+ * @param canShow whether the layout is allowed to be shown by the parent controller.
+ */
+ void createLayout(boolean canShow) {
+ if (!canShow || !eligibleToShowLayout() || getLayout() != null) {
+ // Wait until layout should be visible.
+ return;
+ }
+
+ if (mViewHost != null) {
+ throw new IllegalStateException(
+ "A UI has already been created with this window manager.");
+ }
+
+ // Construction extracted into separate methods to allow injection for tests.
+ mViewHost = createSurfaceViewHost();
+ mViewHost.setView(createLayout(), getWindowLayoutParams());
+
+ updateSurfacePosition();
+ }
+
+ /** Inflates and inits the layout of this window manager. */
+ protected abstract View createLayout();
+
+ protected abstract void removeLayout();
+
+ /**
+ * Whether the layout is eligible to be shown according to the internal state of the subclass.
+ * Returns true by default if subclass doesn't override this method.
+ */
+ protected boolean eligibleToShowLayout() {
+ return true;
+ }
+
+ @Override
+ public void setConfiguration(Configuration configuration) {
+ super.setConfiguration(configuration);
+ mContext = mContext.createConfigurationContext(configuration);
+ }
+
+ @Override
+ protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
+ String className = getClass().getSimpleName();
+ final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
+ .setContainerLayer()
+ .setName(className + "Leash")
+ .setHidden(false)
+ .setCallsite(className + "#attachToParentSurface");
+ attachToParentSurface(builder);
+ mLeash = builder.build();
+ b.setParent(mLeash);
+
+ initSurface(mLeash);
+ }
+
+ /** Inits the z-order of the surface. */
+ private void initSurface(SurfaceControl leash) {
+ final int z = getZOrder();
+ mSyncQueue.runInSync(t -> {
+ if (leash == null || !leash.isValid()) {
+ Log.w(getTag(), "The leash has been released.");
+ return;
+ }
+ t.setLayer(leash, z);
+ });
+ }
+
+ /**
+ * Called when compat info changed.
+ *
+ * @param canShow whether the layout is allowed to be shown by the parent controller.
+ */
+ void updateCompatInfo(TaskInfo taskInfo,
+ ShellTaskOrganizer.TaskListener taskListener, boolean canShow) {
+ final Configuration prevTaskConfig = mTaskConfig;
+ final ShellTaskOrganizer.TaskListener prevTaskListener = mTaskListener;
+ mTaskConfig = taskInfo.configuration;
+ mTaskListener = taskListener;
+
+ // Update configuration.
+ setConfiguration(mTaskConfig);
+
+ View layout = getLayout();
+ if (layout == null || prevTaskListener != taskListener) {
+ // TaskListener changed, recreate the layout for new surface parent.
+ release();
+ createLayout(canShow);
+ return;
+ }
+
+ boolean boundsUpdated = !mTaskConfig.windowConfiguration.getBounds().equals(
+ prevTaskConfig.windowConfiguration.getBounds());
+ boolean layoutDirectionUpdated =
+ mTaskConfig.getLayoutDirection() != prevTaskConfig.getLayoutDirection();
+ if (boundsUpdated || layoutDirectionUpdated) {
+ // Reposition the UI surfaces.
+ updateSurfacePosition();
+ }
+
+ if (layout != null && layoutDirectionUpdated) {
+ // Update layout for RTL.
+ layout.setLayoutDirection(mTaskConfig.getLayoutDirection());
+ }
+ }
+
+
+ /**
+ * Updates the visibility of the layout.
+ *
+ * @param canShow whether the layout is allowed to be shown by the parent controller.
+ */
+ void updateVisibility(boolean canShow) {
+ View layout = getLayout();
+ if (layout == null) {
+ // Layout may not have been created because it was hidden previously.
+ createLayout(canShow);
+ return;
+ }
+
+ final int newVisibility = canShow && eligibleToShowLayout() ? View.VISIBLE : View.GONE;
+ if (layout.getVisibility() != newVisibility) {
+ layout.setVisibility(newVisibility);
+ }
+ }
+
+ /** Called when display layout changed. */
+ void updateDisplayLayout(DisplayLayout displayLayout) {
+ final Rect prevStableBounds = mStableBounds;
+ final Rect curStableBounds = new Rect();
+ displayLayout.getStableBounds(curStableBounds);
+ mDisplayLayout = displayLayout;
+ if (!prevStableBounds.equals(curStableBounds)) {
+ // Stable bounds changed, update UI surface positions.
+ updateSurfacePosition();
+ mStableBounds.set(curStableBounds);
+ }
+ }
+
+ /** Called when the surface is ready to be placed under the task surface. */
+ @VisibleForTesting
+ void attachToParentSurface(SurfaceControl.Builder b) {
+ mTaskListener.attachChildSurfaceToTask(mTaskId, b);
+ }
+
+ int getDisplayId() {
+ return mDisplayId;
+ }
+
+ int getTaskId() {
+ return mTaskId;
+ }
+
+ /** Releases the surface control and tears down the view hierarchy. */
+ void release() {
+ // Hiding before releasing to avoid flickering when transitioning to the Home screen.
+ View layout = getLayout();
+ if (layout != null) {
+ layout.setVisibility(View.GONE);
+ }
+ removeLayout();
+
+ if (mViewHost != null) {
+ mViewHost.release();
+ mViewHost = null;
+ }
+
+ if (mLeash != null) {
+ final SurfaceControl leash = mLeash;
+ mSyncQueue.runInSync(t -> t.remove(leash));
+ mLeash = null;
+ }
+ }
+
+ /** Re-layouts the view host and updates the surface position. */
+ void relayout() {
+ if (mViewHost == null) {
+ return;
+ }
+ mViewHost.relayout(getWindowLayoutParams());
+ updateSurfacePosition();
+ }
+
+ /**
+ * Updates the position of the surface with respect to the task bounds and display layout
+ * stable bounds.
+ */
+ @VisibleForTesting
+ void updateSurfacePosition() {
+ if (mLeash == null) {
+ return;
+ }
+ // Use stable bounds to prevent controls from overlapping with system bars.
+ final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds();
+ final Rect stableBounds = new Rect();
+ mDisplayLayout.getStableBounds(stableBounds);
+ stableBounds.intersect(taskBounds);
+
+ updateSurfacePosition(taskBounds, stableBounds);
+ }
+
+ /**
+ * Updates the position of the surface with respect to the given {@code taskBounds} and {@code
+ * stableBounds}.
+ */
+ protected abstract void updateSurfacePosition(Rect taskBounds, Rect stableBounds);
+
+ /**
+ * Updates the position of the surface with respect to the given {@code positionX} and {@code
+ * positionY}.
+ */
+ protected void updateSurfacePosition(int positionX, int positionY) {
+ mSyncQueue.runInSync(t -> {
+ if (mLeash == null || !mLeash.isValid()) {
+ Log.w(getTag(), "The leash has been released.");
+ return;
+ }
+ t.setPosition(mLeash, positionX, positionY);
+ });
+ }
+
+ protected int getLayoutDirection() {
+ return mContext.getResources().getConfiguration().getLayoutDirection();
+ }
+
+ @VisibleForTesting
+ SurfaceControlViewHost createSurfaceViewHost() {
+ return new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
+ }
+
+ /** Gets the layout params. */
+ private WindowManager.LayoutParams getWindowLayoutParams() {
+ View layout = getLayout();
+ if (layout == null) {
+ return new WindowManager.LayoutParams();
+ }
+ // Measure how big the hint is since its size depends on the text size.
+ layout.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+ return getWindowLayoutParams(layout.getMeasuredWidth(), layout.getMeasuredHeight());
+ }
+
+ /** Gets the layout params given the width and height of the layout. */
+ private WindowManager.LayoutParams getWindowLayoutParams(int width, int height) {
+ final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams(
+ // Cannot be wrap_content as this determines the actual window size
+ width, height,
+ TYPE_APPLICATION_OVERLAY,
+ FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL,
+ PixelFormat.TRANSLUCENT);
+ winParams.token = new Binder();
+ winParams.setTitle(getClass().getSimpleName() + mTaskId);
+ winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
+ return winParams;
+ }
+
+ protected final String getTag() {
+ return getClass().getSimpleName();
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 9f4ff7c8dc06..2e54c792ed57 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -696,11 +696,12 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
static Optional<BackAnimationController> provideBackAnimationController(
+ Context context,
@ShellMainThread ShellExecutor shellExecutor
) {
if (BackAnimationController.IS_ENABLED) {
return Optional.of(
- new BackAnimationController(shellExecutor));
+ new BackAnimationController(shellExecutor, context));
}
return Optional.empty();
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index b738c47ef6ff..21ced0dc5981 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -23,6 +23,7 @@ import static org.mockito.Mockito.verify;
import android.app.IActivityTaskManager;
import android.app.WindowConfiguration;
+import android.content.Context;
import android.hardware.HardwareBuffer;
import android.os.RemoteCallback;
import android.os.RemoteException;
@@ -52,6 +53,9 @@ public class BackAnimationControllerTest {
private final ShellExecutor mShellExecutor = new TestShellExecutor();
@Mock
+ private Context mContext;
+
+ @Mock
private SurfaceControl.Transaction mTransaction;
@Mock
@@ -63,7 +67,7 @@ public class BackAnimationControllerTest {
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mController = new BackAnimationController(
- mShellExecutor, mTransaction, mActivityTaskManager);
+ mShellExecutor, mTransaction, mActivityTaskManager, mContext);
}
private void createNavigationInfo(SurfaceControl topWindowLeash,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index 4352fd3d2c27..741da3fe9f58 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -116,35 +116,17 @@ public class CompatUIControllerTest extends ShellTestCase {
TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, true /* hasSizeCompat */,
CAMERA_COMPAT_CONTROL_HIDDEN);
- // Verify that the restart button is added with non-null size compat info.
+ // Verify that the compat controls are added with non-null size compat info.
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
verify(mController).createLayout(any(), eq(taskInfo), eq(mMockTaskListener));
- // Verify that the restart button is updated with non-null new size compat info.
- mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
- true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN),
- mMockTaskListener);
-
- verify(mMockLayout).updateCompatInfo(new Configuration(), mMockTaskListener,
- true /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
-
- // Verify that the restart button is updated with new camera state.
- mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
- true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED),
- mMockTaskListener);
-
- verify(mMockLayout).updateCompatInfo(new Configuration(), mMockTaskListener,
- true /* show */, true /* hasSizeCompat */,
+ // Verify that the compat controls are updated with non-null new size compat info.
+ taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, true /* hasSizeCompat */,
CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+ mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
- mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
- true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED),
- mMockTaskListener);
-
- verify(mMockLayout).updateCompatInfo(new Configuration(), mMockTaskListener,
- true /* show */, true /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ verify(mMockLayout).updateCompatInfo(taskInfo, mMockTaskListener, true /* canShow */);
// Verify that compat controls are removed with null compat info.
mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
@@ -155,7 +137,7 @@ public class CompatUIControllerTest extends ShellTestCase {
clearInvocations(mMockLayout);
clearInvocations(mController);
- // Verify that compat controls are removed with dismissed camera state.
+ // Verify that compat controls are removed with no size compat and dismissed camera state.
taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID,
true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
@@ -245,11 +227,11 @@ public class CompatUIControllerTest extends ShellTestCase {
verify(mMockLayout).updateVisibility(false);
// Verify button remains hidden while IME is showing.
- mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
- true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
+ TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
+ mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
- verify(mMockLayout).updateCompatInfo(new Configuration(), mMockTaskListener,
- false /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ verify(mMockLayout).updateCompatInfo(taskInfo, mMockTaskListener, false /* canShow */);
// Verify button is shown after IME is hidden.
mController.onImeVisibilityChanged(DISPLAY_ID, false /* isShowing */);
@@ -268,11 +250,11 @@ public class CompatUIControllerTest extends ShellTestCase {
verify(mMockLayout).updateVisibility(false);
// Verify button remains hidden while keyguard is occluded.
- mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
- true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
+ TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
+ mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
- verify(mMockLayout).updateCompatInfo(new Configuration(), mMockTaskListener,
- false /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ verify(mMockLayout).updateCompatInfo(taskInfo, mMockTaskListener, false /* canShow */);
// Verify button is shown after keyguard becomes not occluded.
mController.onKeyguardOccludedChanged(false);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
index 353d8fe8bc52..211781798af7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
@@ -27,6 +27,9 @@ import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
+import android.app.ActivityManager;
+import android.app.TaskInfo;
+import android.app.TaskInfo.CameraCompatControlState;
import android.content.res.Configuration;
import android.testing.AndroidTestingRunner;
import android.view.LayoutInflater;
@@ -83,7 +86,7 @@ public class CompatUILayoutTest extends ShellTestCase {
spyOn(mWindowManager);
spyOn(mCompatUILayout);
doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost();
- doReturn(mCompatUILayout).when(mWindowManager).inflateCompatUILayout();
+ doReturn(mCompatUILayout).when(mWindowManager).inflateLayout();
}
@Test
@@ -107,8 +110,8 @@ public class CompatUILayoutTest extends ShellTestCase {
@Test
public void testOnClickForSizeCompatHint() {
- mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.createLayout(true /* show */, createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN));
final LinearLayout sizeCompatHint = mCompatUILayout.findViewById(R.id.size_compat_hint);
sizeCompatHint.performClick();
@@ -117,8 +120,8 @@ public class CompatUILayoutTest extends ShellTestCase {
@Test
public void testUpdateCameraTreatmentButton_treatmentAppliedByDefault() {
- mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+ mWindowManager.createLayout(true /* show */, createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED));
final ImageButton button =
mCompatUILayout.findViewById(R.id.camera_compat_treatment_button);
button.performClick();
@@ -135,8 +138,8 @@ public class CompatUILayoutTest extends ShellTestCase {
@Test
public void testUpdateCameraTreatmentButton_treatmentSuggestedByDefault() {
- mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ mWindowManager.createLayout(true /* show */, createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
final ImageButton button =
mCompatUILayout.findViewById(R.id.camera_compat_treatment_button);
button.performClick();
@@ -153,8 +156,8 @@ public class CompatUILayoutTest extends ShellTestCase {
@Test
public void testOnCameraDismissButtonClicked() {
- mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ mWindowManager.createLayout(true /* show */, createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
final ImageButton button =
mCompatUILayout.findViewById(R.id.camera_compat_dismiss_button);
button.performClick();
@@ -188,11 +191,19 @@ public class CompatUILayoutTest extends ShellTestCase {
@Test
public void testOnClickForCameraCompatHint() {
- mWindowManager.createLayout(true /* show */, false /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ mWindowManager.createLayout(true /* show */, createTaskInfo(false /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
final LinearLayout hint = mCompatUILayout.findViewById(R.id.camera_compat_hint);
hint.performClick();
verify(mCompatUILayout).setCameraCompatHintVisibility(/* show= */ false);
}
+
+ private static TaskInfo createTaskInfo(boolean hasSizeCompat,
+ @CameraCompatControlState int cameraCompatControlState) {
+ ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
+ taskInfo.topActivityInSizeCompat = hasSizeCompat;
+ taskInfo.cameraCompatControlState = cameraCompatControlState;
+ return taskInfo;
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
index 11c797363819..de882eae1503 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
@@ -26,8 +26,8 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -35,6 +35,8 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.app.ActivityManager;
+import android.app.TaskInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
@@ -75,47 +77,45 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
@Mock private ShellTaskOrganizer.TaskListener mTaskListener;
@Mock private CompatUILayout mCompatUILayout;
@Mock private SurfaceControlViewHost mViewHost;
- private Configuration mTaskConfig;
private CompatUIWindowManager mWindowManager;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mTaskConfig = new Configuration();
mWindowManager = new CompatUIWindowManager(mContext, new Configuration(),
mSyncTransactionQueue, mCallback, TASK_ID, mTaskListener, new DisplayLayout(),
false /* hasShownSizeCompatHint */, false /* hasShownSizeCompatHint */);
spyOn(mWindowManager);
- doReturn(mCompatUILayout).when(mWindowManager).inflateCompatUILayout();
+ doReturn(mCompatUILayout).when(mWindowManager).inflateLayout();
doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost();
}
@Test
public void testCreateSizeCompatButton() {
// Not create layout if show is false.
- mWindowManager.createLayout(false /* show */, true /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.createLayout(false /* canShow */, createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN));
- verify(mWindowManager, never()).inflateCompatUILayout();
+ verify(mWindowManager, never()).inflateLayout();
// Not create hint popup.
mWindowManager.mShouldShowSizeCompatHint = false;
- mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.createLayout(true /* canShow */, createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN));
- verify(mWindowManager).inflateCompatUILayout();
+ verify(mWindowManager).inflateLayout();
verify(mCompatUILayout, never()).setSizeCompatHintVisibility(true /* show */);
// Create hint popup.
mWindowManager.release();
mWindowManager.mShouldShowSizeCompatHint = true;
- mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.createLayout(true /* canShow */, createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN));
- verify(mWindowManager, times(2)).inflateCompatUILayout();
+ verify(mWindowManager, times(2)).inflateLayout();
assertNotNull(mCompatUILayout);
verify(mCompatUILayout).setSizeCompatHintVisibility(true /* show */);
assertFalse(mWindowManager.mShouldShowSizeCompatHint);
@@ -123,10 +123,10 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
@Test
public void testRelease() {
- mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.createLayout(true /* canShow */, createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN));
- verify(mWindowManager).inflateCompatUILayout();
+ verify(mWindowManager).inflateLayout();
mWindowManager.release();
@@ -135,65 +135,104 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
@Test
public void testUpdateCompatInfo() {
- mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_HIDDEN);
+ TaskInfo taskInfo = createTaskInfo(true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.createLayout(true /* canShow */, taskInfo);
// No diff
clearInvocations(mWindowManager);
- mWindowManager.updateCompatInfo(mTaskConfig, mTaskListener, true /* show */,
- true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.updateCompatInfo(taskInfo, mTaskListener, true /* canShow */);
verify(mWindowManager, never()).updateSurfacePosition();
verify(mWindowManager, never()).release();
- verify(mWindowManager, never()).createLayout(anyBoolean(), anyBoolean(), anyInt());
+ verify(mWindowManager, never()).createLayout(anyBoolean());
// Change task listener, recreate button.
clearInvocations(mWindowManager);
final ShellTaskOrganizer.TaskListener newTaskListener = mock(
ShellTaskOrganizer.TaskListener.class);
- mWindowManager.updateCompatInfo(mTaskConfig, newTaskListener,
- true /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */);
verify(mWindowManager).release();
- verify(mWindowManager).createLayout(anyBoolean(), anyBoolean(), anyInt());
+ verify(mWindowManager).createLayout(true);
+
+ // Change in Size Compat to false, hides restart button.
+ clearInvocations(mWindowManager);
+ taskInfo = createTaskInfo(false /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */);
+
+ verify(mCompatUILayout).setRestartButtonVisibility(/* show */ false);
+
+ // Change in Size Compat to true, shows restart button.
+ clearInvocations(mWindowManager);
+ clearInvocations(mCompatUILayout);
+ taskInfo = createTaskInfo(true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */);
+
+ verify(mCompatUILayout).setRestartButtonVisibility(/* show */ true);
// Change Camera Compat state, show a control.
- mWindowManager.updateCompatInfo(mTaskConfig, newTaskListener, true /* show */,
- true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+ clearInvocations(mWindowManager);
+ clearInvocations(mCompatUILayout);
+ taskInfo = createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+ mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */);
verify(mCompatUILayout).setCameraControlVisibility(/* show */ true);
verify(mCompatUILayout).updateCameraTreatmentButton(
CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+ // Change Camera Compat state, update a control.
clearInvocations(mWindowManager);
clearInvocations(mCompatUILayout);
- // Change Camera Compat state, update a control.
- mWindowManager.updateCompatInfo(mTaskConfig, newTaskListener, true /* show */,
- true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ taskInfo = createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */);
verify(mCompatUILayout).setCameraControlVisibility(/* show */ true);
verify(mCompatUILayout).updateCameraTreatmentButton(
CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ // Change Camera Compat state to hidden, hide a control.
clearInvocations(mWindowManager);
clearInvocations(mCompatUILayout);
- // Change Camera Compat state to hidden, hide a control.
- mWindowManager.updateCompatInfo(mTaskConfig, newTaskListener,
- true /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ taskInfo = createTaskInfo(true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */);
verify(mCompatUILayout).setCameraControlVisibility(/* show */ false);
// Change task bounds, update position.
clearInvocations(mWindowManager);
- final Configuration newTaskConfiguration = new Configuration();
- newTaskConfiguration.windowConfiguration.setBounds(new Rect(0, 1000, 0, 2000));
- mWindowManager.updateCompatInfo(newTaskConfiguration, newTaskListener,
- true /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ taskInfo = createTaskInfo(true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ taskInfo.configuration.windowConfiguration.setBounds(new Rect(0, 1000, 0, 2000));
+ mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */);
verify(mWindowManager).updateSurfacePosition();
}
@Test
+ public void testUpdateCompatInfoLayoutNotInflatedYet() {
+ TaskInfo taskInfo = createTaskInfo(true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.createLayout(false /* canShow */, taskInfo);
+
+ verify(mWindowManager, never()).inflateLayout();
+
+ // Change topActivityInSizeCompat to false and pass canShow true, layout shouldn't be
+ // inflated
+ clearInvocations(mWindowManager);
+ taskInfo = createTaskInfo(false /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.updateCompatInfo(taskInfo, mTaskListener, true /* canShow */);
+
+ verify(mWindowManager, never()).inflateLayout();
+
+ // Change topActivityInSizeCompat to true and pass canShow true, layout should be inflated.
+ clearInvocations(mWindowManager);
+ taskInfo = createTaskInfo(true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.updateCompatInfo(taskInfo, mTaskListener, true /* canShow */);
+
+ verify(mWindowManager).inflateLayout();
+ }
+
+ @Test
public void testUpdateDisplayLayout() {
final DisplayInfo displayInfo = new DisplayInfo();
displayInfo.logicalWidth = 1000;
@@ -237,26 +276,25 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
@Test
public void testUpdateVisibility() {
// Create button if it is not created.
- mWindowManager.mCompatUILayout = null;
+ mWindowManager.mLayout = null;
mWindowManager.mHasSizeCompat = true;
- mWindowManager.updateVisibility(true /* show */);
+ mWindowManager.updateVisibility(true /* canShow */);
- verify(mWindowManager).createLayout(true /* show */, true /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_HIDDEN);
+ verify(mWindowManager).createLayout(true /* canShow */);
// Hide button.
clearInvocations(mWindowManager);
doReturn(View.VISIBLE).when(mCompatUILayout).getVisibility();
- mWindowManager.updateVisibility(false /* show */);
+ mWindowManager.updateVisibility(false /* canShow */);
- verify(mWindowManager, never()).createLayout(anyBoolean(), anyBoolean(), anyInt());
+ verify(mWindowManager, never()).createLayout(anyBoolean(), any());
verify(mCompatUILayout).setVisibility(View.GONE);
// Show button.
doReturn(View.GONE).when(mCompatUILayout).getVisibility();
- mWindowManager.updateVisibility(true /* show */);
+ mWindowManager.updateVisibility(true /* canShow */);
- verify(mWindowManager, never()).createLayout(anyBoolean(), anyBoolean(), anyInt());
+ verify(mWindowManager, never()).createLayout(anyBoolean(), any());
verify(mCompatUILayout).setVisibility(View.VISIBLE);
}
@@ -270,8 +308,8 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
@Test
public void testOnCameraDismissButtonClicked() {
- mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ mWindowManager.createLayout(true /* canShow */, createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
clearInvocations(mCompatUILayout);
mWindowManager.onCameraDismissButtonClicked();
@@ -281,8 +319,8 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
@Test
public void testOnCameraTreatmentButtonClicked() {
- mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ mWindowManager.createLayout(true /* canShow */, createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
clearInvocations(mCompatUILayout);
mWindowManager.onCameraTreatmentButtonClicked();
@@ -310,10 +348,10 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
public void testOnRestartButtonLongClicked_showHint() {
// Not create hint popup.
mWindowManager.mShouldShowSizeCompatHint = false;
- mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.createLayout(true /* canShow */, createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN));
- verify(mWindowManager).inflateCompatUILayout();
+ verify(mWindowManager).inflateLayout();
verify(mCompatUILayout, never()).setSizeCompatHintVisibility(true /* show */);
mWindowManager.onRestartButtonLongClicked();
@@ -325,10 +363,10 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
public void testOnCamerControlLongClicked_showHint() {
// Not create hint popup.
mWindowManager.mShouldShowCameraCompatHint = false;
- mWindowManager.createLayout(true /* show */, false /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ mWindowManager.createLayout(true /* canShow */, createTaskInfo(false /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
- verify(mWindowManager).inflateCompatUILayout();
+ verify(mWindowManager).inflateLayout();
verify(mCompatUILayout, never()).setCameraCompatHintVisibility(true /* show */);
mWindowManager.onCameraButtonLongClicked();
@@ -339,30 +377,37 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
@Test
public void testCreateCameraCompatControl() {
// Not create layout if show is false.
- mWindowManager.createLayout(false /* show */, false /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ mWindowManager.createLayout(false /* canShow */, createTaskInfo(false /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
- verify(mWindowManager, never()).inflateCompatUILayout();
+ verify(mWindowManager, never()).inflateLayout();
// Not create hint popup.
mWindowManager.mShouldShowCameraCompatHint = false;
- mWindowManager.createLayout(true /* show */, false /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ mWindowManager.createLayout(true /* canShow */, createTaskInfo(false /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
- verify(mWindowManager).inflateCompatUILayout();
+ verify(mWindowManager).inflateLayout();
verify(mCompatUILayout, never()).setCameraCompatHintVisibility(true /* show */);
verify(mCompatUILayout).setCameraControlVisibility(true /* show */);
// Create hint popup.
mWindowManager.release();
mWindowManager.mShouldShowCameraCompatHint = true;
- mWindowManager.createLayout(true /* show */, false /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ mWindowManager.createLayout(true /* canShow */, createTaskInfo(false /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
- verify(mWindowManager, times(2)).inflateCompatUILayout();
+ verify(mWindowManager, times(2)).inflateLayout();
assertNotNull(mCompatUILayout);
verify(mCompatUILayout, times(2)).setCameraControlVisibility(true /* show */);
assertFalse(mWindowManager.mShouldShowCameraCompatHint);
}
+ private static TaskInfo createTaskInfo(boolean hasSizeCompat,
+ @TaskInfo.CameraCompatControlState int cameraCompatControlState) {
+ ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
+ taskInfo.topActivityInSizeCompat = hasSizeCompat;
+ taskInfo.cameraCompatControlState = cameraCompatControlState;
+ return taskInfo;
+ }
}
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index 5f02a430f384..e2e48d35a672 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -43,7 +43,6 @@ import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.locks.StampedLock;
/**
* <p>The ImageReader class allows direct application access to image data
@@ -676,8 +675,7 @@ public class ImageReader implements AutoCloseable {
* If no handler specified and the calling thread has no looper.
*/
public void setOnImageAvailableListener(OnImageAvailableListener listener, Handler handler) {
- long writeStamp = mListenerLock.writeLock();
- try {
+ synchronized (mListenerLock) {
if (listener != null) {
Looper looper = handler != null ? handler.getLooper() : Looper.myLooper();
if (looper == null) {
@@ -693,8 +691,6 @@ public class ImageReader implements AutoCloseable {
mListenerExecutor = null;
}
mListener = listener;
- } finally {
- mListenerLock.unlockWrite(writeStamp);
}
}
@@ -717,12 +713,9 @@ public class ImageReader implements AutoCloseable {
throw new IllegalArgumentException("executor must not be null");
}
- long writeStamp = mListenerLock.writeLock();
- try {
+ synchronized (mListenerLock) {
mListenerExecutor = executor;
mListener = listener;
- } finally {
- mListenerLock.unlockWrite(writeStamp);
}
}
@@ -738,8 +731,6 @@ public class ImageReader implements AutoCloseable {
/**
* Callback that is called when a new image is available from ImageReader.
*
- * This callback must not modify or close the passed {@code reader}.
- *
* @param reader the ImageReader the callback is associated with.
* @see ImageReader
* @see Image
@@ -898,41 +889,28 @@ public class ImageReader implements AutoCloseable {
return;
}
- synchronized (ir.mCloseLock) {
- if (!ir.mIsReaderValid) {
- // It's dangerous to fire onImageAvailable() callback when the ImageReader
- // is being closed, as application could acquire next image in the
- // onImageAvailable() callback.
- return;
- }
- }
-
final Executor executor;
- final long readStamp = ir.mListenerLock.readLock();
- try {
+ final OnImageAvailableListener listener;
+ synchronized (ir.mListenerLock) {
executor = ir.mListenerExecutor;
- if (executor == null) {
- return;
- }
- } finally {
- ir.mListenerLock.unlockRead(readStamp);
+ listener = ir.mListener;
+ }
+ final boolean isReaderValid;
+ synchronized (ir.mCloseLock) {
+ isReaderValid = ir.mIsReaderValid;
}
- executor.execute(() -> {
- // Acquire readlock to ensure that the ImageReader does not change its
- // state while a listener is actively processing.
- final long rStamp = ir.mListenerLock.readLock();
- try {
- // Fire onImageAvailable of the latest non-null listener
- // This ensures that if the listener changes while messages are in queue, the
- // in-flight messages will call onImageAvailable of the new listener instead
- if (ir.mListener != null) {
- ir.mListener.onImageAvailable(ir);
+ // It's dangerous to fire onImageAvailable() callback when the ImageReader
+ // is being closed, as application could acquire next image in the
+ // onImageAvailable() callback.
+ if (executor != null && listener != null && isReaderValid) {
+ executor.execute(new Runnable() {
+ @Override
+ public void run() {
+ listener.onImageAvailable(ir);
}
- } finally {
- ir.mListenerLock.unlockRead(rStamp);
- }
- });
+ });
+ }
}
/**
@@ -1092,7 +1070,7 @@ public class ImageReader implements AutoCloseable {
private Surface mSurface;
private int mEstimatedNativeAllocBytes;
- private final StampedLock mListenerLock = new StampedLock();
+ private final Object mListenerLock = new Object();
private final Object mCloseLock = new Object();
private boolean mIsReaderValid = false;
private OnImageAvailableListener mListener;
diff --git a/packages/ConnectivityT/service/Android.bp b/packages/ConnectivityT/service/Android.bp
index d2d6ef0cbbb1..c3049dabefc2 100644
--- a/packages/ConnectivityT/service/Android.bp
+++ b/packages/ConnectivityT/service/Android.bp
@@ -28,6 +28,11 @@ filegroup {
"src/com/android/server/net/NetworkStats*.java",
"src/com/android/server/net/BpfInterfaceMapUpdater.java",
"src/com/android/server/net/InterfaceMapValue.java",
+ "src/com/android/server/net/CookieTagMapKey.java",
+ "src/com/android/server/net/CookieTagMapValue.java",
+ "src/com/android/server/net/StatsMapKey.java",
+ "src/com/android/server/net/StatsMapValue.java",
+ "src/com/android/server/net/UidStatsMapKey.java",
],
path: "src",
visibility: [
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/CookieTagMapKey.java b/packages/ConnectivityT/service/src/com/android/server/net/CookieTagMapKey.java
new file mode 100644
index 000000000000..443e5b38475e
--- /dev/null
+++ b/packages/ConnectivityT/service/src/com/android/server/net/CookieTagMapKey.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 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.net;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+/**
+ * Key for cookie tag map.
+ */
+public class CookieTagMapKey extends Struct {
+ @Field(order = 0, type = Type.S64)
+ public final long socketCookie;
+
+ public CookieTagMapKey(final long socketCookie) {
+ this.socketCookie = socketCookie;
+ }
+}
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/CookieTagMapValue.java b/packages/ConnectivityT/service/src/com/android/server/net/CookieTagMapValue.java
new file mode 100644
index 000000000000..93b9195f92da
--- /dev/null
+++ b/packages/ConnectivityT/service/src/com/android/server/net/CookieTagMapValue.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2022 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.net;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+/**
+ * Value for cookie tag map.
+ */
+public class CookieTagMapValue extends Struct {
+ @Field(order = 0, type = Type.U32)
+ public final long uid;
+
+ @Field(order = 1, type = Type.U32)
+ public final long tag;
+
+ public CookieTagMapValue(final long uid, final long tag) {
+ this.uid = uid;
+ this.tag = tag;
+ }
+}
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/StatsMapKey.java b/packages/ConnectivityT/service/src/com/android/server/net/StatsMapKey.java
new file mode 100644
index 000000000000..ea8d83638347
--- /dev/null
+++ b/packages/ConnectivityT/service/src/com/android/server/net/StatsMapKey.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 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.net;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+/**
+ * Key for both stats maps.
+ */
+public class StatsMapKey extends Struct {
+ @Field(order = 0, type = Type.U32)
+ public final long uid;
+
+ @Field(order = 1, type = Type.U32)
+ public final long tag;
+
+ @Field(order = 2, type = Type.U32)
+ public final long counterSet;
+
+ @Field(order = 3, type = Type.U32)
+ public final long ifaceIndex;
+
+ public StatsMapKey(final long uid, final long tag, final long counterSet,
+ final long ifaceIndex) {
+ this.uid = uid;
+ this.tag = tag;
+ this.counterSet = counterSet;
+ this.ifaceIndex = ifaceIndex;
+ }
+}
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/StatsMapValue.java b/packages/ConnectivityT/service/src/com/android/server/net/StatsMapValue.java
new file mode 100644
index 000000000000..48f26ce686e2
--- /dev/null
+++ b/packages/ConnectivityT/service/src/com/android/server/net/StatsMapValue.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 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.net;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+/**
+ * Value used for both stats maps and uid stats map.
+ */
+public class StatsMapValue extends Struct {
+ @Field(order = 0, type = Type.U63)
+ public final long rxPackets;
+
+ @Field(order = 1, type = Type.U63)
+ public final long rxBytes;
+
+ @Field(order = 2, type = Type.U63)
+ public final long txPackets;
+
+ @Field(order = 3, type = Type.U63)
+ public final long txBytes;
+
+ public StatsMapValue(final long rxPackets, final long rxBytes, final long txPackets,
+ final long txBytes) {
+ this.rxPackets = rxPackets;
+ this.rxBytes = rxBytes;
+ this.txPackets = txPackets;
+ this.txBytes = txBytes;
+ }
+}
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/UidStatsMapKey.java b/packages/ConnectivityT/service/src/com/android/server/net/UidStatsMapKey.java
new file mode 100644
index 000000000000..2849f94c9383
--- /dev/null
+++ b/packages/ConnectivityT/service/src/com/android/server/net/UidStatsMapKey.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 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.net;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+/**
+ * Key for uid stats map.
+ */
+public class UidStatsMapKey extends Struct {
+ @Field(order = 0, type = Type.U32)
+ public final long uid;
+
+ public UidStatsMapKey(final long uid) {
+ this.uid = uid;
+ }
+}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 246466e16c74..bbfab0bfa792 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -314,6 +314,7 @@ public class GlobalSettingsValidators {
VALIDATORS.put(Global.USER_PREFERRED_RESOLUTION_HEIGHT, ANY_INTEGER_VALIDATOR);
VALIDATORS.put(Global.USER_PREFERRED_RESOLUTION_WIDTH, ANY_INTEGER_VALIDATOR);
VALIDATORS.put(Global.Wearable.WET_MODE_ON, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.COOLDOWN_MODE_ON, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 51870e2e958e..077337cdc8c3 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -3624,7 +3624,7 @@ public class SettingsProvider extends ContentProvider {
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 209;
+ private static final int SETTINGS_VERSION = 210;
private final int mUserId;
@@ -5498,17 +5498,21 @@ public class SettingsProvider extends ContentProvider {
}
if (currentVersion == 208) {
- // Version 208: Enable enforcement of
+ // Unused
+ currentVersion = 209;
+ }
+ if (currentVersion == 209) {
+ // Version 209: Enable enforcement of
// android.Manifest.permission#POST_NOTIFICATIONS in order for applications
// to post notifications.
final SettingsState secureSettings = getSecureSettingsLocked(userId);
secureSettings.insertSettingLocked(
Secure.NOTIFICATION_PERMISSION_ENABLED,
- /* enabled= */" 1",
+ /* enabled= */ "1",
/* tag= */ null,
/* makeDefault= */ false,
SettingsState.SYSTEM_PACKAGE_NAME);
- currentVersion = 209;
+ currentVersion = 210;
}
// vXXX: Add new settings above this point.
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 13ae87015fa4..3de1fb2b4ba4 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -659,7 +659,8 @@ public class SettingsBackupTest {
Settings.Global.Wearable.CLOCKWORK_SYSUI_MAIN_ACTIVITY,
Settings.Global.Wearable.CLOCKWORK_LONG_PRESS_TO_ASSISTANT_ENABLED,
Settings.Global.Wearable.WEAR_ACTIVITY_AUTO_RESUME_TIMEOUT_SET_BY_USER,
- Settings.Global.Wearable.WET_MODE_ON);
+ Settings.Global.Wearable.WET_MODE_ON,
+ Settings.Global.Wearable.COOLDOWN_MODE_ON);
private static final Set<String> BACKUP_DENY_LIST_SECURE_SETTINGS =
newHashSet(
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 35eeac822d3c..ef5849c73b72 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -562,6 +562,7 @@
<!-- Permissions required for CTS test - TrustTestCases -->
<uses-permission android:name="android.permission.PROVIDE_TRUST_AGENT" />
<uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
+ <uses-permission android:name="android.permission.TRUST_LISTENER" />
<!-- Permission required for CTS test - CtsGameManagerTestCases -->
<uses-permission android:name="android.permission.MANAGE_GAME_MODE" />
diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml
index 536b0423ce16..02c58e468c30 100644
--- a/packages/SystemUI/res/layout/qs_tile_label.xml
+++ b/packages/SystemUI/res/layout/qs_tile_label.xml
@@ -26,9 +26,9 @@
android:layout_marginEnd="0dp"
android:layout_gravity="center_vertical | start">
- <TextView
+ <com.android.systemui.util.SafeMarqueeTextView
android:id="@+id/tile_label"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start"
android:textDirection="locale"
@@ -37,9 +37,9 @@
android:singleLine="true"
android:textAppearance="@style/TextAppearance.QS.TileLabel"/>
- <TextView
+ <com.android.systemui.util.SafeMarqueeTextView
android:id="@+id/app_label"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start"
android:textDirection="locale"
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 1ae3d362cd6e..47822b77a93f 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -686,7 +686,7 @@
<integer name="config_connectionMinDuration">1000</integer>
<!-- Flag to activate notification to contents feature -->
- <bool name="config_notificationToContents">false</bool>
+ <bool name="config_notificationToContents">true</bool>
<!-- Respect drawable/rounded_secondary.xml intrinsic size for multiple radius corner path
customization for secondary display-->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
index 8d98a7540a05..56326e36ff5e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
@@ -18,6 +18,7 @@ package com.android.systemui.shared.recents.utilities;
import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN;
import android.annotation.TargetApi;
import android.content.Context;
@@ -110,11 +111,16 @@ public class Utilities {
hints &= ~NAVIGATION_HINT_BACK_ALT;
break;
}
- if (showImeSwitcher) {
+ if (imeShown) {
hints |= NAVIGATION_HINT_IME_SHOWN;
} else {
hints &= ~NAVIGATION_HINT_IME_SHOWN;
}
+ if (showImeSwitcher) {
+ hints |= NAVIGATION_HINT_IME_SWITCHER_SHOWN;
+ } else {
+ hints &= ~NAVIGATION_HINT_IME_SWITCHER_SHOWN;
+ }
return hints;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index eebc7918c72c..08b4d3f68a87 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -59,6 +59,7 @@ public class QuickStepContract {
public static final String KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER = "unlock_animation";
// See IRecentTasks.aidl
public static final String KEY_EXTRA_RECENT_TASKS = "recent_tasks";
+ public static final String KEY_EXTRA_SHELL_BACK_ANIMATION = "extra_shell_back_animation";
public static final String NAV_BAR_MODE_3BUTTON_OVERLAY =
WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 30429fbf8cfa..2a737970907a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -49,7 +49,6 @@ import androidx.slice.builders.SliceAction;
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SystemUIAppComponentFactory;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -139,6 +138,8 @@ public class KeyguardSliceProvider extends SliceProvider implements
public StatusBarStateController mStatusBarStateController;
@Inject
public KeyguardBypassController mKeyguardBypassController;
+ @Inject
+ public KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private CharSequence mMediaTitle;
private CharSequence mMediaArtist;
protected boolean mDozing;
@@ -333,7 +334,7 @@ public class KeyguardSliceProvider extends SliceProvider implements
mAlarmManager.cancel(mUpdateNextAlarm);
if (mRegistered) {
mRegistered = false;
- getKeyguardUpdateMonitor().removeCallback(mKeyguardUpdateMonitorCallback);
+ mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
getContext().unregisterReceiver(mIntentReceiver);
}
KeyguardSliceProvider.sInstance = null;
@@ -389,7 +390,7 @@ public class KeyguardSliceProvider extends SliceProvider implements
filter.addAction(Intent.ACTION_LOCALE_CHANGED);
getContext().registerReceiver(mIntentReceiver, filter, null /* permission*/,
null /* scheduler */);
- getKeyguardUpdateMonitor().registerCallback(mKeyguardUpdateMonitorCallback);
+ mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
mRegistered = true;
}
}
@@ -441,10 +442,6 @@ public class KeyguardSliceProvider extends SliceProvider implements
updateNextAlarm();
}
- private KeyguardUpdateMonitor getKeyguardUpdateMonitor() {
- return Dependency.get(KeyguardUpdateMonitor.class);
- }
-
/**
* Called whenever new media metadata is available.
* @param metadata New metadata.
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 1bef32ad8caf..4b550f2fb875 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -17,7 +17,7 @@
package com.android.systemui.navigationbar;
import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN;
import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.StatusBarManager.WindowType;
@@ -1410,7 +1410,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
.setFlag(SYSUI_STATE_IME_SHOWING,
(mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0)
.setFlag(SYSUI_STATE_IME_SWITCHER_SHOWING,
- (mNavigationIconHints & NAVIGATION_HINT_IME_SHOWN) != 0)
+ (mNavigationIconHints & NAVIGATION_HINT_IME_SWITCHER_SHOWN) != 0)
.setFlag(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
allowSystemGestureIgnoringBarVisibility())
.commitUpdate(mDisplayId);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 2dd89f3c4dcd..102669397283 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -778,7 +778,7 @@ public class NavigationBarView extends FrameLayout implements
// Update IME button visibility, a11y and rotate button always overrides the appearance
boolean disableImeSwitcher =
- (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) == 0
+ (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN) == 0
|| isImeRenderingNavButtons;
mContextualButtonGroup.setButtonVisibility(R.id.ime_switcher, !disableImeSwitcher);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index ec15b2469358..75a3df7cad0c 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -17,7 +17,7 @@
package com.android.systemui.navigationbar;
import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static android.view.InsetsState.containsType;
@@ -293,7 +293,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
.setFlag(SYSUI_STATE_IME_SHOWING,
(mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0)
.setFlag(SYSUI_STATE_IME_SWITCHER_SHOWING,
- (mNavigationIconHints & NAVIGATION_HINT_IME_SHOWN) != 0)
+ (mNavigationIconHints & NAVIGATION_HINT_IME_SWITCHER_SHOWN) != 0)
.setFlag(SYSUI_STATE_OVERVIEW_DISABLED,
(mDisabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0)
.setFlag(SYSUI_STATE_HOME_DISABLED,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
index eb3415639db6..58ebe89f199a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
@@ -109,7 +109,7 @@ class FgsManagerController @Inject constructor(
}
isAvailable = deviceConfigProxy
- .getBoolean(NAMESPACE_SYSTEMUI, TASK_MANAGER_ENABLED, false)
+ .getBoolean(NAMESPACE_SYSTEMUI, TASK_MANAGER_ENABLED, true)
initialized = true
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 821dfa5fc902..a712ce2e6394 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -25,6 +25,7 @@ import android.content.res.Configuration
import android.content.res.Resources.ID_NULL
import android.graphics.drawable.Drawable
import android.graphics.drawable.RippleDrawable
+import android.os.Trace
import android.service.quicksettings.Tile
import android.text.TextUtils
import android.util.Log
@@ -163,6 +164,12 @@ open class QSTileViewImpl @JvmOverloads constructor(
updateResources()
}
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ Trace.traceBegin(Trace.TRACE_TAG_APP, "QSTileViewImpl#onMeasure")
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+ Trace.endSection()
+ }
+
override fun resetOverride() {
heightOverride = HeightOverrideable.NO_OVERRIDE
updateHeight()
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 00a314943f7a..1218fd307931 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -26,6 +26,7 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_RECENT_TASKS;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_BACK_ANIMATION;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_ONE_HANDED;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_PIP;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
@@ -104,6 +105,7 @@ import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
import com.android.systemui.statusbar.policy.CallbackController;
+import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
@@ -163,6 +165,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
private final Optional<StartingSurface> mStartingSurface;
private final KeyguardUnlockAnimationController mSysuiUnlockAnimationController;
private final Optional<RecentTasks> mRecentTasks;
+ private final Optional<BackAnimation> mBackAnimation;
private final UiEventLogger mUiEventLogger;
private Region mActiveNavBarRegion;
@@ -508,6 +511,9 @@ public class OverviewProxyService extends CurrentUserTracker implements
mRecentTasks.ifPresent(recentTasks -> params.putBinder(
KEY_EXTRA_RECENT_TASKS,
recentTasks.createExternalInterface().asBinder()));
+ mBackAnimation.ifPresent((backAnimation) -> params.putBinder(
+ KEY_EXTRA_SHELL_BACK_ANIMATION,
+ backAnimation.createExternalInterface().asBinder()));
try {
mOverviewProxy.onInitialize(params);
@@ -566,6 +572,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
Optional<SplitScreen> splitScreenOptional,
Optional<OneHanded> oneHandedOptional,
Optional<RecentTasks> recentTasks,
+ Optional<BackAnimation> backAnimation,
Optional<StartingSurface> startingSurface,
BroadcastDispatcher broadcastDispatcher,
ShellTransitions shellTransitions,
@@ -593,6 +600,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
mOneHandedOptional = oneHandedOptional;
mShellTransitions = shellTransitions;
mRecentTasks = recentTasks;
+ mBackAnimation = backAnimation;
mUiEventLogger = uiEventLogger;
// Assumes device always starts with back button until launcher tells it that it does not
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
index 0b6d7594ce50..7d035a78450a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
@@ -35,6 +35,10 @@ public class FooterView extends StackScrollerDecorView {
private FooterViewButton mClearAllButton;
private FooterViewButton mManageButton;
private boolean mShowHistory;
+ // String cache, for performance reasons.
+ // Reading them from a Resources object can be quite slow sometimes.
+ private String mManageNotificationText;
+ private String mManageNotificationHistoryText;
public FooterView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -68,6 +72,8 @@ public class FooterView extends StackScrollerDecorView {
super.onFinishInflate();
mClearAllButton = (FooterViewButton) findSecondaryView();
mManageButton = findViewById(R.id.manage_text);
+ updateResources();
+ updateText();
}
public void setManageButtonClickListener(OnClickListener listener) {
@@ -86,15 +92,20 @@ public class FooterView extends StackScrollerDecorView {
}
public void showHistory(boolean showHistory) {
+ if (mShowHistory == showHistory) {
+ return;
+ }
mShowHistory = showHistory;
+ updateText();
+ }
+
+ private void updateText() {
if (mShowHistory) {
- mManageButton.setText(R.string.manage_notifications_history_text);
- mManageButton.setContentDescription(
- mContext.getString(R.string.manage_notifications_history_text));
+ mManageButton.setText(mManageNotificationHistoryText);
+ mManageButton.setContentDescription(mManageNotificationHistoryText);
} else {
- mManageButton.setText(R.string.manage_notifications_text);
- mManageButton.setContentDescription(
- mContext.getString(R.string.manage_notifications_text));
+ mManageButton.setText(mManageNotificationText);
+ mManageButton.setContentDescription(mManageNotificationText);
}
}
@@ -109,7 +120,8 @@ public class FooterView extends StackScrollerDecorView {
mClearAllButton.setText(R.string.clear_all_notifications_text);
mClearAllButton.setContentDescription(
mContext.getString(R.string.accessibility_clear_all));
- showHistory(mShowHistory);
+ updateResources();
+ updateText();
}
/**
@@ -124,6 +136,12 @@ public class FooterView extends StackScrollerDecorView {
mManageButton.setTextColor(textColor);
}
+ private void updateResources() {
+ mManageNotificationText = getContext().getString(R.string.manage_notifications_text);
+ mManageNotificationHistoryText = getContext()
+ .getString(R.string.manage_notifications_history_text);
+ }
+
@Override
public ExpandableViewState createExpandableViewState() {
return new FooterViewState();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
index 329293409dc2..d464acb7fe76 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -217,8 +217,10 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout {
* frameworks/base/core/res/res/values/config.xml
*/
public void addIgnoredSlot(String slotName) {
- addIgnoredSlotInternal(slotName);
- requestLayout();
+ boolean added = addIgnoredSlotInternal(slotName);
+ if (added) {
+ requestLayout();
+ }
}
/**
@@ -226,17 +228,27 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout {
* @param slots names of the icons to ignore
*/
public void addIgnoredSlots(List<String> slots) {
+ boolean willAddAny = false;
for (String slot : slots) {
- addIgnoredSlotInternal(slot);
+ willAddAny |= addIgnoredSlotInternal(slot);
}
- requestLayout();
+ if (willAddAny) {
+ requestLayout();
+ }
}
- private void addIgnoredSlotInternal(String slotName) {
- if (!mIgnoredSlots.contains(slotName)) {
- mIgnoredSlots.add(slotName);
+ /**
+ *
+ * @param slotName
+ * @return
+ */
+ private boolean addIgnoredSlotInternal(String slotName) {
+ if (mIgnoredSlots.contains(slotName)) {
+ return false;
}
+ mIgnoredSlots.add(slotName);
+ return true;
}
/**
@@ -245,9 +257,10 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout {
* @param slotName name of the icon slot to remove from the ignored list
*/
public void removeIgnoredSlot(String slotName) {
- mIgnoredSlots.remove(slotName);
-
- requestLayout();
+ boolean removed = mIgnoredSlots.remove(slotName);
+ if (removed) {
+ requestLayout();
+ }
}
/**
@@ -256,11 +269,14 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout {
* @param slots name of the icon slots to remove from the ignored list
*/
public void removeIgnoredSlots(List<String> slots) {
+ boolean removedAny = false;
for (String slot : slots) {
- mIgnoredSlots.remove(slot);
+ removedAny |= mIgnoredSlots.remove(slot);
}
- requestLayout();
+ if (removedAny) {
+ requestLayout();
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/util/AutoMarqueeTextView.java b/packages/SystemUI/src/com/android/systemui/util/AutoMarqueeTextView.java
index 09dbfee5e0a4..fa4f314ff0a2 100644
--- a/packages/SystemUI/src/com/android/systemui/util/AutoMarqueeTextView.java
+++ b/packages/SystemUI/src/com/android/systemui/util/AutoMarqueeTextView.java
@@ -19,7 +19,6 @@ package com.android.systemui.util;
import android.content.Context;
import android.text.TextUtils;
import android.util.AttributeSet;
-import android.widget.TextView;
/**
* TextView that changes its ellipsize value with its visibility.
@@ -27,7 +26,7 @@ import android.widget.TextView;
* The View responds to changes in user-visibility to change its ellipsize from MARQUEE to END
* and back. Useful for TextView that need to marquee forever.
*/
-public class AutoMarqueeTextView extends TextView {
+public class AutoMarqueeTextView extends SafeMarqueeTextView {
private boolean mAggregatedVisible = false;
diff --git a/packages/SystemUI/src/com/android/systemui/util/SafeMarqueeTextView.kt b/packages/SystemUI/src/com/android/systemui/util/SafeMarqueeTextView.kt
new file mode 100644
index 000000000000..1c1a990e6018
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/SafeMarqueeTextView.kt
@@ -0,0 +1,44 @@
+package com.android.systemui.util
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.util.AttributeSet
+import android.view.ViewGroup
+import android.widget.TextView
+
+/**
+ * A TextField that doesn't relayout when changing from marquee to ellipsis.
+ */
+@SuppressLint("AppCompatCustomView")
+open class SafeMarqueeTextView @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ defStyleRes: Int = 0
+) : TextView(context, attrs, defStyleAttr, defStyleRes) {
+
+ private var safelyIgnoreLayout = false
+ private val hasStableWidth
+ get() = layoutParams.width != ViewGroup.LayoutParams.WRAP_CONTENT
+
+ override fun requestLayout() {
+ if (safelyIgnoreLayout) {
+ return
+ }
+ super.requestLayout()
+ }
+
+ override fun startMarquee() {
+ val wasIgnoring = safelyIgnoreLayout
+ safelyIgnoreLayout = hasStableWidth
+ super.startMarquee()
+ safelyIgnoreLayout = wasIgnoring
+ }
+
+ override fun stopMarquee() {
+ val wasIgnoring = safelyIgnoreLayout
+ safelyIgnoreLayout = hasStableWidth
+ super.stopMarquee()
+ safelyIgnoreLayout = wasIgnoring
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
index d1f505baa9e3..51c258055465 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
@@ -90,6 +90,7 @@ public class KeyguardSliceProviderTest extends SysuiTestCase {
private DozeParameters mDozeParameters;
@Mock
private NextAlarmController mNextAlarmController;
+ @Mock
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private TestableKeyguardSliceProvider mProvider;
private boolean mIsZenMode;
@@ -97,7 +98,6 @@ public class KeyguardSliceProviderTest extends SysuiTestCase {
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mKeyguardUpdateMonitor = mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
mIsZenMode = false;
mProvider = new TestableKeyguardSliceProvider();
mProvider.setContextAvailableCallback(context -> { });
@@ -265,6 +265,7 @@ public class KeyguardSliceProviderTest extends SysuiTestCase {
mStatusBarStateController = KeyguardSliceProviderTest.this.mStatusBarStateController;
mKeyguardBypassController = KeyguardSliceProviderTest.this.mKeyguardBypassController;
mMediaManager = KeyguardSliceProviderTest.this.mNotificationMediaManager;
+ mKeyguardUpdateMonitor = KeyguardSliceProviderTest.this.mKeyguardUpdateMonitor;
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 9ca898b9dea9..612bad8483d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -18,6 +18,7 @@ package com.android.systemui.navigationbar;
import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN;
import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT;
import static android.inputmethodservice.InputMethodService.IME_INVISIBLE;
import static android.inputmethodservice.InputMethodService.IME_VISIBLE;
@@ -285,20 +286,26 @@ public class NavigationBarTest extends SysuiTestCase {
BACK_DISPOSITION_DEFAULT, true);
// Verify IME window state will be updated in default NavBar & external NavBar state reset.
- assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN,
+ assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN
+ | NAVIGATION_HINT_IME_SWITCHER_SHOWN,
defaultNavBar.getNavigationIconHints());
assertFalse((externalNavBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
assertFalse((externalNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
+ assertFalse((externalNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SWITCHER_SHOWN)
+ != 0);
externalNavBar.setImeWindowStatus(EXTERNAL_DISPLAY_ID, null, IME_VISIBLE,
BACK_DISPOSITION_DEFAULT, true);
defaultNavBar.setImeWindowStatus(
DEFAULT_DISPLAY, null, IME_INVISIBLE, BACK_DISPOSITION_DEFAULT, false);
// Verify IME window state will be updated in external NavBar & default NavBar state reset.
- assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN,
+ assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN
+ | NAVIGATION_HINT_IME_SWITCHER_SHOWN,
externalNavBar.getNavigationIconHints());
assertFalse((defaultNavBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
assertFalse((defaultNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
+ assertFalse((defaultNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SWITCHER_SHOWN)
+ != 0);
}
@Test
@@ -316,6 +323,8 @@ public class NavigationBarTest extends SysuiTestCase {
BACK_DISPOSITION_DEFAULT, true);
assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
+ assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SWITCHER_SHOWN)
+ != 0);
// Verify navbar didn't alter and showing back icon when the keyguard is showing without
// requesting IME insets visible.
@@ -324,6 +333,8 @@ public class NavigationBarTest extends SysuiTestCase {
BACK_DISPOSITION_DEFAULT, true);
assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
+ assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SWITCHER_SHOWN)
+ != 0);
// Verify navbar altered and showing back icon when the keyguard is showing and
// requesting IME insets visible.
@@ -333,6 +344,8 @@ public class NavigationBarTest extends SysuiTestCase {
BACK_DISPOSITION_DEFAULT, true);
assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
+ assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SWITCHER_SHOWN)
+ != 0);
}
@Test
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
index 6938e25ea9af..c9903ea19868 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
@@ -43,6 +43,7 @@ import android.hardware.camera2.extension.IImageProcessorImpl;
import android.hardware.camera2.extension.IInitializeSessionCallback;
import android.hardware.camera2.extension.IPreviewExtenderImpl;
import android.hardware.camera2.extension.IPreviewImageProcessorImpl;
+import android.hardware.camera2.extension.IProcessResultImpl;
import android.hardware.camera2.extension.IRequestCallback;
import android.hardware.camera2.extension.IRequestProcessorImpl;
import android.hardware.camera2.extension.IRequestUpdateProcessorImpl;
@@ -90,6 +91,7 @@ import androidx.camera.extensions.impl.NightPreviewExtenderImpl;
import androidx.camera.extensions.impl.PreviewExtenderImpl;
import androidx.camera.extensions.impl.PreviewExtenderImpl.ProcessorType;
import androidx.camera.extensions.impl.PreviewImageProcessorImpl;
+import androidx.camera.extensions.impl.ProcessResultImpl;
import androidx.camera.extensions.impl.RequestUpdateProcessorImpl;
import androidx.camera.extensions.impl.advanced.AdvancedExtenderImpl;
import androidx.camera.extensions.impl.advanced.AutoAdvancedExtenderImpl;
@@ -124,14 +126,17 @@ public class CameraExtensionsProxyService extends Service {
private static final String LATEST_VERSION = "1.2.0";
private static final String NON_INIT_VERSION_PREFIX = "1.0";
private static final String ADVANCED_VERSION_PREFIX = "1.2";
- private static final String[] SUPPORTED_VERSION_PREFIXES = {ADVANCED_VERSION_PREFIX,
- "1.1", NON_INIT_VERSION_PREFIX};
+ private static final String RESULTS_VERSION_PREFIX = "1.3";
+ private static final String[] SUPPORTED_VERSION_PREFIXES = {RESULTS_VERSION_PREFIX,
+ ADVANCED_VERSION_PREFIX, "1.1", NON_INIT_VERSION_PREFIX};
private static final boolean EXTENSIONS_PRESENT = checkForExtensions();
private static final String EXTENSIONS_VERSION = EXTENSIONS_PRESENT ?
(new ExtensionVersionImpl()).checkApiVersion(LATEST_VERSION) : null;
private static final boolean ADVANCED_API_SUPPORTED = checkForAdvancedAPI();
private static final boolean INIT_API_SUPPORTED = EXTENSIONS_PRESENT &&
(!EXTENSIONS_VERSION.startsWith(NON_INIT_VERSION_PREFIX));
+ private static final boolean RESULT_API_SUPPORTED = EXTENSIONS_PRESENT &&
+ (EXTENSIONS_VERSION.startsWith(RESULTS_VERSION_PREFIX));
private HashMap<String, CameraCharacteristics> mCharacteristicsHashMap = new HashMap<>();
private HashMap<String, Long> mMetadataVendorIdMap = new HashMap<>();
@@ -1242,7 +1247,7 @@ public class CameraExtensionsProxyService extends Service {
}
if (processor != null) {
- return new PreviewImageProcessorImplStub(processor);
+ return new PreviewImageProcessorImplStub(processor, mCameraId);
}
return null;
@@ -1332,7 +1337,7 @@ public class CameraExtensionsProxyService extends Service {
public ICaptureProcessorImpl getCaptureProcessor() {
CaptureProcessorImpl captureProcessor = mImageExtender.getCaptureProcessor();
if (captureProcessor != null) {
- return new CaptureProcessorImplStub(captureProcessor);
+ return new CaptureProcessorImplStub(captureProcessor, mCameraId);
}
return null;
@@ -1390,13 +1395,97 @@ public class CameraExtensionsProxyService extends Service {
return null;
}
+
+ @Override
+ public CameraMetadataNative getAvailableCaptureRequestKeys() {
+ if (RESULT_API_SUPPORTED) {
+ List<CaptureRequest.Key> supportedCaptureKeys =
+ mImageExtender.getAvailableCaptureRequestKeys();
+
+ if ((supportedCaptureKeys != null) && !supportedCaptureKeys.isEmpty()) {
+ CameraMetadataNative ret = new CameraMetadataNative();
+ long vendorId = mMetadataVendorIdMap.containsKey(mCameraId) ?
+ mMetadataVendorIdMap.get(mCameraId) : Long.MAX_VALUE;
+ ret.setVendorId(vendorId);
+ int requestKeyTags [] = new int[supportedCaptureKeys.size()];
+ int i = 0;
+ for (CaptureRequest.Key key : supportedCaptureKeys) {
+ requestKeyTags[i++] = CameraMetadataNative.getTag(key.getName(), vendorId);
+ }
+ ret.set(CameraCharacteristics.REQUEST_AVAILABLE_REQUEST_KEYS, requestKeyTags);
+
+ return ret;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public CameraMetadataNative getAvailableCaptureResultKeys() {
+ if (RESULT_API_SUPPORTED) {
+ List<CaptureResult.Key> supportedResultKeys =
+ mImageExtender.getAvailableCaptureResultKeys();
+
+ if ((supportedResultKeys != null) && !supportedResultKeys.isEmpty()) {
+ CameraMetadataNative ret = new CameraMetadataNative();
+ long vendorId = mMetadataVendorIdMap.containsKey(mCameraId) ?
+ mMetadataVendorIdMap.get(mCameraId) : Long.MAX_VALUE;
+ ret.setVendorId(vendorId);
+ int resultKeyTags [] = new int[supportedResultKeys.size()];
+ int i = 0;
+ for (CaptureResult.Key key : supportedResultKeys) {
+ resultKeyTags[i++] = CameraMetadataNative.getTag(key.getName(), vendorId);
+ }
+ ret.set(CameraCharacteristics.REQUEST_AVAILABLE_RESULT_KEYS, resultKeyTags);
+
+ return ret;
+ }
+ }
+
+ return null;
+ }
+ }
+
+ private class ProcessResultCallback implements ProcessResultImpl {
+ private final IProcessResultImpl mProcessResult;
+ private final String mCameraId;
+
+ private ProcessResultCallback(IProcessResultImpl processResult, String cameraId) {
+ mProcessResult = processResult;
+ mCameraId = cameraId;
+ }
+
+ @Override
+ public void onCaptureCompleted(long shutterTimestamp,
+ List<Pair<CaptureResult.Key, Object>> result) {
+ if (result == null) {
+ Log.e(TAG, "Invalid capture result received!");
+ }
+
+ CameraMetadataNative captureResults = new CameraMetadataNative();
+ if (mMetadataVendorIdMap.containsKey(mCameraId)) {
+ captureResults.setVendorId(mMetadataVendorIdMap.get(mCameraId));
+ }
+ for (Pair<CaptureResult.Key, Object> pair : result) {
+ captureResults.set(pair.first, pair.second);
+ }
+
+ try {
+ mProcessResult.onCaptureCompleted(shutterTimestamp, captureResults);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Remote client doesn't respond to capture results!");
+ }
+ }
}
private class CaptureProcessorImplStub extends ICaptureProcessorImpl.Stub {
private final CaptureProcessorImpl mCaptureProcessor;
+ private final String mCameraId;
- public CaptureProcessorImplStub(CaptureProcessorImpl captureProcessor) {
+ public CaptureProcessorImplStub(CaptureProcessorImpl captureProcessor, String cameraId) {
mCaptureProcessor = captureProcessor;
+ mCameraId = cameraId;
}
@Override
@@ -1415,7 +1504,7 @@ public class CameraExtensionsProxyService extends Service {
}
@Override
- public void process(List<CaptureBundle> captureList) {
+ public void process(List<CaptureBundle> captureList, IProcessResultImpl resultCallback) {
HashMap<Integer, Pair<Image, TotalCaptureResult>> captureMap = new HashMap<>();
for (CaptureBundle captureBundle : captureList) {
captureMap.put(captureBundle.stage, new Pair<> (
@@ -1424,7 +1513,14 @@ public class CameraExtensionsProxyService extends Service {
captureBundle.sequenceId)));
}
if (!captureMap.isEmpty()) {
- mCaptureProcessor.process(captureMap);
+ if ((resultCallback != null) && (RESULT_API_SUPPORTED)) {
+ mCaptureProcessor.process(captureMap, new ProcessResultCallback(resultCallback,
+ mCameraId), null /*executor*/);
+ } else if (resultCallback == null) {
+ mCaptureProcessor.process(captureMap);
+ } else {
+ Log.e(TAG, "Process requests with capture results are not supported!");
+ }
} else {
Log.e(TAG, "Process request with absent capture stages!");
}
@@ -1433,9 +1529,11 @@ public class CameraExtensionsProxyService extends Service {
private class PreviewImageProcessorImplStub extends IPreviewImageProcessorImpl.Stub {
private final PreviewImageProcessorImpl mProcessor;
+ private final String mCameraId;
- public PreviewImageProcessorImplStub(PreviewImageProcessorImpl processor) {
+ public PreviewImageProcessorImplStub(PreviewImageProcessorImpl processor, String cameraId) {
mProcessor = processor;
+ mCameraId = cameraId;
}
@Override
@@ -1455,9 +1553,17 @@ public class CameraExtensionsProxyService extends Service {
@Override
public void process(android.hardware.camera2.extension.ParcelImage image,
- CameraMetadataNative result, int sequenceId) {
- mProcessor.process(new ExtensionImage(image),
- new TotalCaptureResult(result, sequenceId));
+ CameraMetadataNative result, int sequenceId, IProcessResultImpl resultCallback) {
+ if ((resultCallback != null) && RESULT_API_SUPPORTED) {
+ mProcessor.process(new ExtensionImage(image),
+ new TotalCaptureResult(result, sequenceId),
+ new ProcessResultCallback(resultCallback, mCameraId), null /*executor*/);
+ } else if (resultCallback == null) {
+ mProcessor.process(new ExtensionImage(image),
+ new TotalCaptureResult(result, sequenceId));
+ } else {
+
+ }
}
}
diff --git a/proto/src/camera.proto b/proto/src/camera.proto
index 0338b93c8842..2d62f32d3941 100644
--- a/proto/src/camera.proto
+++ b/proto/src/camera.proto
@@ -65,4 +65,6 @@ message CameraStreamProto {
// The dynamic range profile of the stream
optional int32 dynamic_range_profile = 14;
+ // The stream use case
+ optional int32 stream_use_case = 15;
}
diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java
index 2845fbfc6ebf..6667d1b45027 100644
--- a/services/core/java/com/android/server/adb/AdbService.java
+++ b/services/core/java/com/android/server/adb/AdbService.java
@@ -311,14 +311,15 @@ public class AdbService extends IAdbManager.Stub {
}
/**
- * @return true if the device supports secure ADB over Wi-Fi.
+ * @return true if the device supports secure ADB over Wi-Fi or Ethernet.
* @hide
*/
@Override
public boolean isAdbWifiSupported() {
mContext.enforceCallingPermission(
android.Manifest.permission.MANAGE_DEBUGGING, "AdbService");
- return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI);
+ return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI) ||
+ mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_ETHERNET);
}
/**
diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
index 145a298af95e..4eba77168b8e 100644
--- a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
+++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
@@ -473,7 +473,7 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
}
try {
- mWindowManagerInternal.addTaskOverlay(
+ mWindowManagerInternal.addTrustedTaskOverlay(
taskId,
createGameSessionResult.getSurfacePackage());
} catch (IllegalArgumentException ex) {
@@ -519,7 +519,7 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
SurfacePackage surfacePackage = gameSessionRecord.getSurfacePackage();
if (surfacePackage != null) {
try {
- mWindowManagerInternal.removeTaskOverlay(
+ mWindowManagerInternal.removeTrustedTaskOverlay(
gameSessionRecord.getTaskId(),
surfacePackage);
} catch (IllegalArgumentException ex) {
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index 366718c65d84..aa0d3da6a94f 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -35,6 +35,8 @@ import android.app.ActivityThread;
import android.app.IActivityManager;
import android.app.StatsManager;
import android.app.StatsManager.StatsPullAtomCallback;
+import android.app.usage.StorageStats;
+import android.app.usage.StorageStatsManager;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
import android.app.usage.UsageStatsManagerInternal.UsageEventListener;
@@ -78,6 +80,7 @@ import com.android.server.SystemService;
import java.io.File;
import java.io.FileDescriptor;
+import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -111,6 +114,7 @@ public final class AppHibernationService extends SystemService {
private final PackageManagerInternal mPackageManagerInternal;
private final IActivityManager mIActivityManager;
private final UserManager mUserManager;
+ private final StorageStatsManager mStorageStatsManager;
@GuardedBy("mLock")
private final SparseArray<Map<String, UserLevelState>> mUserStates = new SparseArray<>();
@@ -147,6 +151,7 @@ public final class AppHibernationService extends SystemService {
mPackageManagerInternal = injector.getPackageManagerInternal();
mIActivityManager = injector.getActivityManager();
mUserManager = injector.getUserManager();
+ mStorageStatsManager = injector.getStorageStatsManager();
mGlobalLevelHibernationDiskStore = injector.getGlobalLevelDiskStore();
mBackgroundExecutor = injector.getBackgroundExecutor();
mOatArtifactDeletionEnabled = injector.isOatArtifactDeletionEnabled();
@@ -217,7 +222,7 @@ public final class AppHibernationService extends SystemService {
*/
boolean isHibernatingForUser(String packageName, int userId) {
String methodName = "isHibernatingForUser";
- if (!checkHibernationEnabled(methodName)) {
+ if (!sIsServiceEnabled) {
return false;
}
getContext().enforceCallingOrSelfPermission(
@@ -246,7 +251,7 @@ public final class AppHibernationService extends SystemService {
* @param packageName package to check
*/
boolean isHibernatingGlobally(String packageName) {
- if (!checkHibernationEnabled("isHibernatingGlobally")) {
+ if (!sIsServiceEnabled) {
return false;
}
getContext().enforceCallingOrSelfPermission(
@@ -272,7 +277,7 @@ public final class AppHibernationService extends SystemService {
*/
void setHibernatingForUser(String packageName, int userId, boolean isHibernating) {
String methodName = "setHibernatingForUser";
- if (!checkHibernationEnabled(methodName)) {
+ if (!sIsServiceEnabled) {
return;
}
getContext().enforceCallingOrSelfPermission(
@@ -297,7 +302,8 @@ public final class AppHibernationService extends SystemService {
pkgState.hibernated = isHibernating;
if (isHibernating) {
- mBackgroundExecutor.execute(() -> hibernatePackageForUser(packageName, realUserId));
+ mBackgroundExecutor.execute(
+ () -> hibernatePackageForUser(packageName, realUserId, pkgState));
} else {
mBackgroundExecutor.execute(
() -> unhibernatePackageForUser(packageName, realUserId));
@@ -326,7 +332,7 @@ public final class AppHibernationService extends SystemService {
* @param isHibernating new hibernation state
*/
void setHibernatingGlobally(String packageName, boolean isHibernating) {
- if (!checkHibernationEnabled("setHibernatingGlobally")) {
+ if (!sIsServiceEnabled) {
return;
}
getContext().enforceCallingOrSelfPermission(
@@ -359,7 +365,7 @@ public final class AppHibernationService extends SystemService {
@NonNull List<String> getHibernatingPackagesForUser(int userId) {
ArrayList<String> hibernatingPackages = new ArrayList<>();
String methodName = "getHibernatingPackagesForUser";
- if (!checkHibernationEnabled(methodName)) {
+ if (!sIsServiceEnabled) {
return hibernatingPackages;
}
getContext().enforceCallingOrSelfPermission(
@@ -390,6 +396,9 @@ public final class AppHibernationService extends SystemService {
@Nullable Set<String> packageNames, int userId) {
Map<String, HibernationStats> statsMap = new ArrayMap<>();
String methodName = "getHibernationStatsForUser";
+ if (!sIsServiceEnabled) {
+ return statsMap;
+ }
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.MANAGE_APP_HIBERNATION,
"Caller does not have MANAGE_APP_HIBERNATION permission.");
@@ -412,8 +421,9 @@ public final class AppHibernationService extends SystemService {
+ "the package was uninstalled? ", pkgName, userId));
continue;
}
- HibernationStats stats = new HibernationStats(
- mGlobalHibernationStates.get(pkgName).savedByte);
+ long diskBytesSaved = mGlobalHibernationStates.get(pkgName).savedByte
+ + userPackageStates.get(pkgName).savedByte;
+ HibernationStats stats = new HibernationStats(diskBytesSaved);
statsMap.put(pkgName, stats);
}
}
@@ -424,16 +434,28 @@ public final class AppHibernationService extends SystemService {
* Put an app into hibernation for a given user, allowing user-level optimizations to occur. Do
* not hold {@link #mLock} while calling this to avoid deadlock scenarios.
*/
- private void hibernatePackageForUser(@NonNull String packageName, int userId) {
+ private void hibernatePackageForUser(@NonNull String packageName, int userId,
+ UserLevelState state) {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackage");
final long caller = Binder.clearCallingIdentity();
try {
+ ApplicationInfo info = mIPackageManager.getApplicationInfo(
+ packageName, PACKAGE_MATCH_FLAGS, userId);
+ StorageStats stats = mStorageStatsManager.queryStatsForPackage(
+ info.storageUuid, packageName, new UserHandle(userId));
mIActivityManager.forceStopPackage(packageName, userId);
mIPackageManager.deleteApplicationCacheFilesAsUser(packageName, userId,
null /* observer */);
+ synchronized (mLock) {
+ state.savedByte = stats.getCacheBytes();
+ }
} catch (RemoteException e) {
throw new IllegalStateException(
"Failed to hibernate due to manager not being available", e);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "Package name not found when querying storage stats", e);
+ } catch (IOException e) {
+ Slog.e(TAG, "Storage device not found", e);
} finally {
Binder.restoreCallingIdentity(caller);
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
@@ -677,6 +699,7 @@ public final class AppHibernationService extends SystemService {
for (String key : properties.getKeyset()) {
if (TextUtils.equals(KEY_APP_HIBERNATION_ENABLED, key)) {
sIsServiceEnabled = isDeviceConfigAppHibernationEnabled();
+ Slog.d(TAG, "App hibernation changed to enabled=" + sIsServiceEnabled);
break;
}
}
@@ -721,13 +744,6 @@ public final class AppHibernationService extends SystemService {
return true;
}
- private boolean checkHibernationEnabled(String methodName) {
- if (!sIsServiceEnabled) {
- Slog.w(TAG, String.format("Attempted to call %s on unsupported device.", methodName));
- }
- return sIsServiceEnabled;
- }
-
private void dump(PrintWriter pw) {
// Check usage stats permission since hibernation indirectly informs usage.
if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
@@ -923,6 +939,8 @@ public final class AppHibernationService extends SystemService {
UserManager getUserManager();
+ StorageStatsManager getStorageStatsManager();
+
Executor getBackgroundExecutor();
UsageStatsManagerInternal getUsageStatsManagerInternal();
@@ -972,6 +990,11 @@ public final class AppHibernationService extends SystemService {
}
@Override
+ public StorageStatsManager getStorageStatsManager() {
+ return mContext.getSystemService(StorageStatsManager.class);
+ }
+
+ @Override
public Executor getBackgroundExecutor() {
return mScheduledExecutorService;
}
diff --git a/services/core/java/com/android/server/apphibernation/UserLevelState.java b/services/core/java/com/android/server/apphibernation/UserLevelState.java
index 68c363c8256a..6a489d28ac17 100644
--- a/services/core/java/com/android/server/apphibernation/UserLevelState.java
+++ b/services/core/java/com/android/server/apphibernation/UserLevelState.java
@@ -28,6 +28,8 @@ final class UserLevelState {
public String packageName;
public boolean hibernated;
+ // Saved bytes from user level hibernation.
+ public long savedByte;
@CurrentTimeMillisLong
public long lastUnhibernatedMs;
@@ -36,6 +38,7 @@ final class UserLevelState {
UserLevelState(UserLevelState state) {
packageName = state.packageName;
hibernated = state.hibernated;
+ savedByte = state.savedByte;
lastUnhibernatedMs = state.lastUnhibernatedMs;
}
@@ -44,6 +47,7 @@ final class UserLevelState {
return "UserLevelState{"
+ "packageName='" + packageName + '\''
+ ", hibernated=" + hibernated + '\''
+ + ", savedByte=" + savedByte + '\''
+ ", lastUnhibernated=" + DATE_FORMAT.format(lastUnhibernatedMs)
+ '}';
}
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 1b0341c1ce26..e0b768d7c592 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -808,6 +808,7 @@ public class CameraServiceProxy extends SystemService
streamProtos[i].histogramBins = streamStats.getHistogramBins();
streamProtos[i].histogramCounts = streamStats.getHistogramCounts();
streamProtos[i].dynamicRangeProfile = streamStats.getDynamicRangeProfile();
+ streamProtos[i].streamUseCase = streamStats.getStreamUseCase();
if (CameraServiceProxy.DEBUG) {
String histogramTypeName =
@@ -828,7 +829,8 @@ public class CameraServiceProxy extends SystemService
+ Arrays.toString(streamProtos[i].histogramBins)
+ ", histogramCounts "
+ Arrays.toString(streamProtos[i].histogramCounts)
- + ", dynamicRangeProfile " + streamProtos[i].dynamicRangeProfile);
+ + ", dynamicRangeProfile " + streamProtos[i].dynamicRangeProfile
+ + ", streamUseCase " + streamProtos[i].streamUseCase);
}
}
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 8ce67a657740..76d06c8801f4 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -119,9 +119,7 @@ import static com.android.internal.util.XmlUtils.readBooleanAttribute;
import static com.android.internal.util.XmlUtils.readIntAttribute;
import static com.android.internal.util.XmlUtils.readLongAttribute;
import static com.android.internal.util.XmlUtils.readStringAttribute;
-import static com.android.internal.util.XmlUtils.readThisIntArrayXml;
import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
-import static com.android.internal.util.XmlUtils.writeIntArrayXml;
import static com.android.internal.util.XmlUtils.writeIntAttribute;
import static com.android.internal.util.XmlUtils.writeLongAttribute;
import static com.android.internal.util.XmlUtils.writeStringAttribute;
@@ -246,7 +244,6 @@ import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.StatLogger;
-import com.android.internal.util.XmlUtils;
import com.android.net.module.util.NetworkIdentityUtils;
import com.android.net.module.util.NetworkStatsUtils;
import com.android.net.module.util.PermissionUtils;
@@ -260,8 +257,6 @@ import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
import libcore.io.IoUtils;
-import org.xmlpull.v1.XmlPullParserException;
-
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -336,7 +331,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private static final int VERSION_ADDED_CYCLE = 11;
private static final int VERSION_ADDED_NETWORK_TYPES = 12;
private static final int VERSION_SUPPORTED_CARRIER_USAGE = 13;
- private static final int VERSION_LATEST = VERSION_SUPPORTED_CARRIER_USAGE;
+ private static final int VERSION_REMOVED_SUBSCRIPTION_PLANS = 14;
+ private static final int VERSION_LATEST = VERSION_REMOVED_SUBSCRIPTION_PLANS;
@VisibleForTesting
public static final int TYPE_WARNING = SystemMessage.NOTE_NET_WARNING;
@@ -349,7 +345,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private static final String TAG_POLICY_LIST = "policy-list";
private static final String TAG_NETWORK_POLICY = "network-policy";
- private static final String TAG_SUBSCRIPTION_PLAN = "subscription-plan";
private static final String TAG_UID_POLICY = "uid-policy";
private static final String TAG_APP_POLICY = "app-policy";
private static final String TAG_WHITELIST = "whitelist";
@@ -426,6 +421,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
* obj = oldBlockedReasons
*/
private static final int MSG_BLOCKED_REASON_CHANGED = 21;
+ /**
+ * Message to indicate that subscription plans expired and should be cleared.
+ * arg1 = subId
+ * arg2 = setSubscriptionPlans call ID
+ * obj = callingPackage
+ */
+ private static final int MSG_CLEAR_SUBSCRIPTION_PLANS = 22;
private static final int UID_MSG_STATE_CHANGED = 100;
private static final int UID_MSG_GONE = 101;
@@ -492,6 +494,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
/** Map from subId to package name that owns subscription plans. */
@GuardedBy("mNetworkPoliciesSecondLock")
final SparseArray<String> mSubscriptionPlansOwner = new SparseArray<>();
+ /** Map from subId to the ID of the clear plans request. */
+ @GuardedBy("mNetworkPoliciesSecondLock")
+ final SparseIntArray mSetSubscriptionPlansIds = new SparseIntArray();
+ /** Atomic integer to generate a new ID for each clear plans request. */
+ @GuardedBy("mNetworkPoliciesSecondLock")
+ int mSetSubscriptionPlansIdCounter = 0;
/** Map from subId to daily opportunistic quota. */
@GuardedBy("mNetworkPoliciesSecondLock")
@@ -2523,56 +2531,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
warningBytes, limitBytes, lastWarningSnooze,
lastLimitSnooze, metered, inferred));
}
-
- } else if (TAG_SUBSCRIPTION_PLAN.equals(tag)) {
- final String start = readStringAttribute(in, ATTR_CYCLE_START);
- final String end = readStringAttribute(in, ATTR_CYCLE_END);
- final String period = readStringAttribute(in, ATTR_CYCLE_PERIOD);
- final SubscriptionPlan.Builder builder = new SubscriptionPlan.Builder(
- RecurrenceRule.convertZonedDateTime(start),
- RecurrenceRule.convertZonedDateTime(end),
- RecurrenceRule.convertPeriod(period));
- builder.setTitle(readStringAttribute(in, ATTR_TITLE));
- builder.setSummary(readStringAttribute(in, ATTR_SUMMARY));
-
- final long limitBytes = readLongAttribute(in, ATTR_LIMIT_BYTES,
- SubscriptionPlan.BYTES_UNKNOWN);
- final int limitBehavior = readIntAttribute(in, ATTR_LIMIT_BEHAVIOR,
- SubscriptionPlan.LIMIT_BEHAVIOR_UNKNOWN);
- if (limitBytes != SubscriptionPlan.BYTES_UNKNOWN
- && limitBehavior != SubscriptionPlan.LIMIT_BEHAVIOR_UNKNOWN) {
- builder.setDataLimit(limitBytes, limitBehavior);
- }
-
- final long usageBytes = readLongAttribute(in, ATTR_USAGE_BYTES,
- SubscriptionPlan.BYTES_UNKNOWN);
- final long usageTime = readLongAttribute(in, ATTR_USAGE_TIME,
- SubscriptionPlan.TIME_UNKNOWN);
- if (usageBytes != SubscriptionPlan.BYTES_UNKNOWN
- && usageTime != SubscriptionPlan.TIME_UNKNOWN) {
- builder.setDataUsage(usageBytes, usageTime);
- }
-
- final int subId = readIntAttribute(in, ATTR_SUB_ID);
- final String ownerPackage = readStringAttribute(in, ATTR_OWNER_PACKAGE);
-
- if (version >= VERSION_ADDED_NETWORK_TYPES) {
- final int depth = in.getDepth();
- while (XmlUtils.nextElementWithin(in, depth)) {
- if (TAG_XML_UTILS_INT_ARRAY.equals(in.getName())
- && ATTR_NETWORK_TYPES.equals(
- readStringAttribute(in, ATTR_XML_UTILS_NAME))) {
- final int[] networkTypes =
- readThisIntArrayXml(in, TAG_XML_UTILS_INT_ARRAY, null);
- builder.setNetworkTypes(networkTypes);
- }
- }
- }
-
- final SubscriptionPlan plan = builder.build();
- mSubscriptionPlans.put(subId, ArrayUtils.appendElement(
- SubscriptionPlan.class, mSubscriptionPlans.get(subId), plan));
- mSubscriptionPlansOwner.put(subId, ownerPackage);
} else if (TAG_UID_POLICY.equals(tag)) {
final int uid = readIntAttribute(in, ATTR_UID);
final int policy = readIntAttribute(in, ATTR_POLICY);
@@ -2763,38 +2721,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
out.endTag(null, TAG_NETWORK_POLICY);
}
- // write all known subscription plans
- for (int i = 0; i < mSubscriptionPlans.size(); i++) {
- final int subId = mSubscriptionPlans.keyAt(i);
- if (subId == INVALID_SUBSCRIPTION_ID) continue;
- final String ownerPackage = mSubscriptionPlansOwner.get(subId);
- final SubscriptionPlan[] plans = mSubscriptionPlans.valueAt(i);
- if (ArrayUtils.isEmpty(plans)) continue;
-
- for (SubscriptionPlan plan : plans) {
- out.startTag(null, TAG_SUBSCRIPTION_PLAN);
- writeIntAttribute(out, ATTR_SUB_ID, subId);
- writeStringAttribute(out, ATTR_OWNER_PACKAGE, ownerPackage);
- final RecurrenceRule cycleRule = plan.getCycleRule();
- writeStringAttribute(out, ATTR_CYCLE_START,
- RecurrenceRule.convertZonedDateTime(cycleRule.start));
- writeStringAttribute(out, ATTR_CYCLE_END,
- RecurrenceRule.convertZonedDateTime(cycleRule.end));
- writeStringAttribute(out, ATTR_CYCLE_PERIOD,
- RecurrenceRule.convertPeriod(cycleRule.period));
- writeStringAttribute(out, ATTR_TITLE, plan.getTitle());
- writeStringAttribute(out, ATTR_SUMMARY, plan.getSummary());
- writeLongAttribute(out, ATTR_LIMIT_BYTES, plan.getDataLimitBytes());
- writeIntAttribute(out, ATTR_LIMIT_BEHAVIOR, plan.getDataLimitBehavior());
- writeLongAttribute(out, ATTR_USAGE_BYTES, plan.getDataUsageBytes());
- writeLongAttribute(out, ATTR_USAGE_TIME, plan.getDataUsageTime());
- try {
- writeIntArrayXml(plan.getNetworkTypes(), ATTR_NETWORK_TYPES, out);
- } catch (XmlPullParserException ignored) { }
- out.endTag(null, TAG_SUBSCRIPTION_PLAN);
- }
- }
-
// write all known uid policies
for (int i = 0; i < mUidPolicy.size(); i++) {
final int uid = mUidPolicy.keyAt(i);
@@ -3668,7 +3594,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
@Override
- public void setSubscriptionPlans(int subId, SubscriptionPlan[] plans, String callingPackage) {
+ public void setSubscriptionPlans(int subId, SubscriptionPlan[] plans,
+ long expirationDurationMillis, String callingPackage) {
enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage);
enforceSubscriptionPlanValidity(plans);
@@ -3678,31 +3605,44 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final long token = Binder.clearCallingIdentity();
try {
- synchronized (mUidRulesFirstLock) {
- synchronized (mNetworkPoliciesSecondLock) {
- mSubscriptionPlans.put(subId, plans);
- mSubscriptionPlansOwner.put(subId, callingPackage);
+ setSubscriptionPlansInternal(subId, plans, expirationDurationMillis, callingPackage);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
- final String subscriberId = mSubIdToSubscriberId.get(subId, null);
- if (subscriberId != null) {
- ensureActiveCarrierPolicyAL(subId, subscriberId);
- maybeUpdateCarrierPolicyCycleAL(subId, subscriberId);
- } else {
- Slog.wtf(TAG, "Missing subscriberId for subId " + subId);
- }
+ private void setSubscriptionPlansInternal(int subId, SubscriptionPlan[] plans,
+ long expirationDurationMillis, String callingPackage) {
+ synchronized (mUidRulesFirstLock) {
+ synchronized (mNetworkPoliciesSecondLock) {
+ mSubscriptionPlans.put(subId, plans);
+ mSubscriptionPlansOwner.put(subId, callingPackage);
- handleNetworkPoliciesUpdateAL(true);
+ final String subscriberId = mSubIdToSubscriberId.get(subId, null);
+ if (subscriberId != null) {
+ ensureActiveCarrierPolicyAL(subId, subscriberId);
+ maybeUpdateCarrierPolicyCycleAL(subId, subscriberId);
+ } else {
+ Slog.wtf(TAG, "Missing subscriberId for subId " + subId);
}
- }
- final Intent intent = new Intent(SubscriptionManager.ACTION_SUBSCRIPTION_PLANS_CHANGED);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
- mContext.sendBroadcast(intent, android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS);
- mHandler.sendMessage(
- mHandler.obtainMessage(MSG_SUBSCRIPTION_PLANS_CHANGED, subId, 0, plans));
- } finally {
- Binder.restoreCallingIdentity(token);
+ handleNetworkPoliciesUpdateAL(true);
+
+ final Intent intent = new Intent(
+ SubscriptionManager.ACTION_SUBSCRIPTION_PLANS_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
+ mContext.sendBroadcast(intent,
+ android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS);
+ mHandler.sendMessage(mHandler.obtainMessage(
+ MSG_SUBSCRIPTION_PLANS_CHANGED, subId, 0, plans));
+ final int setPlansId = mSetSubscriptionPlansIdCounter++;
+ mSetSubscriptionPlansIds.put(subId, setPlansId);
+ if (expirationDurationMillis > 0) {
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_SUBSCRIPTION_PLANS,
+ subId, setPlansId, callingPackage), expirationDurationMillis);
+ }
+ }
}
}
@@ -3728,7 +3668,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
@Override
public void setSubscriptionOverride(int subId, int overrideMask, int overrideValue,
- int[] networkTypes, long timeoutMillis, String callingPackage) {
+ int[] networkTypes, long expirationDurationMillis, String callingPackage) {
enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage);
final ArraySet<Integer> allNetworksSet = new ArraySet<>();
@@ -3766,10 +3706,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
args.arg3 = overrideValue;
args.arg4 = applicableNetworks.toArray();
mHandler.sendMessage(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE, args));
- if (timeoutMillis > 0) {
+ if (expirationDurationMillis > 0) {
args.arg3 = 0;
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE, args),
- timeoutMillis);
+ expirationDurationMillis);
}
}
}
@@ -5184,6 +5124,22 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
mListeners.finishBroadcast();
return true;
}
+ case MSG_CLEAR_SUBSCRIPTION_PLANS: {
+ synchronized (mUidRulesFirstLock) {
+ synchronized (mNetworkPoliciesSecondLock) {
+ int subId = msg.arg1;
+ if (msg.arg2 == mSetSubscriptionPlansIds.get(subId)) {
+ if (LOGD) Slog.d(TAG, "Clearing expired subscription plans.");
+ setSubscriptionPlansInternal(subId, new SubscriptionPlan[]{},
+ 0 /* expirationDurationMillis */,
+ (String) msg.obj /* callingPackage */);
+ } else {
+ if (LOGD) Slog.d(TAG, "Ignoring stale CLEAR_SUBSCRIPTION_PLANS.");
+ }
+ }
+ }
+ return true;
+ }
case MSG_BLOCKED_REASON_CHANGED: {
final int uid = msg.arg1;
final int newBlockedReasons = msg.arg2;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 6b27321f856f..bc38087ebc73 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2463,7 +2463,7 @@ public class NotificationManagerService extends SystemService {
};
mAllowFgsDismissal = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED, false);
+ SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED, true);
DeviceConfig.addOnPropertiesChangedListener(
DeviceConfig.NAMESPACE_SYSTEMUI,
new HandlerExecutor(mHandler),
diff --git a/services/core/java/com/android/server/sensorprivacy/CameraPrivacyLightController.java b/services/core/java/com/android/server/sensorprivacy/CameraPrivacyLightController.java
new file mode 100644
index 000000000000..750d4004cb60
--- /dev/null
+++ b/services/core/java/com/android/server/sensorprivacy/CameraPrivacyLightController.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2022 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.sensorprivacy;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.hardware.lights.Light;
+import android.hardware.lights.LightState;
+import android.hardware.lights.LightsManager;
+import android.hardware.lights.LightsRequest;
+import android.permission.PermissionManager;
+import android.util.ArraySet;
+
+import com.android.internal.R;
+import com.android.server.FgThread;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+class CameraPrivacyLightController implements AppOpsManager.OnOpActiveChangedListener {
+
+ private final Context mContext;
+ private final LightsManager mLightsManager;
+
+ private final Set<String> mActivePackages = new ArraySet<>();
+ private final Set<String> mActivePhonePackages = new ArraySet<>();
+
+ private final int mCameraPrivacyLightColor;
+
+ private final List<Light> mCameraLights = new ArrayList<>();
+ private final AppOpsManager mAppOpsManager;
+
+ private LightsManager.LightsSession mLightsSession = null;
+
+ CameraPrivacyLightController(Context context) {
+ mContext = context;
+
+ mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
+ mLightsManager = mContext.getSystemService(LightsManager.class);
+
+ mCameraPrivacyLightColor = mContext.getColor(R.color.camera_privacy_light);
+
+ List<Light> lights = mLightsManager.getLights();
+ for (int i = 0; i < lights.size(); i++) {
+ Light light = lights.get(i);
+ if (light.getType() == Light.LIGHT_TYPE_CAMERA) {
+ mCameraLights.add(light);
+ }
+ }
+
+ if (mCameraLights.isEmpty()) {
+ return;
+ }
+
+ mAppOpsManager.startWatchingActive(
+ new String[] {AppOpsManager.OPSTR_CAMERA, AppOpsManager.OPSTR_PHONE_CALL_CAMERA},
+ FgThread.getExecutor(), this);
+ }
+
+ @Override
+ public void onOpActiveChanged(String op, int uid, String packageName, boolean active) {
+ final Set<String> activePackages;
+ if (AppOpsManager.OPSTR_CAMERA.equals(op)) {
+ activePackages = mActivePackages;
+ } else if (AppOpsManager.OPSTR_PHONE_CALL_CAMERA.equals(op)) {
+ activePackages = mActivePhonePackages;
+ } else {
+ return;
+ }
+
+ if (active) {
+ activePackages.add(packageName);
+ } else {
+ activePackages.remove(packageName);
+ }
+
+ updateLightSession();
+ }
+
+ private void updateLightSession() {
+ Set<String> exemptedPackages = PermissionManager.getIndicatorExemptedPackages(mContext);
+
+ boolean shouldSessionEnd = exemptedPackages.containsAll(mActivePackages)
+ && exemptedPackages.containsAll(mActivePhonePackages);
+
+ if (shouldSessionEnd) {
+ if (mLightsSession == null) {
+ return;
+ }
+
+ mLightsSession.close();
+ mLightsSession = null;
+ } else {
+ if (mLightsSession != null) {
+ return;
+ }
+
+ LightsRequest.Builder requestBuilder = new LightsRequest.Builder();
+ for (int i = 0; i < mCameraLights.size(); i++) {
+ requestBuilder.addLight(mCameraLights.get(i),
+ new LightState.Builder()
+ .setColor(mCameraPrivacyLightColor)
+ .build());
+ }
+
+ mLightsSession = mLightsManager.openSession(Integer.MAX_VALUE);
+ mLightsSession.requestLights(requestBuilder.build());
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
index e9b5f1156d39..040fffa885f1 100644
--- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
@@ -154,6 +154,8 @@ public final class SensorPrivacyService extends SystemService {
private final AppOpsManagerInternal mAppOpsManagerInternal;
private final TelephonyManager mTelephonyManager;
+ private CameraPrivacyLightController mCameraPrivacyLightController;
+
private final IBinder mAppOpsRestrictionToken = new Binder();
private SensorPrivacyManagerInternalImpl mSensorPrivacyManagerInternal;
@@ -190,6 +192,8 @@ public final class SensorPrivacyService extends SystemService {
if (phase == PHASE_SYSTEM_SERVICES_READY) {
mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
mEmergencyCallHelper = new EmergencyCallHelper();
+ } else if (phase == PHASE_ACTIVITY_MANAGER_READY) {
+ mCameraPrivacyLightController = new CameraPrivacyLightController(mContext);
}
}
diff --git a/services/core/java/com/android/server/wm/OverlayHost.java b/services/core/java/com/android/server/wm/TrustedOverlayHost.java
index 90f5b09968ea..975b21c6f02c 100644
--- a/services/core/java/com/android/server/wm/OverlayHost.java
+++ b/services/core/java/com/android/server/wm/TrustedOverlayHost.java
@@ -32,14 +32,20 @@ import java.util.ArrayList;
*
* Also handles multiplexing of event dispatch and tracking of overlays
* to make things easier for WindowContainer.
+ *
+ * These overlays are to be used for various types of System UI and UI
+ * under the systems control. Provided SurfacePackages will be able
+ * to overlay application content, without engaging the usual cross process
+ * obscured touch filtering mechanisms. It's imperative that all UI provided
+ * be under complete control of the system.
*/
-class OverlayHost {
+class TrustedOverlayHost {
// Lazily initialized when required
SurfaceControl mSurfaceControl;
final ArrayList<SurfaceControlViewHost.SurfacePackage> mOverlays = new ArrayList<>();
final WindowManagerService mWmService;
- OverlayHost(WindowManagerService wms) {
+ TrustedOverlayHost(WindowManagerService wms) {
mWmService = wms;
}
@@ -51,6 +57,8 @@ class OverlayHost {
.setName("Overlay Host Leash");
mSurfaceControl = b.build();
+ SurfaceControl.Transaction t = mWmService.mTransactionFactory.get();
+ t.setTrustedOverlay(mSurfaceControl, true).apply();
}
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 1bd153b2a577..8a373bf5c09c 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -314,7 +314,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
private final List<WindowContainerListener> mListeners = new ArrayList<>();
- protected OverlayHost mOverlayHost;
+ protected TrustedOverlayHost mOverlayHost;
WindowContainer(WindowManagerService wms) {
mWmService = wms;
@@ -3600,9 +3600,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
@AnimationType int type, @Nullable AnimationAdapter snapshotAnim);
}
- void addOverlay(SurfaceControlViewHost.SurfacePackage overlay) {
+ void addTrustedOverlay(SurfaceControlViewHost.SurfacePackage overlay) {
if (mOverlayHost == null) {
- mOverlayHost = new OverlayHost(mWmService);
+ mOverlayHost = new TrustedOverlayHost(mWmService);
}
mOverlayHost.addOverlay(overlay, mSurfaceControl);
@@ -3613,11 +3613,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
overlay.getRemoteInterface().onConfigurationChanged(getConfiguration());
} catch (Exception e) {
Slog.e(TAG, "Error sending initial configuration change to WindowContainer overlay");
- removeOverlay(overlay);
+ removeTrustedOverlay(overlay);
}
}
- void removeOverlay(SurfaceControlViewHost.SurfacePackage overlay) {
+ void removeTrustedOverlay(SurfaceControlViewHost.SurfacePackage overlay) {
if (mOverlayHost != null && !mOverlayHost.removeOverlay(overlay)) {
mOverlayHost.release();
mOverlayHost = null;
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 4900f9292f2a..9585a4b93a97 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -827,6 +827,11 @@ public abstract class WindowManagerInternal {
* Internal methods for other parts of SystemServer to manage
* SurfacePackage based overlays on tasks.
*
+ * Since these overlays will overlay application content, they exist
+ * in a container with setTrustedOverlay(true). This means its imperative
+ * that this overlay feature only be used with UI completely under the control
+ * of the system, without 3rd party content.
+ *
* Callers prepare a view hierarchy with SurfaceControlViewHost
* and send the package to WM here. The remote view hierarchy will receive
* configuration change, lifecycle events, etc, forwarded over the
@@ -837,8 +842,10 @@ public abstract class WindowManagerInternal {
* The embedded hierarchy exists in a coordinate space relative to the task
* bounds.
*/
- public abstract void addTaskOverlay(int taskId, SurfaceControlViewHost.SurfacePackage overlay);
- public abstract void removeTaskOverlay(int taskId, SurfaceControlViewHost.SurfacePackage overlay);
+ public abstract void addTrustedTaskOverlay(int taskId,
+ SurfaceControlViewHost.SurfacePackage overlay);
+ public abstract void removeTrustedTaskOverlay(int taskId,
+ SurfaceControlViewHost.SurfacePackage overlay);
/**
* Get a SurfaceControl that is the container layer that should be used to receive input to
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c4907c2d9903..c9c3f1da5b40 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8012,27 +8012,29 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public boolean shouldRestoreImeVisibility(IBinder imeTargetWindowToken) {
return WindowManagerService.this.shouldRestoreImeVisibility(imeTargetWindowToken);
- }
+ }
@Override
- public void addTaskOverlay(int taskId, SurfaceControlViewHost.SurfacePackage overlay) {
+ public void addTrustedTaskOverlay(int taskId,
+ SurfaceControlViewHost.SurfacePackage overlay) {
synchronized (mGlobalLock) {
final Task task = mRoot.getRootTask(taskId);
if (task == null) {
throw new IllegalArgumentException("no task with taskId" + taskId);
}
- task.addOverlay(overlay);
+ task.addTrustedOverlay(overlay);
}
}
@Override
- public void removeTaskOverlay(int taskId, SurfaceControlViewHost.SurfacePackage overlay) {
+ public void removeTrustedTaskOverlay(int taskId,
+ SurfaceControlViewHost.SurfacePackage overlay) {
synchronized (mGlobalLock) {
final Task task = mRoot.getRootTask(taskId);
if (task == null) {
throw new IllegalArgumentException("no task with taskId" + taskId);
}
- task.removeOverlay(overlay);
+ task.removeTrustedOverlay(overlay);
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
index 7f571195f5f8..ed232e5458b0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
@@ -406,7 +406,7 @@ public final class GameServiceProviderInstanceImplTest {
mFakeGameSessionService.removePendingFutureForTaskId(10)
.complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
- verify(mMockWindowManagerInternal).addTaskOverlay(eq(10), eq(mockSurfacePackage10));
+ verify(mMockWindowManagerInternal).addTrustedTaskOverlay(eq(10), eq(mockSurfacePackage10));
}
@Test
@@ -556,8 +556,9 @@ public final class GameServiceProviderInstanceImplTest {
stopTask(10);
- verify(mMockWindowManagerInternal).addTaskOverlay(eq(10), eq(mockSurfacePackage10));
- verify(mMockWindowManagerInternal).removeTaskOverlay(eq(10), eq(mockSurfacePackage10));
+ verify(mMockWindowManagerInternal).addTrustedTaskOverlay(eq(10), eq(mockSurfacePackage10));
+ verify(mMockWindowManagerInternal).removeTrustedTaskOverlay(eq(10),
+ eq(mockSurfacePackage10));
}
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java
new file mode 100644
index 000000000000..fa3e05a6b001
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2022 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.sensorprivacy;
+
+import static android.app.AppOpsManager.OPSTR_CAMERA;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.times;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.hardware.lights.Light;
+import android.hardware.lights.LightsManager;
+import android.hardware.lights.LightsRequest;
+import android.permission.PermissionManager;
+import android.util.ArraySet;
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class CameraPrivacyLightControllerTest {
+
+ private MockitoSession mMockitoSession;
+
+ @Mock
+ private Context mContext;
+
+ @Mock
+ private LightsManager mLightsManager;
+
+ @Mock
+ private AppOpsManager mAppOpsManager;
+
+ @Mock
+ private LightsManager.LightsSession mLightsSession;
+
+ private ArgumentCaptor<AppOpsManager.OnOpActiveChangedListener> mAppOpsListenerCaptor =
+ ArgumentCaptor.forClass(AppOpsManager.OnOpActiveChangedListener.class);
+
+ private ArgumentCaptor<LightsRequest> mLightsRequestCaptor =
+ ArgumentCaptor.forClass(LightsRequest.class);
+
+ private Set<String> mExemptedPackages = new ArraySet<>();
+ private List<Light> mLights = new ArrayList<>();
+
+ private int mNextLightId = 1;
+
+ @Before
+ public void setUp() {
+ mMockitoSession = ExtendedMockito.mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.WARN)
+ .spyStatic(PermissionManager.class)
+ .startMocking();
+
+ doReturn(mLightsManager).when(mContext).getSystemService(LightsManager.class);
+ doReturn(mAppOpsManager).when(mContext).getSystemService(AppOpsManager.class);
+
+ doReturn(mLights).when(mLightsManager).getLights();
+ doReturn(mLightsSession).when(mLightsManager).openSession(anyInt());
+
+ doReturn(mExemptedPackages)
+ .when(() -> PermissionManager.getIndicatorExemptedPackages(any()));
+ }
+
+ @After
+ public void tearDown() {
+ mExemptedPackages.clear();
+ mLights.clear();
+
+ mMockitoSession.finishMocking();
+ }
+
+ @Test
+ public void testAppsOpsListenerNotRegisteredWithoutCameraLights() {
+ mLights.add(getNextLight(false));
+ new CameraPrivacyLightController(mContext);
+
+ verify(mAppOpsManager, times(0)).startWatchingActive(any(), any(), any());
+ }
+
+ @Test
+ public void testAppsOpsListenerRegisteredWithCameraLight() {
+ mLights.add(getNextLight(true));
+
+ new CameraPrivacyLightController(mContext);
+
+ verify(mAppOpsManager, times(1)).startWatchingActive(any(), any(), any());
+ }
+
+ @Test
+ public void testAllCameraLightsAreRequestedOnOpActive() {
+ Random r = new Random(0);
+ for (int i = 0; i < 30; i++) {
+ mLights.add(getNextLight(r.nextBoolean()));
+ }
+
+ new CameraPrivacyLightController(mContext);
+
+ // Verify no session has been opened at this point.
+ verify(mLightsManager, times(0)).openSession(anyInt());
+
+ // Set camera op as active.
+ verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture());
+ mAppOpsListenerCaptor.getValue().onOpActiveChanged(OPSTR_CAMERA, 10101, "pkg1", true);
+
+ // Verify session has been opened exactly once
+ verify(mLightsManager, times(1)).openSession(anyInt());
+
+ verify(mLightsSession).requestLights(mLightsRequestCaptor.capture());
+ assertEquals("requestLights() not invoked exactly once",
+ 1, mLightsRequestCaptor.getAllValues().size());
+
+ List<Integer> expectedCameraLightIds = mLights.stream()
+ .filter(l -> l.getType() == Light.LIGHT_TYPE_CAMERA)
+ .map(l -> l.getId())
+ .collect(Collectors.toList());
+ List<Integer> lightsRequestLightIds = mLightsRequestCaptor.getValue().getLights();
+
+ // We don't own lights framework, don't assume it will retain order
+ lightsRequestLightIds.sort(Integer::compare);
+ expectedCameraLightIds.sort(Integer::compare);
+
+ assertEquals(expectedCameraLightIds, lightsRequestLightIds);
+ }
+
+ @Test
+ public void testWillOnlyOpenOnceWhenTwoPackagesStartOp() {
+ mLights.add(getNextLight(true));
+
+ new CameraPrivacyLightController(mContext);
+
+ verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture());
+
+ AppOpsManager.OnOpActiveChangedListener listener = mAppOpsListenerCaptor.getValue();
+ listener.onOpActiveChanged(OPSTR_CAMERA, 10101, "pkg1", true);
+ verify(mLightsManager, times(1)).openSession(anyInt());
+ listener.onOpActiveChanged(OPSTR_CAMERA, 10102, "pkg2", true);
+ verify(mLightsManager, times(1)).openSession(anyInt());
+ }
+
+ @Test
+ public void testWillCloseOnFinishOp() {
+ mLights.add(getNextLight(true));
+
+ new CameraPrivacyLightController(mContext);
+
+ verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture());
+
+ AppOpsManager.OnOpActiveChangedListener listener = mAppOpsListenerCaptor.getValue();
+ listener.onOpActiveChanged(OPSTR_CAMERA, 10101, "pkg1", true);
+
+ verify(mLightsSession, times(0)).close();
+ listener.onOpActiveChanged(OPSTR_CAMERA, 10101, "pkg1", false);
+ verify(mLightsSession, times(1)).close();
+ }
+
+ @Test
+ public void testWillCloseOnFinishOpForAllPackages() {
+ mLights.add(getNextLight(true));
+
+ new CameraPrivacyLightController(mContext);
+
+ int numUids = 100;
+ List<Integer> uids = new ArrayList<>(numUids);
+ for (int i = 0; i < numUids; i++) {
+ uids.add(10001 + i);
+ }
+
+ verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture());
+
+ AppOpsManager.OnOpActiveChangedListener listener = mAppOpsListenerCaptor.getValue();
+
+ for (int i = 0; i < numUids; i++) {
+ listener.onOpActiveChanged(OPSTR_CAMERA, uids.get(i), "pkg" + (int) uids.get(i), true);
+ }
+
+ // Change the order which their ops are finished
+ Collections.shuffle(uids, new Random(0));
+
+ for (int i = 0; i < numUids - 1; i++) {
+ listener.onOpActiveChanged(OPSTR_CAMERA, uids.get(i), "pkg" + (int) uids.get(i), false);
+ }
+
+ verify(mLightsSession, times(0)).close();
+ int lastUid = uids.get(numUids - 1);
+ listener.onOpActiveChanged(OPSTR_CAMERA, lastUid, "pkg" + lastUid, false);
+ verify(mLightsSession, times(1)).close();
+ }
+
+ @Test
+ public void testWontOpenForExemptedPackage() {
+ mLights.add(getNextLight(true));
+ mExemptedPackages.add("pkg1");
+
+ new CameraPrivacyLightController(mContext);
+
+ verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture());
+
+ AppOpsManager.OnOpActiveChangedListener listener = mAppOpsListenerCaptor.getValue();
+ listener.onOpActiveChanged(OPSTR_CAMERA, 10101, "pkg1", true);
+ verify(mLightsManager, times(0)).openSession(anyInt());
+ }
+
+ private Light getNextLight(boolean cameraType) {
+ Light light = ExtendedMockito.mock(Light.class);
+ if (cameraType) {
+ doReturn(Light.LIGHT_TYPE_CAMERA).when(light).getType();
+ } else {
+ doReturn(Light.LIGHT_TYPE_MICROPHONE).when(light).getType();
+ }
+ doReturn(mNextLightId++).when(light).getId();
+ return light;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
index e1aa08d9176a..51607e528216 100644
--- a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
@@ -29,6 +29,8 @@ import static org.mockito.AdditionalAnswers.returnsArgAt;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.longThat;
import static org.mockito.Mockito.doAnswer;
@@ -38,6 +40,8 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.app.IActivityManager;
+import android.app.usage.StorageStats;
+import android.app.usage.StorageStatsManager;
import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStatsManagerInternal;
import android.app.usage.UsageStatsManagerInternal.UsageEventListener;
@@ -48,6 +52,7 @@ import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
@@ -68,10 +73,12 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.UUID;
import java.util.concurrent.Executor;
/**
@@ -104,6 +111,8 @@ public final class AppHibernationServiceTest {
@Mock
private UserManager mUserManager;
@Mock
+ private StorageStatsManager mStorageStatsManager;
+ @Mock
private HibernationStateDiskStore<UserLevelState> mUserLevelDiskStore;
@Mock
private UsageStatsManagerInternal mUsageStatsManagerInternal;
@@ -115,7 +124,7 @@ public final class AppHibernationServiceTest {
private ArgumentCaptor<UsageEventListener> mUsageEventListenerCaptor;
@Before
- public void setUp() throws RemoteException {
+ public void setUp() throws RemoteException, PackageManager.NameNotFoundException, IOException {
// Share class loader to allow access to package-private classes
System.setProperty("dexmaker.share_classloader", "true");
MockitoAnnotations.initMocks(this);
@@ -140,6 +149,11 @@ public final class AppHibernationServiceTest {
packages.add(makePackageInfo(PACKAGE_NAME_3));
doReturn(new ParceledListSlice<>(packages)).when(mIPackageManager).getInstalledPackages(
longThat(arg -> (arg & MATCH_ANY_USER) != 0), anyInt());
+ doReturn(mock(ApplicationInfo.class)).when(mIPackageManager).getApplicationInfo(
+ any(), anyLong(), anyInt());
+ StorageStats storageStats = new StorageStats();
+ doReturn(storageStats).when(mStorageStatsManager).queryStatsForPackage(
+ (UUID) any(), anyString(), any());
mAppHibernationService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
UserInfo userInfo = addUser(USER_ID_1);
@@ -381,18 +395,31 @@ public final class AppHibernationServiceTest {
}
@Test
- public void testGetHibernationStatsForUser_getsStatsForPackage() {
- // GIVEN a package is hibernating globally and for a user
+ public void testGetHibernationStatsForUser_getsStatsForPackage()
+ throws PackageManager.NameNotFoundException, IOException, RemoteException {
+ // GIVEN a package is hibernating globally and for a user with some storage saved
+ final long cacheSavings = 1000;
+ StorageStats storageStats = new StorageStats();
+ storageStats.cacheBytes = cacheSavings;
+ doReturn(storageStats).when(mStorageStatsManager).queryStatsForPackage(
+ (UUID) any(), eq(PACKAGE_NAME_1), any());
+ final long oatDeletionSavings = 2000;
+ doReturn(oatDeletionSavings).when(mPackageManagerInternal).deleteOatArtifactsOfPackage(
+ PACKAGE_NAME_1);
+
mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_1, true);
mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true);
// WHEN we ask for the hibernation stats for the package
- Map<String, HibernationStats> stats =
+ Map<String, HibernationStats> statsMap =
mAppHibernationService.getHibernationStatsForUser(
Set.of(PACKAGE_NAME_1), USER_ID_1);
- // THEN the stats exist for the package
- assertTrue(stats.containsKey(PACKAGE_NAME_1));
+ // THEN the stats exist for the package and add up to the OAT deletion and cache deletion
+ // savings
+ HibernationStats stats = statsMap.get(PACKAGE_NAME_1);
+ assertNotNull(stats);
+ assertEquals(cacheSavings + oatDeletionSavings, stats.getDiskBytesSaved());
}
@Test
@@ -519,6 +546,11 @@ public final class AppHibernationServiceTest {
}
@Override
+ public StorageStatsManager getStorageStatsManager() {
+ return mStorageStatsManager;
+ }
+
+ @Override
public UsageStatsManagerInternal getUsageStatsManagerInternal() {
return mUsageStatsManagerInternal;
}
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 94cf20f9c15b..a7b045f10f2c 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -2237,7 +2237,7 @@ public class NetworkPolicyManagerServiceTest {
private void setSubscriptionPlans(int subId, SubscriptionPlan[] plans, String callingPackage)
throws InterruptedException {
- mService.setSubscriptionPlans(subId, plans, callingPackage);
+ mService.setSubscriptionPlans(subId, plans, 0, callingPackage);
// setSubscriptionPlans() triggers async events, wait for those to be completed before
// moving forward as they could interfere with the tests later.
postMsgAndWaitForCompletion();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 9f92294135c0..d4d076566fb3 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -372,6 +372,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
"android.permission.WRITE_DEVICE_CONFIG",
"android.permission.READ_DEVICE_CONFIG",
"android.permission.READ_CONTACTS");
+ Settings.Secure.putIntForUser(
+ getContext().getContentResolver(),
+ Settings.Secure.NOTIFICATION_PERMISSION_ENABLED, 0, USER_SYSTEM);
MockitoAnnotations.initMocks(this);
diff --git a/telephony/java/android/service/euicc/EuiccService.java b/telephony/java/android/service/euicc/EuiccService.java
index 184e1541f1b0..30ed7c287421 100644
--- a/telephony/java/android/service/euicc/EuiccService.java
+++ b/telephony/java/android/service/euicc/EuiccService.java
@@ -257,6 +257,13 @@ public abstract class EuiccService extends Service {
"android.service.euicc.extra.RESOLUTION_CARD_ID";
/**
+ * Intent extra set for resolution requests containing an int indicating the subscription id
+ * to be enabled.
+ */
+ public static final String EXTRA_RESOLUTION_SUBSCRIPTION_ID =
+ "android.service.euicc.extra.RESOLUTION_SUBSCRIPTION_ID";
+
+ /**
* Intent extra set for resolution requests containing an int indicating the current port index.
*/
public static final String EXTRA_RESOLUTION_PORT_INDEX =
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 250e55cf5014..0aaafef492bf 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2863,10 +2863,43 @@ public class SubscriptionManager {
* outlined above.
* @throws IllegalArgumentException if plans don't meet the requirements
* defined in {@link SubscriptionPlan}.
+ * @deprecated use {@link #setSubscriptionPlans(int, List, long)} instead.
*/
+ @Deprecated
public void setSubscriptionPlans(int subId, @NonNull List<SubscriptionPlan> plans) {
+ setSubscriptionPlans(subId, plans, 0);
+ }
+
+ /**
+ * Set the description of the billing relationship plan between a carrier
+ * and a specific subscriber.
+ * <p>
+ * This method is only accessible to the following narrow set of apps:
+ * <ul>
+ * <li>The carrier app for this subscriberId, as determined by
+ * {@link TelephonyManager#hasCarrierPrivileges()}.
+ * <li>The carrier app explicitly delegated access through
+ * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}.
+ * </ul>
+ *
+ * @param subId the subscriber this relationship applies to. An empty list
+ * may be sent to clear any existing plans.
+ * @param plans the list of plans. The first plan is always the primary and
+ * most important plan. Any additional plans are secondary and
+ * may not be displayed or used by decision making logic.
+ * @param expirationDurationMillis the duration after which the subscription plans
+ * will be automatically cleared, or {@code 0} to leave the plans until
+ * explicitly cleared, or the next reboot, whichever happens first.
+ * @throws SecurityException if the caller doesn't meet the requirements
+ * outlined above.
+ * @throws IllegalArgumentException if plans don't meet the requirements
+ * defined in {@link SubscriptionPlan}.
+ */
+ public void setSubscriptionPlans(int subId, @NonNull List<SubscriptionPlan> plans,
+ @DurationMillisLong long expirationDurationMillis) {
getNetworkPolicyManager().setSubscriptionPlans(subId,
- plans.toArray(new SubscriptionPlan[plans.size()]), mContext.getOpPackageName());
+ plans.toArray(new SubscriptionPlan[0]), expirationDurationMillis,
+ mContext.getOpPackageName());
}
/**
@@ -2885,17 +2918,17 @@ public class SubscriptionManager {
* @param subId the subscriber this override applies to.
* @param overrideUnmetered set if the billing relationship should be
* considered unmetered.
- * @param timeoutMillis the timeout after which the requested override will
- * be automatically cleared, or {@code 0} to leave in the
+ * @param expirationDurationMillis the duration after which the requested override
+ * will be automatically cleared, or {@code 0} to leave in the
* requested state until explicitly cleared, or the next reboot,
* whichever happens first.
* @throws SecurityException if the caller doesn't meet the requirements
* outlined above.
*/
public void setSubscriptionOverrideUnmetered(int subId, boolean overrideUnmetered,
- @DurationMillisLong long timeoutMillis) {
+ @DurationMillisLong long expirationDurationMillis) {
setSubscriptionOverrideUnmetered(subId, overrideUnmetered,
- TelephonyManager.getAllNetworkTypes(), timeoutMillis);
+ TelephonyManager.getAllNetworkTypes(), expirationDurationMillis);
}
/**
@@ -2917,8 +2950,8 @@ public class SubscriptionManager {
* @param networkTypes the network types this override applies to. If no
* network types are specified, override values will be ignored.
* {@see TelephonyManager#getAllNetworkTypes()}
- * @param timeoutMillis the timeout after which the requested override will
- * be automatically cleared, or {@code 0} to leave in the
+ * @param expirationDurationMillis the duration after which the requested override
+ * will be automatically cleared, or {@code 0} to leave in the
* requested state until explicitly cleared, or the next reboot,
* whichever happens first.
* @throws SecurityException if the caller doesn't meet the requirements
@@ -2926,10 +2959,10 @@ public class SubscriptionManager {
*/
public void setSubscriptionOverrideUnmetered(int subId, boolean overrideUnmetered,
@NonNull @Annotation.NetworkType int[] networkTypes,
- @DurationMillisLong long timeoutMillis) {
+ @DurationMillisLong long expirationDurationMillis) {
final int overrideValue = overrideUnmetered ? SUBSCRIPTION_OVERRIDE_UNMETERED : 0;
getNetworkPolicyManager().setSubscriptionOverride(subId, SUBSCRIPTION_OVERRIDE_UNMETERED,
- overrideValue, networkTypes, timeoutMillis, mContext.getOpPackageName());
+ overrideValue, networkTypes, expirationDurationMillis, mContext.getOpPackageName());
}
/**
@@ -2949,17 +2982,17 @@ public class SubscriptionManager {
* @param subId the subscriber this override applies to.
* @param overrideCongested set if the subscription should be considered
* congested.
- * @param timeoutMillis the timeout after which the requested override will
- * be automatically cleared, or {@code 0} to leave in the
+ * @param expirationDurationMillis the duration after which the requested override
+ * will be automatically cleared, or {@code 0} to leave in the
* requested state until explicitly cleared, or the next reboot,
* whichever happens first.
* @throws SecurityException if the caller doesn't meet the requirements
* outlined above.
*/
public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested,
- @DurationMillisLong long timeoutMillis) {
+ @DurationMillisLong long expirationDurationMillis) {
setSubscriptionOverrideCongested(subId, overrideCongested,
- TelephonyManager.getAllNetworkTypes(), timeoutMillis);
+ TelephonyManager.getAllNetworkTypes(), expirationDurationMillis);
}
/**
@@ -2982,8 +3015,8 @@ public class SubscriptionManager {
* @param networkTypes the network types this override applies to. If no
* network types are specified, override values will be ignored.
* {@see TelephonyManager#getAllNetworkTypes()}
- * @param timeoutMillis the timeout after which the requested override will
- * be automatically cleared, or {@code 0} to leave in the
+ * @param expirationDurationMillis the duration after which the requested override
+ * will be automatically cleared, or {@code 0} to leave in the
* requested state until explicitly cleared, or the next reboot,
* whichever happens first.
* @throws SecurityException if the caller doesn't meet the requirements
@@ -2991,10 +3024,10 @@ public class SubscriptionManager {
*/
public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested,
@NonNull @Annotation.NetworkType int[] networkTypes,
- @DurationMillisLong long timeoutMillis) {
+ @DurationMillisLong long expirationDurationMillis) {
final int overrideValue = overrideCongested ? SUBSCRIPTION_OVERRIDE_CONGESTED : 0;
getNetworkPolicyManager().setSubscriptionOverride(subId, SUBSCRIPTION_OVERRIDE_CONGESTED,
- overrideValue, networkTypes, timeoutMillis, mContext.getOpPackageName());
+ overrideValue, networkTypes, expirationDurationMillis, mContext.getOpPackageName());
}
/**
diff --git a/tests/SilkFX/AndroidManifest.xml b/tests/SilkFX/AndroidManifest.xml
index c30d76137f76..21256d8c9d0b 100644
--- a/tests/SilkFX/AndroidManifest.xml
+++ b/tests/SilkFX/AndroidManifest.xml
@@ -20,17 +20,20 @@
<uses-sdk android:minSdkVersion="30"/>
<uses-permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS" />
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<application android:label="SilkFX"
android:theme="@android:style/Theme.Material">
<activity android:name=".Main"
android:label="SilkFX Demos"
+ android:banner="@drawable/background1"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.LAUNCHER"/>
+ <category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
</intent-filter>
</activity>
@@ -41,13 +44,16 @@
<activity android:name=".materials.GlassActivity"
android:label="Glass Examples"
- android:banner="@drawable/background1"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
</activity>
+ <activity android:name=".materials.BackgroundBlurActivity"
+ android:theme="@style/Theme.BackgroundBlurTheme"
+ android:exported="true">
+ </activity>
+
</application>
</manifest>
diff --git a/tests/SilkFX/res/drawable/background_blur_drawable.xml b/tests/SilkFX/res/drawable/background_blur_drawable.xml
new file mode 100644
index 000000000000..173ca99bdfdf
--- /dev/null
+++ b/tests/SilkFX/res/drawable/background_blur_drawable.xml
@@ -0,0 +1,20 @@
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="#20FFFFFF"/>
+ <corners android:radius="10dp"/>
+</shape>
diff --git a/tests/SilkFX/res/drawable/blur_activity_background_drawable_white.xml b/tests/SilkFX/res/drawable/blur_activity_background_drawable_white.xml
new file mode 100644
index 000000000000..bd8942d46383
--- /dev/null
+++ b/tests/SilkFX/res/drawable/blur_activity_background_drawable_white.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners android:radius="10dp"/>
+</shape>
diff --git a/tests/SilkFX/res/layout/activity_background_blur.xml b/tests/SilkFX/res/layout/activity_background_blur.xml
new file mode 100644
index 000000000000..f13c0883cb01
--- /dev/null
+++ b/tests/SilkFX/res/layout/activity_background_blur.xml
@@ -0,0 +1,173 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/background"
+ android:layout_width="390dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:padding="15dp"
+ android:orientation="vertical"
+ tools:context=".materials.BackgroundBlurActivity">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:padding="10dp"
+ android:textColor="#ffffffff"
+ android:text="Hello blurry world!"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textColor="#ffffffff"
+ android:text="Background blur"/>
+
+ <SeekBar
+ android:id="@+id/set_background_blur"
+ android:min="0"
+ android:max="300"
+ android:layout_width="160dp"
+ android:layout_height="wrap_content"/>
+ <TextView
+ android:id="@+id/background_blur_radius"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="#ffffffff"
+ android:ems="3"
+ android:gravity="center"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp"
+ android:text="TODO"/>
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textColor="#ffffffff"
+ android:text="Background alpha"/>
+
+ <SeekBar
+ android:id="@+id/set_background_alpha"
+ android:min="0"
+ android:max="100"
+ android:layout_width="160dp"
+ android:layout_height="wrap_content" />
+ <TextView
+ android:id="@+id/background_alpha"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="#ffffffff"
+ android:ems="3"
+ android:gravity="center"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp"
+ android:text="TODO"/>
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textColor="#ffffffff"
+ android:text="Blur behind"/>
+
+ <SeekBar
+ android:id="@+id/set_blur_behind"
+ android:min="0"
+ android:max="300"
+ android:layout_width="160dp"
+ android:layout_height="wrap_content" />
+ <TextView
+ android:id="@+id/blur_behind_radius"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:textColor="#ffffffff"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp"
+ android:ems="3"
+ android:text="TODO"/>
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textColor="#ffffffff"
+ android:text="Dim amount"/>
+
+ <SeekBar
+ android:id="@+id/set_dim_amount"
+ android:min="0"
+ android:max="100"
+ android:layout_width="160dp"
+ android:layout_height="wrap_content" />
+ <TextView
+ android:id="@+id/dim_amount"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:textColor="#ffffffff"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp"
+ android:ems="3"
+ android:text="TODO"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginTop="5dp"
+ android:orientation="vertical"
+ android:gravity="center">
+
+ <Button
+ android:id="@+id/toggle_blur_enabled"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Disable blur"
+ android:onClick="toggleForceBlurDisabled"/>
+
+ <Button
+ android:id="@+id/toggle_battery_saving_mode"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TODO"
+ android:onClick="toggleBatterySavingMode"/>
+ </LinearLayout>
+ <requestFocus/>
+
+</LinearLayout>
diff --git a/tests/SilkFX/res/values/style.xml b/tests/SilkFX/res/values/style.xml
new file mode 100644
index 000000000000..66edbb5c9382
--- /dev/null
+++ b/tests/SilkFX/res/values/style.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+<!-- Styles for immersive actions UI. -->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <style name="Theme.BackgroundBlurTheme" parent= "Theme.AppCompat.Dialog">
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowBlurBehindEnabled">true</item>
+ <item name="android:backgroundDimEnabled">false</item>
+ <item name="android:windowElevation">0dp</item>
+ <item name="buttonStyle">@style/AppTheme.Button</item>
+ <item name="colorAccent">#bbffffff</item>
+ </style>
+ <style name="AppTheme.Button" parent="Widget.AppCompat.Button">
+ <item name="android:textColor">#ffffffff</item>
+ </style>
+
+</resources>
diff --git a/tests/SilkFX/src/com/android/test/silkfx/Main.kt b/tests/SilkFX/src/com/android/test/silkfx/Main.kt
index 9ed8d2f5edf7..7132ae8772ea 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/Main.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/Main.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -30,6 +30,7 @@ import com.android.test.silkfx.app.EXTRA_LAYOUT
import com.android.test.silkfx.app.EXTRA_TITLE
import com.android.test.silkfx.hdr.GlowActivity
import com.android.test.silkfx.materials.GlassActivity
+import com.android.test.silkfx.materials.BackgroundBlurActivity
import kotlin.reflect.KClass
class Demo(val name: String, val makeIntent: (Context) -> Intent) {
@@ -51,7 +52,8 @@ private val AllDemos = listOf(
Demo("Blingy Notifications", R.layout.bling_notifications)
)),
DemoGroup("Materials", listOf(
- Demo("Glass", GlassActivity::class)
+ Demo("Glass", GlassActivity::class),
+ Demo("Background Blur", BackgroundBlurActivity::class)
))
)
@@ -126,4 +128,4 @@ class Main : Activity() {
AllDemos.forEachIndexed { index, _ -> list.expandGroup(index) }
}
-} \ No newline at end of file
+}
diff --git a/tests/SilkFX/src/com/android/test/silkfx/materials/BackgroundBlurActivity.kt b/tests/SilkFX/src/com/android/test/silkfx/materials/BackgroundBlurActivity.kt
new file mode 100644
index 000000000000..9d17d38d4298
--- /dev/null
+++ b/tests/SilkFX/src/com/android/test/silkfx/materials/BackgroundBlurActivity.kt
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2022 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.test.silkfx.materials
+
+import android.app.Activity
+import android.content.Intent
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.graphics.drawable.PaintDrawable
+import android.graphics.drawable.Drawable
+import android.os.Bundle
+import android.provider.Settings
+import android.util.TypedValue
+import android.view.View
+import android.view.WindowManager
+import android.widget.ImageView
+import android.widget.SeekBar
+import android.widget.Switch
+import android.widget.TextView
+import com.android.test.silkfx.R
+import com.android.internal.graphics.drawable.BackgroundBlurDrawable
+import android.widget.LinearLayout
+import android.widget.Button
+
+import android.view.ViewRootImpl
+
+class BackgroundBlurActivity : Activity(), SeekBar.OnSeekBarChangeListener {
+ var mBackgroundDrawable = PaintDrawable(Color.WHITE)
+ var mBackgroundBlurRadius = 50
+ var mAlphaWithBlur = 0.2f
+ var mAlphaNoBlur = 0.5f
+
+ var mBlurBehindRadius = 10
+ var mDimAmountWithBlur = 0.2f
+ var mDimAmountNoBlur = 0.2f
+
+ var mBlurForceDisabled = false
+ var mBatterySavingModeOn = false
+
+ lateinit var blurBackgroundSeekBar: SeekBar
+ lateinit var backgroundAlphaSeekBar : SeekBar
+ lateinit var blurBehindSeekBar : SeekBar
+ lateinit var dimAmountSeekBar : SeekBar
+
+ val blurEnabledListener = { enabled : Boolean ->
+ blurBackgroundSeekBar.setProgress(mBackgroundBlurRadius)
+ blurBehindSeekBar.setProgress(mBlurBehindRadius)
+
+ if (enabled) {
+ setBackgroundBlur(mBackgroundBlurRadius)
+ setBackgroundColorAlpha(mAlphaWithBlur)
+
+ setBlurBehind(mBlurBehindRadius)
+ setDimAmount(mDimAmountWithBlur)
+
+ backgroundAlphaSeekBar.setProgress((mAlphaWithBlur * 100).toInt())
+ dimAmountSeekBar.setProgress((mDimAmountWithBlur * 100).toInt())
+ } else {
+ setBackgroundColorAlpha(mAlphaNoBlur)
+ setDimAmount(mDimAmountNoBlur)
+
+ backgroundAlphaSeekBar.setProgress((mAlphaNoBlur * 100).toInt())
+ dimAmountSeekBar.setProgress((mDimAmountNoBlur * 100).toInt())
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_background_blur)
+
+ window.addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND)
+ window.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
+
+ mBackgroundDrawable.setCornerRadius(30f)
+ window.setBackgroundDrawable(mBackgroundDrawable)
+
+ mBatterySavingModeOn =
+ Settings.Global.getInt(getContentResolver(), Settings.Global.LOW_POWER_MODE, 0) == 1
+ setBatterySavingModeOn(mBatterySavingModeOn)
+
+ blurBackgroundSeekBar = requireViewById(R.id.set_background_blur)
+ backgroundAlphaSeekBar = requireViewById(R.id.set_background_alpha)
+ blurBehindSeekBar = requireViewById(R.id.set_blur_behind)
+ dimAmountSeekBar = requireViewById(R.id.set_dim_amount)
+
+ arrayOf(blurBackgroundSeekBar, backgroundAlphaSeekBar, blurBehindSeekBar, dimAmountSeekBar)
+ .forEach {
+ it.setOnSeekBarChangeListener(this)
+ }
+ }
+
+ override fun onAttachedToWindow() {
+ super.onAttachedToWindow()
+ getWindowManager().addCrossWindowBlurEnabledListener(blurEnabledListener)
+ }
+
+ override fun onDetachedFromWindow() {
+ super.onDetachedFromWindow()
+ getWindowManager().removeCrossWindowBlurEnabledListener(blurEnabledListener)
+ }
+
+ override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
+ when (seekBar) {
+ blurBackgroundSeekBar -> setBackgroundBlur(progress)
+ backgroundAlphaSeekBar -> setBackgroundColorAlpha(progress / 100.0f)
+ blurBehindSeekBar -> setBlurBehind(progress)
+ dimAmountSeekBar -> setDimAmount(progress / 100.0f)
+ else -> throw IllegalArgumentException("Unknown seek bar")
+ }
+ }
+
+ override fun onStartTrackingTouch(seekBar: SeekBar?) {}
+ override fun onStopTrackingTouch(seekBar: SeekBar?) {}
+
+ fun setBlurDisabled(disabled: Boolean) {
+ mBlurForceDisabled = disabled
+ Settings.Global.putInt(getContentResolver(), Settings.Global.DISABLE_WINDOW_BLURS,
+ if (mBlurForceDisabled) 1 else 0)
+ (findViewById(R.id.toggle_blur_enabled) as Button)
+ .setText(if (mBlurForceDisabled) "Enable blurs" else "Disable blurs")
+ }
+
+ fun toggleForceBlurDisabled(v: View) {
+ setBlurDisabled(!mBlurForceDisabled)
+ }
+
+ fun setBackgroundBlur(radius: Int) {
+ mBackgroundBlurRadius = radius
+ (findViewById(R.id.background_blur_radius) as TextView).setText(radius.toString())
+ window.setBackgroundBlurRadius(mBackgroundBlurRadius)
+ }
+
+ fun setBlurBehind(radius: Int) {
+ mBlurBehindRadius = radius
+ (findViewById(R.id.blur_behind_radius) as TextView).setText(radius.toString())
+ window.getAttributes().setBlurBehindRadius(mBlurBehindRadius)
+ window.setAttributes(window.getAttributes())
+ }
+
+ fun setDimAmount(amount: Float) {
+ if (getWindowManager().isCrossWindowBlurEnabled()) {
+ mDimAmountWithBlur = amount
+ } else {
+ mDimAmountNoBlur = amount
+ }
+ (findViewById(R.id.dim_amount) as TextView).setText("%.2f".format(amount))
+ window.getAttributes().dimAmount = amount
+ window.setAttributes(window.getAttributes())
+ }
+
+ fun setBatterySavingModeOn(on: Boolean) {
+ mBatterySavingModeOn = on
+ Settings.Global.putInt(getContentResolver(),
+ Settings.Global.LOW_POWER_MODE, if (on) 1 else 0)
+ (findViewById(R.id.toggle_battery_saving_mode) as Button).setText(
+ if (on) "Exit low power mode" else "Enter low power mode")
+ }
+
+ fun toggleBatterySavingMode(v: View) {
+ setBatterySavingModeOn(!mBatterySavingModeOn)
+ }
+
+ fun setBackgroundColorAlpha(alpha: Float) {
+ if (getWindowManager().isCrossWindowBlurEnabled()) {
+ mAlphaWithBlur = alpha
+ } else {
+ mAlphaNoBlur = alpha
+ }
+ (findViewById(R.id.background_alpha) as TextView).setText("%.2f".format(alpha))
+ mBackgroundDrawable.setAlpha((alpha * 255f).toInt())
+ getWindowManager().updateViewLayout(window.getDecorView(), window.getAttributes())
+ }
+}