diff options
104 files changed, 2802 insertions, 1293 deletions
diff --git a/apct-tests/perftests/healthconnect/Android.bp b/apct-tests/perftests/healthconnect/Android.bp new file mode 100644 index 000000000000..c2d0a6f200a0 --- /dev/null +++ b/apct-tests/perftests/healthconnect/Android.bp @@ -0,0 +1,44 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +android_test { + name: "HealthConnectPerfTests", + + srcs: [ + "src/**/*.java", + "src/**/*.kt", + ], + + static_libs: [ + "androidx.test.rules", + "androidx.test.ext.junit", + "apct-perftests-utils", + "collector-device-lib-platform", + ], + + libs: ["android.test.base"], + platform_apis: true, + test_suites: ["device-tests"], + data: [":perfetto_artifacts"], + certificate: "platform", +} diff --git a/apct-tests/perftests/healthconnect/AndroidManifest.xml b/apct-tests/perftests/healthconnect/AndroidManifest.xml new file mode 100644 index 000000000000..6a6370b6214a --- /dev/null +++ b/apct-tests/perftests/healthconnect/AndroidManifest.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.perftests.healthconnect"> + + <application> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.perftests.healthconnect"/> +</manifest> diff --git a/apct-tests/perftests/healthconnect/AndroidTest.xml b/apct-tests/perftests/healthconnect/AndroidTest.xml new file mode 100644 index 000000000000..5036202a9f4a --- /dev/null +++ b/apct-tests/perftests/healthconnect/AndroidTest.xml @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Runs HealthConnectPerfTests metric instrumentation."> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-metric-instrumentation" /> + + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="test-file-name" value="HealthConnectPerfTests.apk" /> + </target_preparer> + + <!-- Needed for pushing the trace config file --> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="push-file" key="trace_config_detailed.textproto" value="/data/misc/perfetto-traces/trace_config.textproto" /> + </target_preparer> + + <!-- Needed for pulling the collected trace config on to the host --> + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="pull-pattern-keys" value="perfetto_file_path" /> + </metrics_collector> + + <!-- Needed for storing the perfetto trace files in the sdcard/test_results--> + <option name="isolated-storage" value="false" /> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.perftests.healthconnect" /> + <option name="hidden-api-checks" value="false"/> + + <!-- Listener related args for collecting the traces and waiting for the device to stabilize. --> + <option name="device-listeners" value="android.device.collectors.ProcLoadListener,android.device.collectors.PerfettoListener" /> + <!-- Guarantee that user defined RunListeners will be running before any of the default listeners defined in this runner. --> + <option name="instrumentation-arg" key="newRunListenerMode" value="true" /> + + <!-- ProcLoadListener related arguments --> + <!-- Wait for device last minute threshold to reach 3 with 2 minute timeout before starting the test run --> + <option name="instrumentation-arg" key="procload-collector:per_run" value="true" /> + <option name="instrumentation-arg" key="proc-loadavg-threshold" value="3" /> + <option name="instrumentation-arg" key="proc-loadavg-timeout" value="120000" /> + <option name="instrumentation-arg" key="proc-loadavg-interval" value="10000" /> + + <!-- PerfettoListener related arguments --> + <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true" /> + <option name="instrumentation-arg" key="perfetto_config_file" value="trace_config.textproto" /> + + <option name="instrumentation-arg" key="newRunListenerMode" value="true" /> + </test> +</configuration> diff --git a/apct-tests/perftests/healthconnect/OWNERS b/apct-tests/perftests/healthconnect/OWNERS new file mode 100644 index 000000000000..da0b46adeaef --- /dev/null +++ b/apct-tests/perftests/healthconnect/OWNERS @@ -0,0 +1,6 @@ +# Bug component: 1219472 + +arkivanov@google.com +jstembridge@google.com +pratyushmore@google.com +itsleo@google.com diff --git a/apct-tests/perftests/healthconnect/src/com/android/perftests/healthconnect/HealthConnectReadWritePerfTest.kt b/apct-tests/perftests/healthconnect/src/com/android/perftests/healthconnect/HealthConnectReadWritePerfTest.kt new file mode 100644 index 000000000000..21a4ca048f62 --- /dev/null +++ b/apct-tests/perftests/healthconnect/src/com/android/perftests/healthconnect/HealthConnectReadWritePerfTest.kt @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.perftests.healthconnect + +import android.health.connect.HealthConnectManager +import android.os.SystemClock +import android.perftests.utils.PerfStatusReporter +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Read/write benchmark tests for [HealthConnectManager] + * + * Build/Install/Run: atest HealthConnectReadWritePerfTest + */ +@RunWith(AndroidJUnit4::class) +class HealthConnectReadWritePerfTest { + + @get:Rule + val perfStatusReporter = PerfStatusReporter() + + private val context by lazy { InstrumentationRegistry.getInstrumentation().context } + + private val manager by lazy { + requireNotNull(context.getSystemService(HealthConnectManager::class.java)) + } + + /** + * A first empty test just to setup the test package and make sure it runs properly. + */ + @Test + fun placeholder() { + val state = perfStatusReporter.benchmarkState + while (state.keepRunning()) { + SystemClock.sleep(100) + } + } +}
\ No newline at end of file diff --git a/core/api/current.txt b/core/api/current.txt index ace00fcb58aa..ecb242a6a217 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -23972,6 +23972,7 @@ package android.media { method @Nullable public android.net.Uri getIconUri(); method @NonNull public String getId(); method @NonNull public CharSequence getName(); + method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public int getSuitabilityStatus(); method public int getType(); method public int getVolume(); method public int getVolumeHandling(); @@ -23989,6 +23990,9 @@ package android.media { field public static final String FEATURE_REMOTE_VIDEO_PLAYBACK = "android.media.route.feature.REMOTE_VIDEO_PLAYBACK"; field public static final int PLAYBACK_VOLUME_FIXED = 0; // 0x0 field public static final int PLAYBACK_VOLUME_VARIABLE = 1; // 0x1 + field @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public static final int SUITABILITY_STATUS_NOT_SUITABLE_FOR_TRANSFER = 2; // 0x2 + field @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public static final int SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER = 0; // 0x0 + field @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public static final int SUITABILITY_STATUS_SUITABLE_FOR_MANUAL_TRANSFER = 1; // 0x1 field public static final int TYPE_BLE_HEADSET = 26; // 0x1a field public static final int TYPE_BLUETOOTH_A2DP = 8; // 0x8 field public static final int TYPE_BUILTIN_SPEAKER = 2; // 0x2 @@ -24029,6 +24033,7 @@ package android.media { method @NonNull public android.media.MediaRoute2Info.Builder setDescription(@Nullable CharSequence); method @NonNull public android.media.MediaRoute2Info.Builder setExtras(@Nullable android.os.Bundle); method @NonNull public android.media.MediaRoute2Info.Builder setIconUri(@Nullable android.net.Uri); + method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") @NonNull public android.media.MediaRoute2Info.Builder setSuitabilityStatus(int); method @NonNull public android.media.MediaRoute2Info.Builder setType(int); method @NonNull public android.media.MediaRoute2Info.Builder setVisibilityPublic(); method @NonNull public android.media.MediaRoute2Info.Builder setVisibilityRestricted(@NonNull java.util.Set<java.lang.String>); @@ -24242,6 +24247,7 @@ package android.media { method public void release(); method public void selectRoute(@NonNull android.media.MediaRoute2Info); method public void setVolume(int); + method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public boolean wasTransferRequestedBySelf(); } public abstract static class MediaRouter2.TransferCallback { @@ -24639,12 +24645,16 @@ package android.media { method @Nullable public CharSequence getName(); method @NonNull public java.util.List<java.lang.String> getSelectableRoutes(); method @NonNull public java.util.List<java.lang.String> getSelectedRoutes(); + method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public int getTransferReason(); method @NonNull public java.util.List<java.lang.String> getTransferableRoutes(); method public int getVolume(); method public int getVolumeHandling(); method public int getVolumeMax(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.media.RoutingSessionInfo> CREATOR; + field @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public static final int TRANSFER_REASON_APP = 2; // 0x2 + field @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public static final int TRANSFER_REASON_FALLBACK = 0; // 0x0 + field @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public static final int TRANSFER_REASON_SYSTEM_REQUEST = 1; // 0x1 } public static final class RoutingSessionInfo.Builder { @@ -24665,6 +24675,8 @@ package android.media { method @NonNull public android.media.RoutingSessionInfo.Builder removeTransferableRoute(@NonNull String); method @NonNull public android.media.RoutingSessionInfo.Builder setControlHints(@Nullable android.os.Bundle); method @NonNull public android.media.RoutingSessionInfo.Builder setName(@Nullable CharSequence); + method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") @NonNull public android.media.RoutingSessionInfo.Builder setTransferInitiator(@Nullable android.os.UserHandle, @Nullable String); + method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") @NonNull public android.media.RoutingSessionInfo.Builder setTransferReason(int); method @NonNull public android.media.RoutingSessionInfo.Builder setVolume(int); method @NonNull public android.media.RoutingSessionInfo.Builder setVolumeHandling(int); method @NonNull public android.media.RoutingSessionInfo.Builder setVolumeMax(int); diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index ffd7212a7525..86024ea41363 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -751,6 +751,13 @@ public abstract class DisplayManagerInternal { */ boolean blockScreenOn(Runnable unblocker); + /** Get the current brightness levels used to determine automatic brightness based on lux + * levels. */ + float[] getCurrentAutoBrightnessLevels(); + + /** Get the current lux levels used to determine automatic brightness. */ + float[] getCurrentAutoBrightnessLuxLevels(); + /** Returns whether displayoffload supports the given display state. */ static boolean isSupportedOffloadState(int displayState) { return Display.isSuspendedState(displayState); diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index cbbe7856178d..b957b31a5bb7 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -22,7 +22,6 @@ import static android.graphics.Matrix.MSKEW_X; import static android.graphics.Matrix.MSKEW_Y; import static android.graphics.Matrix.MTRANS_X; import static android.graphics.Matrix.MTRANS_Y; -import static android.view.Display.INVALID_DISPLAY; import static android.view.SurfaceControlProto.HASH_CODE; import static android.view.SurfaceControlProto.LAYER_ID; import static android.view.SurfaceControlProto.NAME; @@ -38,7 +37,6 @@ import android.annotation.RequiresPermission; import android.annotation.Size; import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; -import android.content.Context; import android.graphics.ColorSpace; import android.graphics.GraphicBuffer; import android.graphics.Matrix; @@ -53,13 +51,8 @@ import android.hardware.HardwareBuffer; import android.hardware.OverlayProperties; import android.hardware.SyncFence; import android.hardware.display.DeviceProductInfo; -import android.hardware.display.DisplayManager; -import android.hardware.display.DisplayManagerGlobal; import android.hardware.display.DisplayedContentSample; import android.hardware.display.DisplayedContentSamplingAttributes; -import android.hardware.display.IDisplayManager; -import android.hardware.display.IVirtualDisplayCallback; -import android.hardware.display.VirtualDisplay; import android.hardware.graphics.common.DisplayDecorationSupport; import android.opengl.EGLDisplay; import android.opengl.EGLSync; @@ -68,8 +61,6 @@ import android.os.IBinder; import android.os.Looper; import android.os.Parcel; import android.os.Parcelable; -import android.os.RemoteException; -import android.os.ServiceManager; import android.util.ArrayMap; import android.util.Log; import android.util.Slog; @@ -2355,92 +2346,6 @@ public final class SurfaceControl implements Parcelable { } /** - * Because this API is now going through {@link DisplayManager}, orientation and displayRect - * will automatically be computed based on configuration changes. Because of this, the params - * orientation and displayRect are ignored - * - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU, - publicAlternatives = "Use {@code VirtualDisplay#resize(int, int, int)} instead.", - trackingBug = 247078497) - public static void setDisplayProjection(IBinder displayToken, int orientation, - Rect layerStackRect, Rect displayRect) { - DisplayManagerGlobal.getInstance().resizeVirtualDisplay( - IVirtualDisplayCallback.Stub.asInterface(displayToken), layerStackRect.width(), - layerStackRect.height(), 1); - } - - /** - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU, - publicAlternatives = "Use {@code MediaProjection#createVirtualDisplay()} with flag " - + " {@code VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR} for mirroring instead.", - trackingBug = 247078497) - public static void setDisplayLayerStack(IBinder displayToken, int layerStack) { - IBinder b = ServiceManager.getService(Context.DISPLAY_SERVICE); - if (b == null) { - throw new UnsupportedOperationException(); - } - - IDisplayManager dm = IDisplayManager.Stub.asInterface(b); - try { - dm.setDisplayIdToMirror(displayToken, layerStack); - } catch (RemoteException e) { - throw new UnsupportedOperationException(e); - } - } - - /** - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU, - publicAlternatives = "Use {@code VirtualDisplay#setSurface(Surface)} instead.", - trackingBug = 247078497) - public static void setDisplaySurface(IBinder displayToken, Surface surface) { - IVirtualDisplayCallback virtualDisplayCallback = - IVirtualDisplayCallback.Stub.asInterface(displayToken); - DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance(); - dm.setVirtualDisplaySurface(virtualDisplayCallback, surface); - } - - /** - * Secure is no longer supported because this is only called from outside system which cannot - * create secure displays. - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU, - publicAlternatives = "Use {@code MediaProjection#createVirtualDisplay()} or " - + "{@code DisplayManager#createVirtualDisplay()} instead.", - trackingBug = 247078497) - public static IBinder createDisplay(String name, boolean secure) { - if (name == null) { - throw new IllegalArgumentException("name must not be null"); - } - - // We don't have a size yet so pass in 1 for width and height since 0 is invalid - VirtualDisplay vd = DisplayManager.createVirtualDisplay(name, 1 /* width */, 1 /* height */, - INVALID_DISPLAY, null /* Surface */); - return vd == null ? null : vd.getToken().asBinder(); - } - - /** - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU, - publicAlternatives = "Use {@code VirtualDisplay#release()} instead.", - trackingBug = 247078497) - public static void destroyDisplay(IBinder displayToken) { - if (displayToken == null) { - throw new IllegalArgumentException("displayToken must not be null"); - } - - DisplayManagerGlobal.getInstance().releaseVirtualDisplay( - IVirtualDisplayCallback.Stub.asInterface(displayToken)); - } - - /** * Returns whether protected content is supported in GPU composition. * @hide */ diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index e4b709e66fbc..ec994590e339 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -32,6 +32,7 @@ import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_H import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH_ERROR_CODE; import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY; import static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API; +import static android.view.flags.Flags.toolkitMetricsForFrameRateDecision; import static android.view.flags.Flags.toolkitSetFrameRateReadOnly; import static android.view.flags.Flags.viewVelocityApi; import static android.view.inputmethod.Flags.FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR; @@ -2309,6 +2310,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, protected static final int[] PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET; private static boolean sToolkitSetFrameRateReadOnlyFlagValue; + private static boolean sToolkitMetricsForFrameRateDecisionFlagValue; static { EMPTY_STATE_SET = StateSet.get(0); @@ -2393,6 +2395,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, | StateSet.VIEW_STATE_PRESSED); sToolkitSetFrameRateReadOnlyFlagValue = toolkitSetFrameRateReadOnly(); + sToolkitMetricsForFrameRateDecisionFlagValue = toolkitMetricsForFrameRateDecision(); } /** @@ -33084,11 +33087,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private void votePreferredFrameRate() { // use toolkitSetFrameRate flag to gate the change - if (sToolkitSetFrameRateReadOnlyFlagValue) { - ViewRootImpl viewRootImpl = getViewRootImpl(); - float sizePercentage = getSizePercentage(); - int frameRateCateogry = calculateFrameRateCategory(sizePercentage); - if (viewRootImpl != null && sizePercentage > 0) { + ViewRootImpl viewRootImpl = getViewRootImpl(); + float sizePercentage = getSizePercentage(); + int frameRateCateogry = calculateFrameRateCategory(sizePercentage); + if (viewRootImpl != null && sizePercentage > 0) { + if (sToolkitSetFrameRateReadOnlyFlagValue) { if (mPreferredFrameRate < 0) { if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE) { frameRateCateogry = FRAME_RATE_CATEGORY_NO_PREFERENCE; @@ -33104,6 +33107,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } viewRootImpl.votePreferredFrameRateCategory(frameRateCateogry); } + if (sToolkitMetricsForFrameRateDecisionFlagValue) { + viewRootImpl.recordViewPercentage(sizePercentage); + } } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 185328277dfd..487b15c03292 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -827,6 +827,8 @@ public final class ViewRootImpl implements ViewParent, private boolean mInsetsAnimationRunning; private long mPreviousFrameDrawnTime = -1; + // The largest view size percentage to the display size. Used on trace to collect metric. + private float mLargestChildPercentage = 0.0f; /** * The resolved pointer icon type requested by this window. @@ -1066,6 +1068,7 @@ public final class ViewRootImpl implements ViewParent, private String mTag = TAG; private String mFpsTraceName; + private String mLargestViewTraceName; private static boolean sToolkitSetFrameRateReadOnlyFlagValue; private static boolean sToolkitMetricsForFrameRateDecisionFlagValue; @@ -1317,6 +1320,7 @@ public final class ViewRootImpl implements ViewParent, attrs = mWindowAttributes; setTag(); mFpsTraceName = "FPS of " + getTitle(); + mLargestViewTraceName = "Largest view percentage(per hundred) of " + getTitle(); if (DEBUG_KEEP_SCREEN_ON && (mClientWindowLayoutFlags & WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) != 0 @@ -4738,6 +4742,10 @@ public final class ViewRootImpl implements ViewParent, long fps = NANOS_PER_SEC / timeDiff; Trace.setCounter(mFpsTraceName, fps); mPreviousFrameDrawnTime = expectedDrawnTime; + + long percentage = (long) (mLargestChildPercentage * 100); + Trace.setCounter(mLargestViewTraceName, percentage); + mLargestChildPercentage = 0.0f; } private void reportDrawFinished(@Nullable Transaction t, int seqId) { @@ -5058,6 +5066,7 @@ public final class ViewRootImpl implements ViewParent, if (DEBUG_FPS) { trackFPS(); } + if (sToolkitMetricsForFrameRateDecisionFlagValue) { collectFrameRateDecisionMetrics(); } @@ -12261,4 +12270,10 @@ public final class ViewRootImpl implements ViewParent, void setBackKeyCallbackForWindowlessWindow(@NonNull Predicate<KeyEvent> callback) { mWindowlessBackKeyCallback = callback; } + + void recordViewPercentage(float percentage) { + if (!Trace.isEnabled()) return; + // Record the largest view of percentage to the display size. + mLargestChildPercentage = Math.max(percentage, mLargestChildPercentage); + } } diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java index 5bfa3d759a66..7c9340e72f72 100644 --- a/core/java/android/window/TransitionInfo.java +++ b/core/java/android/window/TransitionInfo.java @@ -165,6 +165,9 @@ public final class TransitionInfo implements Parcelable { public static final int FLAGS_IS_NON_APP_WINDOW = FLAG_IS_WALLPAPER | FLAG_IS_INPUT_METHOD | FLAG_IS_SYSTEM_WINDOW; + /** The change will not participate in the animation. */ + public static final int FLAGS_IS_OCCLUDED_NO_ANIMATION = FLAG_IS_OCCLUDED | FLAG_NO_ANIMATION; + /** @hide */ @IntDef(prefix = { "FLAG_" }, value = { FLAG_NONE, diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index d0de5f0e4104..5400c5848f02 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -6862,4 +6862,8 @@ <!-- Whether the media player is shown on the quick settings --> <bool name="config_quickSettingsShowMediaPlayer">true</bool> + + <!-- Defines suitability of the built-in speaker route. + Refer to {@link MediaRoute2Info} to see supported values. --> + <integer name="config_mediaRouter_builtInSpeakerSuitability">0</integer> </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 38943306b962..eeef19291c4f 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -5306,4 +5306,7 @@ <java-symbol type="bool" name="config_viewBasedRotaryEncoderHapticsEnabled" /> <java-symbol type="bool" name="config_quickSettingsShowMediaPlayer" /> + + <!-- Android MediaRouter framework configs. --> + <java-symbol type="integer" name="config_mediaRouter_builtInSpeakerSuitability" /> </resources> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index af69b5272ad5..b0d8b47b170a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -757,6 +757,10 @@ public class Transitions implements RemoteCallable<Transitions>, } if (!change.hasFlags(FLAG_IS_OCCLUDED)) { allOccluded = false; + } else if (change.hasAllFlags(TransitionInfo.FLAGS_IS_OCCLUDED_NO_ANIMATION)) { + // Remove the change because it should be invisible in the animation. + info.getChanges().remove(i); + continue; } // The change has already animated by back gesture, don't need to play transition // animation on it. diff --git a/media/java/android/media/IMediaRouter2.aidl b/media/java/android/media/IMediaRouter2.aidl index 29bfd1acae17..e2dddad274e1 100644 --- a/media/java/android/media/IMediaRouter2.aidl +++ b/media/java/android/media/IMediaRouter2.aidl @@ -19,6 +19,7 @@ package android.media; import android.media.MediaRoute2Info; import android.media.RoutingSessionInfo; import android.os.Bundle; +import android.os.UserHandle; /** * @hide @@ -35,5 +36,6 @@ oneway interface IMediaRouter2 { * Call MediaRouterService#requestCreateSessionWithRouter2 to pass the result. */ void requestCreateSessionByManager(long uniqueRequestId, in RoutingSessionInfo oldSession, - in MediaRoute2Info route); + in MediaRoute2Info route, in UserHandle transferInitiatorUserHandle, + in String transferInitiatorPackageName); } diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl index fa4d1a1ff935..04e99ea8a57f 100644 --- a/media/java/android/media/IMediaRouterService.aidl +++ b/media/java/android/media/IMediaRouterService.aidl @@ -64,7 +64,8 @@ interface IMediaRouterService { void requestCreateSessionWithRouter2(IMediaRouter2 router, int requestId, long managerRequestId, in RoutingSessionInfo oldSession, in MediaRoute2Info route, - in @nullable Bundle sessionHints); + in @nullable Bundle sessionHints, in UserHandle transferInitiatorUserHandle, + in String transferInitiatorPackageName); void selectRouteWithRouter2(IMediaRouter2 router, String sessionId, in MediaRoute2Info route); void deselectRouteWithRouter2(IMediaRouter2 router, String sessionId, in MediaRoute2Info route); void transferToRouteWithRouter2(IMediaRouter2 router, String sessionId, @@ -84,13 +85,16 @@ interface IMediaRouterService { void stopScan(IMediaRouter2Manager manager); void requestCreateSessionWithManager(IMediaRouter2Manager manager, int requestId, - in RoutingSessionInfo oldSession, in @nullable MediaRoute2Info route); + in RoutingSessionInfo oldSession, in @nullable MediaRoute2Info route, + in UserHandle transferInitiatorUserHandle, in String transferInitiatorPackageName); void selectRouteWithManager(IMediaRouter2Manager manager, int requestId, String sessionId, in MediaRoute2Info route); void deselectRouteWithManager(IMediaRouter2Manager manager, int requestId, String sessionId, in MediaRoute2Info route); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL)") void transferToRouteWithManager(IMediaRouter2Manager manager, int requestId, - String sessionId, in MediaRoute2Info route); + String sessionId, in MediaRoute2Info route, + in UserHandle transferInitiatorUserHandle, String transferInitiatorPackageName); void setSessionVolumeWithManager(IMediaRouter2Manager manager, int requestId, String sessionId, int volume); void releaseSessionWithManager(IMediaRouter2Manager manager, int requestId, String sessionId); diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java index 8ad35876989d..0eabe66e9a69 100644 --- a/media/java/android/media/MediaRoute2Info.java +++ b/media/java/android/media/MediaRoute2Info.java @@ -19,6 +19,7 @@ package android.media; import static android.media.MediaRouter2Utils.toUniqueId; import static com.android.media.flags.Flags.FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER; +import static com.android.media.flags.Flags.FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES; import static com.android.media.flags.Flags.FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES; import android.annotation.FlaggedApi; @@ -479,6 +480,37 @@ public final class MediaRoute2Info implements Parcelable { public static final String FEATURE_REMOTE_GROUP_PLAYBACK = "android.media.route.feature.REMOTE_GROUP_PLAYBACK"; + /** Indicates the route is always suitable for media playback. */ + @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES) + public static final int SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER = 0; + + /** + * Indicates that the route is suitable for media playback only after explicit user selection. + */ + @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES) + public static final int SUITABILITY_STATUS_SUITABLE_FOR_MANUAL_TRANSFER = 1; + + /** Indicates that the route is never suitable for media playback. */ + @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES) + public static final int SUITABILITY_STATUS_NOT_SUITABLE_FOR_TRANSFER = 2; + + /** + * Route suitability status. + * + * <p>Signals whether the route is suitable to play media. + * + * @hide + */ + @IntDef( + value = { + SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER, + SUITABILITY_STATUS_SUITABLE_FOR_MANUAL_TRANSFER, + SUITABILITY_STATUS_NOT_SUITABLE_FOR_TRANSFER + }) + @Retention(RetentionPolicy.SOURCE) + @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES) + public @interface SuitabilityStatus {} + private final String mId; private final CharSequence mName; private final List<String> mFeatures; @@ -500,6 +532,7 @@ public final class MediaRoute2Info implements Parcelable { private final String mProviderId; private final boolean mIsVisibilityRestricted; private final Set<String> mAllowedPackages; + @SuitabilityStatus private final int mSuitabilityStatus; MediaRoute2Info(@NonNull Builder builder) { mId = builder.mId; @@ -521,6 +554,7 @@ public final class MediaRoute2Info implements Parcelable { mProviderId = builder.mProviderId; mIsVisibilityRestricted = builder.mIsVisibilityRestricted; mAllowedPackages = builder.mAllowedPackages; + mSuitabilityStatus = builder.mSuitabilityStatus; } MediaRoute2Info(@NonNull Parcel in) { @@ -544,6 +578,7 @@ public final class MediaRoute2Info implements Parcelable { mProviderId = in.readString(); mIsVisibilityRestricted = in.readBoolean(); mAllowedPackages = Set.of(in.createString8Array()); + mSuitabilityStatus = in.readInt(); } /** @@ -778,6 +813,13 @@ public final class MediaRoute2Info implements Parcelable { || mAllowedPackages.contains(packageName); } + /** Returns the route suitability status. */ + @SuitabilityStatus + @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES) + public int getSuitabilityStatus() { + return mSuitabilityStatus; + } + /** * Dumps the current state of the object to the given {@code pw} as a human-readable string. * @@ -809,6 +851,7 @@ public final class MediaRoute2Info implements Parcelable { pw.println(indent + "mProviderId=" + mProviderId); pw.println(indent + "mIsVisibilityRestricted=" + mIsVisibilityRestricted); pw.println(indent + "mAllowedPackages=" + mAllowedPackages); + pw.println(indent + "mSuitabilityStatus=" + mSuitabilityStatus); } private void dumpVolume(@NonNull PrintWriter pw, @NonNull String prefix) { @@ -861,39 +904,74 @@ public final class MediaRoute2Info implements Parcelable { && Objects.equals(mDeduplicationIds, other.mDeduplicationIds) && Objects.equals(mProviderId, other.mProviderId) && (mIsVisibilityRestricted == other.mIsVisibilityRestricted) - && Objects.equals(mAllowedPackages, other.mAllowedPackages); + && Objects.equals(mAllowedPackages, other.mAllowedPackages) + && mSuitabilityStatus == other.mSuitabilityStatus; } @Override public int hashCode() { // Note: mExtras is not included. - return Objects.hash(mId, mName, mFeatures, mType, mIsSystem, mIconUri, mDescription, - mConnectionState, mClientPackageName, mPackageName, mVolumeHandling, mVolumeMax, - mVolume, mAddress, mDeduplicationIds, mProviderId, mIsVisibilityRestricted, - mAllowedPackages); + return Objects.hash( + mId, + mName, + mFeatures, + mType, + mIsSystem, + mIconUri, + mDescription, + mConnectionState, + mClientPackageName, + mPackageName, + mVolumeHandling, + mVolumeMax, + mVolume, + mAddress, + mDeduplicationIds, + mProviderId, + mIsVisibilityRestricted, + mAllowedPackages, + mSuitabilityStatus); } @Override public String toString() { // Note: mExtras is not printed here. - StringBuilder result = new StringBuilder() - .append("MediaRoute2Info{ ") - .append("id=").append(getId()) - .append(", name=").append(getName()) - .append(", features=").append(getFeatures()) - .append(", iconUri=").append(getIconUri()) - .append(", description=").append(getDescription()) - .append(", connectionState=").append(getConnectionState()) - .append(", clientPackageName=").append(getClientPackageName()) - .append(", volumeHandling=").append(getVolumeHandling()) - .append(", volumeMax=").append(getVolumeMax()) - .append(", volume=").append(getVolume()) - .append(", address=").append(getAddress()) - .append(", deduplicationIds=").append(String.join(",", getDeduplicationIds())) - .append(", providerId=").append(getProviderId()) - .append(", isVisibilityRestricted=").append(mIsVisibilityRestricted) - .append(", allowedPackages=").append(String.join(",", mAllowedPackages)) - .append(" }"); + StringBuilder result = + new StringBuilder() + .append("MediaRoute2Info{ ") + .append("id=") + .append(getId()) + .append(", name=") + .append(getName()) + .append(", features=") + .append(getFeatures()) + .append(", iconUri=") + .append(getIconUri()) + .append(", description=") + .append(getDescription()) + .append(", connectionState=") + .append(getConnectionState()) + .append(", clientPackageName=") + .append(getClientPackageName()) + .append(", volumeHandling=") + .append(getVolumeHandling()) + .append(", volumeMax=") + .append(getVolumeMax()) + .append(", volume=") + .append(getVolume()) + .append(", address=") + .append(getAddress()) + .append(", deduplicationIds=") + .append(String.join(",", getDeduplicationIds())) + .append(", providerId=") + .append(getProviderId()) + .append(", isVisibilityRestricted=") + .append(mIsVisibilityRestricted) + .append(", allowedPackages=") + .append(String.join(",", mAllowedPackages)) + .append(", suitabilityStatus=") + .append(mSuitabilityStatus) + .append(" }"); return result.toString(); } @@ -923,6 +1001,7 @@ public final class MediaRoute2Info implements Parcelable { dest.writeString(mProviderId); dest.writeBoolean(mIsVisibilityRestricted); dest.writeString8Array(mAllowedPackages.toArray(new String[0])); + dest.writeInt(mSuitabilityStatus); } private static String getDeviceTypeString(@Type int deviceType) { @@ -1005,6 +1084,7 @@ public final class MediaRoute2Info implements Parcelable { private String mProviderId; private boolean mIsVisibilityRestricted; private Set<String> mAllowedPackages; + @SuitabilityStatus private int mSuitabilityStatus; /** * Constructor for builder to create {@link MediaRoute2Info}. @@ -1028,6 +1108,7 @@ public final class MediaRoute2Info implements Parcelable { mFeatures = new ArrayList<>(); mDeduplicationIds = Set.of(); mAllowedPackages = Set.of(); + mSuitabilityStatus = SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER; } /** @@ -1075,6 +1156,7 @@ public final class MediaRoute2Info implements Parcelable { mProviderId = routeInfo.mProviderId; mIsVisibilityRestricted = routeInfo.mIsVisibilityRestricted; mAllowedPackages = routeInfo.mAllowedPackages; + mSuitabilityStatus = routeInfo.mSuitabilityStatus; } /** @@ -1318,6 +1400,23 @@ public final class MediaRoute2Info implements Parcelable { } /** + * Sets route suitability status. + * + * <p>The default value is {@link + * MediaRoute2Info#SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER}. + * + * <p> Apps are not supposed to set {@link + * MediaRoute2Info#SUITABILITY_STATUS_NOT_SUITABLE_FOR_TRANSFER}. Publishing a non-system + * route with such status throws {@link SecurityException}. + */ + @NonNull + @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES) + public Builder setSuitabilityStatus(@SuitabilityStatus int suitabilityStatus) { + mSuitabilityStatus = suitabilityStatus; + return this; + } + + /** * Builds the {@link MediaRoute2Info media route info}. * * @throws IllegalArgumentException if no features are added. diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java index ba26df922f23..5e235515c852 100644 --- a/media/java/android/media/MediaRouter2.java +++ b/media/java/android/media/MediaRouter2.java @@ -17,6 +17,7 @@ package android.media; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; +import static com.android.media.flags.Flags.FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES; import static com.android.media.flags.Flags.FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2; import static com.android.media.flags.Flags.FLAG_ENABLE_CROSS_USER_ROUTING_IN_MEDIA_ROUTER2; @@ -699,15 +700,48 @@ public final class MediaRouter2 { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL) + @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void transfer(@NonNull RoutingController controller, @NonNull MediaRoute2Info route) { - mImpl.transfer(controller.getRoutingSessionInfo(), route); + mImpl.transfer( + controller.getRoutingSessionInfo(), + route, + android.os.Process.myUserHandle(), + mContext.getPackageName()); + } + + /** + * Transfers the media of a routing controller to the given route. + * + * <p>This will be no-op for non-system media routers. + * + * @param controller a routing controller controlling media routing. + * @param route the route you want to transfer the media to. + * @param transferInitiatorUserHandle the user handle of the app that initiated the transfer + * request. + * @param transferInitiatorPackageName the package name of the app that initiated the transfer. + * This value is used with the user handle to populate {@link + * RoutingController#wasTransferRequestedBySelf()}. + * @hide + */ + @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES) + public void transfer( + @NonNull RoutingController controller, + @NonNull MediaRoute2Info route, + @NonNull UserHandle transferInitiatorUserHandle, + @NonNull String transferInitiatorPackageName) { + mImpl.transfer( + controller.getRoutingSessionInfo(), + route, + transferInitiatorUserHandle, + transferInitiatorPackageName); } void requestCreateController( @NonNull RoutingController controller, @NonNull MediaRoute2Info route, - long managerRequestId) { + long managerRequestId, + @NonNull UserHandle transferInitiatorUserHandle, + @NonNull String transferInitiatorPackageName) { final int requestId = mNextRequestId.getAndIncrement(); @@ -736,7 +770,9 @@ public final class MediaRouter2 { managerRequestId, controller.getRoutingSessionInfo(), route, - controllerHints); + controllerHints, + transferInitiatorUserHandle, + transferInitiatorPackageName); } catch (RemoteException ex) { Log.e(TAG, "createControllerForTransfer: " + "Failed to request for creating a controller.", ex); @@ -1053,7 +1089,11 @@ public final class MediaRouter2 { } void onRequestCreateControllerByManagerOnHandler( - RoutingSessionInfo oldSession, MediaRoute2Info route, long managerRequestId) { + RoutingSessionInfo oldSession, + MediaRoute2Info route, + long managerRequestId, + @NonNull UserHandle transferInitiatorUserHandle, + @NonNull String transferInitiatorPackageName) { Log.i( TAG, TextUtils.formatSimple( @@ -1070,7 +1110,8 @@ public final class MediaRouter2 { if (controller == null) { return; } - requestCreateController(controller, route, managerRequestId); + requestCreateController(controller, route, managerRequestId, transferInitiatorUserHandle, + transferInitiatorPackageName); } private List<MediaRoute2Info> getSortedRoutes( @@ -1469,6 +1510,21 @@ public final class MediaRouter2 { } /** + * Returns whether the transfer was requested by the calling app (as determined by comparing + * {@link UserHandle} and package name). + */ + @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES) + public boolean wasTransferRequestedBySelf() { + RoutingSessionInfo sessionInfo = getRoutingSessionInfo(); + + UserHandle transferInitiatorUserHandle = sessionInfo.getTransferInitiatorUserHandle(); + String transferInitiatorPackageName = sessionInfo.getTransferInitiatorPackageName(); + + return Objects.equals(android.os.Process.myUserHandle(), transferInitiatorUserHandle) + && Objects.equals(mContext.getPackageName(), transferInitiatorPackageName); + } + + /** * Returns the current {@link RoutingSessionInfo} associated to this controller. */ @NonNull @@ -1980,14 +2036,20 @@ public final class MediaRouter2 { @Override public void requestCreateSessionByManager( - long managerRequestId, RoutingSessionInfo oldSession, MediaRoute2Info route) { + long managerRequestId, + RoutingSessionInfo oldSession, + MediaRoute2Info route, + UserHandle transferInitiatorUserHandle, + String transferInitiatorPackageName) { mHandler.sendMessage( obtainMessage( MediaRouter2::onRequestCreateControllerByManagerOnHandler, MediaRouter2.this, oldSession, route, - managerRequestId)); + managerRequestId, + transferInitiatorUserHandle, + transferInitiatorPackageName)); } } @@ -2027,7 +2089,11 @@ public final class MediaRouter2 { void stop(); - void transfer(RoutingSessionInfo sessionInfo, MediaRoute2Info route); + void transfer( + @NonNull RoutingSessionInfo sessionInfo, + @NonNull MediaRoute2Info route, + @NonNull UserHandle transferInitiatorUserHandle, + @NonNull String transferInitiatorPackageName); List<RoutingController> getControllers(); @@ -2220,7 +2286,11 @@ public final class MediaRouter2 { List<RoutingSessionInfo> sessionInfos = getRoutingSessions(); RoutingSessionInfo targetSession = sessionInfos.get(sessionInfos.size() - 1); - transfer(targetSession, route); + transfer( + targetSession, + route, + android.os.Process.myUserHandle(), + mContext.getPackageName()); } @Override @@ -2243,14 +2313,24 @@ public final class MediaRouter2 { * * @param sessionInfo The {@link RoutingSessionInfo routing session} to transfer. * @param route The {@link MediaRoute2Info route} to transfer to. - * @see #transferToRoute(RoutingSessionInfo, MediaRoute2Info) + * @param transferInitiatorUserHandle The user handle of the app that initiated the + * transfer. + * @param transferInitiatorPackageName The package name if of the app that initiated the + * transfer. + * @see #transferToRoute(RoutingSessionInfo, MediaRoute2Info, UserHandle, String) * @see #requestCreateSession(RoutingSessionInfo, MediaRoute2Info) */ @Override + @SuppressWarnings("AndroidFrameworkRequiresPermission") public void transfer( - @NonNull RoutingSessionInfo sessionInfo, @NonNull MediaRoute2Info route) { + @NonNull RoutingSessionInfo sessionInfo, + @NonNull MediaRoute2Info route, + @NonNull UserHandle transferInitiatorUserHandle, + @NonNull String transferInitiatorPackageName) { Objects.requireNonNull(sessionInfo, "sessionInfo must not be null"); Objects.requireNonNull(route, "route must not be null"); + Objects.requireNonNull(transferInitiatorUserHandle); + Objects.requireNonNull(transferInitiatorPackageName); Log.v( TAG, @@ -2268,9 +2348,14 @@ public final class MediaRouter2 { } if (sessionInfo.getTransferableRoutes().contains(route.getId())) { - transferToRoute(sessionInfo, route); + transferToRoute( + sessionInfo, + route, + transferInitiatorUserHandle, + transferInitiatorPackageName); } else { - requestCreateSession(sessionInfo, route); + requestCreateSession(sessionInfo, route, transferInitiatorUserHandle, + transferInitiatorPackageName); } } @@ -2282,21 +2367,30 @@ public final class MediaRouter2 { * RoutingSessionInfo routing session's} {@link RoutingSessionInfo#getTransferableRoutes() * transferable routes list}. Otherwise, the request will fail. * - * <p>Use {@link #requestCreateSession(RoutingSessionInfo, MediaRoute2Info)} to request - * an out-of-session transfer. + * <p>Use {@link #requestCreateSession(RoutingSessionInfo, MediaRoute2Info)} to request an + * out-of-session transfer. * * @param session The {@link RoutingSessionInfo routing session} to transfer. * @param route The {@link MediaRoute2Info route} to transfer to. Must be one of the {@link * RoutingSessionInfo routing session's} {@link * RoutingSessionInfo#getTransferableRoutes() transferable routes}. */ + @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL) private void transferToRoute( - @NonNull RoutingSessionInfo session, @NonNull MediaRoute2Info route) { + @NonNull RoutingSessionInfo session, + @NonNull MediaRoute2Info route, + @NonNull UserHandle transferInitiatorUserHandle, + @NonNull String transferInitiatorPackageName) { int requestId = createTransferRequest(session, route); try { mMediaRouterService.transferToRouteWithManager( - mClient, requestId, session.getId(), route); + mClient, + requestId, + session.getId(), + route, + transferInitiatorUserHandle, + transferInitiatorPackageName); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } @@ -2317,7 +2411,10 @@ public final class MediaRouter2 { * @param route The {@link MediaRoute2Info route} to transfer to. */ private void requestCreateSession( - @NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route) { + @NonNull RoutingSessionInfo oldSession, + @NonNull MediaRoute2Info route, + @NonNull UserHandle transferInitiatorUserHandle, + @NonNull String transferInitiatorPackageName) { if (TextUtils.isEmpty(oldSession.getClientPackageName())) { Log.w(TAG, "requestCreateSession: Can't create a session without package name."); this.onTransferFailed(oldSession, route); @@ -2328,7 +2425,12 @@ public final class MediaRouter2 { try { mMediaRouterService.requestCreateSessionWithManager( - mClient, requestId, oldSession, route); + mClient, + requestId, + oldSession, + route, + transferInitiatorUserHandle, + transferInitiatorPackageName); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } @@ -3055,7 +3157,8 @@ public final class MediaRouter2 { return; } - requestCreateController(controller, route, MANAGER_REQUEST_ID_NONE); + requestCreateController(controller, route, MANAGER_REQUEST_ID_NONE, + android.os.Process.myUserHandle(), mContext.getPackageName()); } @Override @@ -3071,7 +3174,11 @@ public final class MediaRouter2 { * #transferTo(MediaRoute2Info)}. */ @Override - public void transfer(RoutingSessionInfo sessionInfo, MediaRoute2Info route) { + public void transfer( + @NonNull RoutingSessionInfo sessionInfo, + @NonNull MediaRoute2Info route, + @NonNull UserHandle transferInitiatorUserHandle, + @NonNull String transferInitiatorPackageName) { // Do nothing. } diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java index 830708cb38b2..06c0996785c2 100644 --- a/media/java/android/media/MediaRouter2Manager.java +++ b/media/java/android/media/MediaRouter2Manager.java @@ -18,9 +18,11 @@ package android.media; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; +import android.Manifest; import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.content.Context; import android.media.session.MediaController; import android.media.session.MediaSessionManager; @@ -28,6 +30,7 @@ import android.os.Handler; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.UserHandle; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; @@ -467,30 +470,42 @@ public final class MediaRouter2Manager { * <p>Same as {@link #transfer(RoutingSessionInfo, MediaRoute2Info)}, but resolves the routing * session based on the provided package name. */ - public void transfer(@NonNull String packageName, @NonNull MediaRoute2Info route) { + @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL) + public void transfer( + @NonNull String packageName, + @NonNull MediaRoute2Info route, + @NonNull UserHandle userHandle) { Objects.requireNonNull(packageName, "packageName must not be null"); Objects.requireNonNull(route, "route must not be null"); List<RoutingSessionInfo> sessionInfos = getRoutingSessions(packageName); RoutingSessionInfo targetSession = sessionInfos.get(sessionInfos.size() - 1); - transfer(targetSession, route); + transfer(targetSession, route, userHandle, packageName); } /** * Transfers a routing session to a media route. + * * <p>{@link Callback#onTransferred} or {@link Callback#onTransferFailed} will be called * depending on the result. * * @param sessionInfo the routing session info to transfer * @param route the route transfer to - * + * @param transferInitiatorUserHandle the user handle of an app initiated the transfer + * @param transferInitiatorPackageName the package name of an app initiated the transfer * @see Callback#onTransferred(RoutingSessionInfo, RoutingSessionInfo) * @see Callback#onTransferFailed(RoutingSessionInfo, MediaRoute2Info) */ - public void transfer(@NonNull RoutingSessionInfo sessionInfo, - @NonNull MediaRoute2Info route) { + @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL) + public void transfer( + @NonNull RoutingSessionInfo sessionInfo, + @NonNull MediaRoute2Info route, + @NonNull UserHandle transferInitiatorUserHandle, + @NonNull String transferInitiatorPackageName) { Objects.requireNonNull(sessionInfo, "sessionInfo must not be null"); Objects.requireNonNull(route, "route must not be null"); + Objects.requireNonNull(transferInitiatorUserHandle); + Objects.requireNonNull(transferInitiatorPackageName); Log.v(TAG, "Transferring routing session. session= " + sessionInfo + ", route=" + route); @@ -503,9 +518,11 @@ public final class MediaRouter2Manager { } if (sessionInfo.getTransferableRoutes().contains(route.getId())) { - transferToRoute(sessionInfo, route); + transferToRoute( + sessionInfo, route, transferInitiatorUserHandle, transferInitiatorPackageName); } else { - requestCreateSession(sessionInfo, route); + requestCreateSession(sessionInfo, route, transferInitiatorUserHandle, + transferInitiatorPackageName); } } @@ -873,19 +890,30 @@ public final class MediaRouter2Manager { * * @hide */ - private void transferToRoute(@NonNull RoutingSessionInfo session, - @NonNull MediaRoute2Info route) { + @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL) + private void transferToRoute( + @NonNull RoutingSessionInfo session, + @NonNull MediaRoute2Info route, + @NonNull UserHandle transferInitiatorUserHandle, + @NonNull String transferInitiatorPackageName) { int requestId = createTransferRequest(session, route); try { mMediaRouterService.transferToRouteWithManager( - mClient, requestId, session.getId(), route); + mClient, + requestId, + session.getId(), + route, + transferInitiatorUserHandle, + transferInitiatorPackageName); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } - private void requestCreateSession(RoutingSessionInfo oldSession, MediaRoute2Info route) { + private void requestCreateSession(RoutingSessionInfo oldSession, MediaRoute2Info route, + @NonNull UserHandle transferInitiatorUserHandle, + @NonNull String transferInitiationPackageName) { if (TextUtils.isEmpty(oldSession.getClientPackageName())) { Log.w(TAG, "requestCreateSession: Can't create a session without package name."); notifyTransferFailed(oldSession, route); @@ -896,7 +924,8 @@ public final class MediaRouter2Manager { try { mMediaRouterService.requestCreateSessionWithManager( - mClient, requestId, oldSession, route); + mClient, requestId, oldSession, route, transferInitiatorUserHandle, + transferInitiationPackageName); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } diff --git a/media/java/android/media/RoutingSessionInfo.java b/media/java/android/media/RoutingSessionInfo.java index a77c9432f197..d28c26df6749 100644 --- a/media/java/android/media/RoutingSessionInfo.java +++ b/media/java/android/media/RoutingSessionInfo.java @@ -16,18 +16,25 @@ package android.media; +import static com.android.media.flags.Flags.FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES; + +import android.annotation.FlaggedApi; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.res.Resources; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.os.UserHandle; import android.text.TextUtils; import com.android.internal.util.Preconditions; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -55,6 +62,33 @@ public final class RoutingSessionInfo implements Parcelable { private static final String KEY_GROUP_ROUTE = "androidx.mediarouter.media.KEY_GROUP_ROUTE"; private static final String KEY_VOLUME_HANDLING = "volumeHandling"; + /** + * Indicates that the transfer happened by the default logic without explicit system's or user's + * request. + * + * <p>For example, an automatically connected Bluetooth device will have this transfer reason. + */ + @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES) + public static final int TRANSFER_REASON_FALLBACK = 0; + + /** Indicates that the transfer happened from within a privileged application. */ + @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES) + public static final int TRANSFER_REASON_SYSTEM_REQUEST = 1; + + /** Indicates that the transfer happened from a non-privileged app. */ + @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES) + public static final int TRANSFER_REASON_APP = 2; + + /** + * Indicates the transfer reason. + * + * @hide + */ + @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES) + @IntDef(value = {TRANSFER_REASON_FALLBACK, TRANSFER_REASON_SYSTEM_REQUEST, TRANSFER_REASON_APP}) + @Retention(RetentionPolicy.SOURCE) + public @interface TransferReason {} + @NonNull final String mId; @Nullable @@ -82,6 +116,10 @@ public final class RoutingSessionInfo implements Parcelable { final Bundle mControlHints; final boolean mIsSystemSession; + @TransferReason final int mTransferReason; + + @Nullable final UserHandle mTransferInitiatorUserHandle; + @Nullable final String mTransferInitiatorPackageName; RoutingSessionInfo(@NonNull Builder builder) { Objects.requireNonNull(builder, "builder must not be null."); @@ -116,6 +154,9 @@ public final class RoutingSessionInfo implements Parcelable { volumeAdjustmentForRemoteGroupSessions); mControlHints = updateVolumeHandlingInHints(builder.mControlHints, mVolumeHandling); + mTransferReason = builder.mTransferReason; + mTransferInitiatorUserHandle = builder.mTransferInitiatorUserHandle; + mTransferInitiatorPackageName = builder.mTransferInitiatorPackageName; } RoutingSessionInfo(@NonNull Parcel src) { @@ -140,6 +181,9 @@ public final class RoutingSessionInfo implements Parcelable { mControlHints = src.readBundle(); mIsSystemSession = src.readBoolean(); + mTransferReason = src.readInt(); + mTransferInitiatorUserHandle = src.readParcelable(null, android.os.UserHandle.class); + mTransferInitiatorPackageName = src.readString(); } @Nullable @@ -330,6 +374,27 @@ public final class RoutingSessionInfo implements Parcelable { return mIsSystemSession; } + /** Returns the transfer reason for this routing session. */ + @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES) + @TransferReason + public int getTransferReason() { + return mTransferReason; + } + + /** @hide */ + @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES) + @Nullable + public UserHandle getTransferInitiatorUserHandle() { + return mTransferInitiatorUserHandle; + } + + /** @hide */ + @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES) + @Nullable + public String getTransferInitiatorPackageName() { + return mTransferInitiatorPackageName; + } + @Override public int describeContents() { return 0; @@ -351,6 +416,13 @@ public final class RoutingSessionInfo implements Parcelable { dest.writeInt(mVolume); dest.writeBundle(mControlHints); dest.writeBoolean(mIsSystemSession); + dest.writeInt(mTransferReason); + if (mTransferInitiatorUserHandle != null) { + mTransferInitiatorUserHandle.writeToParcel(dest, /* flags= */ 0); + } else { + dest.writeParcelable(null, /* flags= */ 0); + } + dest.writeString(mTransferInitiatorPackageName); } /** @@ -379,6 +451,9 @@ public final class RoutingSessionInfo implements Parcelable { pw.println(indent + "mVolume=" + mVolume); pw.println(indent + "mControlHints=" + mControlHints); pw.println(indent + "mIsSystemSession=" + mIsSystemSession); + pw.println(indent + "mTransferReason=" + mTransferReason); + pw.println(indent + "mtransferInitiatorUserHandle=" + mTransferInitiatorUserHandle); + pw.println(indent + "mtransferInitiatorPackageName=" + mTransferInitiatorPackageName); } @Override @@ -406,39 +481,69 @@ public final class RoutingSessionInfo implements Parcelable { && Objects.equals(mTransferableRoutes, other.mTransferableRoutes) && (mVolumeHandling == other.mVolumeHandling) && (mVolumeMax == other.mVolumeMax) - && (mVolume == other.mVolume); + && (mVolume == other.mVolume) + && (mTransferReason == other.mTransferReason) + && Objects.equals(mTransferInitiatorUserHandle, other.mTransferInitiatorUserHandle) + && Objects.equals( + mTransferInitiatorPackageName, other.mTransferInitiatorPackageName); } @Override public int hashCode() { - return Objects.hash(mId, mName, mOwnerPackageName, mClientPackageName, mProviderId, - mSelectedRoutes, mSelectableRoutes, mDeselectableRoutes, mTransferableRoutes, - mVolumeMax, mVolumeHandling, mVolume); + return Objects.hash( + mId, + mName, + mOwnerPackageName, + mClientPackageName, + mProviderId, + mSelectedRoutes, + mSelectableRoutes, + mDeselectableRoutes, + mTransferableRoutes, + mVolumeMax, + mVolumeHandling, + mVolume, + mTransferReason, + mTransferInitiatorUserHandle, + mTransferInitiatorPackageName); } @Override public String toString() { - StringBuilder result = new StringBuilder() - .append("RoutingSessionInfo{ ") - .append("sessionId=").append(getId()) - .append(", name=").append(getName()) - .append(", clientPackageName=").append(getClientPackageName()) - .append(", selectedRoutes={") - .append(String.join(",", getSelectedRoutes())) - .append("}") - .append(", selectableRoutes={") - .append(String.join(",", getSelectableRoutes())) - .append("}") - .append(", deselectableRoutes={") - .append(String.join(",", getDeselectableRoutes())) - .append("}") - .append(", transferableRoutes={") - .append(String.join(",", getTransferableRoutes())) - .append("}") - .append(", volumeHandling=").append(getVolumeHandling()) - .append(", volumeMax=").append(getVolumeMax()) - .append(", volume=").append(getVolume()) - .append(" }"); + StringBuilder result = + new StringBuilder() + .append("RoutingSessionInfo{ ") + .append("sessionId=") + .append(getId()) + .append(", name=") + .append(getName()) + .append(", clientPackageName=") + .append(getClientPackageName()) + .append(", selectedRoutes={") + .append(String.join(",", getSelectedRoutes())) + .append("}") + .append(", selectableRoutes={") + .append(String.join(",", getSelectableRoutes())) + .append("}") + .append(", deselectableRoutes={") + .append(String.join(",", getDeselectableRoutes())) + .append("}") + .append(", transferableRoutes={") + .append(String.join(",", getTransferableRoutes())) + .append("}") + .append(", volumeHandling=") + .append(getVolumeHandling()) + .append(", volumeMax=") + .append(getVolumeMax()) + .append(", volume=") + .append(getVolume()) + .append(", transferReason=") + .append(getTransferReason()) + .append(", transferInitiatorUserHandle=") + .append(getTransferInitiatorUserHandle()) + .append(", transferInitiatorPackageName=") + .append(getTransferInitiatorPackageName()) + .append(" }"); return result.toString(); } @@ -494,6 +599,9 @@ public final class RoutingSessionInfo implements Parcelable { @Nullable private Bundle mControlHints; private boolean mIsSystemSession; + @TransferReason private int mTransferReason = TRANSFER_REASON_FALLBACK; + @Nullable private UserHandle mTransferInitiatorUserHandle; + @Nullable private String mTransferInitiatorPackageName; /** * Constructor for builder to create {@link RoutingSessionInfo}. @@ -555,6 +663,9 @@ public final class RoutingSessionInfo implements Parcelable { mControlHints = sessionInfo.mControlHints; mIsSystemSession = sessionInfo.mIsSystemSession; + mTransferReason = sessionInfo.mTransferReason; + mTransferInitiatorUserHandle = sessionInfo.mTransferInitiatorUserHandle; + mTransferInitiatorPackageName = sessionInfo.mTransferInitiatorPackageName; } /** @@ -784,6 +895,35 @@ public final class RoutingSessionInfo implements Parcelable { } /** + * Sets transfer reason for the current session. + * + * <p>By default the transfer reason is set to {@link + * RoutingSessionInfo#TRANSFER_REASON_FALLBACK}. + */ + @NonNull + @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES) + public Builder setTransferReason(@TransferReason int transferReason) { + mTransferReason = transferReason; + return this; + } + + /** + * Sets the user handle and package name of the process that initiated the transfer. + * + * <p>By default the transfer initiation user handle and package name are set to {@code + * null}. + */ + @NonNull + @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES) + public Builder setTransferInitiator( + @Nullable UserHandle transferInitiatorUserHandle, + @Nullable String transferInitiatorPackageName) { + mTransferInitiatorUserHandle = transferInitiatorUserHandle; + mTransferInitiatorPackageName = transferInitiatorPackageName; + return this; + } + + /** * Builds a routing session info. * * @throws IllegalArgumentException if no selected routes are added. diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig index 07f63e5441af..3da52ccbafed 100644 --- a/media/java/android/media/flags/media_better_together.aconfig +++ b/media/java/android/media/flags/media_better_together.aconfig @@ -69,3 +69,11 @@ flag { description: "Use BluetoothDevice.getAlias to populate the name of Bluetooth MediaRoute2Infos." bug: "314324170" } + +flag { + name: "enable_built_in_speaker_route_suitability_statuses" + namespace: "media_solutions" + description: "Make MediaRoute2Info provide information about routes suitability for transfer." + bug: "279555229" +} + diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java index 8ed4bf2b9cc3..c836df3b2c4d 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java @@ -385,7 +385,9 @@ public class MediaRouter2ManagerTest { MediaRoute2Info routeToSelect = routes.get(ROUTE_ID1); assertThat(routeToSelect).isNotNull(); - mManager.transfer(mPackageName, routeToSelect); + mManager.transfer( + mPackageName, routeToSelect, + android.os.Process.myUserHandle()); assertThat(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue(); assertThat(mManager.getRemoteSessions()).hasSize(1); } @@ -411,7 +413,9 @@ public class MediaRouter2ManagerTest { assertThat(mManager.getRoutingSessions(mPackageName)).hasSize(1); - mManager.transfer(mPackageName, routeToSelect); + mManager.transfer( + mPackageName, routeToSelect, + android.os.Process.myUserHandle()); assertThat(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue(); List<RoutingSessionInfo> sessions = mManager.getRoutingSessions(mPackageName); @@ -450,7 +454,11 @@ public class MediaRouter2ManagerTest { .addFeature(FEATURE_REMOTE_PLAYBACK) .build(); - mManager.transfer(mManager.getSystemRoutingSession(null), unknownRoute); + mManager.transfer( + mManager.getSystemRoutingSession(null), + unknownRoute, + android.os.Process.myUserHandle(), + mContext.getPackageName()); assertThat(onSessionCreatedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)).isFalse(); assertThat(onTransferFailedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue(); } @@ -484,7 +492,11 @@ public class MediaRouter2ManagerTest { assertThat(mManager.getRoutingSessions(mPackageName)).hasSize(1); assertThat(mRouter2.getControllers()).hasSize(1); - mManager.transfer(mManager.getRoutingSessions(mPackageName).get(0), routeToSelect); + mManager.transfer( + mManager.getRoutingSessions(mPackageName).get(0), + routeToSelect, + android.os.Process.myUserHandle(), + mContext.getPackageName()); assertThat(transferLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue(); assertThat(mManager.getRoutingSessions(mPackageName)).hasSize(2); @@ -516,7 +528,11 @@ public class MediaRouter2ManagerTest { } }); awaitOnRouteChangedManager( - () -> mManager.transfer(mPackageName, routes.get(ROUTE_ID1)), + () -> + mManager.transfer( + mPackageName, + routes.get(ROUTE_ID1), + android.os.Process.myUserHandle()), ROUTE_ID1, route -> TextUtils.equals(route.getClientPackageName(), mPackageName)); assertThat(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue(); @@ -527,7 +543,11 @@ public class MediaRouter2ManagerTest { RoutingSessionInfo sessionInfo = sessions.get(1); awaitOnRouteChangedManager( - () -> mManager.transfer(mPackageName, routes.get(ROUTE_ID5_TO_TRANSFER_TO)), + () -> + mManager.transfer( + mPackageName, + routes.get(ROUTE_ID5_TO_TRANSFER_TO), + android.os.Process.myUserHandle()), ROUTE_ID5_TO_TRANSFER_TO, route -> TextUtils.equals(route.getClientPackageName(), mPackageName)); @@ -585,9 +605,11 @@ public class MediaRouter2ManagerTest { assertThat(route1).isNotNull(); assertThat(route2).isNotNull(); - mManager.transfer(mPackageName, route1); + mManager.transfer( + mPackageName, route1, android.os.Process.myUserHandle()); assertThat(successLatch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue(); - mManager.transfer(mPackageName, route2); + mManager.transfer( + mPackageName, route2, android.os.Process.myUserHandle()); assertThat(successLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue(); // onTransferFailed/onSessionReleased should not be called. @@ -634,7 +656,11 @@ public class MediaRouter2ManagerTest { List<RoutingSessionInfo> sessions = mManager.getRoutingSessions(mPackageName); RoutingSessionInfo targetSession = sessions.get(sessions.size() - 1); - mManager.transfer(targetSession, routes.get(ROUTE_ID6_TO_BE_IGNORED)); + mManager.transfer( + targetSession, + routes.get(ROUTE_ID6_TO_BE_IGNORED), + android.os.Process.myUserHandle(), + mContext.getPackageName()); assertThat(onSessionCreatedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)).isFalse(); assertThat(onFailedLatch.await(MediaRouter2Manager.TRANSFER_TIMEOUT_MS, @@ -705,7 +731,10 @@ public class MediaRouter2ManagerTest { } }); - mManager.transfer(mPackageName, routes.get(ROUTE_ID1)); + mManager.transfer( + mPackageName, + routes.get(ROUTE_ID1), + android.os.Process.myUserHandle()); assertThat(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue(); List<RoutingSessionInfo> sessions = mManager.getRoutingSessions(mPackageName); @@ -860,7 +889,8 @@ public class MediaRouter2ManagerTest { }); mRouter2.setOnGetControllerHintsListener(listener); - mManager.transfer(mPackageName, route); + mManager.transfer( + mPackageName, route, android.os.Process.myUserHandle()); assertThat(hintLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue(); assertThat(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue(); @@ -905,7 +935,10 @@ public class MediaRouter2ManagerTest { } }); - mManager.transfer(mPackageName, routes.get(ROUTE_ID4_TO_SELECT_AND_DESELECT)); + mManager.transfer( + mPackageName, + routes.get(ROUTE_ID4_TO_SELECT_AND_DESELECT), + android.os.Process.myUserHandle()); assertThat(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue(); } diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt index 66bd6f502274..d5cf1a35b4df 100644 --- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt +++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt @@ -26,6 +26,7 @@ import androidx.compose.material.icons.outlined.Error import androidx.compose.material.icons.outlined.PowerOff import androidx.compose.material.icons.outlined.Shield import androidx.compose.material.icons.outlined.WarningAmber +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -78,24 +79,19 @@ object CardPageProvider : SettingsPageProvider { imageVector = Icons.Outlined.WarningAmber, buttons = listOf( CardButton(text = "Action") {}, - CardButton(text = "Action", isMain = true) {}, - ) + ), + tintColor = MaterialTheme.colorScheme.error, + containerColor = MaterialTheme.colorScheme.errorContainer, ) ) } @Composable private fun SettingsCardWithoutIcon() { - var isVisible by rememberSaveable { mutableStateOf(true) } SettingsCard( CardModel( title = stringResource(R.string.sample_title), text = stringResource(R.string.sample_text), - isVisible = { isVisible }, - onDismiss = { isVisible = false }, - buttons = listOf( - CardButton(text = "Action") {}, - ), ) ) } @@ -104,6 +100,7 @@ object CardPageProvider : SettingsPageProvider { fun SampleSettingsCollapsibleCard() { val context = LocalContext.current var isVisible0 by rememberSaveable { mutableStateOf(true) } + var isVisible1 by rememberSaveable { mutableStateOf(true) } val cards = remember { mutableStateListOf( CardModel( @@ -114,16 +111,17 @@ object CardPageProvider : SettingsPageProvider { onDismiss = { isVisible0 = false }, buttons = listOf( CardButton(text = "Action") {}, - ) + ), ), CardModel( title = context.getString(R.string.sample_title), text = context.getString(R.string.sample_text), imageVector = Icons.Outlined.Shield, + isVisible = { isVisible1 }, + onDismiss = { isVisible1 = false }, buttons = listOf( CardButton(text = "Action") {}, - CardButton(text = "Main action", isMain = true) {}, - ) + ), ) ) } diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt index 993cb4ac85e5..c143390f269c 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt @@ -37,7 +37,6 @@ object SettingsDimension { val itemPaddingAround = 8.dp val itemDividerHeight = 32.dp - val iconSmall = 16.dp val iconLarge = 48.dp /** The size when app icon is displayed in list. */ diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt index b18a1bc01388..b2a8b87a4495 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt @@ -16,11 +16,11 @@ package com.android.settingslib.spa.widget.card +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector data class CardButton( val text: String, - val isMain: Boolean = false, val onClick: () -> Unit, ) @@ -38,4 +38,10 @@ data class CardModel( val onDismiss: (() -> Unit)? = null, val buttons: List<CardButton> = emptyList(), + + /** If specified, this color will be used to tint the icon and the buttons. */ + val tintColor: Color = Color.Unspecified, + + /** If specified, this color will be used to tint the icon and the buttons. */ + val containerColor: Color = Color.Unspecified, ) diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt index 7eec8888f025..c7845fa724d4 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt @@ -23,26 +23,26 @@ import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Close import androidx.compose.material.icons.outlined.WarningAmber -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Surface import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.takeOrElse import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp @@ -72,11 +72,14 @@ fun SettingsCard(content: @Composable ColumnScope.() -> Unit) { } @Composable -fun SettingsCardContent(content: @Composable ColumnScope.() -> Unit) { +fun SettingsCardContent( + containerColor: Color = Color.Unspecified, + content: @Composable ColumnScope.() -> Unit, +) { Card( shape = CornerExtraSmall, colors = CardDefaults.cardColors( - containerColor = SettingsTheme.colorScheme.surface, + containerColor = containerColor.takeOrElse { SettingsTheme.colorScheme.surface }, ), modifier = Modifier .fillMaxWidth() @@ -95,37 +98,43 @@ fun SettingsCard(model: CardModel) { @Composable internal fun SettingsCardImpl(model: CardModel) { AnimatedVisibility(visible = model.isVisible()) { - SettingsCardContent { + SettingsCardContent(containerColor = model.containerColor) { Column( - modifier = Modifier.padding(SettingsDimension.itemPaddingStart), + modifier = Modifier.padding( + horizontal = SettingsDimension.dialogItemPaddingHorizontal, + vertical = SettingsDimension.itemPaddingAround, + ), verticalArrangement = Arrangement.spacedBy(SettingsDimension.itemPaddingAround) ) { - CardHeader(model.imageVector, model.onDismiss) + CardHeader(model.imageVector, model.tintColor, model.onDismiss) SettingsTitle(model.title) SettingsBody(model.text) - Buttons(model.buttons) + Buttons(model.buttons, model.tintColor) } } } } @Composable -fun CardHeader(imageVector: ImageVector?, onDismiss: (() -> Unit)? = null) { +fun CardHeader(imageVector: ImageVector?, iconColor: Color, onDismiss: (() -> Unit)? = null) { + if (imageVector != null || onDismiss != null) { + Spacer(Modifier.height(SettingsDimension.buttonPaddingVertical)) + } Row(Modifier.fillMaxWidth()) { - CardIcon(imageVector) + CardIcon(imageVector, iconColor) Spacer(modifier = Modifier.weight(1f)) DismissButton(onDismiss) } } @Composable -private fun CardIcon(imageVector: ImageVector?) { +private fun CardIcon(imageVector: ImageVector?, color: Color) { if (imageVector != null) { Icon( imageVector = imageVector, contentDescription = null, modifier = Modifier.size(SettingsDimension.itemIconSize), - tint = MaterialTheme.colorScheme.primary, + tint = color.takeOrElse { MaterialTheme.colorScheme.primary }, ) } } @@ -146,52 +155,35 @@ private fun DismissButton(onDismiss: (() -> Unit)?) { contentDescription = stringResource( androidx.compose.material3.R.string.m3c_snackbar_dismiss ), - modifier = Modifier.size(SettingsDimension.iconSmall), + modifier = Modifier.padding(SettingsDimension.paddingSmall), ) } } } @Composable -private fun Buttons(buttons: List<CardButton>) { +private fun Buttons(buttons: List<CardButton>, color: Color) { if (buttons.isNotEmpty()) { Row( - modifier = Modifier - .fillMaxWidth() - .padding(top = SettingsDimension.itemPaddingAround), + modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy( space = SettingsDimension.itemPaddingEnd, alignment = Alignment.End, ), ) { for (button in buttons) { - Button(button) + Button(button, color) } } + } else { + Spacer(Modifier.height(SettingsDimension.itemPaddingAround)) } } @Composable -private fun Button(button: CardButton) { - if (button.isMain) { - Button( - onClick = button.onClick, - colors = ButtonDefaults.buttonColors( - containerColor = SettingsTheme.colorScheme.primaryContainer, - ), - ) { - Text( - text = button.text, - color = SettingsTheme.colorScheme.onPrimaryContainer, - ) - } - } else { - OutlinedButton(onClick = button.onClick) { - Text( - text = button.text, - color = MaterialTheme.colorScheme.onSurface, - ) - } +private fun Button(button: CardButton, color: Color) { + TextButton(onClick = button.onClick) { + Text(text = button.text, color = color) } } @@ -206,7 +198,6 @@ private fun SettingsCardPreview() { imageVector = Icons.Outlined.WarningAmber, buttons = listOf( CardButton(text = "Action") {}, - CardButton(text = "Action", isMain = true) {}, ) ) ) diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCard.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCard.kt index 6e36490beac7..c34df653f03c 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCard.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCard.kt @@ -141,7 +141,6 @@ private fun SettingsCollapsibleCardPreview() { imageVector = Icons.Outlined.Shield, buttons = listOf( CardButton(text = "Action") {}, - CardButton(text = "Main action", isMain = true) {}, ) ) ) diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingHelper.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingHelper.java index c9512cd01aa3..8eaea0e3561b 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingHelper.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingHelper.java @@ -151,7 +151,9 @@ public class HearingAidAudioRoutingHelper { private boolean removePreferredDeviceForStrategies(List<AudioProductStrategy> strategies) { boolean status = true; for (AudioProductStrategy strategy : strategies) { - status &= mAudioManager.removePreferredDeviceForStrategy(strategy); + if (mAudioManager.getPreferredDeviceForStrategy(strategy) != null) { + status &= mAudioManager.removePreferredDeviceForStrategy(strategy); + } } return status; diff --git a/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java index d5b5af7a98c4..97bbf12fd055 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java @@ -81,14 +81,17 @@ public class ManagerInfoMediaManager extends InfoMediaManager { @Override protected void transferToRoute(@NonNull MediaRoute2Info route) { - mRouterManager.transfer(mPackageName, route); + // TODO: b/279555229 - provide real user handle of a caller. + mRouterManager.transfer(mPackageName, route, android.os.Process.myUserHandle()); } @Override protected boolean connectDeviceWithoutPackageName(@NonNull MediaDevice device) { final RoutingSessionInfo info = mRouterManager.getSystemRoutingSession(null); if (info != null) { - mRouterManager.transfer(info, device.mRouteInfo); + // TODO: b/279555229 - provide real user handle and package name of a caller. + mRouterManager.transfer( + info, device.mRouteInfo, android.os.Process.myUserHandle(), mPackageName); return true; } return false; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingHelperTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingHelperTest.java index 8b5ea30e17fe..c83524462b15 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingHelperTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingHelperTest.java @@ -18,8 +18,10 @@ package com.android.settingslib.bluetooth; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -43,6 +45,7 @@ import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -79,6 +82,8 @@ public class HearingAidAudioRoutingHelperTest { when(mAudioDeviceInfo.getAddress()).thenReturn(TEST_DEVICE_ADDRESS); when(mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)).thenReturn( new AudioDeviceInfo[]{mAudioDeviceInfo}); + doReturn(Collections.emptyList()).when(mAudioManager).getPreferredDevicesForStrategy( + any(AudioProductStrategy.class)); when(mAudioStrategy.getAudioAttributesForLegacyStreamType( AudioManager.STREAM_MUSIC)) .thenReturn((new AudioAttributes.Builder()).build()); @@ -92,7 +97,10 @@ public class HearingAidAudioRoutingHelperTest { } @Test - public void setPreferredDeviceRoutingStrategies_valueAuto_callRemoveStrategy() { + public void setPreferredDeviceRoutingStrategies_hadValueThenValueAuto_callRemoveStrategy() { + when(mAudioManager.getPreferredDeviceForStrategy(mAudioStrategy)).thenReturn( + mHearingDeviceAttribute); + mHelper.setPreferredDeviceRoutingStrategies(List.of(mAudioStrategy), mHearingDeviceAttribute, HearingAidAudioRoutingConstants.RoutingValue.AUTO); @@ -101,6 +109,17 @@ public class HearingAidAudioRoutingHelperTest { } @Test + public void setPreferredDeviceRoutingStrategies_NoValueThenValueAuto_notCallRemoveStrategy() { + when(mAudioManager.getPreferredDeviceForStrategy(mAudioStrategy)).thenReturn(null); + + mHelper.setPreferredDeviceRoutingStrategies(List.of(mAudioStrategy), + mHearingDeviceAttribute, + HearingAidAudioRoutingConstants.RoutingValue.AUTO); + + verify(mAudioManager, never()).removePreferredDeviceForStrategy(mAudioStrategy); + } + + @Test public void setPreferredDeviceRoutingStrategies_valueHearingDevice_callSetStrategy() { mHelper.setPreferredDeviceRoutingStrategies(List.of(mAudioStrategy), mHearingDeviceAttribute, diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index 782b850a5c0c..98a2d9ff2d19 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -276,3 +276,9 @@ flag { bug: "312784809" } +flag { + name: "bluetooth_qs_tile_dialog_auto_on_toggle" + namespace: "systemui" + description: "Displays the auto on toggle in the bluetooth QS tile dialog" + bug: "316985153" +} diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt index b9d66432b5f2..2bfa7d9bbd7b 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt @@ -33,6 +33,8 @@ import com.android.internal.annotations.VisibleForTesting import com.android.systemui.animation.GlyphCallback import com.android.systemui.animation.TextAnimator import com.android.systemui.customization.R +import com.android.systemui.log.core.LogcatOnlyMessageBuffer +import com.android.systemui.log.core.LogLevel import com.android.systemui.log.core.Logger import com.android.systemui.log.core.MessageBuffer import java.io.PrintWriter @@ -51,12 +53,13 @@ class AnimatableClockView @JvmOverloads constructor( defStyleAttr: Int = 0, defStyleRes: Int = 0 ) : TextView(context, attrs, defStyleAttr, defStyleRes) { - var messageBuffer: MessageBuffer? = null - set(value) { - logger = if (value != null) Logger(value, TAG) else null - } - - private var logger: Logger? = null + // To protect us from issues from this being null while the TextView constructor is running, we + // implement the get method and ensure a value is returned before initialization is complete. + private var logger = DEFAULT_LOGGER + get() = field ?: DEFAULT_LOGGER + var messageBuffer: MessageBuffer + get() = logger.buffer + set(value) { logger = Logger(value, TAG) } private val time = Calendar.getInstance() @@ -133,8 +136,8 @@ class AnimatableClockView @JvmOverloads constructor( } override fun onAttachedToWindow() { + logger.d("onAttachedToWindow") super.onAttachedToWindow() - logger?.d("onAttachedToWindow") refreshFormat() } @@ -150,13 +153,13 @@ class AnimatableClockView @JvmOverloads constructor( time.timeInMillis = timeOverrideInMillis ?: System.currentTimeMillis() contentDescription = DateFormat.format(descFormat, time) val formattedText = DateFormat.format(format, time) - logger?.d({ "refreshTime: new formattedText=$str1" }) { str1 = formattedText?.toString() } + logger.d({ "refreshTime: new formattedText=$str1" }) { str1 = formattedText?.toString() } // Setting text actually triggers a layout pass (because the text view is set to // wrap_content width and TextView always relayouts for this). Avoid needless // relayout if the text didn't actually change. if (!TextUtils.equals(text, formattedText)) { text = formattedText - logger?.d({ "refreshTime: done setting new time text to: $str1" }) { + logger.d({ "refreshTime: done setting new time text to: $str1" }) { str1 = formattedText?.toString() } // Because the TextLayout may mutate under the hood as a result of the new text, we @@ -165,21 +168,22 @@ class AnimatableClockView @JvmOverloads constructor( // without being notified TextInterpolator being notified. if (layout != null) { textAnimator?.updateLayout(layout) - logger?.d("refreshTime: done updating textAnimator layout") + logger.d("refreshTime: done updating textAnimator layout") } requestLayout() - logger?.d("refreshTime: after requestLayout") + logger.d("refreshTime: after requestLayout") } } fun onTimeZoneChanged(timeZone: TimeZone?) { + logger.d({ "onTimeZoneChanged($str1)" }) { str1 = timeZone?.toString() } time.timeZone = timeZone refreshFormat() - logger?.d({ "onTimeZoneChanged newTimeZone=$str1" }) { str1 = timeZone?.toString() } } @SuppressLint("DrawAllocation") override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + logger.d("onMeasure") super.onMeasure(widthMeasureSpec, heightMeasureSpec) val animator = textAnimator if (animator == null) { @@ -189,10 +193,10 @@ class AnimatableClockView @JvmOverloads constructor( } else { animator.updateLayout(layout) } - logger?.d("onMeasure") } override fun onDraw(canvas: Canvas) { + logger.d({ "onDraw($str1)"}) { str1 = text.toString() } // Use textAnimator to render text if animation is enabled. // Otherwise default to using standard draw functions. if (isAnimationEnabled) { @@ -201,22 +205,23 @@ class AnimatableClockView @JvmOverloads constructor( } else { super.onDraw(canvas) } - logger?.d("onDraw") } override fun invalidate() { + @Suppress("UNNECESSARY_SAFE_CALL") + // logger won't be initialized when called by TextView's constructor + logger.d("invalidate") super.invalidate() - logger?.d("invalidate") } override fun onTextChanged( - text: CharSequence, - start: Int, - lengthBefore: Int, - lengthAfter: Int + text: CharSequence, + start: Int, + lengthBefore: Int, + lengthAfter: Int ) { + logger.d({ "onTextChanged($str1)" }) { str1 = text.toString() } super.onTextChanged(text, start, lengthBefore, lengthAfter) - logger?.d({ "onTextChanged text=$str1" }) { str1 = text.toString() } } fun setLineSpacingScale(scale: Float) { @@ -230,7 +235,7 @@ class AnimatableClockView @JvmOverloads constructor( } fun animateColorChange() { - logger?.d("animateColorChange") + logger.d("animateColorChange") setTextStyle( weight = lockScreenWeight, textSize = -1f, @@ -252,7 +257,7 @@ class AnimatableClockView @JvmOverloads constructor( } fun animateAppearOnLockscreen() { - logger?.d("animateAppearOnLockscreen") + logger.d("animateAppearOnLockscreen") setTextStyle( weight = dozingWeight, textSize = -1f, @@ -278,7 +283,7 @@ class AnimatableClockView @JvmOverloads constructor( if (isAnimationEnabled && textAnimator == null) { return } - logger?.d("animateFoldAppear") + logger.d("animateFoldAppear") setTextStyle( weight = lockScreenWeightInternal, textSize = -1f, @@ -305,7 +310,7 @@ class AnimatableClockView @JvmOverloads constructor( // Skip charge animation if dozing animation is already playing. return } - logger?.d("animateCharge") + logger.d("animateCharge") val startAnimPhase2 = Runnable { setTextStyle( weight = if (isDozing()) dozingWeight else lockScreenWeight, @@ -329,7 +334,7 @@ class AnimatableClockView @JvmOverloads constructor( } fun animateDoze(isDozing: Boolean, animate: Boolean) { - logger?.d("animateDoze") + logger.d("animateDoze") setTextStyle( weight = if (isDozing) dozingWeight else lockScreenWeight, textSize = -1f, @@ -448,7 +453,7 @@ class AnimatableClockView @JvmOverloads constructor( isSingleLineInternal && !use24HourFormat -> Patterns.sClockView12 else -> DOUBLE_LINE_FORMAT_12_HOUR } - logger?.d({ "refreshFormat format=$str1" }) { str1 = format?.toString() } + logger.d({ "refreshFormat($str1)" }) { str1 = format?.toString() } descFormat = if (use24HourFormat) Patterns.sClockView24 else Patterns.sClockView12 refreshTime() @@ -552,6 +557,8 @@ class AnimatableClockView @JvmOverloads constructor( companion object { private val TAG = AnimatableClockView::class.simpleName!! + private val DEFAULT_LOGGER = Logger(LogcatOnlyMessageBuffer(LogLevel.WARNING), TAG) + const val ANIMATION_DURATION_FOLD_TO_AOD: Int = 600 private const val DOUBLE_LINE_FORMAT_12_HOUR = "hh\nmm" private const val DOUBLE_LINE_FORMAT_24_HOUR = "HH\nmm" diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt index cdd074d872c0..41bde5298c66 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt @@ -21,20 +21,16 @@ import android.graphics.drawable.Drawable import android.net.Uri import android.os.UserHandle import android.provider.Settings -import android.util.Log import androidx.annotation.OpenForTesting -import com.android.systemui.log.LogMessageImpl import com.android.systemui.log.core.LogLevel -import com.android.systemui.log.core.LogMessage +import com.android.systemui.log.core.LogcatOnlyMessageBuffer import com.android.systemui.log.core.Logger -import com.android.systemui.log.core.MessageBuffer -import com.android.systemui.log.core.MessageInitializer -import com.android.systemui.log.core.MessagePrinter import com.android.systemui.plugins.PluginLifecycleManager import com.android.systemui.plugins.PluginListener import com.android.systemui.plugins.PluginManager import com.android.systemui.plugins.clocks.ClockController import com.android.systemui.plugins.clocks.ClockId +import com.android.systemui.plugins.clocks.ClockMessageBuffers import com.android.systemui.plugins.clocks.ClockMetadata import com.android.systemui.plugins.clocks.ClockProvider import com.android.systemui.plugins.clocks.ClockProviderPlugin @@ -77,32 +73,6 @@ private fun <TKey : Any, TVal : Any> ConcurrentHashMap<TKey, TVal>.concurrentGet return result ?: value } -private val TMP_MESSAGE: LogMessage by lazy { LogMessageImpl.Factory.create() } - -private inline fun Logger?.tryLog( - tag: String, - level: LogLevel, - messageInitializer: MessageInitializer, - noinline messagePrinter: MessagePrinter, - ex: Throwable? = null, -) { - if (this != null) { - // Wrap messagePrinter to convert it from crossinline to noinline - this.log(level, messagePrinter, ex, messageInitializer) - } else { - messageInitializer(TMP_MESSAGE) - val msg = messagePrinter(TMP_MESSAGE) - when (level) { - LogLevel.VERBOSE -> Log.v(tag, msg, ex) - LogLevel.DEBUG -> Log.d(tag, msg, ex) - LogLevel.INFO -> Log.i(tag, msg, ex) - LogLevel.WARNING -> Log.w(tag, msg, ex) - LogLevel.ERROR -> Log.e(tag, msg, ex) - LogLevel.WTF -> Log.wtf(tag, msg, ex) - } - } -} - /** ClockRegistry aggregates providers and plugins */ open class ClockRegistry( val context: Context, @@ -114,12 +84,15 @@ open class ClockRegistry( val handleAllUsers: Boolean, defaultClockProvider: ClockProvider, val fallbackClockId: ClockId = DEFAULT_CLOCK_ID, - messageBuffer: MessageBuffer? = null, + val clockBuffers: ClockMessageBuffers? = null, val keepAllLoaded: Boolean, subTag: String, var isTransitClockEnabled: Boolean = false, ) { private val TAG = "${ClockRegistry::class.simpleName} ($subTag)" + private val logger: Logger = + Logger(clockBuffers?.infraMessageBuffer ?: LogcatOnlyMessageBuffer(LogLevel.DEBUG), TAG) + interface ClockChangeListener { // Called when the active clock changes fun onCurrentClockChanged() {} @@ -128,7 +101,6 @@ open class ClockRegistry( fun onAvailableClocksChanged() {} } - private val logger: Logger? = if (messageBuffer != null) Logger(messageBuffer, TAG) else null private val availableClocks = ConcurrentHashMap<ClockId, ClockInfo>() private val clockChangeListeners = mutableListOf<ClockChangeListener>() private val settingObserver = @@ -157,21 +129,15 @@ open class ClockRegistry( val knownClocks = KNOWN_PLUGINS.get(manager.getPackage()) if (knownClocks == null) { - logger.tryLog( - TAG, - LogLevel.WARNING, - { str1 = manager.getPackage() }, - { "Loading unrecognized clock package: $str1" } - ) + logger.w({ "Loading unrecognized clock package: $str1" }) { + str1 = manager.getPackage() + } return true } - logger.tryLog( - TAG, - LogLevel.INFO, - { str1 = manager.getPackage() }, - { "Skipping initial load of known clock package package: $str1" } - ) + logger.i({ "Skipping initial load of known clock package package: $str1" }) { + str1 = manager.getPackage() + } var isCurrentClock = false var isClockListChanged = false @@ -185,19 +151,14 @@ open class ClockRegistry( } if (manager != info.manager) { - logger.tryLog( - TAG, - LogLevel.ERROR, - { - str1 = id - str2 = info.manager.toString() - str3 = manager.toString() - }, - { - "Clock Id conflict on attach: " + - "$str1 is double registered by $str2 and $str3" - } - ) + logger.e({ + "Clock Id conflict on attach: " + + "$str1 is double registered by $str2 and $str3" + }) { + str1 = id + str2 = info.manager.toString() + str3 = manager.toString() + } continue } @@ -219,6 +180,8 @@ open class ClockRegistry( pluginContext: Context, manager: PluginLifecycleManager<ClockProviderPlugin> ) { + plugin.initialize(clockBuffers) + var isClockListChanged = false for (clock in plugin.getClocks()) { val id = clock.clockId @@ -233,19 +196,14 @@ open class ClockRegistry( } if (manager != info.manager) { - logger.tryLog( - TAG, - LogLevel.ERROR, - { - str1 = id - str2 = info.manager.toString() - str3 = manager.toString() - }, - { - "Clock Id conflict on load: " + - "$str1 is double registered by $str2 and $str3" - } - ) + logger.e({ + "Clock Id conflict on load: " + + "$str1 is double registered by $str2 and $str3" + }) { + str1 = id + str2 = info.manager.toString() + str3 = manager.toString() + } manager.unloadPlugin() continue } @@ -268,19 +226,14 @@ open class ClockRegistry( val id = clock.clockId val info = availableClocks[id] if (info?.manager != manager) { - logger.tryLog( - TAG, - LogLevel.ERROR, - { - str1 = id - str2 = info?.manager.toString() - str3 = manager.toString() - }, - { - "Clock Id conflict on unload: " + - "$str1 is double registered by $str2 and $str3" - } - ) + logger.e({ + "Clock Id conflict on unload: " + + "$str1 is double registered by $str2 and $str3" + }) { + str1 = id + str2 = info?.manager.toString() + str3 = manager.toString() + } continue } info.provider = null @@ -350,7 +303,7 @@ open class ClockRegistry( ClockSettings.deserialize(json) } catch (ex: Exception) { - logger.tryLog(TAG, LogLevel.ERROR, {}, { "Failed to parse clock settings" }, ex) + logger.e("Failed to parse clock settings", ex) null } settings = result @@ -379,7 +332,7 @@ open class ClockRegistry( ) } } catch (ex: Exception) { - logger.tryLog(TAG, LogLevel.ERROR, {}, { "Failed to set clock settings" }, ex) + logger.e("Failed to set clock settings", ex) } settings = value } @@ -451,7 +404,8 @@ open class ClockRegistry( } init { - // Register default clock designs + // Initialize & register default clock designs + defaultClockProvider.initialize(clockBuffers) for (clock in defaultClockProvider.getClocks()) { availableClocks[clock.clockId] = ClockInfo(clock, defaultClockProvider, null) } @@ -514,12 +468,7 @@ open class ClockRegistry( fun verifyLoadedProviders() { val shouldSchedule = isQueued.compareAndSet(false, true) if (!shouldSchedule) { - logger.tryLog( - TAG, - LogLevel.VERBOSE, - {}, - { "verifyLoadedProviders: shouldSchedule=false" } - ) + logger.v("verifyLoadedProviders: shouldSchedule=false") return } @@ -528,12 +477,7 @@ open class ClockRegistry( synchronized(availableClocks) { isQueued.set(false) if (keepAllLoaded) { - logger.tryLog( - TAG, - LogLevel.INFO, - {}, - { "verifyLoadedProviders: keepAllLoaded=true" } - ) + logger.i("verifyLoadedProviders: keepAllLoaded=true") // Enforce that all plugins are loaded if requested for ((_, info) in availableClocks) { info.manager?.loadPlugin() @@ -543,12 +487,7 @@ open class ClockRegistry( val currentClock = availableClocks[currentClockId] if (currentClock == null) { - logger.tryLog( - TAG, - LogLevel.INFO, - {}, - { "verifyLoadedProviders: currentClock=null" } - ) + logger.i("verifyLoadedProviders: currentClock=null") // Current Clock missing, load no plugins and use default for ((_, info) in availableClocks) { info.manager?.unloadPlugin() @@ -556,12 +495,7 @@ open class ClockRegistry( return@launch } - logger.tryLog( - TAG, - LogLevel.INFO, - {}, - { "verifyLoadedProviders: load currentClock" } - ) + logger.i("verifyLoadedProviders: load currentClock") val currentManager = currentClock.manager currentManager?.loadPlugin() @@ -577,30 +511,26 @@ open class ClockRegistry( private fun onConnected(info: ClockInfo) { val isCurrent = currentClockId == info.metadata.clockId - logger.tryLog( - TAG, + logger.log( if (isCurrent) LogLevel.INFO else LogLevel.DEBUG, - { - str1 = info.metadata.clockId - str2 = info.manager.toString() - bool1 = isCurrent - }, { "Connected $str1 @$str2" + if (bool1) " (Current Clock)" else "" } - ) + ) { + str1 = info.metadata.clockId + str2 = info.manager.toString() + bool1 = isCurrent + } } private fun onLoaded(info: ClockInfo) { val isCurrent = currentClockId == info.metadata.clockId - logger.tryLog( - TAG, + logger.log( if (isCurrent) LogLevel.INFO else LogLevel.DEBUG, - { - str1 = info.metadata.clockId - str2 = info.manager.toString() - bool1 = isCurrent - }, { "Loaded $str1 @$str2" + if (bool1) " (Current Clock)" else "" } - ) + ) { + str1 = info.metadata.clockId + str2 = info.manager.toString() + bool1 = isCurrent + } if (isCurrent) { triggerOnCurrentClockChanged() @@ -609,16 +539,14 @@ open class ClockRegistry( private fun onUnloaded(info: ClockInfo) { val isCurrent = currentClockId == info.metadata.clockId - logger.tryLog( - TAG, + logger.log( if (isCurrent) LogLevel.WARNING else LogLevel.DEBUG, - { - str1 = info.metadata.clockId - str2 = info.manager.toString() - bool1 = isCurrent - }, { "Unloaded $str1 @$str2" + if (bool1) " (Current Clock)" else "" } - ) + ) { + str1 = info.metadata.clockId + str2 = info.manager.toString() + bool1 = isCurrent + } if (isCurrent) { triggerOnCurrentClockChanged() @@ -627,16 +555,14 @@ open class ClockRegistry( private fun onDisconnected(info: ClockInfo) { val isCurrent = currentClockId == info.metadata.clockId - logger.tryLog( - TAG, + logger.log( if (isCurrent) LogLevel.INFO else LogLevel.DEBUG, - { - str1 = info.metadata.clockId - str2 = info.manager.toString() - bool1 = isCurrent - }, { "Disconnected $str1 @$str2" + if (bool1) " (Current Clock)" else "" } - ) + ) { + str1 = info.metadata.clockId + str2 = info.manager.toString() + bool1 = isCurrent + } } fun getClocks(): List<ClockMetadata> { @@ -676,23 +602,13 @@ open class ClockRegistry( if (isEnabled && clockId.isNotEmpty()) { val clock = createClock(clockId) if (clock != null) { - logger.tryLog(TAG, LogLevel.INFO, { str1 = clockId }, { "Rendering clock $str1" }) + logger.i({ "Rendering clock $str1" }) { str1 = clockId } return clock } else if (availableClocks.containsKey(clockId)) { - logger.tryLog( - TAG, - LogLevel.WARNING, - { str1 = clockId }, - { "Clock $str1 not loaded; using default" } - ) + logger.w({ "Clock $str1 not loaded; using default" }) { str1 = clockId } verifyLoadedProviders() } else { - logger.tryLog( - TAG, - LogLevel.ERROR, - { str1 = clockId }, - { "Clock $str1 not found; using default" } - ) + logger.e({ "Clock $str1 not found; using default" }) { str1 = clockId } } } diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt index 01c03b1f25f6..99d321695d04 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt @@ -33,6 +33,7 @@ import com.android.systemui.plugins.clocks.ClockEvents import com.android.systemui.plugins.clocks.ClockFaceConfig import com.android.systemui.plugins.clocks.ClockFaceController import com.android.systemui.plugins.clocks.ClockFaceEvents +import com.android.systemui.plugins.clocks.ClockMessageBuffers import com.android.systemui.plugins.clocks.ClockSettings import com.android.systemui.plugins.clocks.DefaultClockFaceLayout import com.android.systemui.plugins.clocks.WeatherData @@ -41,8 +42,6 @@ import java.io.PrintWriter import java.util.Locale import java.util.TimeZone -private val TAG = DefaultClockController::class.simpleName - /** * Controls the default clock visuals. * @@ -56,6 +55,7 @@ class DefaultClockController( private val settings: ClockSettings?, private val hasStepClockAnimation: Boolean = false, private val migratedClocks: Boolean = false, + messageBuffers: ClockMessageBuffers? = null, ) : ClockController { override val smallClock: DefaultClockFaceController override val largeClock: LargeClockFaceController @@ -83,13 +83,15 @@ class DefaultClockController( DefaultClockFaceController( layoutInflater.inflate(R.layout.clock_default_small, parent, false) as AnimatableClockView, - settings?.seedColor + settings?.seedColor, + messageBuffers?.smallClockMessageBuffer ) largeClock = LargeClockFaceController( layoutInflater.inflate(R.layout.clock_default_large, parent, false) as AnimatableClockView, - settings?.seedColor + settings?.seedColor, + messageBuffers?.largeClockMessageBuffer ) clocks = listOf(smallClock.view, largeClock.view) @@ -110,6 +112,7 @@ class DefaultClockController( open inner class DefaultClockFaceController( override val view: AnimatableClockView, var seedColor: Int?, + messageBuffer: MessageBuffer?, ) : ClockFaceController { // MAGENTA is a placeholder, and will be assigned correctly in initialize @@ -120,12 +123,6 @@ class DefaultClockController( override val config = ClockFaceConfig() override val layout = DefaultClockFaceLayout(view) - override var messageBuffer: MessageBuffer? - get() = view.messageBuffer - set(value) { - view.messageBuffer = value - } - override var animations: DefaultClockAnimations = DefaultClockAnimations(view, 0f, 0f) internal set @@ -134,6 +131,7 @@ class DefaultClockController( currentColor = seedColor!! } view.setColors(DOZE_COLOR, currentColor) + messageBuffer?.let { view.messageBuffer = it } } override val events = @@ -188,7 +186,8 @@ class DefaultClockController( inner class LargeClockFaceController( view: AnimatableClockView, seedColor: Int?, - ) : DefaultClockFaceController(view, seedColor) { + messageBuffer: MessageBuffer?, + ) : DefaultClockFaceController(view, seedColor, messageBuffer) { override val layout = DefaultClockFaceLayout(view) override val config = ClockFaceConfig(hasCustomPositionUpdatedAnimation = hasStepClockAnimation) diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt index a219be53bd1a..20f87a059656 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt @@ -20,6 +20,7 @@ import android.view.LayoutInflater import com.android.systemui.customization.R import com.android.systemui.plugins.clocks.ClockController import com.android.systemui.plugins.clocks.ClockId +import com.android.systemui.plugins.clocks.ClockMessageBuffers import com.android.systemui.plugins.clocks.ClockMetadata import com.android.systemui.plugins.clocks.ClockProvider import com.android.systemui.plugins.clocks.ClockSettings @@ -35,6 +36,12 @@ class DefaultClockProvider( val hasStepClockAnimation: Boolean = false, val migratedClocks: Boolean = false ) : ClockProvider { + private var messageBuffers: ClockMessageBuffers? = null + + override fun initialize(buffers: ClockMessageBuffers?) { + messageBuffers = buffers + } + override fun getClocks(): List<ClockMetadata> = listOf(ClockMetadata(DEFAULT_CLOCK_ID)) override fun createClock(settings: ClockSettings): ClockController { @@ -49,6 +56,7 @@ class DefaultClockProvider( settings, hasStepClockAnimation, migratedClocks, + messageBuffers, ) } diff --git a/packages/SystemUI/log/src/com/android/systemui/log/core/LogcatOnlyMessageBuffer.kt b/packages/SystemUI/log/src/com/android/systemui/log/core/LogcatOnlyMessageBuffer.kt new file mode 100644 index 000000000000..006b521ad867 --- /dev/null +++ b/packages/SystemUI/log/src/com/android/systemui/log/core/LogcatOnlyMessageBuffer.kt @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.log.core + +import android.util.Log +import com.android.systemui.log.LogMessageImpl + +/** + * A simple implementation of [MessageBuffer] that forwards messages to [android.util.Log] + * immediately. This defeats the intention behind [LogBuffer] and should only be used when + * [LogBuffer]s are unavailable in a certain context. + */ +class LogcatOnlyMessageBuffer( + val targetLogLevel: LogLevel, +) : MessageBuffer { + private val singleMessage = LogMessageImpl.Factory.create() + private var isObtained: Boolean = false + + @Synchronized + override fun obtain( + tag: String, + level: LogLevel, + messagePrinter: MessagePrinter, + exception: Throwable?, + ): LogMessage { + if (isObtained) { + throw UnsupportedOperationException( + "Message has already been obtained. Call order is incorrect." + ) + } + + singleMessage.reset(tag, level, System.currentTimeMillis(), messagePrinter, exception) + isObtained = true + return singleMessage + } + + @Synchronized + override fun commit(message: LogMessage) { + if (singleMessage != message) { + throw IllegalArgumentException("Message argument is not the expected message.") + } + if (!isObtained) { + throw UnsupportedOperationException( + "Message has not been obtained. Call order is incorrect." + ) + } + + if (message.level >= targetLogLevel) { + val strMessage = message.messagePrinter(message) + when (message.level) { + LogLevel.VERBOSE -> Log.v(message.tag, strMessage, message.exception) + LogLevel.DEBUG -> Log.d(message.tag, strMessage, message.exception) + LogLevel.INFO -> Log.i(message.tag, strMessage, message.exception) + LogLevel.WARNING -> Log.w(message.tag, strMessage, message.exception) + LogLevel.ERROR -> Log.e(message.tag, strMessage, message.exception) + LogLevel.WTF -> Log.wtf(message.tag, strMessage, message.exception) + } + } + + isObtained = false + } +} diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt index 1c5f221f2efb..4436be7cd7d7 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt @@ -41,16 +41,13 @@ interface ClockProviderPlugin : Plugin, ClockProvider { /** Interface for building clocks and providing information about those clocks */ interface ClockProvider { + /** Initializes the clock provider with debug log buffers */ + fun initialize(buffers: ClockMessageBuffers?) + /** Returns metadata for all clocks this provider knows about */ fun getClocks(): List<ClockMetadata> /** Initializes and returns the target clock design */ - @Deprecated("Use overload with ClockSettings") - fun createClock(id: ClockId): ClockController { - return createClock(ClockSettings(id, null)) - } - - /** Initializes and returns the target clock design */ fun createClock(settings: ClockSettings): ClockController /** A static thumbnail for rendering in some examples */ @@ -98,11 +95,20 @@ interface ClockFaceController { /** Triggers for various animations */ val animations: ClockAnimations - - /** Some clocks may log debug information */ - var messageBuffer: MessageBuffer? } +/** For clocks that want to report debug information */ +data class ClockMessageBuffers( + /** Message buffer for general infra */ + val infraMessageBuffer: MessageBuffer, + + /** Message buffer for small clock renering */ + val smallClockMessageBuffer: MessageBuffer, + + /** Message buffer for large clock rendering */ + val largeClockMessageBuffer: MessageBuffer, +) + /** Specifies layout information for the */ interface ClockFaceLayout { /** All clock views to add to the root constraint layout before applying constraints. */ 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 d8c1e41465db..131eb6b63df3 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 @@ -367,7 +367,7 @@ public class QuickStepContract { /** * Corner radius that should be used on windows in order to cover the display. * These values are expressed in pixels because they should not respect display or font - * scaling, this means that we don't have to reload them on config changes. + * scaling. The corner radius may change when folding/unfolding the device. */ public static float getWindowCornerRadius(Context context) { return ScreenDecorationsUtils.getWindowCornerRadius(context); diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt index 76abad8ae863..bcc20448297d 100644 --- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt +++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt @@ -45,12 +45,11 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.lifecycle.repeatWhenAttached -import com.android.systemui.log.LogBuffer +import com.android.systemui.log.core.Logger import com.android.systemui.log.core.LogLevel.DEBUG -import com.android.systemui.log.dagger.KeyguardLargeClockLog -import com.android.systemui.log.dagger.KeyguardSmallClockLog import com.android.systemui.plugins.clocks.ClockController import com.android.systemui.plugins.clocks.ClockFaceController +import com.android.systemui.plugins.clocks.ClockMessageBuffers import com.android.systemui.plugins.clocks.ClockTickRate import com.android.systemui.plugins.clocks.AlarmData import com.android.systemui.plugins.clocks.WeatherData @@ -91,117 +90,120 @@ constructor( private val context: Context, @Main private val mainExecutor: DelayableExecutor, @Background private val bgExecutor: Executor, - @KeyguardSmallClockLog private val smallLogBuffer: LogBuffer?, - @KeyguardLargeClockLog private val largeLogBuffer: LogBuffer?, + private val clockBuffers: ClockMessageBuffers, private val featureFlags: FeatureFlags, private val zenModeController: ZenModeController, ) { + var loggers = listOf( + clockBuffers.infraMessageBuffer, + clockBuffers.smallClockMessageBuffer, + clockBuffers.largeClockMessageBuffer + ).map { Logger(it, TAG) } + var clock: ClockController? = null + get() = field set(value) { - smallClockOnAttachStateChangeListener?.let { - field?.smallClock?.view?.removeOnAttachStateChangeListener(it) - smallClockFrame?.viewTreeObserver - ?.removeOnGlobalLayoutListener(onGlobalLayoutListener) - } - largeClockOnAttachStateChangeListener?.let { - field?.largeClock?.view?.removeOnAttachStateChangeListener(it) - } - + disconnectClock(field) field = value - if (value != null) { - smallLogBuffer?.log(TAG, DEBUG, {}, { "New Clock" }) - value.smallClock.messageBuffer = smallLogBuffer - largeLogBuffer?.log(TAG, DEBUG, {}, { "New Clock" }) - value.largeClock.messageBuffer = largeLogBuffer - - value.initialize(resources, dozeAmount, 0f) - - if (!regionSamplingEnabled) { - updateColors() - } else { - clock?.let { - smallRegionSampler = createRegionSampler( - it.smallClock.view, - mainExecutor, - bgExecutor, - regionSamplingEnabled, - isLockscreen = true, - ::updateColors - )?.apply { startRegionSampler() } - - largeRegionSampler = createRegionSampler( - it.largeClock.view, - mainExecutor, - bgExecutor, - regionSamplingEnabled, - isLockscreen = true, - ::updateColors - )?.apply { startRegionSampler() } - - updateColors() - } - } - updateFontSizes() - updateTimeListeners() - weatherData?.let { - if (WeatherData.DEBUG) { - Log.i(TAG, "Pushing cached weather data to new clock: $it") - } - value.events.onWeatherDataChanged(it) - } - zenData?.let { - value.events.onZenDataChanged(it) - } - alarmData?.let { - value.events.onAlarmDataChanged(it) - } + connectClock(value) + } - smallClockOnAttachStateChangeListener = - object : OnAttachStateChangeListener { - var pastVisibility: Int? = null - override fun onViewAttachedToWindow(view: View) { - value.events.onTimeFormatChanged(DateFormat.is24HourFormat(context)) - // Match the asing for view.parent's layout classes. - smallClockFrame = view.parent as ViewGroup - smallClockFrame?.let { frame -> - pastVisibility = frame.visibility - onGlobalLayoutListener = OnGlobalLayoutListener { - val currentVisibility = frame.visibility - if (pastVisibility != currentVisibility) { - pastVisibility = currentVisibility - // when small clock is visible, - // recalculate bounds and sample - if (currentVisibility == View.VISIBLE) { - smallRegionSampler?.stopRegionSampler() - smallRegionSampler?.startRegionSampler() - } - } - } - frame.viewTreeObserver - .addOnGlobalLayoutListener(onGlobalLayoutListener) - } - } + private fun disconnectClock(clock: ClockController?) { + if (clock == null) { return; } + smallClockOnAttachStateChangeListener?.let { + clock.smallClock.view.removeOnAttachStateChangeListener(it) + smallClockFrame?.viewTreeObserver + ?.removeOnGlobalLayoutListener(onGlobalLayoutListener) + } + largeClockOnAttachStateChangeListener?.let { + clock.largeClock.view.removeOnAttachStateChangeListener(it) + } + } - override fun onViewDetachedFromWindow(p0: View) { - smallClockFrame?.viewTreeObserver - ?.removeOnGlobalLayoutListener(onGlobalLayoutListener) - } - } - value.smallClock.view - .addOnAttachStateChangeListener(smallClockOnAttachStateChangeListener) + private fun connectClock(clock: ClockController?) { + if (clock == null) { return; } + val clockStr = clock.toString() + loggers.forEach { it.d({ "New Clock: $str1" }) { str1 = clockStr } } - largeClockOnAttachStateChangeListener = - object : OnAttachStateChangeListener { - override fun onViewAttachedToWindow(p0: View) { - value.events.onTimeFormatChanged(DateFormat.is24HourFormat(context)) - } - override fun onViewDetachedFromWindow(p0: View) { + clock.initialize(resources, dozeAmount, 0f) + + if (!regionSamplingEnabled) { + updateColors() + } else { + smallRegionSampler = createRegionSampler( + clock.smallClock.view, + mainExecutor, + bgExecutor, + regionSamplingEnabled, + isLockscreen = true, + ::updateColors + ).apply { startRegionSampler() } + + largeRegionSampler = createRegionSampler( + clock.largeClock.view, + mainExecutor, + bgExecutor, + regionSamplingEnabled, + isLockscreen = true, + ::updateColors + ).apply { startRegionSampler() } + + updateColors() + } + updateFontSizes() + updateTimeListeners() + + weatherData?.let { + if (WeatherData.DEBUG) { + Log.i(TAG, "Pushing cached weather data to new clock: $it") + } + clock.events.onWeatherDataChanged(it) + } + zenData?.let { + clock.events.onZenDataChanged(it) + } + alarmData?.let { + clock.events.onAlarmDataChanged(it) + } + + smallClockOnAttachStateChangeListener = object : OnAttachStateChangeListener { + var pastVisibility: Int? = null + override fun onViewAttachedToWindow(view: View) { + clock.events.onTimeFormatChanged(DateFormat.is24HourFormat(context)) + // Match the asing for view.parent's layout classes. + smallClockFrame = (view.parent as ViewGroup)?.also { frame -> + pastVisibility = frame.visibility + onGlobalLayoutListener = OnGlobalLayoutListener { + val currentVisibility = frame.visibility + if (pastVisibility != currentVisibility) { + pastVisibility = currentVisibility + // when small clock is visible, + // recalculate bounds and sample + if (currentVisibility == View.VISIBLE) { + smallRegionSampler?.stopRegionSampler() + smallRegionSampler?.startRegionSampler() + } } + } + frame.viewTreeObserver.addOnGlobalLayoutListener(onGlobalLayoutListener) } - value.largeClock.view - .addOnAttachStateChangeListener(largeClockOnAttachStateChangeListener) + } + + override fun onViewDetachedFromWindow(p0: View) { + smallClockFrame?.viewTreeObserver + ?.removeOnGlobalLayoutListener(onGlobalLayoutListener) } } + clock.smallClock.view.addOnAttachStateChangeListener(smallClockOnAttachStateChangeListener) + + largeClockOnAttachStateChangeListener = object : OnAttachStateChangeListener { + override fun onViewAttachedToWindow(p0: View) { + clock.events.onTimeFormatChanged(DateFormat.is24HourFormat(context)) + } + override fun onViewDetachedFromWindow(p0: View) {} + } + clock.largeClock.view.addOnAttachStateChangeListener(largeClockOnAttachStateChangeListener) + } @VisibleForTesting var smallClockOnAttachStateChangeListener: OnAttachStateChangeListener? = null @@ -247,6 +249,7 @@ constructor( largeClock.events.onRegionDarknessChanged(isRegionDark) } } + protected open fun createRegionSampler( sampledView: View, mainExecutor: Executor?, @@ -254,7 +257,7 @@ constructor( regionSamplingEnabled: Boolean, isLockscreen: Boolean, updateColors: () -> Unit - ): RegionSampler? { + ): RegionSampler { return RegionSampler( sampledView, mainExecutor, diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java index 661ce2ce60ba..878a5d88f164 100644 --- a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java +++ b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java @@ -28,9 +28,8 @@ import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; -import com.android.systemui.log.LogBuffer; -import com.android.systemui.log.dagger.KeyguardClockLog; import com.android.systemui.plugins.PluginManager; +import com.android.systemui.plugins.clocks.ClockMessageBuffers; import com.android.systemui.res.R; import com.android.systemui.shared.clocks.ClockRegistry; import com.android.systemui.shared.clocks.DefaultClockProvider; @@ -56,7 +55,7 @@ public abstract class ClockRegistryModule { FeatureFlags featureFlags, @Main Resources resources, LayoutInflater layoutInflater, - @KeyguardClockLog LogBuffer logBuffer) { + ClockMessageBuffers clockBuffers) { ClockRegistry registry = new ClockRegistry( context, pluginManager, @@ -72,7 +71,7 @@ public abstract class ClockRegistryModule { featureFlags.isEnabled(Flags.STEP_CLOCK_ANIMATION), migrateClocksToBlueprint()), context.getString(R.string.lockscreen_clock_id_fallback), - logBuffer, + clockBuffers, /* keepAllLoaded = */ false, /* subTag = */ "System", /* isTransitClockEnabled = */ featureFlags.isEnabled(Flags.TRANSIT_CLOCK)); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt index 7d290c3c61fd..05fe0b25381d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt @@ -32,8 +32,6 @@ import com.android.systemui.plugins.clocks.ClockController import com.android.systemui.res.R import kotlinx.coroutines.launch -private val TAG = KeyguardClockViewBinder::class.simpleName - object KeyguardClockViewBinder { @JvmStatic fun bind( @@ -74,12 +72,6 @@ object KeyguardClockViewBinder { applyConstraints(clockSection, keyguardRootView, true) } } - launch { - if (!migrateClocksToBlueprint()) return@launch - viewModel.hasCustomWeatherDataDisplay.collect { - applyConstraints(clockSection, keyguardRootView, true) - } - } } } } @@ -132,7 +124,7 @@ object KeyguardClockViewBinder { fun applyConstraints( clockSection: ClockSection, rootView: ConstraintLayout, - animated: Boolean + animated: Boolean, ) { val constraintSet = ConstraintSet().apply { clone(rootView) } clockSection.applyConstraints(constraintSet) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt index 954d2cf6ed8a..e36819b54524 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt @@ -16,13 +16,19 @@ package com.android.systemui.keyguard.ui.binder +import android.transition.TransitionManager +import android.view.View +import androidx.constraintlayout.helper.widget.Layer import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel +import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.res.R +import kotlinx.coroutines.launch object KeyguardSmartspaceViewBinder { @JvmStatic @@ -30,15 +36,63 @@ object KeyguardSmartspaceViewBinder { smartspaceSection: SmartspaceSection, keyguardRootView: ConstraintLayout, clockViewModel: KeyguardClockViewModel, + smartspaceViewModel: KeyguardSmartspaceViewModel, ) { keyguardRootView.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.STARTED) { - clockViewModel.hasCustomWeatherDataDisplay.collect { - val constraintSet = ConstraintSet().apply { clone(keyguardRootView) } - smartspaceSection.applyConstraints(constraintSet) - constraintSet.applyTo(keyguardRootView) + launch { + clockViewModel.hasCustomWeatherDataDisplay.collect { hasCustomWeatherDataDisplay + -> + if (hasCustomWeatherDataDisplay) { + removeDateWeatherToBurnInLayer(keyguardRootView, smartspaceViewModel) + } else { + addDateWeatherToBurnInLayer(keyguardRootView, smartspaceViewModel) + } + clockViewModel.burnInLayer?.updatePostLayout(keyguardRootView) + val constraintSet = ConstraintSet().apply { clone(keyguardRootView) } + smartspaceSection.applyConstraints(constraintSet) + TransitionManager.beginDelayedTransition(keyguardRootView) + constraintSet.applyTo(keyguardRootView) + } } } } } + + private fun addDateWeatherToBurnInLayer( + constraintLayout: ConstraintLayout, + smartspaceViewModel: KeyguardSmartspaceViewModel + ) { + val burnInLayer = constraintLayout.requireViewById<Layer>(R.id.burn_in_layer) + burnInLayer.apply { + if ( + smartspaceViewModel.isSmartspaceEnabled && + smartspaceViewModel.isDateWeatherDecoupled + ) { + val dateView = constraintLayout.requireViewById<View>(smartspaceViewModel.dateId) + val weatherView = + constraintLayout.requireViewById<View>(smartspaceViewModel.weatherId) + addView(weatherView) + addView(dateView) + } + } + } + + private fun removeDateWeatherToBurnInLayer( + constraintLayout: ConstraintLayout, + smartspaceViewModel: KeyguardSmartspaceViewModel + ) { + val burnInLayer = constraintLayout.requireViewById<Layer>(R.id.burn_in_layer) + burnInLayer.apply { + if ( + smartspaceViewModel.isSmartspaceEnabled && + smartspaceViewModel.isDateWeatherDecoupled + ) { + val dateView = smartspaceViewModel.dateView + val weatherView = smartspaceViewModel.weatherView + removeView(weatherView) + removeView(dateView) + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt index f890ae612ccc..16539db648bc 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt @@ -30,6 +30,8 @@ import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSec import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSectionsModule +import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection +import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeClockSection import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeNotificationStackScrollLayoutSection import com.android.systemui.util.kotlin.getOrNull @@ -59,6 +61,8 @@ constructor( aodNotificationIconsSection: AodNotificationIconsSection, aodBurnInSection: AodBurnInSection, communalTutorialIndicatorSection: CommunalTutorialIndicatorSection, + smartspaceSection: SmartspaceSection, + clockSection: SplitShadeClockSection, ) : KeyguardBlueprint { override val id: String = ID @@ -73,8 +77,10 @@ constructor( splitShadeNotificationStackScrollLayoutSection, splitShadeGuidelines, aodNotificationIconsSection, + smartspaceSection, aodBurnInSection, communalTutorialIndicatorSection, + clockSection, defaultDeviceEntrySection, // Add LAST: Intentionally has z-order above other views. ) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt index 8166b454fcff..d89e1e41d1bc 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt @@ -23,7 +23,6 @@ import androidx.constraintlayout.helper.widget.Layer import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import com.android.systemui.Flags.migrateClocksToBlueprint -import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl import com.android.systemui.keyguard.shared.model.KeyguardSection import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel @@ -38,7 +37,6 @@ constructor( private val context: Context, private val clockViewModel: KeyguardClockViewModel, private val smartspaceViewModel: KeyguardSmartspaceViewModel, - private val featureFlags: FeatureFlagsClassic, ) : KeyguardSection() { lateinit var burnInLayer: Layer @@ -59,6 +57,8 @@ constructor( } } if (migrateClocksToBlueprint()) { + // weather and date parts won't be added here, cause their visibility doesn't align + // with others in burnInLayer addSmartspaceViews(constraintLayout) } constraintLayout.addView(burnInLayer) @@ -89,14 +89,6 @@ constructor( val smartspaceView = constraintLayout.requireViewById<View>(smartspaceViewModel.smartspaceViewId) addView(smartspaceView) - if (smartspaceViewModel.isDateWeatherDecoupled) { - val dateView = - constraintLayout.requireViewById<View>(smartspaceViewModel.dateId) - val weatherView = - constraintLayout.requireViewById<View>(smartspaceViewModel.weatherId) - addView(weatherView) - addView(dateView) - } } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt index 39a0547ded26..b429ab4fac0a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt @@ -28,7 +28,6 @@ import androidx.constraintlayout.widget.ConstraintSet.START import androidx.constraintlayout.widget.ConstraintSet.TOP import com.android.systemui.Flags.migrateClocksToBlueprint import com.android.systemui.common.ui.ConfigurationState -import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl import com.android.systemui.keyguard.shared.model.KeyguardSection import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel @@ -49,7 +48,6 @@ class AodNotificationIconsSection constructor( private val context: Context, private val configurationState: ConfigurationState, - private val featureFlags: FeatureFlagsClassic, private val iconBindingFailureTracker: StatusBarIconViewBindingFailureTracker, private val nicAodViewModel: NotificationIconContainerAlwaysOnDisplayViewModel, private val nicAodIconViewStore: AlwaysOnDisplayNotificationIconViewStore, @@ -119,14 +117,8 @@ constructor( } constraintSet.apply { if (migrateClocksToBlueprint()) { - connect( - nicId, - TOP, - smartspaceViewModel.smartspaceViewId, - topAlignment, - bottomMargin - ) - setGoneMargin(nicId, topAlignment, bottomMargin) + connect(nicId, TOP, smartspaceViewModel.smartspaceViewId, BOTTOM, bottomMargin) + setGoneMargin(nicId, BOTTOM, bottomMargin) } else { connect(nicId, TOP, R.id.keyguard_status_view, topAlignment, bottomMargin) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt index 1df920aab833..656c75c7bfec 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt @@ -27,7 +27,7 @@ import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID import androidx.constraintlayout.widget.ConstraintSet.START import androidx.constraintlayout.widget.ConstraintSet.TOP import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT -import com.android.systemui.flags.FeatureFlagsClassic +import com.android.systemui.Flags import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor import com.android.systemui.keyguard.shared.model.KeyguardSection import com.android.systemui.keyguard.ui.binder.KeyguardClockViewBinder @@ -50,19 +50,21 @@ internal fun ConstraintSet.setAlpha( alpha: Float, ) = views.forEach { view -> this.setAlpha(view.id, alpha) } -class ClockSection +open class ClockSection @Inject constructor( private val clockInteractor: KeyguardClockInteractor, - private val keyguardClockViewModel: KeyguardClockViewModel, - val smartspaceViewModel: KeyguardSmartspaceViewModel, + protected val keyguardClockViewModel: KeyguardClockViewModel, + private val smartspaceViewModel: KeyguardSmartspaceViewModel, private val context: Context, private val splitShadeStateController: SplitShadeStateController, - private val featureFlags: FeatureFlagsClassic, ) : KeyguardSection() { override fun addViews(constraintLayout: ConstraintLayout) {} override fun bindData(constraintLayout: ConstraintLayout) { + if (!Flags.migrateClocksToBlueprint()) { + return + } KeyguardClockViewBinder.bind( this, constraintLayout, @@ -72,6 +74,9 @@ constructor( } override fun applyConstraints(constraintSet: ConstraintSet) { + if (!Flags.migrateClocksToBlueprint()) { + return + } clockInteractor.clock?.let { clock -> constraintSet.applyDeltaFrom(buildConstraints(clock, constraintSet)) } @@ -94,16 +99,6 @@ constructor( } } - var largeClockEndGuideline = PARENT_ID - - // Return if largeClockEndGuideline changes, - // and use it to decide whether to refresh blueprint - fun setClockShouldBeCentered(shouldBeCentered: Boolean): Boolean { - val previousValue = largeClockEndGuideline - largeClockEndGuideline = if (shouldBeCentered) PARENT_ID else R.id.split_shade_guideline - return previousValue != largeClockEndGuideline - } - private fun getTargetClockFace(clock: ClockController): ClockFaceLayout = if (keyguardClockViewModel.useLargeClock) getLargeClockFace(clock) else getSmallClockFace(clock) @@ -113,10 +108,10 @@ constructor( private fun getLargeClockFace(clock: ClockController): ClockFaceLayout = clock.largeClock.layout private fun getSmallClockFace(clock: ClockController): ClockFaceLayout = clock.smallClock.layout - fun applyDefaultConstraints(constraints: ConstraintSet) { + open fun applyDefaultConstraints(constraints: ConstraintSet) { constraints.apply { connect(R.id.lockscreen_clock_view_large, START, PARENT_ID, START) - connect(R.id.lockscreen_clock_view_large, END, largeClockEndGuideline, END) + connect(R.id.lockscreen_clock_view_large, END, PARENT_ID, END) connect(R.id.lockscreen_clock_view_large, BOTTOM, R.id.lock_icon_view, TOP) var largeClockTopMargin = context.resources.getDimensionPixelSize(R.dimen.status_bar_height) + diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt index 368b388062a1..8cd51cd3b1e3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt @@ -17,7 +17,6 @@ package com.android.systemui.keyguard.ui.view.layout.sections import android.content.Context -import android.view.View import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.constraintlayout.widget.ConstraintSet.BOTTOM @@ -36,7 +35,7 @@ import com.android.systemui.res.R import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController import javax.inject.Inject -class SmartspaceSection +open class SmartspaceSection @Inject constructor( val keyguardClockViewModel: KeyguardClockViewModel, @@ -45,9 +44,13 @@ constructor( val smartspaceController: LockscreenSmartspaceController, val keyguardUnlockAnimationController: KeyguardUnlockAnimationController, ) : KeyguardSection() { - private var smartspaceView: View? = null - private var weatherView: View? = null - private var dateView: View? = null + var smartspaceView by keyguardSmartspaceViewModel::smartspaceView + var weatherView by keyguardSmartspaceViewModel::weatherView + var dateView by keyguardSmartspaceViewModel::dateView + + val smartspaceViewId = keyguardSmartspaceViewModel.smartspaceViewId + val weatherViewId = keyguardSmartspaceViewModel.weatherId + val dateViewId = keyguardSmartspaceViewModel.dateId override fun addViews(constraintLayout: ConstraintLayout) { if (!migrateClocksToBlueprint()) { @@ -67,10 +70,21 @@ constructor( } override fun bindData(constraintLayout: ConstraintLayout) { - KeyguardSmartspaceViewBinder.bind(this, constraintLayout, keyguardClockViewModel) + if (!migrateClocksToBlueprint()) { + return + } + KeyguardSmartspaceViewBinder.bind( + this, + constraintLayout, + keyguardClockViewModel, + keyguardSmartspaceViewModel, + ) } override fun applyConstraints(constraintSet: ConstraintSet) { + if (!migrateClocksToBlueprint()) { + return + } // Generally, weather should be next to dateView // smartspace should be below date & weather views constraintSet.apply { @@ -130,7 +144,20 @@ constructor( } } } - updateVisibility(constraintSet) + } + updateVisibility(constraintSet) + } + + override fun removeViews(constraintLayout: ConstraintLayout) { + if (!migrateClocksToBlueprint()) { + return + } + listOf(smartspaceView, dateView, weatherView).forEach { + it?.let { + if (it.parent == constraintLayout) { + constraintLayout.removeView(it) + } + } } } @@ -158,14 +185,4 @@ constructor( } } } - - override fun removeViews(constraintLayout: ConstraintLayout) { - listOf(smartspaceView, dateView, weatherView).forEach { - it?.let { - if (it.parent == constraintLayout) { - constraintLayout.removeView(it) - } - } - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeClockSection.kt new file mode 100644 index 000000000000..1302bfa6fc31 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeClockSection.kt @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.ui.view.layout.sections + +import android.content.Context +import androidx.constraintlayout.widget.ConstraintSet +import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor +import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel +import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel +import com.android.systemui.res.R +import com.android.systemui.statusbar.policy.SplitShadeStateController +import javax.inject.Inject + +class SplitShadeClockSection +@Inject +constructor( + clockInteractor: KeyguardClockInteractor, + keyguardClockViewModel: KeyguardClockViewModel, + smartspaceViewModel: KeyguardSmartspaceViewModel, + context: Context, + splitShadeStateController: SplitShadeStateController, +) : + ClockSection( + clockInteractor, + keyguardClockViewModel, + smartspaceViewModel, + context, + splitShadeStateController + ) { + override fun applyDefaultConstraints(constraints: ConstraintSet) { + super.applyDefaultConstraints(constraints) + val largeClockEndGuideline = + if (keyguardClockViewModel.clockShouldBeCentered.value) ConstraintSet.PARENT_ID + else R.id.split_shade_guideline + constraints.apply { + connect( + R.id.lockscreen_clock_view_large, + ConstraintSet.END, + largeClockEndGuideline, + ConstraintSet.END + ) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt index b0b5c81dd11c..0f8e67340cc7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt @@ -23,7 +23,6 @@ import androidx.constraintlayout.widget.ConstraintSet.END import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID import androidx.constraintlayout.widget.ConstraintSet.START import androidx.constraintlayout.widget.ConstraintSet.TOP -import com.android.systemui.Flags.migrateClocksToBlueprint import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel @@ -72,25 +71,9 @@ constructor( return } constraintSet.apply { - val bottomMargin = - context.resources.getDimensionPixelSize(R.dimen.keyguard_status_view_bottom_margin) - - if (migrateClocksToBlueprint()) { - connect( - R.id.nssl_placeholder, - TOP, - smartspaceViewModel.smartspaceViewId, - TOP, - bottomMargin - ) - setGoneMargin(R.id.nssl_placeholder, TOP, bottomMargin) - } else { - val splitShadeTopMargin = - context.resources.getDimensionPixelSize( - R.dimen.large_screen_shade_header_height - ) - connect(R.id.nssl_placeholder, TOP, PARENT_ID, TOP, splitShadeTopMargin) - } + val splitShadeTopMargin = + context.resources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height) + connect(R.id.nssl_placeholder, TOP, PARENT_ID, TOP, splitShadeTopMargin) connect(R.id.nssl_placeholder, START, PARENT_ID, START) connect(R.id.nssl_placeholder, END, PARENT_ID, END) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt index 3aeff61c15e7..528a2eebc9cd 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt @@ -27,8 +27,8 @@ import com.android.systemui.keyguard.shared.model.SettingsClockSize import com.android.systemui.plugins.clocks.ClockController import javax.inject.Inject import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.stateIn @@ -79,10 +79,10 @@ constructor( ?: false ) - val clockShouldBeCentered: Flow<Boolean> = + val clockShouldBeCentered: StateFlow<Boolean> = keyguardInteractor.clockShouldBeCentered.stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), - initialValue = true + initialValue = false ) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt index 4541458892bb..26e7ee8f561b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt @@ -18,6 +18,7 @@ package com.android.systemui.keyguard.ui.viewmodel import android.content.Context import android.util.Log +import android.view.View import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController import javax.inject.Inject @@ -25,7 +26,7 @@ import javax.inject.Inject @SysUISingleton class KeyguardSmartspaceViewModel @Inject -constructor(val context: Context, smartspaceController: LockscreenSmartspaceController) { +constructor(val context: Context, val smartspaceController: LockscreenSmartspaceController) { val isSmartspaceEnabled: Boolean = smartspaceController.isEnabled() val isWeatherEnabled: Boolean = smartspaceController.isWeatherEnabled() val isDateWeatherDecoupled: Boolean = smartspaceController.isDateWeatherDecoupled() @@ -38,6 +39,10 @@ constructor(val context: Context, smartspaceController: LockscreenSmartspaceCont val weatherId: Int get() = getId("weather_smartspace_view") + var smartspaceView: View? = null + var weatherView: View? = null + var dateView: View? = null + private fun getId(name: String): Int { return context.resources.getIdentifier(name, "id", context.packageName).also { if (it == 0) { diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java index d8bb3e65392f..0d5ba641b599 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java @@ -26,6 +26,7 @@ import com.android.systemui.log.echo.LogcatEchoTrackerDebug; import com.android.systemui.log.echo.LogcatEchoTrackerProd; import com.android.systemui.log.table.TableLogBuffer; import com.android.systemui.log.table.TableLogBufferFactory; +import com.android.systemui.plugins.clocks.ClockMessageBuffers; import com.android.systemui.qs.QSFragmentLegacy; import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository; import com.android.systemui.qs.pipeline.shared.TileSpec; @@ -417,6 +418,18 @@ public class LogModule { } /** + * Provides a {@link ClockMessageBuffers} which contains the keyguard clock message buffers. + */ + @Provides + public static ClockMessageBuffers provideKeyguardClockMessageBuffers( + @KeyguardClockLog LogBuffer infraClockLog, + @KeyguardSmallClockLog LogBuffer smallClockLog, + @KeyguardLargeClockLog LogBuffer largeClockLog + ) { + return new ClockMessageBuffers(infraClockLog, smallClockLog, largeClockLog); + } + + /** * Provides a {@link LogBuffer} for use by {@link com.android.keyguard.KeyguardUpdateMonitor}. */ @Provides diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/QSTileServiceWrapper.java b/packages/SystemUI/src/com/android/systemui/qs/external/QSTileServiceWrapper.java index 2345667f0409..83b6f0d40aff 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/QSTileServiceWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/QSTileServiceWrapper.java @@ -19,16 +19,21 @@ import android.os.IBinder; import android.service.quicksettings.IQSTileService; import android.util.Log; +import androidx.annotation.NonNull; + public class QSTileServiceWrapper { private static final String TAG = "IQSTileServiceWrapper"; + @NonNull private final IQSTileService mService; - public QSTileServiceWrapper(IQSTileService service) { + public QSTileServiceWrapper(@NonNull IQSTileService service) { mService = service; } + // This can never be null, as it's the constructor parameter and it's final + @NonNull public IBinder asBinder() { return mService.asBinder(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java index e08eb37b79ba..880289e88813 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java @@ -40,6 +40,7 @@ import android.text.format.DateUtils; import android.util.ArraySet; import android.util.Log; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; @@ -54,8 +55,10 @@ import dagger.assisted.AssistedInject; import java.util.NoSuchElementException; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Predicate; /** * Manages the lifecycle of a TileService. @@ -101,8 +104,8 @@ public class TileLifecycleManager extends BroadcastReceiver implements private final ActivityManager mActivityManager; private Set<Integer> mQueuedMessages = new ArraySet<>(); - @Nullable - private volatile QSTileServiceWrapper mWrapper; + @NonNull + private volatile Optional<QSTileServiceWrapper> mOptionalWrapper = Optional.empty(); private boolean mListening; private IBinder mClickBinder; @@ -222,6 +225,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements // Only try a new binding if we are not currently bound. mIsBound.compareAndSet(false, bindServices()); if (!mIsBound.get()) { + Log.d(TAG, "Failed to bind to service"); mContext.unbindService(this); } } catch (SecurityException e) { @@ -281,7 +285,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements service.linkToDeath(this, 0); } catch (RemoteException e) { } - mWrapper = wrapper; + mOptionalWrapper = Optional.of(wrapper); handlePendingMessages(); } @@ -368,6 +372,10 @@ public class TileLifecycleManager extends BroadcastReceiver implements * are supposed to be bound, we will try to bind after some amount of time. */ private void handleDeath() { + if (!mIsBound.get()) { + // If we are already not bound, don't do anything else. + return; + } mExecutor.execute(() -> { if (!mIsBound.get()) { // If we are already not bound, don't do anything else. @@ -522,7 +530,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements @Override public void onTileAdded() { if (mDebug) Log.d(TAG, "onTileAdded " + getComponent()); - if (mWrapper == null || !mWrapper.onTileAdded()) { + if (isNullOrFailedAction(mOptionalWrapper, QSTileServiceWrapper::onTileAdded)) { queueMessage(MSG_ON_ADDED); handleDeath(); } @@ -531,7 +539,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements @Override public void onTileRemoved() { if (mDebug) Log.d(TAG, "onTileRemoved " + getComponent()); - if (mWrapper == null || !mWrapper.onTileRemoved()) { + if (isNullOrFailedAction(mOptionalWrapper, QSTileServiceWrapper::onTileRemoved)) { queueMessage(MSG_ON_REMOVED); handleDeath(); } @@ -541,7 +549,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements public void onStartListening() { if (mDebug) Log.d(TAG, "onStartListening " + getComponent()); mListening = true; - if (mWrapper != null && !mWrapper.onStartListening()) { + if (isNotNullAndFailedAction(mOptionalWrapper, QSTileServiceWrapper::onStartListening)) { handleDeath(); } } @@ -550,7 +558,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements public void onStopListening() { if (mDebug) Log.d(TAG, "onStopListening " + getComponent()); mListening = false; - if (mWrapper != null && !mWrapper.onStopListening()) { + if (isNotNullAndFailedAction(mOptionalWrapper, QSTileServiceWrapper::onStopListening)) { handleDeath(); } } @@ -558,7 +566,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements @Override public void onClick(IBinder iBinder) { if (mDebug) Log.d(TAG, "onClick " + iBinder + " " + getComponent() + " " + mUser); - if (mWrapper == null || !mWrapper.onClick(iBinder)) { + if (isNullOrFailedAction(mOptionalWrapper, (wrapper) -> wrapper.onClick(iBinder))) { mClickBinder = iBinder; queueMessage(MSG_ON_CLICK); handleDeath(); @@ -568,7 +576,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements @Override public void onUnlockComplete() { if (mDebug) Log.d(TAG, "onUnlockComplete " + getComponent()); - if (mWrapper == null || !mWrapper.onUnlockComplete()) { + if (isNullOrFailedAction(mOptionalWrapper, QSTileServiceWrapper::onUnlockComplete)) { queueMessage(MSG_ON_UNLOCK_COMPLETE); handleDeath(); } @@ -577,7 +585,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements @Nullable @Override public IBinder asBinder() { - return mWrapper != null ? mWrapper.asBinder() : null; + return mOptionalWrapper.map(QSTileServiceWrapper::asBinder).orElse(null); } @Override @@ -591,18 +599,42 @@ public class TileLifecycleManager extends BroadcastReceiver implements } private void freeWrapper() { - if (mWrapper != null) { + if (mOptionalWrapper.isPresent()) { try { - mWrapper.asBinder().unlinkToDeath(this, 0); + mOptionalWrapper.ifPresent( + (wrapper) -> wrapper.asBinder().unlinkToDeath(this, 0) + ); } catch (NoSuchElementException e) { Log.w(TAG, "Trying to unlink not linked recipient for component" + mIntent.getComponent().flattenToShortString()); } - mWrapper = null; + mOptionalWrapper = Optional.empty(); } } public interface TileChangeListener { void onTileChanged(ComponentName tile); } + + /** + * Returns true if the Optional is empty OR performing the action on the content of the Optional + * (when not empty) fails. + */ + private static boolean isNullOrFailedAction( + Optional<QSTileServiceWrapper> optionalWrapper, + Predicate<QSTileServiceWrapper> action + ) { + return !optionalWrapper.map(action::test).orElse(false); + } + + /** + * Returns true if the Optional is not empty AND performing the action on the content of + * the Optional fails. + */ + private static boolean isNotNullAndFailedAction( + Optional<QSTileServiceWrapper> optionalWrapper, + Predicate<QSTileServiceWrapper> action + ) { + return !optionalWrapper.map(action::test).orElse(true); + } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 878e6faf32e7..17eb3c83fefe 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -1720,9 +1720,12 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } // To prevent the weather clock from overlapping with the notification shelf on AOD, we use // the small clock here - if (mKeyguardStatusViewController.isLargeClockBlockingNotificationShelf() - && hasVisibleNotifications() && isOnAod()) { - return SMALL; + // With migrateClocksToBlueprint, weather clock will have behaviors similar to other clocks + if (!migrateClocksToBlueprint()) { + if (mKeyguardStatusViewController.isLargeClockBlockingNotificationShelf() + && hasVisibleNotifications() && isOnAod()) { + return SMALL; + } } return LARGE; } @@ -1742,8 +1745,9 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } else { layout = mNotificationContainerParent; } + if (migrateClocksToBlueprint()) { - mKeyguardInteractor.setClockShouldBeCentered(shouldBeCentered); + mKeyguardInteractor.setClockShouldBeCentered(mSplitShadeEnabled && shouldBeCentered); } else { mKeyguardStatusViewController.updateAlignment( layout, mSplitShadeEnabled, shouldBeCentered, animate); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java index 2740cc6682a6..11456ffaa4f3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java @@ -453,11 +453,11 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue public void initNotificationIconArea() { ViewGroup notificationIconArea = mStatusBar.requireViewById(R.id.notification_icon_area); if (NotificationIconContainerRefactor.isEnabled()) { - mNotificationIconAreaInner = - LayoutInflater.from(getContext()) - .inflate(R.layout.notification_icon_area, notificationIconArea, true); + LayoutInflater.from(getContext()) + .inflate(R.layout.notification_icon_area, notificationIconArea, true); NotificationIconContainer notificationIcons = notificationIconArea.requireViewById(R.id.notificationIcons); + mNotificationIconAreaInner = notificationIcons; mNicBindingDisposable = mNicViewBinder.bindWhileAttached(notificationIcons); } else { mNotificationIconAreaInner = diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt index 6f58bc2fcc9f..e6637e60b146 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt @@ -30,13 +30,15 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep -import com.android.systemui.log.LogBuffer +import com.android.systemui.log.core.LogLevel +import com.android.systemui.log.core.LogcatOnlyMessageBuffer import com.android.systemui.plugins.clocks.ClockAnimations import com.android.systemui.plugins.clocks.ClockController import com.android.systemui.plugins.clocks.ClockEvents import com.android.systemui.plugins.clocks.ClockFaceConfig import com.android.systemui.plugins.clocks.ClockFaceController import com.android.systemui.plugins.clocks.ClockFaceEvents +import com.android.systemui.plugins.clocks.ClockMessageBuffers import com.android.systemui.plugins.clocks.ClockTickRate import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.ConfigurationController @@ -94,9 +96,9 @@ class ClockEventControllerTest : SysuiTestCase() { @Mock private lateinit var largeClockEvents: ClockFaceEvents @Mock private lateinit var parentView: View private lateinit var repository: FakeKeyguardRepository - @Mock private lateinit var smallLogBuffer: LogBuffer - @Mock private lateinit var largeLogBuffer: LogBuffer @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor + private val messageBuffer = LogcatOnlyMessageBuffer(LogLevel.DEBUG) + private val clockBuffers = ClockMessageBuffers(messageBuffer, messageBuffer, messageBuffer) private lateinit var underTest: ClockEventController @Mock private lateinit var zenModeController: ZenModeController @@ -140,8 +142,7 @@ class ClockEventControllerTest : SysuiTestCase() { context, mainExecutor, bgExecutor, - smallLogBuffer, - largeLogBuffer, + clockBuffers, withDeps.featureFlags, zenModeController ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt index e89b61f6d44e..dc0d9c5f987e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt @@ -20,7 +20,6 @@ package com.android.systemui.keyguard.ui.view.layout.sections import androidx.constraintlayout.widget.ConstraintSet import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.flags.FakeFeatureFlagsClassic import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel @@ -43,7 +42,6 @@ class ClockSectionTest : SysuiTestCase() { @Mock private lateinit var keyguardClockViewModel: KeyguardClockViewModel @Mock private lateinit var smartspaceViewModel: KeyguardSmartspaceViewModel @Mock private lateinit var splitShadeStateController: SplitShadeStateController - private var featureFlags: FakeFeatureFlagsClassic = FakeFeatureFlagsClassic() private lateinit var underTest: ClockSection @@ -88,7 +86,6 @@ class ClockSectionTest : SysuiTestCase() { smartspaceViewModel, mContext, splitShadeStateController, - featureFlags ) } @@ -147,24 +144,6 @@ class ClockSectionTest : SysuiTestCase() { assetSmallClockTop(cs, expectedSmallClockTopMargin) } - @Test - fun testLargeClockShouldBeCentered() { - underTest.setClockShouldBeCentered(true) - val cs = ConstraintSet() - underTest.applyDefaultConstraints(cs) - val constraint = cs.getConstraint(R.id.lockscreen_clock_view_large) - assertThat(constraint.layout.endToEnd).isEqualTo(ConstraintSet.PARENT_ID) - } - - @Test - fun testLargeClockShouldNotBeCentered() { - underTest.setClockShouldBeCentered(false) - val cs = ConstraintSet() - underTest.applyDefaultConstraints(cs) - val constraint = cs.getConstraint(R.id.lockscreen_clock_view_large) - assertThat(constraint.layout.endToEnd).isEqualTo(R.id.split_shade_guideline) - } - private fun setLargeClock(useLargeClock: Boolean) { whenever(keyguardClockViewModel.useLargeClock).thenReturn(useLargeClock) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt index bff27f6910ab..740110b4fee0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt @@ -69,14 +69,13 @@ class SmartspaceSectionTest : SysuiTestCase() { keyguardUnlockAnimationController, ) constraintLayout = ConstraintLayout(mContext) - whenever(lockscreenSmartspaceController.buildAndConnectView(constraintLayout)) - .thenReturn(smartspaceView) - whenever(lockscreenSmartspaceController.buildAndConnectWeatherView(constraintLayout)) - .thenReturn(weatherView) - whenever(lockscreenSmartspaceController.buildAndConnectDateView(constraintLayout)) - .thenReturn(dateView) + whenever(keyguardSmartspaceViewModel.smartspaceView).thenReturn(smartspaceView) + whenever(keyguardSmartspaceViewModel.weatherView).thenReturn(weatherView) + whenever(keyguardSmartspaceViewModel.dateView).thenReturn(dateView) whenever(keyguardClockViewModel.hasCustomWeatherDataDisplay) .thenReturn(hasCustomWeatherDataDisplay) + whenever(keyguardSmartspaceViewModel.smartspaceController) + .thenReturn(lockscreenSmartspaceController) constraintSet = ConstraintSet() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt index 459a74c82da4..ee1be10607cf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt @@ -24,7 +24,6 @@ import androidx.test.filters.SmallTest import com.android.systemui.Flags as AConfigFlags import com.android.systemui.Flags.FLAG_NEW_AOD_TRANSITION import com.android.systemui.SysuiTestCase -import com.android.systemui.collectLastValue import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository import com.android.systemui.common.ui.domain.interactor.configurationInteractor import com.android.systemui.coroutines.collectLastValue diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt index ee94cbbcfd79..ee27c5c9ba0d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt @@ -26,6 +26,7 @@ import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags.TRANSIT_CLOCK import com.android.systemui.plugins.clocks.ClockController import com.android.systemui.plugins.clocks.ClockId +import com.android.systemui.plugins.clocks.ClockMessageBuffers import com.android.systemui.plugins.clocks.ClockMetadata import com.android.systemui.plugins.clocks.ClockProviderPlugin import com.android.systemui.plugins.clocks.ClockSettings @@ -128,6 +129,7 @@ class ClockRegistryTest : SysuiTestCase() { override fun createClock(settings: ClockSettings): ClockController = createCallbacks[settings.clockId!!]!!(settings.clockId!!) override fun getClockThumbnail(id: ClockId): Drawable? = thumbnailCallbacks[id]!!(id) + override fun initialize(buffers: ClockMessageBuffers?) { } fun addClock( id: ClockId, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt index fef262f50306..e0e8d1f51884 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt @@ -26,6 +26,7 @@ import android.widget.FrameLayout import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.customization.R +import com.android.systemui.plugins.clocks.ClockId import com.android.systemui.plugins.clocks.ClockSettings import com.android.systemui.shared.clocks.DefaultClockController.Companion.DOZE_COLOR import com.android.systemui.util.mockito.any @@ -49,6 +50,9 @@ import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever import org.mockito.junit.MockitoJUnit +private fun DefaultClockProvider.createClock(id: ClockId): DefaultClockController = + createClock(ClockSettings(id, null)) as DefaultClockController + @RunWith(AndroidTestingRunner::class) @SmallTest class DefaultClockProviderTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java index 14751c2dc29e..54d3607c7a83 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java @@ -36,6 +36,7 @@ import android.os.UserHandle; import android.provider.Settings; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; +import android.view.LayoutInflater; import android.view.View; import androidx.test.filters.SmallTest; @@ -91,7 +92,6 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { private NotificationIconAreaController mMockNotificationAreaController; private ShadeExpansionStateManager mShadeExpansionStateManager; - private View mNotificationAreaInner; private OngoingCallController mOngoingCallController; private SystemStatusAnimationScheduler mAnimationScheduler; private StatusBarLocationPublisher mLocationPublisher; @@ -270,15 +270,15 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false); - assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility()); + assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility()); fragment.disable(DEFAULT_DISPLAY, 0, 0, false); - assertEquals(View.VISIBLE, mNotificationAreaInner.getVisibility()); + assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility()); fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false); - assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility()); + assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility()); } @Test @@ -310,7 +310,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { // THEN all views are hidden assertEquals(View.INVISIBLE, getClockView().getVisibility()); - assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility()); + assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility()); assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility()); } @@ -326,7 +326,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { // THEN all views are shown assertEquals(View.VISIBLE, getClockView().getVisibility()); - assertEquals(View.VISIBLE, mNotificationAreaInner.getVisibility()); + assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility()); assertEquals(View.VISIBLE, getEndSideContentView().getVisibility()); } @@ -343,7 +343,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { // THEN all views are hidden assertEquals(View.INVISIBLE, getClockView().getVisibility()); - assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility()); + assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility()); assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility()); // WHEN the shade is updated to no longer be open @@ -354,7 +354,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { // THEN all views are shown assertEquals(View.VISIBLE, getClockView().getVisibility()); - assertEquals(View.VISIBLE, mNotificationAreaInner.getVisibility()); + assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility()); assertEquals(View.VISIBLE, getEndSideContentView().getVisibility()); } @@ -368,7 +368,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { // THEN all views are shown assertEquals(View.VISIBLE, getClockView().getVisibility()); - assertEquals(View.VISIBLE, mNotificationAreaInner.getVisibility()); + assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility()); assertEquals(View.VISIBLE, getEndSideContentView().getVisibility()); } @@ -382,7 +382,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { // THEN all views are hidden assertEquals(View.GONE, getClockView().getVisibility()); - assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility()); + assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility()); assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility()); } @@ -396,7 +396,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { // THEN all views are hidden assertEquals(View.GONE, getClockView().getVisibility()); - assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility()); + assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility()); assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility()); // WHEN the transition has finished @@ -405,7 +405,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { // THEN all views are shown assertEquals(View.VISIBLE, getClockView().getVisibility()); - assertEquals(View.VISIBLE, mNotificationAreaInner.getVisibility()); + assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility()); assertEquals(View.VISIBLE, getEndSideContentView().getVisibility()); } @@ -438,7 +438,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility()); - assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility()); + assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility()); } @Test @@ -503,8 +503,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { fragment.disable(DEFAULT_DISPLAY, 0, 0, true); // Notification area is hidden without delay - assertEquals(0f, mNotificationAreaInner.getAlpha(), 0.01); - assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility()); + assertEquals(0f, getNotificationAreaView().getAlpha(), 0.01); + assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility()); } @Test @@ -723,11 +723,10 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { private void setUpNotificationIconAreaController() { mMockNotificationAreaController = mock(NotificationIconAreaController.class); - - mNotificationAreaInner = new View(mContext); - - when(mMockNotificationAreaController.getNotificationInnerAreaView()).thenReturn( - mNotificationAreaInner); + View notificationAreaInner = + LayoutInflater.from(mContext).inflate(R.layout.notification_icon_area, null); + when(mMockNotificationAreaController.getNotificationInnerAreaView()) + .thenReturn(notificationAreaInner); } /** @@ -782,4 +781,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { private View getEndSideContentView() { return mFragment.getView().findViewById(R.id.status_bar_end_side_content); } + + private View getNotificationAreaView() { + return mFragment.getView().findViewById(R.id.notificationIcons); + } } diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index badd7f085e56..2b81dbcb6fb2 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -2059,9 +2059,6 @@ class UserController implements Handler.Callback { mTargetUserId = targetUserId; userSwitchUiEnabled = mUserSwitchUiEnabled; } - if (android.multiuser.Flags.useAllCpusDuringUserSwitch()) { - mInjector.setHasTopUi(true); - } if (userSwitchUiEnabled) { UserInfo currentUserInfo = getUserInfo(currentUserId); Pair<UserInfo, UserInfo> userNames = new Pair<>(currentUserInfo, targetUserInfo); @@ -2130,9 +2127,6 @@ class UserController implements Handler.Callback { } private void endUserSwitch() { - if (android.multiuser.Flags.useAllCpusDuringUserSwitch()) { - mInjector.setHasTopUi(false); - } final int nextUserId; synchronized (mLock) { nextUserId = ObjectUtils.getOrElse(mPendingTargetUserIds.poll(), UserHandle.USER_NULL); @@ -3816,15 +3810,6 @@ class UserController implements Handler.Callback { getSystemServiceManager().onUserStarting(TimingsTraceAndSlog.newAsyncLog(), userId); } - void setHasTopUi(boolean hasTopUi) { - try { - Slogf.i(TAG, "Setting hasTopUi to " + hasTopUi); - mService.setHasTopUi(hasTopUi); - } catch (RemoteException e) { - Slogf.e(TAG, "Failed to allow using all CPU cores", e); - } - } - void onSystemUserVisibilityChanged(boolean visible) { getUserManagerInternal().onSystemUserVisibilityChanged(visible); } diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index 3024dd27b5c4..2d6177d9d27b 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -403,7 +403,8 @@ public class AutomaticBrightnessController { | (!mAmbientLuxValid ? BrightnessEvent.FLAG_INVALID_LUX : 0) | (mDisplayPolicy == DisplayPowerRequest.POLICY_DOZE ? BrightnessEvent.FLAG_DOZE_SCALE : 0) - | (isInIdleMode() ? BrightnessEvent.FLAG_IDLE_CURVE : 0)); + | (getMode() == AUTO_BRIGHTNESS_MODE_IDLE + ? BrightnessEvent.FLAG_IDLE_CURVE : 0)); } if (!mAmbientLuxValid) { @@ -514,7 +515,8 @@ public class AutomaticBrightnessController { if (mLoggingEnabled) { Slog.d(TAG, "Display policy transitioning from " + oldPolicy + " to " + policy); } - if (!isInteractivePolicy(policy) && isInteractivePolicy(oldPolicy) && !isInIdleMode()) { + if (!isInteractivePolicy(policy) && isInteractivePolicy(oldPolicy) + && getMode() != AUTO_BRIGHTNESS_MODE_IDLE) { mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_CURRENT_SHORT_TERM_MODEL, mCurrentBrightnessMapper.getShortTermModelTimeout()); } else if (isInteractivePolicy(policy) && !isInteractivePolicy(oldPolicy)) { @@ -555,7 +557,7 @@ public class AutomaticBrightnessController { boolean shouldResetShortTermModel) { if (mBrightnessMappingStrategyMap.get(AUTO_BRIGHTNESS_MODE_DEFAULT) .setBrightnessConfiguration(configuration)) { - if (!isInIdleMode() && shouldResetShortTermModel) { + if (getMode() != AUTO_BRIGHTNESS_MODE_IDLE && shouldResetShortTermModel) { resetShortTermModel(); } return true; @@ -563,8 +565,9 @@ public class AutomaticBrightnessController { return false; } - public boolean isInIdleMode() { - return mCurrentBrightnessMapper.getMode() == AUTO_BRIGHTNESS_MODE_IDLE; + @AutomaticBrightnessController.AutomaticBrightnessMode + public int getMode() { + return mCurrentBrightnessMapper.getMode(); } public void dump(PrintWriter pw) { @@ -620,8 +623,7 @@ public class AutomaticBrightnessController { pw.println(" mPendingForegroundAppPackageName=" + mPendingForegroundAppPackageName); pw.println(" mForegroundAppCategory=" + mForegroundAppCategory); pw.println(" mPendingForegroundAppCategory=" + mPendingForegroundAppCategory); - pw.println(" Current mode=" - + autoBrightnessModeToString(mCurrentBrightnessMapper.getMode())); + pw.println(" Current mode=" + autoBrightnessModeToString(getMode())); pw.println(); for (int i = 0; i < mBrightnessMappingStrategyMap.size(); i++) { @@ -743,7 +745,7 @@ public class AutomaticBrightnessController { lux = 0; } mAmbientLux = lux; - if (isInIdleMode()) { + if (getMode() == AUTO_BRIGHTNESS_MODE_IDLE) { mAmbientBrighteningThreshold = mAmbientBrightnessThresholdsIdle.getBrighteningThreshold(lux); mAmbientDarkeningThreshold = @@ -835,7 +837,7 @@ public class AutomaticBrightnessController { } earliestValidTime = mAmbientLightRingBuffer.getTime(i); } - return earliestValidTime + (isInIdleMode() + return earliestValidTime + (getMode() == AUTO_BRIGHTNESS_MODE_IDLE ? mBrighteningLightDebounceConfigIdle : mBrighteningLightDebounceConfig); } @@ -848,7 +850,7 @@ public class AutomaticBrightnessController { } earliestValidTime = mAmbientLightRingBuffer.getTime(i); } - return earliestValidTime + (isInIdleMode() + return earliestValidTime + (getMode() == AUTO_BRIGHTNESS_MODE_IDLE ? mDarkeningLightDebounceConfigIdle : mDarkeningLightDebounceConfig); } @@ -973,7 +975,7 @@ public class AutomaticBrightnessController { mPreThresholdBrightness = mScreenAutoBrightness; } mScreenAutoBrightness = newScreenAutoBrightness; - if (isInIdleMode()) { + if (getMode() == AUTO_BRIGHTNESS_MODE_IDLE) { mScreenBrighteningThreshold = clampScreenBrightness( mScreenBrightnessThresholdsIdle.getBrighteningThreshold( newScreenAutoBrightness)); diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java index 6a4b00f7551f..8405e0a52084 100644 --- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java +++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java @@ -23,12 +23,14 @@ import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIG import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE; import android.annotation.Nullable; +import android.content.Context; import android.content.pm.ApplicationInfo; -import android.content.res.Resources; import android.content.res.TypedArray; import android.hardware.display.BrightnessConfiguration; import android.hardware.display.BrightnessCorrection; import android.os.PowerManager; +import android.os.UserHandle; +import android.provider.Settings; import android.util.LongArray; import android.util.MathUtils; import android.util.Pair; @@ -80,45 +82,50 @@ public abstract class BrightnessMappingStrategy { * Creates a BrightnessMapping strategy. We do not create a simple mapping strategy for idle * mode. * - * @param resources + * @param context * @param displayDeviceConfig * @param mode The auto-brightness mode. Different modes use different brightness curves * @param displayWhiteBalanceController * @return the BrightnessMappingStrategy */ @Nullable - static BrightnessMappingStrategy create(Resources resources, + static BrightnessMappingStrategy create(Context context, DisplayDeviceConfig displayDeviceConfig, @AutomaticBrightnessController.AutomaticBrightnessMode int mode, - DisplayWhiteBalanceController displayWhiteBalanceController) { + @Nullable DisplayWhiteBalanceController displayWhiteBalanceController) { // Display independent, mode dependent values float[] brightnessLevelsNits = null; float[] brightnessLevels = null; float[] luxLevels = null; + int preset = Settings.System.getIntForUser(context.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_FOR_ALS, + Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL, UserHandle.USER_CURRENT); switch (mode) { case AUTO_BRIGHTNESS_MODE_DEFAULT -> { brightnessLevelsNits = displayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(); - luxLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(mode); - brightnessLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevels(mode); + luxLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(mode, preset); + brightnessLevels = + displayDeviceConfig.getAutoBrightnessBrighteningLevels(mode, preset); } case AUTO_BRIGHTNESS_MODE_IDLE -> { - brightnessLevelsNits = getFloatArray(resources.obtainTypedArray( + brightnessLevelsNits = getFloatArray(context.getResources().obtainTypedArray( com.android.internal.R.array.config_autoBrightnessDisplayValuesNitsIdle)); - luxLevels = getLuxLevels(resources.getIntArray( + luxLevels = getLuxLevels(context.getResources().getIntArray( com.android.internal.R.array.config_autoBrightnessLevelsIdle)); } case AUTO_BRIGHTNESS_MODE_DOZE -> { - luxLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(mode); - brightnessLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevels(mode); + luxLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(mode, preset); + brightnessLevels = + displayDeviceConfig.getAutoBrightnessBrighteningLevels(mode, preset); } } // Display independent, mode independent values - float autoBrightnessAdjustmentMaxGamma = resources.getFraction( + float autoBrightnessAdjustmentMaxGamma = context.getResources().getFraction( com.android.internal.R.fraction.config_autoBrightnessAdjustmentMaxGamma, 1, 1); - long shortTermModelTimeout = resources.getInteger( + long shortTermModelTimeout = context.getResources().getInteger( com.android.internal.R.integer.config_autoBrightnessShortTermModelTimeout); // Display dependent values - used for physical mapping strategy nits -> brightness @@ -823,6 +830,8 @@ public abstract class BrightnessMappingStrategy { private float mAutoBrightnessAdjustment; private float mUserLux; private float mUserBrightness; + + @Nullable private final DisplayWhiteBalanceController mDisplayWhiteBalanceController; @AutomaticBrightnessController.AutomaticBrightnessMode @@ -838,7 +847,7 @@ public abstract class BrightnessMappingStrategy { public PhysicalMappingStrategy(BrightnessConfiguration config, float[] nits, float[] brightness, float maxGamma, @AutomaticBrightnessController.AutomaticBrightnessMode int mode, - DisplayWhiteBalanceController displayWhiteBalanceController) { + @Nullable DisplayWhiteBalanceController displayWhiteBalanceController) { Preconditions.checkArgument(nits.length != 0 && brightness.length != 0, "Nits and brightness arrays must not be empty!"); diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index a6f42d7677b2..bd22e1d21dea 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -1592,24 +1592,13 @@ public class DisplayDeviceConfig { /** * @param mode The auto-brightness mode - * @return The default auto-brightness brightening ambient lux levels for the specified mode - * and the normal brightness preset - */ - public float[] getAutoBrightnessBrighteningLevelsLux( - @AutomaticBrightnessController.AutomaticBrightnessMode int mode) { - if (mDisplayBrightnessMapping == null) { - return null; - } - return mDisplayBrightnessMapping.getLuxArray(mode); - } - - /** - * @param mode The auto-brightness mode * @param preset The brightness preset. Presets are used on devices that allow users to choose * from a set of predefined options in display auto-brightness settings. - * @return Auto brightness brightening ambient lux levels for the specified mode and preset + * @return The default auto-brightness brightening ambient lux levels for the specified mode + * and preset */ - public float[] getAutoBrightnessBrighteningLevelsLux(String mode, String preset) { + public float[] getAutoBrightnessBrighteningLevelsLux( + @AutomaticBrightnessController.AutomaticBrightnessMode int mode, int preset) { if (mDisplayBrightnessMapping == null) { return null; } @@ -1628,24 +1617,12 @@ public class DisplayDeviceConfig { /** * @param mode The auto-brightness mode - * @return The default auto-brightness brightening levels for the specified mode and the normal - * brightness preset - */ - public float[] getAutoBrightnessBrighteningLevels( - @AutomaticBrightnessController.AutomaticBrightnessMode int mode) { - if (mDisplayBrightnessMapping == null) { - return null; - } - return mDisplayBrightnessMapping.getBrightnessArray(mode); - } - - /** - * @param mode The auto-brightness mode * @param preset The brightness preset. Presets are used on devices that allow users to choose * from a set of predefined options in display auto-brightness settings. - * @return Auto brightness brightening backlight levels for the specified mode and preset + * @return The default auto-brightness brightening levels for the specified mode and preset */ - public float[] getAutoBrightnessBrighteningLevels(String mode, String preset) { + public float[] getAutoBrightnessBrighteningLevels( + @AutomaticBrightnessController.AutomaticBrightnessMode int mode, int preset) { if (mDisplayBrightnessMapping == null) { return null; } diff --git a/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java b/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java index 4e341a9c19b4..c17709144e1d 100644 --- a/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java +++ b/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java @@ -65,6 +65,16 @@ public class DisplayOffloadSessionImpl implements DisplayManagerInternal.Display return true; } + @Override + public float[] getCurrentAutoBrightnessLevels() { + return mDisplayPowerController.getCurrentAutoBrightnessLevels(); + } + + @Override + public float[] getCurrentAutoBrightnessLuxLevels() { + return mDisplayPowerController.getCurrentAutoBrightnessLuxLevels(); + } + /** * Start the offload session. The method returns if the session is already active. * @return Whether the session was started successfully diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 2685efecd6a1..c17378d790bd 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -735,7 +735,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mCdsi = null; } - setUpAutoBrightness(resources, handler); + setUpAutoBrightness(context, handler); mColorFadeEnabled = !ActivityManager.isLowRamDeviceStatic() && !resources.getBoolean( @@ -1075,7 +1075,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call loadBrightnessRampRates(); loadProximitySensor(); loadNitsRange(mContext.getResources()); - setUpAutoBrightness(mContext.getResources(), mHandler); + setUpAutoBrightness(mContext, mHandler); reloadReduceBrightColours(); setAnimatorRampSpeeds(/* isIdleMode= */ false); mBrightnessRangeController.loadFromConfig(hbmMetadata, token, info, mDisplayDeviceConfig); @@ -1119,7 +1119,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call DisplayPowerState.SCREEN_BRIGHTNESS_FLOAT, DisplayPowerState.SCREEN_SDR_BRIGHTNESS_FLOAT); setAnimatorRampSpeeds(mAutomaticBrightnessController != null - && mAutomaticBrightnessController.isInIdleMode()); + && mAutomaticBrightnessController.getMode() == AUTO_BRIGHTNESS_MODE_IDLE); mScreenBrightnessRampAnimator.setListener(mRampAnimatorListener); noteScreenState(mPowerState.getScreenState()); @@ -1145,7 +1145,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call handleBrightnessModeChange(); } - private void setUpAutoBrightness(Resources resources, Handler handler) { + private void setUpAutoBrightness(Context context, Handler handler) { mUseSoftwareAutoBrightnessConfig = mDisplayDeviceConfig.isAutoBrightnessAvailable(); if (!mUseSoftwareAutoBrightnessConfig) { @@ -1155,21 +1155,19 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call SparseArray<BrightnessMappingStrategy> brightnessMappers = new SparseArray<>(); BrightnessMappingStrategy defaultModeBrightnessMapper = - mInjector.getDefaultModeBrightnessMapper(resources, mDisplayDeviceConfig, + mInjector.getDefaultModeBrightnessMapper(context, mDisplayDeviceConfig, mDisplayWhiteBalanceController); brightnessMappers.append(AUTO_BRIGHTNESS_MODE_DEFAULT, defaultModeBrightnessMapper); - final boolean isIdleScreenBrightnessEnabled = resources.getBoolean( + final boolean isIdleScreenBrightnessEnabled = context.getResources().getBoolean( R.bool.config_enableIdleScreenBrightnessMode); if (isIdleScreenBrightnessEnabled) { BrightnessMappingStrategy idleModeBrightnessMapper = - BrightnessMappingStrategy.create(resources, mDisplayDeviceConfig, - AUTO_BRIGHTNESS_MODE_IDLE, - mDisplayWhiteBalanceController); + BrightnessMappingStrategy.create(context, mDisplayDeviceConfig, + AUTO_BRIGHTNESS_MODE_IDLE, mDisplayWhiteBalanceController); if (idleModeBrightnessMapper != null) { - brightnessMappers.append(AUTO_BRIGHTNESS_MODE_IDLE, - idleModeBrightnessMapper); + brightnessMappers.append(AUTO_BRIGHTNESS_MODE_IDLE, idleModeBrightnessMapper); } } @@ -1181,7 +1179,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } if (defaultModeBrightnessMapper != null) { - final float dozeScaleFactor = resources.getFraction( + final float dozeScaleFactor = context.getResources().getFraction( com.android.internal.R.fraction.config_screenAutoBrightnessDozeScaleFactor, 1, 1); @@ -1265,14 +1263,14 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call .getAutoBrightnessBrighteningLightDebounceIdle(); long darkeningLightDebounceIdle = mDisplayDeviceConfig .getAutoBrightnessDarkeningLightDebounceIdle(); - boolean autoBrightnessResetAmbientLuxAfterWarmUp = resources.getBoolean( + boolean autoBrightnessResetAmbientLuxAfterWarmUp = context.getResources().getBoolean( com.android.internal.R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp); - int lightSensorWarmUpTimeConfig = resources.getInteger( + int lightSensorWarmUpTimeConfig = context.getResources().getInteger( com.android.internal.R.integer.config_lightSensorWarmupTime); - int lightSensorRate = resources.getInteger( + int lightSensorRate = context.getResources().getInteger( com.android.internal.R.integer.config_autoBrightnessLightSensorRate); - int initialLightSensorRate = resources.getInteger( + int initialLightSensorRate = context.getResources().getInteger( com.android.internal.R.integer.config_autoBrightnessInitialLightSensorRate); if (initialLightSensorRate == -1) { initialLightSensorRate = lightSensorRate; @@ -1992,7 +1990,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call boolean isIncreasing = animateValue > currentBrightness; final float rampSpeed; final boolean idle = mAutomaticBrightnessController != null - && mAutomaticBrightnessController.isInIdleMode(); + && mAutomaticBrightnessController.getMode() + == AUTO_BRIGHTNESS_MODE_IDLE; if (isIncreasing && slowChange) { rampSpeed = idle ? mBrightnessRampRateSlowIncreaseIdle : mBrightnessRampRateSlowIncrease; @@ -2216,6 +2215,18 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } @Override + public float[] getCurrentAutoBrightnessLevels() { + // The old DPC is no longer supported + return new float[0]; + } + + @Override + public float[] getCurrentAutoBrightnessLuxLevels() { + // The old DPC is no longer supported + return new float[0]; + } + + @Override public BrightnessInfo getBrightnessInfo() { synchronized (mCachedBrightnessInfo) { return new BrightnessInfo( @@ -2900,7 +2911,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // or the nits is invalid. if (brightnessIsTemporary || mAutomaticBrightnessController == null - || mAutomaticBrightnessController.isInIdleMode() + || mAutomaticBrightnessController.getMode() == AUTO_BRIGHTNESS_MODE_IDLE || !autobrightnessEnabled || mBrightnessTracker == null || !mUseAutoBrightness @@ -3645,12 +3656,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call userNits); } - BrightnessMappingStrategy getDefaultModeBrightnessMapper(Resources resources, + BrightnessMappingStrategy getDefaultModeBrightnessMapper(Context context, DisplayDeviceConfig displayDeviceConfig, DisplayWhiteBalanceController displayWhiteBalanceController) { - return BrightnessMappingStrategy.create(resources, - displayDeviceConfig, AUTO_BRIGHTNESS_MODE_DEFAULT, - displayWhiteBalanceController); + return BrightnessMappingStrategy.create(context, displayDeviceConfig, + AUTO_BRIGHTNESS_MODE_DEFAULT, displayWhiteBalanceController); } HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages, diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java index c088a6dfa95f..2efda087d4ac 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController2.java +++ b/services/core/java/com/android/server/display/DisplayPowerController2.java @@ -19,6 +19,7 @@ package com.android.server.display; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE; +import static com.android.server.display.config.DisplayBrightnessMappingConfig.autoBrightnessPresetToString; import android.animation.Animator; import android.animation.ObjectAnimator; @@ -624,7 +625,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mCdsi = null; } - setUpAutoBrightness(resources, handler); + setUpAutoBrightness(context, handler); mColorFadeEnabled = mInjector.isColorFadeEnabled() && !resources.getBoolean( @@ -905,7 +906,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal // updated here. loadBrightnessRampRates(); loadNitsRange(mContext.getResources()); - setUpAutoBrightness(mContext.getResources(), mHandler); + setUpAutoBrightness(mContext, mHandler); reloadReduceBrightColours(); setAnimatorRampSpeeds(/* isIdleMode= */ false); @@ -950,7 +951,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal DisplayPowerState.SCREEN_BRIGHTNESS_FLOAT, DisplayPowerState.SCREEN_SDR_BRIGHTNESS_FLOAT); setAnimatorRampSpeeds(mAutomaticBrightnessController != null - && mAutomaticBrightnessController.isInIdleMode()); + && mAutomaticBrightnessController.getMode() == AUTO_BRIGHTNESS_MODE_IDLE); mScreenBrightnessRampAnimator.setListener(mRampAnimatorListener); noteScreenState(mPowerState.getScreenState()); @@ -976,10 +977,15 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mContext.getContentResolver().registerContentObserver( Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE), false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL); + if (mFlags.areAutoBrightnessModesEnabled()) { + mContext.getContentResolver().registerContentObserver( + Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FOR_ALS), + /* notifyForDescendants= */ false, mSettingsObserver, UserHandle.USER_CURRENT); + } handleBrightnessModeChange(); } - private void setUpAutoBrightness(Resources resources, Handler handler) { + private void setUpAutoBrightness(Context context, Handler handler) { mUseSoftwareAutoBrightnessConfig = mDisplayDeviceConfig.isAutoBrightnessAvailable(); if (!mUseSoftwareAutoBrightnessConfig) { @@ -989,16 +995,16 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal SparseArray<BrightnessMappingStrategy> brightnessMappers = new SparseArray<>(); BrightnessMappingStrategy defaultModeBrightnessMapper = - mInjector.getDefaultModeBrightnessMapper(resources, mDisplayDeviceConfig, + mInjector.getDefaultModeBrightnessMapper(context, mDisplayDeviceConfig, mDisplayWhiteBalanceController); brightnessMappers.append(AUTO_BRIGHTNESS_MODE_DEFAULT, defaultModeBrightnessMapper); - final boolean isIdleScreenBrightnessEnabled = resources.getBoolean( + final boolean isIdleScreenBrightnessEnabled = context.getResources().getBoolean( R.bool.config_enableIdleScreenBrightnessMode); if (isIdleScreenBrightnessEnabled) { BrightnessMappingStrategy idleModeBrightnessMapper = - BrightnessMappingStrategy.create(resources, mDisplayDeviceConfig, + BrightnessMappingStrategy.create(context, mDisplayDeviceConfig, AUTO_BRIGHTNESS_MODE_IDLE, mDisplayWhiteBalanceController); if (idleModeBrightnessMapper != null) { @@ -1008,7 +1014,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal } BrightnessMappingStrategy dozeModeBrightnessMapper = - BrightnessMappingStrategy.create(resources, mDisplayDeviceConfig, + BrightnessMappingStrategy.create(context, mDisplayDeviceConfig, AUTO_BRIGHTNESS_MODE_DOZE, mDisplayWhiteBalanceController); if (mFlags.areAutoBrightnessModesEnabled() && dozeModeBrightnessMapper != null) { brightnessMappers.put(AUTO_BRIGHTNESS_MODE_DOZE, dozeModeBrightnessMapper); @@ -1022,7 +1028,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal } if (defaultModeBrightnessMapper != null) { - final float dozeScaleFactor = resources.getFraction( + final float dozeScaleFactor = context.getResources().getFraction( R.fraction.config_screenAutoBrightnessDozeScaleFactor, 1, 1); @@ -1106,14 +1112,14 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal .getAutoBrightnessBrighteningLightDebounceIdle(); long darkeningLightDebounceIdle = mDisplayDeviceConfig .getAutoBrightnessDarkeningLightDebounceIdle(); - boolean autoBrightnessResetAmbientLuxAfterWarmUp = resources.getBoolean( + boolean autoBrightnessResetAmbientLuxAfterWarmUp = context.getResources().getBoolean( R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp); - int lightSensorWarmUpTimeConfig = resources.getInteger( + int lightSensorWarmUpTimeConfig = context.getResources().getInteger( R.integer.config_lightSensorWarmupTime); - int lightSensorRate = resources.getInteger( + int lightSensorRate = context.getResources().getInteger( R.integer.config_autoBrightnessLightSensorRate); - int initialLightSensorRate = resources.getInteger( + int initialLightSensorRate = context.getResources().getInteger( R.integer.config_autoBrightnessInitialLightSensorRate); if (initialLightSensorRate == -1) { initialLightSensorRate = lightSensorRate; @@ -1359,7 +1365,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal // Switch to doze auto-brightness mode if needed if (mFlags.areAutoBrightnessModesEnabled() && mAutomaticBrightnessController != null - && !mAutomaticBrightnessController.isInIdleMode()) { + && mAutomaticBrightnessController.getMode() != AUTO_BRIGHTNESS_MODE_IDLE) { setAutomaticScreenBrightnessMode(Display.isDozeState(state) ? AUTO_BRIGHTNESS_MODE_DOZE : AUTO_BRIGHTNESS_MODE_DEFAULT); } @@ -1648,7 +1654,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal boolean isIncreasing = animateValue > currentBrightness; final float rampSpeed; final boolean idle = mAutomaticBrightnessController != null - && mAutomaticBrightnessController.isInIdleMode(); + && mAutomaticBrightnessController.getMode() + == AUTO_BRIGHTNESS_MODE_IDLE; if (isIncreasing && slowChange) { rampSpeed = idle ? mBrightnessRampRateSlowIncreaseIdle : mBrightnessRampRateSlowIncrease; @@ -1880,6 +1887,18 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal } @Override + public float[] getCurrentAutoBrightnessLevels() { + return mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels( + mAutomaticBrightnessController.getMode()); + } + + @Override + public float[] getCurrentAutoBrightnessLuxLevels() { + return mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux( + mAutomaticBrightnessController.getMode()); + } + + @Override public BrightnessInfo getBrightnessInfo() { synchronized (mCachedBrightnessInfo) { return new BrightnessInfo( @@ -2454,7 +2473,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal // or the nits is invalid. if (brightnessIsTemporary || mAutomaticBrightnessController == null - || mAutomaticBrightnessController.isInIdleMode() + || mAutomaticBrightnessController.getMode() == AUTO_BRIGHTNESS_MODE_IDLE || !autobrightnessEnabled || mBrightnessTracker == null || !shouldUseAutoBrightness @@ -2999,6 +3018,16 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal public void onChange(boolean selfChange, Uri uri) { if (uri.equals(Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE))) { handleBrightnessModeChange(); + } else if (uri.equals(Settings.System.getUriFor( + Settings.System.SCREEN_BRIGHTNESS_FOR_ALS))) { + int preset = Settings.System.getIntForUser(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_FOR_ALS, + Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL, + UserHandle.USER_CURRENT); + Slog.i(mTag, "Setting up auto-brightness for preset " + + autoBrightnessPresetToString(preset)); + setUpAutoBrightness(mContext, mHandler); + sendUpdatePowerState(); } else { handleSettingsChange(false /* userSwitch */); } @@ -3117,12 +3146,11 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal userNits); } - BrightnessMappingStrategy getDefaultModeBrightnessMapper(Resources resources, + BrightnessMappingStrategy getDefaultModeBrightnessMapper(Context context, DisplayDeviceConfig displayDeviceConfig, DisplayWhiteBalanceController displayWhiteBalanceController) { - return BrightnessMappingStrategy.create(resources, - displayDeviceConfig, AUTO_BRIGHTNESS_MODE_DEFAULT, - displayWhiteBalanceController); + return BrightnessMappingStrategy.create(context, displayDeviceConfig, + AUTO_BRIGHTNESS_MODE_DEFAULT, displayWhiteBalanceController); } HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages, diff --git a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java index c27918430254..5b350db5fa7b 100644 --- a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java +++ b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java @@ -237,4 +237,14 @@ public interface DisplayPowerControllerInterface { * Indicate that boot has been completed and the screen is ready to update. */ void onBootCompleted(); + + /** + * Get the current brightness levels used to determine automatic brightness based on lux levels. + */ + float[] getCurrentAutoBrightnessLevels(); + + /** + * Get the current lux levels used to determine automatic brightness. + */ + float[] getCurrentAutoBrightnessLuxLevels(); } diff --git a/services/core/java/com/android/server/display/config/DisplayBrightnessMappingConfig.java b/services/core/java/com/android/server/display/config/DisplayBrightnessMappingConfig.java index 8f123291d89c..6978686faa12 100644 --- a/services/core/java/com/android/server/display/config/DisplayBrightnessMappingConfig.java +++ b/services/core/java/com/android/server/display/config/DisplayBrightnessMappingConfig.java @@ -22,6 +22,7 @@ import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIG import android.content.Context; import android.os.PowerManager; +import android.provider.Settings; import android.util.Spline; import com.android.internal.display.BrightnessSynchronizer; @@ -38,9 +39,9 @@ import java.util.Map; */ public class DisplayBrightnessMappingConfig { - private static final String DEFAULT_BRIGHTNESS_PRESET_NAME = "normal"; private static final String DEFAULT_BRIGHTNESS_MAPPING_KEY = - AutoBrightnessModeName._default.getRawName() + "_" + DEFAULT_BRIGHTNESS_PRESET_NAME; + AutoBrightnessModeName._default.getRawName() + "_" + + AutoBrightnessSettingName.normal.getRawName(); /** * Array of desired screen brightness in nits corresponding to the lux values @@ -152,22 +153,15 @@ public class DisplayBrightnessMappingConfig { /** * @param mode The auto-brightness mode - * @return The default auto-brightness brightening ambient lux levels for the specified mode - * and the normal brightness preset - */ - public float[] getLuxArray(@AutomaticBrightnessController.AutomaticBrightnessMode int mode) { - return mBrightnessLevelsLuxMap.get( - autoBrightnessModeToString(mode) + "_" + DEFAULT_BRIGHTNESS_PRESET_NAME); - } - - /** - * @param mode The auto-brightness mode * @param preset The brightness preset. Presets are used on devices that allow users to choose * from a set of predefined options in display auto-brightness settings. - * @return Auto brightness brightening ambient lux levels for the specified mode and preset + * @return The default auto-brightness brightening ambient lux levels for the specified mode + * and preset */ - public float[] getLuxArray(String mode, String preset) { - return mBrightnessLevelsLuxMap.get(mode + "_" + preset); + public float[] getLuxArray(@AutomaticBrightnessController.AutomaticBrightnessMode int mode, + int preset) { + return mBrightnessLevelsLuxMap.get( + autoBrightnessModeToString(mode) + "_" + autoBrightnessPresetToString(preset)); } /** @@ -179,23 +173,14 @@ public class DisplayBrightnessMappingConfig { /** * @param mode The auto-brightness mode - * @return The default auto-brightness brightening levels for the specified mode and the normal - * brightness preset - */ - public float[] getBrightnessArray( - @AutomaticBrightnessController.AutomaticBrightnessMode int mode) { - return mBrightnessLevelsMap.get( - autoBrightnessModeToString(mode) + "_" + DEFAULT_BRIGHTNESS_PRESET_NAME); - } - - /** - * @param mode The auto-brightness mode * @param preset The brightness preset. Presets are used on devices that allow users to choose * from a set of predefined options in display auto-brightness settings. - * @return Auto brightness brightening ambient lux levels for the specified mode and preset + * @return The default auto-brightness brightening levels for the specified mode and preset */ - public float[] getBrightnessArray(String mode, String preset) { - return mBrightnessLevelsMap.get(mode + "_" + preset); + public float[] getBrightnessArray( + @AutomaticBrightnessController.AutomaticBrightnessMode int mode, int preset) { + return mBrightnessLevelsMap.get( + autoBrightnessModeToString(mode) + "_" + autoBrightnessPresetToString(preset)); } @Override @@ -247,6 +232,24 @@ public class DisplayBrightnessMappingConfig { } } + /** + * @param preset The brightness preset. Presets are used on devices that allow users to choose + * from a set of predefined options in display auto-brightness settings. + * @return The string representing the preset + */ + public static String autoBrightnessPresetToString(int preset) { + return switch (preset) { + case Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_DIM -> + AutoBrightnessSettingName.dim.getRawName(); + case Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL -> + AutoBrightnessSettingName.normal.getRawName(); + case Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_BRIGHT -> + AutoBrightnessSettingName.bright.getRawName(); + default -> throw new IllegalArgumentException( + "Unknown auto-brightness preset value: " + preset); + }; + } + private float[] brightnessArrayIntToFloat(int[] brightnessInt, Spline backlightToBrightnessSpline) { float[] brightnessFloat = new float[brightnessInt.length]; diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java index 64abb81d0e7a..81204ef5d7ed 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java @@ -1314,7 +1314,6 @@ abstract class HdmiCecLocalDevice extends HdmiLocalDevice { */ protected void disableDevice( boolean initiatedByCec, final PendingActionClearedCallback originalCallback) { - removeAction(AbsoluteVolumeAudioStatusAction.class); removeAction(SetAudioVolumeLevelDiscoveryAction.class); removeAction(ActiveSourceAction.class); removeAction(ResendCecCommandAction.class); diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java index 29303aab6fa9..6157402279a9 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java @@ -308,7 +308,6 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { protected void disableDevice(boolean initiatedByCec, PendingActionClearedCallback callback) { removeAction(OneTouchPlayAction.class); removeAction(DevicePowerStatusAction.class); - removeAction(AbsoluteVolumeAudioStatusAction.class); super.disableDevice(initiatedByCec, callback); } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index 5831b29437dc..1cd267dee2fe 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -1336,7 +1336,6 @@ public final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { removeAction(OneTouchRecordAction.class); removeAction(TimerRecordingAction.class); removeAction(NewDeviceAction.class); - removeAction(AbsoluteVolumeAudioStatusAction.class); // Remove pending actions. removeAction(RequestActiveSourceAction.class); diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 92537064f766..eaf754dc7520 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -3793,6 +3793,11 @@ public class HdmiControlService extends SystemService { } } }); + + // Make sure we switch away from absolute volume behavior (AVB) when entering standby. + // We do this because AVB should not be used unless AbsoluteVolumeAudioStatusAction exists, + // and the action cannot exist in standby because there are no local devices. + checkAndUpdateAbsoluteVolumeBehavior(); } boolean canGoToStandby() { @@ -4446,10 +4451,11 @@ public class HdmiControlService extends SystemService { * This allows the volume level of the System Audio device to be tracked and set by Android. * * Absolute volume behavior requires the following conditions: - * 1. If the System Audio Device is an Audio System: System Audio Mode is active - * 2. All AVB-capable audio output devices are already using full/absolute volume behavior - * 3. CEC volume is enabled - * 4. The System Audio device supports the <Set Audio Volume Level> message + * 1. The device is not in standby or transient to standby + * 2. If the System Audio Device is an Audio System: System Audio Mode is active + * 3. All AVB-capable audio output devices are already using full/absolute volume behavior + * 4. CEC volume is enabled + * 5. The System Audio device supports the <Set Audio Volume Level> message * * This method enables adjust-only absolute volume behavior on TV panels when conditions * 1, 2, and 3 are met, but condition 4 is not. This allows TVs to track the volume level of @@ -4465,10 +4471,16 @@ public class HdmiControlService extends SystemService { return; } + // Condition 1: The device is not in standby or transient to standby + if (mPowerStatusController != null && isPowerStandbyOrTransient()) { + switchToFullVolumeBehavior(); + return; + } + HdmiCecLocalDevice localCecDevice; if (isTvDevice() && tv() != null) { localCecDevice = tv(); - // Condition 1: TVs need System Audio Mode to be active + // Condition 2: TVs need System Audio Mode to be active // (Doesn't apply to Playback Devices, where if SAM isn't active, we assume the // TV is the System Audio Device instead.) if (!isSystemAudioActivated()) { @@ -4485,7 +4497,7 @@ public class HdmiControlService extends SystemService { HdmiDeviceInfo systemAudioDeviceInfo = getDeviceInfo( localCecDevice.findAudioReceiverAddress()); - // Condition 2: All AVB-capable audio outputs already use full/absolute volume behavior + // Condition 3: All AVB-capable audio outputs already use full/absolute volume behavior // We only need to check the first AVB-capable audio output because only TV panels // have more than one of them, and they always have the same volume behavior. @AudioManager.DeviceVolumeBehavior int currentVolumeBehavior = @@ -4493,7 +4505,7 @@ public class HdmiControlService extends SystemService { boolean alreadyUsingFullOrAbsoluteVolume = FULL_AND_ABSOLUTE_VOLUME_BEHAVIORS.contains(currentVolumeBehavior); - // Condition 3: CEC volume is enabled + // Condition 4: CEC volume is enabled boolean cecVolumeEnabled = getHdmiCecVolumeControl() == HdmiControlManager.VOLUME_CONTROL_ENABLED; @@ -4509,7 +4521,7 @@ public class HdmiControlService extends SystemService { return; } - // Condition 4: The System Audio device supports <Set Audio Volume Level> + // Condition 5: The System Audio device supports <Set Audio Volume Level> switch (systemAudioDeviceInfo.getDeviceFeatures().getSetAudioVolumeLevelSupport()) { case DeviceFeatures.FEATURE_SUPPORTED: if (currentVolumeBehavior != AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE) { @@ -4556,6 +4568,8 @@ public class HdmiControlService extends SystemService { * are currently used. Removes the action for handling volume updates for these behaviors. */ private void switchToFullVolumeBehavior() { + Slog.d(TAG, "Switching to full volume behavior"); + if (playback() != null) { playback().removeAvbAudioStatusAction(); } else if (tv() != null) { @@ -4597,12 +4611,14 @@ public class HdmiControlService extends SystemService { // Otherwise, enable adjust-only AVB on TVs only. if (systemAudioDevice.getDeviceFeatures().getSetAudioVolumeLevelSupport() == DeviceFeatures.FEATURE_SUPPORTED) { + Slog.d(TAG, "Enabling absolute volume behavior"); for (AudioDeviceAttributes device : getAvbCapableAudioOutputDevices()) { getAudioDeviceVolumeManager().setDeviceAbsoluteVolumeBehavior( device, volumeInfo, mServiceThreadExecutor, mAbsoluteVolumeChangedListener, true); } } else if (tv() != null) { + Slog.d(TAG, "Enabling adjust-only absolute volume behavior"); for (AudioDeviceAttributes device : getAvbCapableAudioOutputDevices()) { getAudioDeviceVolumeManager().setDeviceAbsoluteVolumeAdjustOnlyBehavior( device, volumeInfo, mServiceThreadExecutor, diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java index 773293f83323..a6c5ad5ffc6c 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java @@ -356,15 +356,6 @@ final class InputMethodUtils { new TextUtils.SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATOR)); } - List<String> getEnabledInputMethodNames() { - List<String> result = new ArrayList<>(); - for (Pair<String, ArrayList<String>> pair : - getEnabledInputMethodsAndSubtypeListLocked()) { - result.add(pair.first); - } - return result; - } - void appendAndPutEnabledInputMethodLocked(String id) { if (TextUtils.isEmpty(mEnabledInputMethodsStrCache)) { // Add in the newly enabled input method. diff --git a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java b/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java index 850449595d74..0eb9166371dc 100644 --- a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java +++ b/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java @@ -87,6 +87,8 @@ import java.util.Objects; @NonNull private final AudioDeviceCallback mAudioDeviceCallback = new AudioDeviceCallbackImpl(); + @MediaRoute2Info.SuitabilityStatus private final int mBuiltInSpeakerSuitabilityStatus; + @NonNull private final AudioManager.OnDevicesForAttributesChangedListener mOnDevicesForAttributesChangedListener = this::onDevicesForAttributesChangedListener; @@ -113,6 +115,10 @@ import java.util.Objects; mHandler = new Handler(Objects.requireNonNull(looper)); mStrategyForMedia = Objects.requireNonNull(strategyForMedia); mOnDeviceRouteChangedListener = Objects.requireNonNull(onDeviceRouteChangedListener); + + mBuiltInSpeakerSuitabilityStatus = + DeviceRouteController.getBuiltInSpeakerSuitabilityStatus(mContext); + mBluetoothRouteController = new AudioPoliciesBluetoothRouteController( mContext, btAdapter, this::rebuildAvailableRoutesAndNotify); @@ -373,14 +379,19 @@ import java.util.Objects; // from getting an id using BluetoothRouteController#getRouteIdForBluetoothAddress. routeId = systemRouteInfo.mDefaultRouteId; } - return new MediaRoute2Info.Builder(routeId, humanReadableName) + MediaRoute2Info.Builder builder = new MediaRoute2Info.Builder(routeId, humanReadableName) .setType(systemRouteInfo.mMediaRoute2InfoType) .setAddress(address) .setSystemRoute(true) .addFeature(FEATURE_LIVE_AUDIO) .addFeature(FEATURE_LOCAL_PLAYBACK) - .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED) - .build(); + .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED); + + if (systemRouteInfo.mMediaRoute2InfoType == MediaRoute2Info.TYPE_BUILTIN_SPEAKER) { + builder.setSuitabilityStatus(mBuiltInSpeakerSuitabilityStatus); + } + + return builder.build(); } /** diff --git a/services/core/java/com/android/server/media/DeviceRouteController.java b/services/core/java/com/android/server/media/DeviceRouteController.java index 9f175a9a0277..8b62cc974862 100644 --- a/services/core/java/com/android/server/media/DeviceRouteController.java +++ b/services/core/java/com/android/server/media/DeviceRouteController.java @@ -81,6 +81,30 @@ import java.util.List; } } + /** Returns device route availability status. */ + @MediaRoute2Info.SuitabilityStatus + static int getBuiltInSpeakerSuitabilityStatus(@NonNull Context context) { + if (!Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) { + // Route is always suitable if the flag is disabled. + return MediaRoute2Info.SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER; + } + + int availabilityStatus = + context.getResources() + .getInteger( + com.android.internal.R.integer + .config_mediaRouter_builtInSpeakerSuitability); + + switch (availabilityStatus) { + case MediaRoute2Info.SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER: + case MediaRoute2Info.SUITABILITY_STATUS_SUITABLE_FOR_MANUAL_TRANSFER: + case MediaRoute2Info.SUITABILITY_STATUS_NOT_SUITABLE_FOR_TRANSFER: + return availabilityStatus; + default: + return MediaRoute2Info.SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER; + } + } + /** Returns the currently selected device (built-in or wired) route. */ @NonNull MediaRoute2Info getSelectedRoute(); diff --git a/services/core/java/com/android/server/media/LegacyDeviceRouteController.java b/services/core/java/com/android/server/media/LegacyDeviceRouteController.java index c0f28346705c..65b0ad0d61a0 100644 --- a/services/core/java/com/android/server/media/LegacyDeviceRouteController.java +++ b/services/core/java/com/android/server/media/LegacyDeviceRouteController.java @@ -72,6 +72,8 @@ import java.util.Objects; @NonNull private final AudioRoutesObserver mAudioRoutesObserver = new AudioRoutesObserver(); + @MediaRoute2Info.SuitabilityStatus private final int mBuiltInSpeakerSuitabilityStatus; + private int mDeviceVolume; private MediaRoute2Info mDeviceRoute; @@ -90,6 +92,9 @@ import java.util.Objects; mAudioManager = audioManager; mAudioService = audioService; + mBuiltInSpeakerSuitabilityStatus = + DeviceRouteController.getBuiltInSpeakerSuitabilityStatus(mContext); + AudioRoutesInfo newAudioRoutes = null; try { newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver); @@ -165,19 +170,28 @@ import java.util.Objects; } synchronized (this) { - return new MediaRoute2Info.Builder( - DEVICE_ROUTE_ID, mContext.getResources().getText(name).toString()) - .setVolumeHandling(mAudioManager.isVolumeFixed() - ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED - : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE) - .setVolume(mDeviceVolume) - .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)) - .setType(type) - .addFeature(FEATURE_LIVE_AUDIO) - .addFeature(FEATURE_LIVE_VIDEO) - .addFeature(FEATURE_LOCAL_PLAYBACK) - .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED) - .build(); + MediaRoute2Info.Builder builder = + new MediaRoute2Info.Builder( + DEVICE_ROUTE_ID, + mContext.getResources().getText(name).toString()) + .setVolumeHandling( + mAudioManager.isVolumeFixed() + ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED + : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE) + .setVolume(mDeviceVolume) + .setVolumeMax( + mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)) + .setType(type) + .addFeature(FEATURE_LIVE_AUDIO) + .addFeature(FEATURE_LIVE_VIDEO) + .addFeature(FEATURE_LOCAL_PLAYBACK) + .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED); + + if (type == TYPE_BUILTIN_SPEAKER) { + builder.setSuitabilityStatus(mBuiltInSpeakerSuitabilityStatus); + } + + return builder.build(); } } diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java index 8149847d70c0..1bc2a5eb1351 100644 --- a/services/core/java/com/android/server/media/MediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java @@ -24,6 +24,7 @@ import android.media.MediaRoute2ProviderInfo; import android.media.RouteDiscoveryPreference; import android.media.RoutingSessionInfo; import android.os.Bundle; +import android.os.UserHandle; import com.android.internal.annotations.GuardedBy; @@ -54,8 +55,15 @@ abstract class MediaRoute2Provider { mCallback = callback; } - public abstract void requestCreateSession(long requestId, String packageName, String routeId, - @Nullable Bundle sessionHints); + public abstract void requestCreateSession( + long requestId, + String packageName, + String routeId, + @Nullable Bundle sessionHints, + @RoutingSessionInfo.TransferReason int transferReason, + @NonNull UserHandle transferInitiatorUserHandle, + @NonNull String transferInitiatorPackageName); + public abstract void releaseSession(long requestId, String sessionId); public abstract void updateDiscoveryPreference( @@ -63,7 +71,14 @@ abstract class MediaRoute2Provider { public abstract void selectRoute(long requestId, String sessionId, String routeId); public abstract void deselectRoute(long requestId, String sessionId, String routeId); - public abstract void transferToRoute(long requestId, String sessionId, String routeId); + + public abstract void transferToRoute( + long requestId, + @NonNull UserHandle transferInitiatorUserHandle, + @NonNull String transferInitiatorPackageName, + String sessionId, + String routeId, + @RoutingSessionInfo.TransferReason int transferReason); public abstract void setRouteVolume(long requestId, String routeId, int volume); public abstract void setSessionVolume(long requestId, String sessionId, int volume); diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java index 330818ed17ca..ae889d8255c6 100644 --- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java +++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java @@ -98,8 +98,14 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider } @Override - public void requestCreateSession(long requestId, String packageName, String routeId, - Bundle sessionHints) { + public void requestCreateSession( + long requestId, + String packageName, + String routeId, + Bundle sessionHints, + @RoutingSessionInfo.TransferReason int transferReason, + @NonNull UserHandle transferInitiatorUserHandle, + @NonNull String transferInitiatorPackageName) { if (mConnectionReady) { mActiveConnection.requestCreateSession(requestId, packageName, routeId, sessionHints); updateBinding(); @@ -141,7 +147,13 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider } @Override - public void transferToRoute(long requestId, String sessionId, String routeId) { + public void transferToRoute( + long requestId, + @NonNull UserHandle transferInitiatorUserHandle, + @NonNull String transferInitiatorPackageName, + String sessionId, + String routeId, + @RoutingSessionInfo.TransferReason int transferReason) { if (mConnectionReady) { mActiveConnection.transferToRoute(requestId, sessionId, routeId); } @@ -649,6 +661,14 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider + "Disallowed route: " + route); } + + if (route.getSuitabilityStatus() + == MediaRoute2Info.SUITABILITY_STATUS_NOT_SUITABLE_FOR_TRANSFER) { + throw new SecurityException( + "Only the system is allowed to set not suitable for transfer status. " + + "Disallowed route: " + + route); + } } Connection connection = mConnectionRef.get(); diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index 5e18727459c6..38f0df41db04 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -337,18 +337,47 @@ class MediaRouter2ServiceImpl { } } - public void requestCreateSessionWithRouter2(@NonNull IMediaRouter2 router, int requestId, - long managerRequestId, @NonNull RoutingSessionInfo oldSession, - @NonNull MediaRoute2Info route, Bundle sessionHints) { + public void requestCreateSessionWithRouter2( + @NonNull IMediaRouter2 router, + int requestId, + long managerRequestId, + @NonNull RoutingSessionInfo oldSession, + @NonNull MediaRoute2Info route, + Bundle sessionHints, + @Nullable UserHandle transferInitiatorUserHandle, + @Nullable String transferInitiatorPackageName) { Objects.requireNonNull(router, "router must not be null"); Objects.requireNonNull(oldSession, "oldSession must not be null"); Objects.requireNonNull(route, "route must not be null"); + synchronized (mLock) { + if (managerRequestId == MediaRoute2ProviderService.REQUEST_ID_NONE + || transferInitiatorUserHandle == null + || transferInitiatorPackageName == null) { + final IBinder binder = router.asBinder(); + final RouterRecord routerRecord = mAllRouterRecords.get(binder); + + transferInitiatorUserHandle = Binder.getCallingUserHandle(); + if (routerRecord != null) { + transferInitiatorPackageName = routerRecord.mPackageName; + } else { + transferInitiatorPackageName = mContext.getPackageName(); + } + } + } + final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { - requestCreateSessionWithRouter2Locked(requestId, managerRequestId, - router, oldSession, route, sessionHints); + requestCreateSessionWithRouter2Locked( + requestId, + managerRequestId, + transferInitiatorUserHandle, + transferInitiatorPackageName, + router, + oldSession, + route, + sessionHints); } } finally { Binder.restoreCallingIdentity(token); @@ -399,10 +428,11 @@ class MediaRouter2ServiceImpl { throw new IllegalArgumentException("uniqueSessionId must not be empty"); } + UserHandle userHandle = Binder.getCallingUserHandle(); final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { - transferToRouteWithRouter2Locked(router, uniqueSessionId, route); + transferToRouteWithRouter2Locked(router, userHandle, uniqueSessionId, route); } } finally { Binder.restoreCallingIdentity(token); @@ -588,16 +618,28 @@ class MediaRouter2ServiceImpl { } } - public void requestCreateSessionWithManager(@NonNull IMediaRouter2Manager manager, - int requestId, @NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route) { + public void requestCreateSessionWithManager( + @NonNull IMediaRouter2Manager manager, + int requestId, + @NonNull RoutingSessionInfo oldSession, + @NonNull MediaRoute2Info route, + @NonNull UserHandle transferInitiatorUserHandle, + @NonNull String transferInitiatorPackageName) { Objects.requireNonNull(manager, "manager must not be null"); Objects.requireNonNull(oldSession, "oldSession must not be null"); Objects.requireNonNull(route, "route must not be null"); + Objects.requireNonNull(transferInitiatorUserHandle); final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { - requestCreateSessionWithManagerLocked(requestId, manager, oldSession, route); + requestCreateSessionWithManagerLocked( + requestId, + manager, + oldSession, + route, + transferInitiatorUserHandle, + transferInitiatorPackageName); } } finally { Binder.restoreCallingIdentity(token); @@ -640,18 +682,32 @@ class MediaRouter2ServiceImpl { } } - public void transferToRouteWithManager(@NonNull IMediaRouter2Manager manager, int requestId, - @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { + public void transferToRouteWithManager( + @NonNull IMediaRouter2Manager manager, + int requestId, + @NonNull String uniqueSessionId, + @NonNull MediaRoute2Info route, + @NonNull UserHandle transferInitiatorUserHandle, + @NonNull String transferInitiatorPackageName) { Objects.requireNonNull(manager, "manager must not be null"); if (TextUtils.isEmpty(uniqueSessionId)) { throw new IllegalArgumentException("uniqueSessionId must not be empty"); } Objects.requireNonNull(route, "route must not be null"); + Objects.requireNonNull(transferInitiatorUserHandle); + Objects.requireNonNull(transferInitiatorPackageName); final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { - transferToRouteWithManagerLocked(requestId, manager, uniqueSessionId, route); + transferToRouteWithManagerLocked( + requestId, + manager, + uniqueSessionId, + route, + RoutingSessionInfo.TRANSFER_REASON_SYSTEM_REQUEST, + transferInitiatorUserHandle, + transferInitiatorPackageName); } } finally { Binder.restoreCallingIdentity(token); @@ -1038,9 +1094,15 @@ class MediaRouter2ServiceImpl { } @GuardedBy("mLock") - private void requestCreateSessionWithRouter2Locked(int requestId, long managerRequestId, - @NonNull IMediaRouter2 router, @NonNull RoutingSessionInfo oldSession, - @NonNull MediaRoute2Info route, @Nullable Bundle sessionHints) { + private void requestCreateSessionWithRouter2Locked( + int requestId, + long managerRequestId, + @NonNull UserHandle transferInitiatorUserHandle, + @NonNull String transferInitiatorPackageName, + @NonNull IMediaRouter2 router, + @NonNull RoutingSessionInfo oldSession, + @NonNull MediaRoute2Info route, + @Nullable Bundle sessionHints) { final IBinder binder = router.asBinder(); final RouterRecord routerRecord = mAllRouterRecords.get(binder); @@ -1114,9 +1176,16 @@ class MediaRouter2ServiceImpl { long uniqueRequestId = toUniqueRequestId(routerRecord.mRouterId, requestId); routerRecord.mUserRecord.mHandler.sendMessage( - obtainMessage(UserHandler::requestCreateSessionWithRouter2OnHandler, + obtainMessage( + UserHandler::requestCreateSessionWithRouter2OnHandler, routerRecord.mUserRecord.mHandler, - uniqueRequestId, managerRequestId, routerRecord, oldSession, route, + uniqueRequestId, + managerRequestId, + transferInitiatorUserHandle, + transferInitiatorPackageName, + routerRecord, + oldSession, + route, sessionHints)); } @@ -1165,8 +1234,11 @@ class MediaRouter2ServiceImpl { } @GuardedBy("mLock") - private void transferToRouteWithRouter2Locked(@NonNull IMediaRouter2 router, - @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { + private void transferToRouteWithRouter2Locked( + @NonNull IMediaRouter2 router, + @NonNull UserHandle transferInitiatorUserHandle, + @NonNull String uniqueSessionId, + @NonNull MediaRoute2Info route) { final IBinder binder = router.asBinder(); final RouterRecord routerRecord = mAllRouterRecords.get(binder); @@ -1191,9 +1263,16 @@ class MediaRouter2ServiceImpl { routerRecord, toOriginalRequestId(DUMMY_REQUEST_ID))); } else { routerRecord.mUserRecord.mHandler.sendMessage( - obtainMessage(UserHandler::transferToRouteOnHandler, + obtainMessage( + UserHandler::transferToRouteOnHandler, routerRecord.mUserRecord.mHandler, - DUMMY_REQUEST_ID, routerRecord, uniqueSessionId, route)); + DUMMY_REQUEST_ID, + transferInitiatorUserHandle, + routerRecord.mPackageName, + routerRecord, + uniqueSessionId, + route, + RoutingSessionInfo.TRANSFER_REASON_APP)); } } @@ -1416,9 +1495,13 @@ class MediaRouter2ServiceImpl { } @GuardedBy("mLock") - private void requestCreateSessionWithManagerLocked(int requestId, - @NonNull IMediaRouter2Manager manager, @NonNull RoutingSessionInfo oldSession, - @NonNull MediaRoute2Info route) { + private void requestCreateSessionWithManagerLocked( + int requestId, + @NonNull IMediaRouter2Manager manager, + @NonNull RoutingSessionInfo oldSession, + @NonNull MediaRoute2Info route, + @NonNull UserHandle transferInitiatorUserHandle, + @NonNull String transferInitiatorPackageName) { ManagerRecord managerRecord = mAllManagerRecords.get(manager.asBinder()); if (managerRecord == null) { return; @@ -1464,9 +1547,16 @@ class MediaRouter2ServiceImpl { // Before requesting to the provider, get session hints from the media router. // As a return, media router will request to create a session. routerRecord.mUserRecord.mHandler.sendMessage( - obtainMessage(UserHandler::requestRouterCreateSessionOnHandler, + obtainMessage( + UserHandler::requestRouterCreateSessionOnHandler, routerRecord.mUserRecord.mHandler, - uniqueRequestId, routerRecord, managerRecord, oldSession, route)); + uniqueRequestId, + routerRecord, + managerRecord, + oldSession, + route, + transferInitiatorUserHandle, + transferInitiatorPackageName)); } @GuardedBy("mLock") @@ -1521,9 +1611,14 @@ class MediaRouter2ServiceImpl { } @GuardedBy("mLock") - private void transferToRouteWithManagerLocked(int requestId, + private void transferToRouteWithManagerLocked( + int requestId, @NonNull IMediaRouter2Manager manager, - @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { + @NonNull String uniqueSessionId, + @NonNull MediaRoute2Info route, + @RoutingSessionInfo.TransferReason int transferReason, + @NonNull UserHandle transferInitiatorUserHandle, + @NonNull String transferInitiatorPackageName) { final IBinder binder = manager.asBinder(); ManagerRecord managerRecord = mAllManagerRecords.get(binder); @@ -1541,9 +1636,16 @@ class MediaRouter2ServiceImpl { long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId); managerRecord.mUserRecord.mHandler.sendMessage( - obtainMessage(UserHandler::transferToRouteOnHandler, + obtainMessage( + UserHandler::transferToRouteOnHandler, managerRecord.mUserRecord.mHandler, - uniqueRequestId, routerRecord, uniqueSessionId, route)); + uniqueRequestId, + transferInitiatorUserHandle, + transferInitiatorPackageName, + routerRecord, + uniqueSessionId, + route, + transferReason)); } @GuardedBy("mLock") @@ -1850,6 +1952,19 @@ class MediaRouter2ServiceImpl { } } + public void notifySessionCreated(int requestId, @NonNull RoutingSessionInfo sessionInfo) { + try { + mRouter.notifySessionCreated( + requestId, maybeClearTransferInitiatorIdentity(sessionInfo)); + } catch (RemoteException ex) { + Slog.w( + TAG, + "Failed to notify router of the session creation." + + " Router probably died.", + ex); + } + } + /** * Sends the corresponding router an update for the given session. * @@ -1857,12 +1972,27 @@ class MediaRouter2ServiceImpl { */ public void notifySessionInfoChanged(RoutingSessionInfo sessionInfo) { try { - mRouter.notifySessionInfoChanged(sessionInfo); + mRouter.notifySessionInfoChanged(maybeClearTransferInitiatorIdentity(sessionInfo)); } catch (RemoteException ex) { Slog.w(TAG, "Failed to notify session info changed. Router probably died.", ex); } } + private RoutingSessionInfo maybeClearTransferInitiatorIdentity( + @NonNull RoutingSessionInfo sessionInfo) { + UserHandle transferInitiatorUserHandle = sessionInfo.getTransferInitiatorUserHandle(); + String transferInitiatorPackageName = sessionInfo.getTransferInitiatorPackageName(); + + if (!Objects.equals(UserHandle.of(mUserRecord.mUserId), transferInitiatorUserHandle) + || !Objects.equals(mPackageName, transferInitiatorPackageName)) { + return new RoutingSessionInfo.Builder(sessionInfo) + .setTransferInitiator(null, null) + .build(); + } + + return sessionInfo; + } + /** * Returns a filtered copy of {@code routes} that contains only the routes that are {@link * MediaRoute2Info#isVisibleTo visible} to the router corresponding to this record. @@ -2307,9 +2437,14 @@ class MediaRouter2ServiceImpl { return -1; } - private void requestRouterCreateSessionOnHandler(long uniqueRequestId, - @NonNull RouterRecord routerRecord, @NonNull ManagerRecord managerRecord, - @NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route) { + private void requestRouterCreateSessionOnHandler( + long uniqueRequestId, + @NonNull RouterRecord routerRecord, + @NonNull ManagerRecord managerRecord, + @NonNull RoutingSessionInfo oldSession, + @NonNull MediaRoute2Info route, + @NonNull UserHandle transferInitiatorUserHandle, + @NonNull String transferInitiatorPackageName) { try { if (route.isSystemRoute() && !routerRecord.hasSystemRoutingPermission()) { // The router lacks permission to modify system routing, so we hide system @@ -2317,7 +2452,11 @@ class MediaRouter2ServiceImpl { route = mSystemProvider.getDefaultRoute(); } routerRecord.mRouter.requestCreateSessionByManager( - uniqueRequestId, oldSession, route); + uniqueRequestId, + oldSession, + route, + transferInitiatorUserHandle, + transferInitiatorPackageName); } catch (RemoteException ex) { Slog.w(TAG, "getSessionHintsForCreatingSessionOnHandler: " + "Failed to request. Router probably died.", ex); @@ -2326,10 +2465,15 @@ class MediaRouter2ServiceImpl { } } - private void requestCreateSessionWithRouter2OnHandler(long uniqueRequestId, - long managerRequestId, @NonNull RouterRecord routerRecord, + private void requestCreateSessionWithRouter2OnHandler( + long uniqueRequestId, + long managerRequestId, + @NonNull UserHandle transferInitiatorUserHandle, + @NonNull String transferInitiatorPackageName, + @NonNull RouterRecord routerRecord, @NonNull RoutingSessionInfo oldSession, - @NonNull MediaRoute2Info route, @Nullable Bundle sessionHints) { + @NonNull MediaRoute2Info route, + @Nullable Bundle sessionHints) { final MediaRoute2Provider provider = findProvider(route.getProviderId()); if (provider == null) { @@ -2345,8 +2489,19 @@ class MediaRouter2ServiceImpl { managerRequestId, oldSession, route); mSessionCreationRequests.add(request); - provider.requestCreateSession(uniqueRequestId, routerRecord.mPackageName, - route.getOriginalId(), sessionHints); + int transferReason = RoutingSessionInfo.TRANSFER_REASON_APP; + if (managerRequestId != MediaRoute2ProviderService.REQUEST_ID_NONE) { + transferReason = RoutingSessionInfo.TRANSFER_REASON_SYSTEM_REQUEST; + } + + provider.requestCreateSession( + uniqueRequestId, + routerRecord.mPackageName, + route.getOriginalId(), + sessionHints, + transferReason, + transferInitiatorUserHandle, + transferInitiatorPackageName); } // routerRecord can be null if the session is system's or RCN. @@ -2386,9 +2541,14 @@ class MediaRouter2ServiceImpl { } // routerRecord can be null if the session is system's or RCN. - private void transferToRouteOnHandler(long uniqueRequestId, + private void transferToRouteOnHandler( + long uniqueRequestId, + @NonNull UserHandle transferInitiatorUserHandle, + @NonNull String transferInitiatorPackageName, @Nullable RouterRecord routerRecord, - @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { + @NonNull String uniqueSessionId, + @NonNull MediaRoute2Info route, + @RoutingSessionInfo.TransferReason int transferReason) { if (!checkArgumentsForSessionControl(routerRecord, uniqueSessionId, route, "transferring to")) { return; @@ -2399,8 +2559,13 @@ class MediaRouter2ServiceImpl { if (provider == null) { return; } - provider.transferToRoute(uniqueRequestId, getOriginalId(uniqueSessionId), - route.getOriginalId()); + provider.transferToRoute( + uniqueRequestId, + transferInitiatorUserHandle, + transferInitiatorPackageName, + getOriginalId(uniqueSessionId), + route.getOriginalId(), + transferReason); } // routerRecord is null if and only if the session is created without the request, which @@ -2535,6 +2700,7 @@ class MediaRouter2ServiceImpl { // session info from them. sessionInfo = mSystemProvider.getDefaultSessionInfo(); } + // TODO: b/279555229 - replace with matchingRequest.mRouterRecord.notifySessionCreated. notifySessionCreatedToRouter( matchingRequest.mRouterRecord, toOriginalRequestId(uniqueRequestId), @@ -2648,12 +2814,7 @@ class MediaRouter2ServiceImpl { private void notifySessionCreatedToRouter(@NonNull RouterRecord routerRecord, int requestId, @NonNull RoutingSessionInfo sessionInfo) { - try { - routerRecord.mRouter.notifySessionCreated(requestId, sessionInfo); - } catch (RemoteException ex) { - Slog.w(TAG, "Failed to notify router of the session creation." - + " Router probably died.", ex); - } + routerRecord.notifySessionCreated(requestId, sessionInfo); } private void notifySessionCreationFailedToRouter(@NonNull RouterRecord routerRecord, diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java index e562b3f0845c..7dd13142f52e 100644 --- a/services/core/java/com/android/server/media/MediaRouterService.java +++ b/services/core/java/com/android/server/media/MediaRouterService.java @@ -461,11 +461,24 @@ public final class MediaRouterService extends IMediaRouterService.Stub // Binder call @Override - public void requestCreateSessionWithRouter2(IMediaRouter2 router, int requestId, - long managerRequestId, RoutingSessionInfo oldSession, - MediaRoute2Info route, Bundle sessionHints) { - mService2.requestCreateSessionWithRouter2(router, requestId, managerRequestId, - oldSession, route, sessionHints); + public void requestCreateSessionWithRouter2( + IMediaRouter2 router, + int requestId, + long managerRequestId, + RoutingSessionInfo oldSession, + MediaRoute2Info route, + Bundle sessionHints, + @Nullable UserHandle transferInitiatorUserHandle, + @Nullable String transferInitiatorPackageName) { + mService2.requestCreateSessionWithRouter2( + router, + requestId, + managerRequestId, + oldSession, + route, + sessionHints, + transferInitiatorUserHandle, + transferInitiatorPackageName); } // Binder call @@ -580,9 +593,20 @@ public final class MediaRouterService extends IMediaRouterService.Stub // Binder call @Override - public void requestCreateSessionWithManager(IMediaRouter2Manager manager, - int requestId, RoutingSessionInfo oldSession, MediaRoute2Info route) { - mService2.requestCreateSessionWithManager(manager, requestId, oldSession, route); + public void requestCreateSessionWithManager( + IMediaRouter2Manager manager, + int requestId, + RoutingSessionInfo oldSession, + MediaRoute2Info route, + UserHandle transferInitiatorUserHandle, + String transferInitiatorPackageName) { + mService2.requestCreateSessionWithManager( + manager, + requestId, + oldSession, + route, + transferInitiatorUserHandle, + transferInitiatorPackageName); } // Binder call @@ -601,9 +625,20 @@ public final class MediaRouterService extends IMediaRouterService.Stub // Binder call @Override - public void transferToRouteWithManager(IMediaRouter2Manager manager, int requestId, - String sessionId, MediaRoute2Info route) { - mService2.transferToRouteWithManager(manager, requestId, sessionId, route); + public void transferToRouteWithManager( + IMediaRouter2Manager manager, + int requestId, + String sessionId, + MediaRoute2Info route, + UserHandle transferInitiatorUserHandle, + String transferInitiatorPackageName) { + mService2.transferToRouteWithManager( + manager, + requestId, + sessionId, + route, + transferInitiatorUserHandle, + transferInitiatorPackageName); } // Binder call diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java index 9d151c27e7c7..f7210dd1ef70 100644 --- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java @@ -16,6 +16,7 @@ package com.android.server.media; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -26,6 +27,7 @@ import android.media.AudioManager; import android.media.MediaRoute2Info; import android.media.MediaRoute2ProviderInfo; import android.media.MediaRoute2ProviderService; +import android.media.MediaRouter2Utils; import android.media.RouteDiscoveryPreference; import android.media.RoutingSessionInfo; import android.os.Bundle; @@ -39,6 +41,7 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.media.flags.Flags; +import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.Set; @@ -79,6 +82,10 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { @GuardedBy("mRequestLock") private volatile SessionCreationRequest mPendingSessionCreationRequest; + private final Object mTransferLock = new Object(); + @GuardedBy("mTransferLock") + @Nullable private volatile SessionCreationRequest mPendingTransferRequest; + SystemMediaRoute2Provider(Context context, UserHandle user) { super(COMPONENT_NAME); mIsSystemRouteProvider = true; @@ -146,17 +153,30 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } @Override - public void requestCreateSession(long requestId, String packageName, String routeId, - Bundle sessionHints) { + public void requestCreateSession( + long requestId, + String packageName, + String routeId, + Bundle sessionHints, + @RoutingSessionInfo.TransferReason int transferReason, + @NonNull UserHandle transferInitiatorUserHandle, + @NonNull String transferInitiatorPackageName) { // Assume a router without MODIFY_AUDIO_ROUTING permission can't request with // a route ID different from the default route ID. The service should've filtered. if (TextUtils.equals(routeId, MediaRoute2Info.ROUTE_ID_DEFAULT)) { mCallback.onSessionCreated(this, requestId, mDefaultSessionInfo); return; } - if (TextUtils.equals(routeId, mSelectedRouteId)) { - mCallback.onSessionCreated(this, requestId, mSessionInfos.get(0)); - return; + + if (!Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) { + if (TextUtils.equals(routeId, mSelectedRouteId)) { + RoutingSessionInfo currentSessionInfo; + synchronized (mLock) { + currentSessionInfo = mSessionInfos.get(0); + } + mCallback.onSessionCreated(this, requestId, currentSessionInfo); + return; + } } synchronized (mRequestLock) { @@ -165,10 +185,23 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { mCallback.onRequestFailed(this, mPendingSessionCreationRequest.mRequestId, MediaRoute2ProviderService.REASON_UNKNOWN_ERROR); } - mPendingSessionCreationRequest = new SessionCreationRequest(requestId, routeId); + mPendingSessionCreationRequest = + new SessionCreationRequest( + requestId, + routeId, + RoutingSessionInfo.TRANSFER_REASON_FALLBACK, + transferInitiatorUserHandle, + transferInitiatorPackageName); } - transferToRoute(requestId, SYSTEM_SESSION_ID, routeId); + // Only unprivileged routers call this method, therefore we use TRANSFER_REASON_APP. + transferToRoute( + requestId, + transferInitiatorUserHandle, + transferInitiatorPackageName, + SYSTEM_SESSION_ID, + routeId, + transferReason); } @Override @@ -193,12 +226,31 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } @Override - public void transferToRoute(long requestId, String sessionId, String routeId) { + public void transferToRoute( + long requestId, + @NonNull UserHandle transferInitiatorUserHandle, + @NonNull String transferInitiatorPackageName, + String sessionId, + String routeId, + @RoutingSessionInfo.TransferReason int transferReason) { if (TextUtils.equals(routeId, MediaRoute2Info.ROUTE_ID_DEFAULT)) { // The currently selected route is the default route. Log.w(TAG, "Ignoring transfer to " + MediaRoute2Info.ROUTE_ID_DEFAULT); return; } + + if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) { + synchronized (mTransferLock) { + mPendingTransferRequest = + new SessionCreationRequest( + requestId, + routeId, + transferReason, + transferInitiatorUserHandle, + transferInitiatorPackageName); + } + } + MediaRoute2Info selectedDeviceRoute = mDeviceRouteController.getSelectedRoute(); boolean isAvailableDeviceRoute = mDeviceRouteController.getAvailableRoutes().stream() @@ -218,6 +270,11 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { mDeviceRouteController.transferTo(null); mBluetoothRouteController.transferTo(routeId); } + + if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses() + && updateSessionInfosIfNeeded()) { + notifySessionInfoUpdated(); + } } @Override @@ -322,9 +379,11 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { MediaRoute2Info selectedDeviceRoute = mDeviceRouteController.getSelectedRoute(); MediaRoute2Info selectedRoute = selectedDeviceRoute; MediaRoute2Info selectedBtRoute = mBluetoothRouteController.getSelectedRoute(); + List<String> transferableRoutes = new ArrayList<>(); + if (selectedBtRoute != null) { selectedRoute = selectedBtRoute; - builder.addTransferableRoute(selectedDeviceRoute.getId()); + transferableRoutes.add(selectedDeviceRoute.getId()); } mSelectedRouteId = selectedRoute.getId(); mDefaultRoute = @@ -337,12 +396,54 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { for (MediaRoute2Info route : mDeviceRouteController.getAvailableRoutes()) { String routeId = route.getId(); if (!mSelectedRouteId.equals(routeId)) { - builder.addTransferableRoute(routeId); + transferableRoutes.add(routeId); } } } for (MediaRoute2Info route : mBluetoothRouteController.getTransferableRoutes()) { - builder.addTransferableRoute(route.getId()); + transferableRoutes.add(route.getId()); + } + + for (String route : transferableRoutes) { + builder.addTransferableRoute(route); + } + + if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) { + int transferReason = RoutingSessionInfo.TRANSFER_REASON_FALLBACK; + UserHandle transferInitiatorUserHandle = null; + String transferInitiatorPackageName = null; + + if (oldSessionInfo != null + && containsSelectedRouteWithId(oldSessionInfo, selectedRoute.getId())) { + transferReason = oldSessionInfo.getTransferReason(); + transferInitiatorUserHandle = oldSessionInfo.getTransferInitiatorUserHandle(); + transferInitiatorPackageName = oldSessionInfo.getTransferInitiatorPackageName(); + } + + synchronized (mTransferLock) { + if (mPendingTransferRequest != null) { + boolean isTransferringToTheSelectedRoute = + mPendingTransferRequest.isTargetRoute(selectedRoute); + boolean canBePotentiallyTransferred = + mPendingTransferRequest.isInsideOfRoutesList(transferableRoutes); + + if (isTransferringToTheSelectedRoute) { + transferReason = mPendingTransferRequest.mTransferReason; + transferInitiatorUserHandle = + mPendingTransferRequest.mTransferInitiatorUserHandle; + transferInitiatorPackageName = + mPendingTransferRequest.mTransferInitiatorPackageName; + + mPendingTransferRequest = null; + } else if (!canBePotentiallyTransferred) { + mPendingTransferRequest = null; + } + } + } + + builder.setTransferReason(transferReason) + .setTransferInitiator( + transferInitiatorUserHandle, transferInitiatorPackageName); } RoutingSessionInfo newSessionInfo = builder.setProviderId(mUniqueId).build(); @@ -424,6 +525,22 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { return false; } + private boolean containsSelectedRouteWithId( + @Nullable RoutingSessionInfo sessionInfo, @NonNull String selectedRouteId) { + if (sessionInfo == null) { + return false; + } + + List<String> selectedRoutes = sessionInfo.getSelectedRoutes(); + + if (selectedRoutes.size() != 1) { + throw new IllegalStateException("Selected routes list should contain only 1 route id."); + } + + String oldSelectedRouteId = MediaRouter2Utils.getOriginalId(selectedRoutes.get(0)); + return oldSelectedRouteId != null && oldSelectedRouteId.equals(selectedRouteId); + } + void publishProviderState() { updateProviderState(); notifyProviderState(); @@ -452,12 +569,47 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } private static class SessionCreationRequest { - final long mRequestId; - final String mRouteId; + private final long mRequestId; + @NonNull private final String mRouteId; + + @RoutingSessionInfo.TransferReason private final int mTransferReason; + + @NonNull private final UserHandle mTransferInitiatorUserHandle; + @NonNull private final String mTransferInitiatorPackageName; + + SessionCreationRequest( + long requestId, + @NonNull String routeId, + @RoutingSessionInfo.TransferReason int transferReason, + @NonNull UserHandle transferInitiatorUserHandle, + @NonNull String transferInitiatorPackageName) { + mRequestId = requestId; + mRouteId = routeId; + mTransferReason = transferReason; + mTransferInitiatorUserHandle = transferInitiatorUserHandle; + mTransferInitiatorPackageName = transferInitiatorPackageName; + } + + private boolean isTargetRoute(@Nullable MediaRoute2Info route2Info) { + if (route2Info == null) { + return false; + } + + return isTargetRoute(route2Info.getId()); + } + + private boolean isTargetRoute(@Nullable String routeId) { + return mRouteId.equals(routeId); + } + + private boolean isInsideOfRoutesList(@NonNull List<String> routesList) { + for (String routeId : routesList) { + if (isTargetRoute(routeId)) { + return true; + } + } - SessionCreationRequest(long requestId, String routeId) { - this.mRequestId = requestId; - this.mRouteId = routeId; + return false; } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 135a467cc6b0..f49d51c70c1d 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -5617,7 +5617,8 @@ public class NotificationManagerService extends SystemService { return !isCompatChangeEnabled || isCallerSystemOrSystemUi() || hasCompanionDevice(callingPkg, UserHandle.getUserId(callingUid), - AssociationRequest.DEVICE_PROFILE_WATCH); + Set.of(AssociationRequest.DEVICE_PROFILE_WATCH, + AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION)); } private void enforcePolicyAccess(String pkg, String method) { @@ -10800,7 +10801,7 @@ public class NotificationManagerService extends SystemService { } private boolean hasCompanionDevice(String pkg, @UserIdInt int userId, - @Nullable @AssociationRequest.DeviceProfile String withDeviceProfile) { + @Nullable Set</* @AssociationRequest.DeviceProfile */ String> withDeviceProfiles) { if (mCompanionManager == null) { mCompanionManager = getCompanionManager(); } @@ -10812,7 +10813,7 @@ public class NotificationManagerService extends SystemService { try { List<AssociationInfo> associations = mCompanionManager.getAssociations(pkg, userId); for (AssociationInfo association : associations) { - if (withDeviceProfile == null || withDeviceProfile.equals( + if (withDeviceProfiles == null || withDeviceProfiles.contains( association.getDeviceProfile())) { return true; } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 1ce87a7b6af3..145eb3b8464c 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -125,6 +125,7 @@ import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND; import static android.view.WindowManager.TRANSIT_OLD_UNSET; import static android.view.WindowManager.TRANSIT_RELAUNCH; +import static android.window.TransitionInfo.FLAGS_IS_OCCLUDED_NO_ANIMATION; import static android.window.TransitionInfo.FLAG_IS_OCCLUDED; import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; @@ -683,6 +684,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private final WindowState.UpdateReportedVisibilityResults mReportedVisibilityResults = new WindowState.UpdateReportedVisibilityResults(); + // TODO(b/317000737): Replace it with visibility states lookup. int mTransitionChangeFlags; /** Whether we need to setup the animation to animate only within the letterbox. */ @@ -5468,8 +5470,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Defer committing visibility until transition starts. if (isCollecting) { // It may be occluded by the activity above that calls convertFromTranslucent(). - if (!visible && mTransitionController.inPlayingTransition(this)) { - mTransitionChangeFlags |= FLAG_IS_OCCLUDED; + // Or it may be restoring transient launch to invisible when finishing transition. + if (!visible) { + if (mTransitionController.inPlayingTransition(this)) { + mTransitionChangeFlags |= FLAG_IS_OCCLUDED; + } else if (mTransitionController.inFinishingTransition(this)) { + mTransitionChangeFlags |= FLAGS_IS_OCCLUDED_NO_ANIMATION; + } } return; } diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java index 24c953b127bc..eed46fee1ae1 100644 --- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java +++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java @@ -36,6 +36,7 @@ import static com.android.window.flags.Flags.balShowToastsBlocked; import static com.android.server.wm.PendingRemoteAnimationRegistry.TIMEOUT_MS; import static java.lang.annotation.RetentionPolicy.SOURCE; +import static java.util.Objects.requireNonNull; import android.annotation.IntDef; import android.annotation.NonNull; @@ -152,36 +153,25 @@ public class BackgroundActivityStartController { static final int BAL_ALLOW_SDK_SANDBOX = 10; static String balCodeToString(@BalCode int balCode) { - switch (balCode) { - case BAL_ALLOW_ALLOWLISTED_COMPONENT: - return "BAL_ALLOW_ALLOWLISTED_COMPONENT"; - case BAL_ALLOW_ALLOWLISTED_UID: - return "BAL_ALLOW_ALLOWLISTED_UID"; - case BAL_ALLOW_DEFAULT: - return "BAL_ALLOW_DEFAULT"; - case BAL_ALLOW_FOREGROUND: - return "BAL_ALLOW_FOREGROUND"; - case BAL_ALLOW_GRACE_PERIOD: - return "BAL_ALLOW_GRACE_PERIOD"; - case BAL_ALLOW_PENDING_INTENT: - return "BAL_ALLOW_PENDING_INTENT"; - case BAL_ALLOW_PERMISSION: - return "BAL_ALLOW_PERMISSION"; - case BAL_ALLOW_SAW_PERMISSION: - return "BAL_ALLOW_SAW_PERMISSION"; - case BAL_ALLOW_SDK_SANDBOX: - return "BAL_ALLOW_SDK_SANDBOX"; - case BAL_ALLOW_VISIBLE_WINDOW: - return "BAL_ALLOW_VISIBLE_WINDOW"; - case BAL_BLOCK: - return "BAL_BLOCK"; - default: - throw new IllegalArgumentException("Unexpected value: " + balCode); - } + return switch (balCode) { + case BAL_ALLOW_ALLOWLISTED_COMPONENT -> "BAL_ALLOW_ALLOWLISTED_COMPONENT"; + case BAL_ALLOW_ALLOWLISTED_UID -> "BAL_ALLOW_ALLOWLISTED_UID"; + case BAL_ALLOW_DEFAULT -> "BAL_ALLOW_DEFAULT"; + case BAL_ALLOW_FOREGROUND -> "BAL_ALLOW_FOREGROUND"; + case BAL_ALLOW_GRACE_PERIOD -> "BAL_ALLOW_GRACE_PERIOD"; + case BAL_ALLOW_PENDING_INTENT -> "BAL_ALLOW_PENDING_INTENT"; + case BAL_ALLOW_PERMISSION -> "BAL_ALLOW_PERMISSION"; + case BAL_ALLOW_SAW_PERMISSION -> "BAL_ALLOW_SAW_PERMISSION"; + case BAL_ALLOW_SDK_SANDBOX -> "BAL_ALLOW_SDK_SANDBOX"; + case BAL_ALLOW_VISIBLE_WINDOW -> "BAL_ALLOW_VISIBLE_WINDOW"; + case BAL_BLOCK -> "BAL_BLOCK"; + default -> throw new IllegalArgumentException("Unexpected value: " + balCode); + }; } @GuardedBy("mService.mGlobalLock") - private HashMap<Integer, FinishedActivityEntry> mTaskIdToFinishedActivity = new HashMap<>(); + private final HashMap<Integer, FinishedActivityEntry> mTaskIdToFinishedActivity = + new HashMap<>(); @GuardedBy("mService.mGlobalLock") private FinishedActivityEntry mTopFinishedActivity = null; @@ -467,9 +457,8 @@ public class BackgroundActivityStartController { return !blocks(); } - BalVerdict setOnlyCreatorAllows(boolean onlyCreatorAllows) { + void setOnlyCreatorAllows(boolean onlyCreatorAllows) { mOnlyCreatorAllows = onlyCreatorAllows; - return this; } boolean onlyCreatorAllows() { @@ -481,10 +470,6 @@ public class BackgroundActivityStartController { return this; } - private boolean isBasedOnRealCaller() { - return mBasedOnRealCaller; - } - public String toString() { StringBuilder builder = new StringBuilder(); builder.append(balCodeToString(mCode)); @@ -583,15 +568,14 @@ public class BackgroundActivityStartController { BalVerdict resultForCaller = checkBackgroundActivityStartAllowedByCaller(state); if (!state.hasRealCaller()) { - BalVerdict resultForRealCaller = null; // nothing to compute if (resultForCaller.allows()) { if (DEBUG_ACTIVITY_STARTS) { Slog.d(TAG, "Background activity start allowed. " - + state.dump(resultForCaller, resultForRealCaller)); + + state.dump(resultForCaller, resultForCaller)); } return statsLog(resultForCaller, state); } - return abortLaunch(state, resultForCaller, resultForRealCaller); + return abortLaunch(state, resultForCaller, resultForCaller); } // The realCaller result is only calculated for PendingIntents (indicated by a valid @@ -910,7 +894,7 @@ public class BackgroundActivityStartController { /** * Check if the app allows BAL. - * + * <p> * See {@link BackgroundLaunchProcessController#areBackgroundActivityStartsAllowed(int, int, * String, int, boolean, boolean, boolean, long, long, long)} for details on the * exceptions. @@ -1277,7 +1261,7 @@ public class BackgroundActivityStartController { * 2. Or top of an adjacent task fragment to (1) * <p> * The 'sourceRecord' can be considered top even if it is 'finishing' - * + * <p> * Returns a class where the elements are: * <pre> * shouldBlockActivityStart: {@code true} if we should actually block the transition (takes into @@ -1340,7 +1324,7 @@ public class BackgroundActivityStartController { /** * Determines if a source is allowed to add or remove activities from the task, * if the current ActivityRecord is above it in the stack - * + * <p> * A transition is blocked ({@code false} returned) if all of the following are met: * <pre> * 1. The source activity and the current activity record belong to different apps @@ -1485,8 +1469,8 @@ public class BackgroundActivityStartController { if (code == BAL_ALLOW_PENDING_INTENT && (callingUid == Process.SYSTEM_UID || realCallingUid == Process.SYSTEM_UID)) { - String activityName = - intent != null ? intent.getComponent().flattenToShortString() : ""; + String activityName = intent != null + ? requireNonNull(intent.getComponent()).flattenToShortString() : ""; FrameworkStatsLog.write(FrameworkStatsLog.BAL_ALLOWED, activityName, BAL_ALLOW_PENDING_INTENT, diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index f020bfa8cbc7..3117db5f27f0 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -43,6 +43,7 @@ import static android.view.WindowManager.TransitionFlags; import static android.view.WindowManager.TransitionType; import static android.view.WindowManager.transitTypeToString; import static android.window.TaskFragmentAnimationParams.DEFAULT_ANIMATION_BACKGROUND_COLOR; +import static android.window.TransitionInfo.FLAGS_IS_OCCLUDED_NO_ANIMATION; import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS; import static android.window.TransitionInfo.FLAG_FILLS_TASK; import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY; @@ -3067,6 +3068,10 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { Slog.e(TAG, "Unexpected launch-task-behind operation in shell transition"); flags |= FLAG_TASK_LAUNCHING_BEHIND; } + if ((topActivity.mTransitionChangeFlags & FLAGS_IS_OCCLUDED_NO_ANIMATION) + == FLAGS_IS_OCCLUDED_NO_ANIMATION) { + flags |= FLAGS_IS_OCCLUDED_NO_ANIMATION; + } } if (task.voiceSession != null) { flags |= FLAG_IS_VOICE_INTERACTION; diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 9c21e4c0121e..ec4bdf91bcca 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -202,7 +202,6 @@ import android.os.Build; import android.os.Debug; import android.os.IBinder; import android.os.PowerManager; -import android.os.PowerManager.WakeReason; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.SystemClock; @@ -702,11 +701,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP */ private final Region mTapExcludeRegion = new Region(); - /** - * Used for testing because the real PowerManager is final. - */ - private PowerManagerWrapper mPowerManagerWrapper; - private static final StringBuilder sTmpSB = new StringBuilder(); /** @@ -1061,34 +1055,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return mOnBackInvokedCallbackInfo; } - interface PowerManagerWrapper { - void wakeUp(long time, @WakeReason int reason, String details); - - boolean isInteractive(); - - } - WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token, WindowState parentWindow, int appOp, WindowManager.LayoutParams a, int viewVisibility, int ownerId, int showUserId, boolean ownerCanAddInternalSystemWindow) { - this(service, s, c, token, parentWindow, appOp, a, viewVisibility, ownerId, showUserId, - ownerCanAddInternalSystemWindow, new PowerManagerWrapper() { - @Override - public void wakeUp(long time, @WakeReason int reason, String details) { - service.mPowerManager.wakeUp(time, reason, details); - } - - @Override - public boolean isInteractive() { - return service.mPowerManager.isInteractive(); - } - }); - } - - WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token, - WindowState parentWindow, int appOp, WindowManager.LayoutParams a, int viewVisibility, - int ownerId, int showUserId, boolean ownerCanAddInternalSystemWindow, - PowerManagerWrapper powerManagerWrapper) { super(service); mTmpTransaction = service.mTransactionFactory.get(); mSession = s; @@ -1106,7 +1075,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mViewVisibility = viewVisibility; mPolicy = mWmService.mPolicy; mContext = mWmService.mContext; - mPowerManagerWrapper = powerManagerWrapper; mForceSeamlesslyRotate = token.mRoundedCornerOverlay; mInputWindowHandle = new InputWindowHandleWrapper(new InputWindowHandle( mActivityRecord != null @@ -2831,12 +2799,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP boolean canTurnScreenOn = mActivityRecord == null || mActivityRecord.currentLaunchCanTurnScreenOn(); if (allowTheaterMode && canTurnScreenOn - && (mWmService.mAtmService.isDreaming() - || !mPowerManagerWrapper.isInteractive())) { + && (mWmService.mAtmService.isDreaming() + || !mWmService.mPowerManager.isInteractive())) { if (DEBUG_VISIBILITY || DEBUG_POWER) { Slog.v(TAG, "Relayout window turning screen on: " + this); } - mPowerManagerWrapper.wakeUp(SystemClock.uptimeMillis(), + mWmService.mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_APPLICATION, "android.server.wm:SCREEN_ON_FLAG"); } diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd index 807874522b8c..3cbceec5b9cd 100644 --- a/services/core/xsd/display-device-config/display-device-config.xsd +++ b/services/core/xsd/display-device-config/display-device-config.xsd @@ -632,7 +632,7 @@ <xs:annotation name="final"/> </xs:element> <xs:element name="mode" type="AutoBrightnessModeName" minOccurs="0"/> - <xs:element name="setting" type="xs:string" minOccurs="0"/> + <xs:element name="setting" type="AutoBrightnessSettingName" minOccurs="0"/> </xs:complexType> <!-- Represents a point in the display brightness mapping, representing the lux level from the @@ -775,4 +775,13 @@ <xs:enumeration value="doze"/> </xs:restriction> </xs:simpleType> + + <!-- Predefined auto-brighntess settings --> + <xs:simpleType name="AutoBrightnessSettingName"> + <xs:restriction base="xs:string"> + <xs:enumeration value="dim"/> + <xs:enumeration value="normal"/> + <xs:enumeration value="bright"/> + </xs:restriction> + </xs:simpleType> </xs:schema> diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt index 91172a334bb1..79ea274e2fca 100644 --- a/services/core/xsd/display-device-config/schema/current.txt +++ b/services/core/xsd/display-device-config/schema/current.txt @@ -23,6 +23,13 @@ package com.android.server.display.config { enum_constant public static final com.android.server.display.config.AutoBrightnessModeName idle; } + public enum AutoBrightnessSettingName { + method public String getRawName(); + enum_constant public static final com.android.server.display.config.AutoBrightnessSettingName bright; + enum_constant public static final com.android.server.display.config.AutoBrightnessSettingName dim; + enum_constant public static final com.android.server.display.config.AutoBrightnessSettingName normal; + } + public class BlockingZoneConfig { ctor public BlockingZoneConfig(); method public final com.android.server.display.config.BlockingZoneThreshold getBlockingZoneThreshold(); @@ -227,10 +234,10 @@ package com.android.server.display.config { ctor public LuxToBrightnessMapping(); method @NonNull public final com.android.server.display.config.NonNegativeFloatToFloatMap getMap(); method public com.android.server.display.config.AutoBrightnessModeName getMode(); - method public String getSetting(); + method public com.android.server.display.config.AutoBrightnessSettingName getSetting(); method public final void setMap(@NonNull com.android.server.display.config.NonNegativeFloatToFloatMap); method public void setMode(com.android.server.display.config.AutoBrightnessModeName); - method public void setSetting(String); + method public void setSetting(com.android.server.display.config.AutoBrightnessSettingName); } public class NitsMap { diff --git a/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java index f4eaa5b7046c..fb73aff44c64 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java @@ -17,6 +17,7 @@ package com.android.server.display; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT; +import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE; import static org.junit.Assert.assertEquals; @@ -29,21 +30,22 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import android.content.res.Resources; import android.content.res.TypedArray; import android.hardware.display.BrightnessConfiguration; import android.os.PowerManager; +import android.provider.Settings; +import android.testing.TestableContext; import android.util.MathUtils; import android.util.Spline; import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; -import com.android.server.display.whitebalance.DisplayWhiteBalanceController; - +import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; import java.util.Arrays; @@ -154,15 +156,23 @@ public class BrightnessMappingStrategyTest { private static final float TOLERANCE = 0.0001f; - @Mock - DisplayWhiteBalanceController mMockDwbc; + @Rule + public final TestableContext mContext = new TestableContext( + InstrumentationRegistry.getInstrumentation().getContext()); + + @Before + public void setUp() { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_FOR_ALS, + Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL); + } @Test public void testSimpleStrategyMappingAtControlPoints() { - Resources res = createResources(); + setUpResources(); DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevels(DISPLAY_LEVELS).build(); - BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc, - AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc); + BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(mContext, ddc, + AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null); assertNotNull("BrightnessMappingStrategy should not be null", simple); for (int i = 0; i < LUX_LEVELS.length; i++) { assertEquals(DISPLAY_LEVELS[i], simple.getBrightness(LUX_LEVELS[i]), TOLERANCE); @@ -171,10 +181,10 @@ public class BrightnessMappingStrategyTest { @Test public void testSimpleStrategyMappingBetweenControlPoints() { - Resources res = createResources(); + setUpResources(); DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevels(DISPLAY_LEVELS).build(); - BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc, - AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc); + BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(mContext, ddc, + AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null); assertNotNull("BrightnessMappingStrategy should not be null", simple); for (int i = 1; i < LUX_LEVELS.length; i++) { final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2; @@ -186,10 +196,10 @@ public class BrightnessMappingStrategyTest { @Test public void testSimpleStrategyIgnoresNewConfiguration() { - Resources res = createResources(); + setUpResources(); DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevels(DISPLAY_LEVELS).build(); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, - AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc, + AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null); final float[] lux = { 0f, 1f }; final float[] nits = { 0, PowerManager.BRIGHTNESS_ON }; @@ -202,10 +212,10 @@ public class BrightnessMappingStrategyTest { @Test public void testSimpleStrategyIgnoresNullConfiguration() { - Resources res = createResources(); + setUpResources(); DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevels(DISPLAY_LEVELS).build(); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, - AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc, + AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null); strategy.setBrightnessConfiguration(null); final int n = DISPLAY_LEVELS.length; @@ -216,11 +226,11 @@ public class BrightnessMappingStrategyTest { @Test public void testPhysicalStrategyMappingAtControlPoints() { - Resources res = createResources(); + setUpResources(); DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevelsNits(DISPLAY_LEVELS_NITS) .build(); - BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc, - AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc); + BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(mContext, ddc, + AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null); assertNotNull("BrightnessMappingStrategy should not be null", physical); for (int i = 0; i < LUX_LEVELS.length; i++) { final float expectedLevel = MathUtils.map(DISPLAY_RANGE_NITS[0], DISPLAY_RANGE_NITS[1], @@ -235,11 +245,11 @@ public class BrightnessMappingStrategyTest { @Test public void testPhysicalStrategyMappingBetweenControlPoints() { - Resources res = createResources(); + setUpResources(); DisplayDeviceConfig ddc = new DdcBuilder().setBrightnessRange(BACKLIGHT_RANGE_ZERO_TO_ONE) .setAutoBrightnessLevelsNits(DISPLAY_LEVELS_NITS).build(); - BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc, - AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc); + BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(mContext, ddc, + AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null); assertNotNull("BrightnessMappingStrategy should not be null", physical); Spline brightnessToNits = Spline.createSpline(BACKLIGHT_RANGE_ZERO_TO_ONE, DISPLAY_RANGE_NITS); @@ -254,11 +264,11 @@ public class BrightnessMappingStrategyTest { @Test public void testPhysicalStrategyUsesNewConfigurations() { - Resources res = createResources(); + setUpResources(); DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevelsNits(DISPLAY_LEVELS_NITS) .build(); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, - AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc, + AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null); final float[] lux = {0f, 1f}; final float[] nits = { @@ -281,11 +291,11 @@ public class BrightnessMappingStrategyTest { @Test public void testPhysicalStrategyRecalculateSplines() { - Resources res = createResources(); + setUpResources(); DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevelsNits(DISPLAY_LEVELS_NITS) .build(); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, - AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc, + AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null); float[] adjustedNits50p = new float[DISPLAY_RANGE_NITS.length]; for (int i = 0; i < DISPLAY_RANGE_NITS.length; i++) { adjustedNits50p[i] = DISPLAY_RANGE_NITS[i] * 0.5f; @@ -326,12 +336,12 @@ public class BrightnessMappingStrategyTest { @Test public void testDefaultStrategyIsPhysical() { - Resources res = createResources(); + setUpResources(); DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevels(DISPLAY_LEVELS) .setAutoBrightnessLevelsNits(DISPLAY_LEVELS_NITS) .build(); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, - AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc, + AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null); assertTrue(strategy instanceof BrightnessMappingStrategy.PhysicalMappingStrategy); } @@ -342,19 +352,19 @@ public class BrightnessMappingStrategyTest { float tmp = lux[idx]; lux[idx] = lux[idx + 1]; lux[idx + 1] = tmp; - Resources res = createResources(); + setUpResources(); DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevelsLux(lux) .setAutoBrightnessLevelsNits(DISPLAY_LEVELS_NITS).build(); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, - AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc, + AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null); assertNull(strategy); // And make sure we get the same result even if it's monotone but not increasing. lux[idx] = lux[idx + 1]; ddc = new DdcBuilder().setAutoBrightnessLevelsLux(lux) .setAutoBrightnessLevelsNits(DISPLAY_LEVELS_NITS).build(); - strategy = BrightnessMappingStrategy.create(res, ddc, AUTO_BRIGHTNESS_MODE_DEFAULT, - mMockDwbc); + strategy = BrightnessMappingStrategy.create(mContext, ddc, AUTO_BRIGHTNESS_MODE_DEFAULT, + /* displayWhiteBalanceController= */ null); assertNull(strategy); } @@ -365,74 +375,74 @@ public class BrightnessMappingStrategyTest { // Make sure it's strictly increasing so that the only failure is the differing array // lengths lux[lux.length - 1] = lux[lux.length - 2] + 1; - Resources res = createResources(); + setUpResources(); DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevelsLux(lux) .setAutoBrightnessLevelsNits(DISPLAY_LEVELS_NITS).build(); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, - AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc, + AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null); assertNull(strategy); ddc = new DdcBuilder().setAutoBrightnessLevelsLux(lux) .setAutoBrightnessLevelsNits(DISPLAY_LEVELS_NITS) .setAutoBrightnessLevels(DISPLAY_LEVELS).build(); - strategy = BrightnessMappingStrategy.create(res, ddc, AUTO_BRIGHTNESS_MODE_DEFAULT, - mMockDwbc); + strategy = BrightnessMappingStrategy.create(mContext, ddc, AUTO_BRIGHTNESS_MODE_DEFAULT, + /* displayWhiteBalanceController= */ null); assertNull(strategy); // Extra backlight level final float[] backlight = Arrays.copyOf(DISPLAY_LEVELS, DISPLAY_LEVELS.length + 1); backlight[backlight.length - 1] = backlight[backlight.length - 2] + 1; - res = createResources(); + setUpResources(); ddc = new DdcBuilder().setAutoBrightnessLevels(backlight).build(); - strategy = BrightnessMappingStrategy.create(res, ddc, AUTO_BRIGHTNESS_MODE_DEFAULT, - mMockDwbc); + strategy = BrightnessMappingStrategy.create(mContext, ddc, AUTO_BRIGHTNESS_MODE_DEFAULT, + /* displayWhiteBalanceController= */ null); assertNull(strategy); // Extra nits level final float[] nits = Arrays.copyOf(DISPLAY_RANGE_NITS, DISPLAY_LEVELS_NITS.length + 1); nits[nits.length - 1] = nits[nits.length - 2] + 1; - res = createResources(); + setUpResources(); ddc = new DdcBuilder().setAutoBrightnessLevelsNits(nits) .setAutoBrightnessLevels(EMPTY_FLOAT_ARRAY).build(); - strategy = BrightnessMappingStrategy.create(res, ddc, AUTO_BRIGHTNESS_MODE_DEFAULT, - mMockDwbc); + strategy = BrightnessMappingStrategy.create(mContext, ddc, AUTO_BRIGHTNESS_MODE_DEFAULT, + /* displayWhiteBalanceController= */ null); assertNull(strategy); } @Test public void testPhysicalStrategyRequiresNitsMapping() { - Resources res = createResources(); + setUpResources(); DisplayDeviceConfig ddc = new DdcBuilder().setNitsRange(EMPTY_FLOAT_ARRAY).build(); - BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc, - AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc); + BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(mContext, ddc, + AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null); assertNull(physical); } @Test public void testStrategiesAdaptToUserDataPoint() { - Resources res = createResources(); + setUpResources(); DisplayDeviceConfig ddc = new DdcBuilder().setBrightnessRange(BACKLIGHT_RANGE_ZERO_TO_ONE) .setAutoBrightnessLevelsNits(DISPLAY_LEVELS_NITS).build(); - assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc, - AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc)); + assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(mContext, ddc, + AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null)); ddc = new DdcBuilder().setBrightnessRange(BACKLIGHT_RANGE_ZERO_TO_ONE) .setAutoBrightnessLevels(DISPLAY_LEVELS).build(); - res = createResources(); - assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc, - AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc)); + setUpResources(); + assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(mContext, ddc, + AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null)); } @Test public void testIdleModeConfigLoadsCorrectly() { - Resources res = createResources(LUX_LEVELS_IDLE, DISPLAY_LEVELS_NITS_IDLE); + setUpResources(LUX_LEVELS_IDLE, DISPLAY_LEVELS_NITS_IDLE); DisplayDeviceConfig ddc = new DdcBuilder().setBrightnessRange(BACKLIGHT_RANGE_ZERO_TO_ONE) .build(); // Create an idle mode bms // This will fail if it tries to fetch the wrong configuration. - BrightnessMappingStrategy bms = BrightnessMappingStrategy.create(res, ddc, + BrightnessMappingStrategy bms = BrightnessMappingStrategy.create(mContext, ddc, AUTO_BRIGHTNESS_MODE_IDLE, - mMockDwbc); + /* displayWhiteBalanceController= */ null); assertNotNull("BrightnessMappingStrategy should not be null", bms); // Ensure that the config is the one we set @@ -500,36 +510,31 @@ public class BrightnessMappingStrategyTest { assertEquals(minBrightness, strategy.getBrightness(LUX_LEVELS[0]), 0.0001f /*tolerance*/); } - private Resources createResources() { - return createResources(EMPTY_INT_ARRAY, EMPTY_FLOAT_ARRAY); + private void setUpResources() { + setUpResources(EMPTY_INT_ARRAY, EMPTY_FLOAT_ARRAY); } - private Resources createResources(int[] luxLevelsIdle, float[] brightnessLevelsNitsIdle) { - - Resources mockResources = mock(Resources.class); + private void setUpResources(int[] luxLevelsIdle, float[] brightnessLevelsNitsIdle) { if (luxLevelsIdle.length > 0) { int[] luxLevelsIdleResource = Arrays.copyOfRange(luxLevelsIdle, 1, luxLevelsIdle.length); - when(mockResources.getIntArray( - com.android.internal.R.array.config_autoBrightnessLevelsIdle)) - .thenReturn(luxLevelsIdleResource); + mContext.getOrCreateTestableResources().addOverride( + com.android.internal.R.array.config_autoBrightnessLevelsIdle, + luxLevelsIdleResource); } TypedArray mockBrightnessLevelNitsIdle = createFloatTypedArray(brightnessLevelsNitsIdle); - when(mockResources.obtainTypedArray( - com.android.internal.R.array.config_autoBrightnessDisplayValuesNitsIdle)) - .thenReturn(mockBrightnessLevelNitsIdle); - - when(mockResources.getInteger( - com.android.internal.R.integer.config_screenBrightnessSettingMinimum)) - .thenReturn(1); - when(mockResources.getInteger( - com.android.internal.R.integer.config_screenBrightnessSettingMaximum)) - .thenReturn(255); - when(mockResources.getFraction( - com.android.internal.R.fraction.config_autoBrightnessAdjustmentMaxGamma, 1, 1)) - .thenReturn(MAXIMUM_GAMMA); - return mockResources; + mContext.getOrCreateTestableResources().addOverride( + com.android.internal.R.array.config_autoBrightnessDisplayValuesNitsIdle, + mockBrightnessLevelNitsIdle); + + mContext.getOrCreateTestableResources().addOverride( + com.android.internal.R.integer.config_screenBrightnessSettingMinimum, 1); + mContext.getOrCreateTestableResources().addOverride( + com.android.internal.R.integer.config_screenBrightnessSettingMaximum, 255); + mContext.getOrCreateTestableResources().addOverride( + com.android.internal.R.fraction.config_autoBrightnessAdjustmentMaxGamma, + MAXIMUM_GAMMA); } private TypedArray createFloatTypedArray(float[] vals) { @@ -570,11 +575,11 @@ public class BrightnessMappingStrategyTest { final float y2 = GAMMA_CORRECTION_SPLINE.interpolate(x2); final float y3 = GAMMA_CORRECTION_SPLINE.interpolate(x3); - Resources resources = createResources(); + setUpResources(); DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevelsLux(GAMMA_CORRECTION_LUX) .setAutoBrightnessLevelsNits(GAMMA_CORRECTION_NITS).build(); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc, - AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc, + AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null); // Let's start with a validity check: assertEquals(y1, strategy.getBrightness(x1), 0.0001f /* tolerance */); assertEquals(y2, strategy.getBrightness(x2), 0.0001f /* tolerance */); @@ -600,11 +605,11 @@ public class BrightnessMappingStrategyTest { final float y1 = GAMMA_CORRECTION_SPLINE.interpolate(x1); final float y2 = GAMMA_CORRECTION_SPLINE.interpolate(x2); final float y3 = GAMMA_CORRECTION_SPLINE.interpolate(x3); - Resources resources = createResources(); + setUpResources(); DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevelsLux(GAMMA_CORRECTION_LUX) .setAutoBrightnessLevelsNits(GAMMA_CORRECTION_NITS).build(); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc, - AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc, + AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null); // Validity check: assertEquals(y1, strategy.getBrightness(x1), 0.0001f /* tolerance */); assertEquals(y2, strategy.getBrightness(x2), 0.0001f /* tolerance */); @@ -627,11 +632,11 @@ public class BrightnessMappingStrategyTest { public void testGammaCorrectionExtremeChangeAtCenter() { // Extreme changes (e.g. setting brightness to 0.0 or 1.0) can't be gamma corrected, so we // just make sure the adjustment reflects the change. - Resources resources = createResources(); + setUpResources(); DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevelsLux(GAMMA_CORRECTION_LUX) .setAutoBrightnessLevelsNits(GAMMA_CORRECTION_NITS).build(); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc, - AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc, + AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null); assertEquals(0.0f, strategy.getAutoBrightnessAdjustment(), /* delta= */ 0.0001f); strategy.addUserDataPoint(/* lux= */ 2500, /* brightness= */ 1.0f); assertEquals(+1.0f, strategy.getAutoBrightnessAdjustment(), /* delta= */ 0.0001f); @@ -650,11 +655,11 @@ public class BrightnessMappingStrategyTest { final float y0 = GAMMA_CORRECTION_SPLINE.interpolate(x0); final float y2 = GAMMA_CORRECTION_SPLINE.interpolate(x2); final float y4 = GAMMA_CORRECTION_SPLINE.interpolate(x4); - Resources resources = createResources(); + setUpResources(); DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevelsLux(GAMMA_CORRECTION_LUX) .setAutoBrightnessLevelsNits(GAMMA_CORRECTION_NITS).build(); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc, - AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc, + AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null); // Validity, as per tradition: assertEquals(y0, strategy.getBrightness(x0), 0.0001f /* tolerance */); assertEquals(y2, strategy.getBrightness(x2), 0.0001f /* tolerance */); @@ -679,15 +684,33 @@ public class BrightnessMappingStrategyTest { @Test public void testGetMode() { - Resources res = createResources(LUX_LEVELS_IDLE, DISPLAY_LEVELS_NITS_IDLE); + setUpResources(LUX_LEVELS_IDLE, DISPLAY_LEVELS_NITS_IDLE); DisplayDeviceConfig ddc = new DdcBuilder().setBrightnessRange(BACKLIGHT_RANGE_ZERO_TO_ONE) .build(); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, - AUTO_BRIGHTNESS_MODE_IDLE, - mMockDwbc); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc, + AUTO_BRIGHTNESS_MODE_IDLE, /* displayWhiteBalanceController= */ null); assertEquals(AUTO_BRIGHTNESS_MODE_IDLE, strategy.getMode()); } + @Test + public void testAutoBrightnessModeAndPreset() { + int mode = AUTO_BRIGHTNESS_MODE_DOZE; + int preset = Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_DIM; + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_FOR_ALS, preset); + + setUpResources(); + DisplayDeviceConfig ddc = new DdcBuilder() + .setAutoBrightnessLevels(mode, preset, DISPLAY_LEVELS) + .setAutoBrightnessLevelsLux(mode, preset, LUX_LEVELS).build(); + BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(mContext, ddc, mode, + /* displayWhiteBalanceController= */ null); + assertNotNull("BrightnessMappingStrategy should not be null", simple); + for (int i = 0; i < LUX_LEVELS.length; i++) { + assertEquals(DISPLAY_LEVELS[i], simple.getBrightness(LUX_LEVELS[i]), TOLERANCE); + } + } + private static class DdcBuilder { private DisplayDeviceConfig mDdc; @@ -695,10 +718,11 @@ public class BrightnessMappingStrategyTest { mDdc = mock(DisplayDeviceConfig.class); when(mDdc.getNits()).thenReturn(DISPLAY_RANGE_NITS); when(mDdc.getBrightness()).thenReturn(DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT); - when(mDdc.getAutoBrightnessBrighteningLevelsLux(AUTO_BRIGHTNESS_MODE_DEFAULT)) - .thenReturn(LUX_LEVELS); + when(mDdc.getAutoBrightnessBrighteningLevelsLux(AUTO_BRIGHTNESS_MODE_DEFAULT, + Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL)).thenReturn(LUX_LEVELS); when(mDdc.getAutoBrightnessBrighteningLevelsNits()).thenReturn(EMPTY_FLOAT_ARRAY); - when(mDdc.getAutoBrightnessBrighteningLevels(AUTO_BRIGHTNESS_MODE_DEFAULT)) + when(mDdc.getAutoBrightnessBrighteningLevels(AUTO_BRIGHTNESS_MODE_DEFAULT, + Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL)) .thenReturn(EMPTY_FLOAT_ARRAY); } @@ -713,8 +737,15 @@ public class BrightnessMappingStrategyTest { } DdcBuilder setAutoBrightnessLevelsLux(float[] luxLevels) { - when(mDdc.getAutoBrightnessBrighteningLevelsLux(AUTO_BRIGHTNESS_MODE_DEFAULT)) - .thenReturn(luxLevels); + when(mDdc.getAutoBrightnessBrighteningLevelsLux(AUTO_BRIGHTNESS_MODE_DEFAULT, + Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL)).thenReturn(luxLevels); + return this; + } + + DdcBuilder setAutoBrightnessLevelsLux( + @AutomaticBrightnessController.AutomaticBrightnessMode int mode, int preset, + float[] luxLevels) { + when(mDdc.getAutoBrightnessBrighteningLevelsLux(mode, preset)).thenReturn(luxLevels); return this; } @@ -724,7 +755,16 @@ public class BrightnessMappingStrategyTest { } DdcBuilder setAutoBrightnessLevels(float[] brightnessLevels) { - when(mDdc.getAutoBrightnessBrighteningLevels(AUTO_BRIGHTNESS_MODE_DEFAULT)) + when(mDdc.getAutoBrightnessBrighteningLevels(AUTO_BRIGHTNESS_MODE_DEFAULT, + Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL)) + .thenReturn(brightnessLevels); + return this; + } + + DdcBuilder setAutoBrightnessLevels( + @AutomaticBrightnessController.AutomaticBrightnessMode int mode, int preset, + float[] brightnessLevels) { + when(mDdc.getAutoBrightnessBrighteningLevels(mode, preset)) .thenReturn(brightnessLevels); return this; } diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java index 61c60765c966..7a84406f1b08 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java @@ -19,6 +19,7 @@ package com.android.server.display; import static com.android.internal.display.BrightnessSynchronizer.brightnessIntToFloat; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT; +import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE; import static com.android.server.display.config.SensorData.SupportedMode; import static com.android.server.display.utils.DeviceConfigParsingUtils.ambientBrightnessThresholdsIntToFloat; import static com.android.server.display.utils.DeviceConfigParsingUtils.displayBrightnessThresholdsIntToFloat; @@ -41,6 +42,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.hardware.display.DisplayManagerInternal; import android.os.Temperature; +import android.provider.Settings; import android.util.SparseArray; import android.util.Spline; import android.view.SurfaceControl; @@ -609,10 +611,12 @@ public final class DisplayDeviceConfigTest { assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(), new float[]{2.0f, 200.0f, 600.0f}, ZERO_DELTA); assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux( - AUTO_BRIGHTNESS_MODE_DEFAULT), new float[]{0.0f, 110.0f, 500.0f}, ZERO_DELTA); + AUTO_BRIGHTNESS_MODE_DEFAULT, Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL), + new float[]{0.0f, 110.0f, 500.0f}, ZERO_DELTA); assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels( - AUTO_BRIGHTNESS_MODE_DEFAULT), new float[]{brightnessIntToFloat(50), - brightnessIntToFloat(100), brightnessIntToFloat(150)}, SMALL_DELTA); + AUTO_BRIGHTNESS_MODE_DEFAULT, Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL), + new float[]{brightnessIntToFloat(50), brightnessIntToFloat(100), + brightnessIntToFloat(150)}, SMALL_DELTA); // Test thresholds assertEquals(0, mDisplayDeviceConfig.getAmbientLuxBrighteningMinThreshold(), ZERO_DELTA); @@ -739,31 +743,39 @@ public final class DisplayDeviceConfigTest { assertArrayEquals(new float[]{0.0f, 80}, mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux( - AUTO_BRIGHTNESS_MODE_DEFAULT), ZERO_DELTA); + AUTO_BRIGHTNESS_MODE_DEFAULT, + Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL), ZERO_DELTA); assertArrayEquals(new float[]{0.2f, 0.3f}, mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels( - AUTO_BRIGHTNESS_MODE_DEFAULT), SMALL_DELTA); + AUTO_BRIGHTNESS_MODE_DEFAULT, + Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL), SMALL_DELTA); assertArrayEquals(new float[]{0.0f, 90}, - mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux("default", "dim"), - ZERO_DELTA); + mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux( + AUTO_BRIGHTNESS_MODE_DEFAULT, + Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_DIM), ZERO_DELTA); assertArrayEquals(new float[]{0.3f, 0.4f}, - mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels("default", "dim"), - SMALL_DELTA); + mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels( + AUTO_BRIGHTNESS_MODE_DEFAULT, + Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_DIM), SMALL_DELTA); assertArrayEquals(new float[]{0.0f, 95}, - mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux("doze", "normal"), - ZERO_DELTA); + mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux( + AUTO_BRIGHTNESS_MODE_DOZE, + Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL), ZERO_DELTA); assertArrayEquals(new float[]{0.35f, 0.45f}, - mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels("doze", "normal"), - SMALL_DELTA); + mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels( + AUTO_BRIGHTNESS_MODE_DOZE, + Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL), SMALL_DELTA); assertArrayEquals(new float[]{0.0f, 100}, - mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux("doze", "bright"), - ZERO_DELTA); + mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux( + AUTO_BRIGHTNESS_MODE_DOZE, + Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_BRIGHT), ZERO_DELTA); assertArrayEquals(new float[]{0.4f, 0.5f}, - mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels("doze", "bright"), - SMALL_DELTA); + mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels( + AUTO_BRIGHTNESS_MODE_DOZE, + Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_BRIGHT), SMALL_DELTA); } @Test @@ -776,10 +788,12 @@ public final class DisplayDeviceConfigTest { assertArrayEquals(new float[]{brightnessIntToFloat(50), brightnessIntToFloat(100), brightnessIntToFloat(150)}, mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels( - AUTO_BRIGHTNESS_MODE_DEFAULT), SMALL_DELTA); + AUTO_BRIGHTNESS_MODE_DEFAULT, + Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL), SMALL_DELTA); assertArrayEquals(new float[]{0, 110, 500}, mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux( - AUTO_BRIGHTNESS_MODE_DEFAULT), ZERO_DELTA); + AUTO_BRIGHTNESS_MODE_DEFAULT, + Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL), ZERO_DELTA); assertArrayEquals(new float[]{2, 200, 600}, mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(), SMALL_DELTA); } diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java index ffdc8b431c39..7a708487293e 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java @@ -775,7 +775,7 @@ public final class DisplayPowerController2Test { final float hdrBrightness = 0.3f; when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(mHolder.automaticBrightnessController.isInIdleMode()).thenReturn(true); + when(mHolder.automaticBrightnessController.getMode()).thenReturn(AUTO_BRIGHTNESS_MODE_IDLE); when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness( any(BrightnessEvent.class))).thenReturn(sdrBrightness); when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(1.0f); @@ -1234,7 +1234,7 @@ public final class DisplayPowerController2Test { float brightness = 0.6f; when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(mHolder.automaticBrightnessController.isInIdleMode()).thenReturn(true); + when(mHolder.automaticBrightnessController.getMode()).thenReturn(AUTO_BRIGHTNESS_MODE_IDLE); when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness( any(BrightnessEvent.class))).thenReturn(brightness); @@ -1597,7 +1597,7 @@ public final class DisplayPowerController2Test { public void testDoesNotSwitchFromIdleToDozeAutoBrightnessMode() { when(mDisplayManagerFlagsMock.areAutoBrightnessModesEnabled()).thenReturn(true); when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE); - when(mHolder.automaticBrightnessController.isInIdleMode()).thenReturn(true); + when(mHolder.automaticBrightnessController.getMode()).thenReturn(AUTO_BRIGHTNESS_MODE_IDLE); DisplayPowerRequest dpr = new DisplayPowerRequest(); mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); @@ -1905,7 +1905,7 @@ public final class DisplayPowerController2Test { } @Override - BrightnessMappingStrategy getDefaultModeBrightnessMapper(Resources resources, + BrightnessMappingStrategy getDefaultModeBrightnessMapper(Context context, DisplayDeviceConfig displayDeviceConfig, DisplayWhiteBalanceController displayWhiteBalanceController) { return mBrightnessMappingStrategy; diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java index 64cdac464720..d69c140716f3 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java @@ -1164,7 +1164,7 @@ public final class DisplayPowerControllerTest { float brightness = 0.6f; when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(mHolder.automaticBrightnessController.isInIdleMode()).thenReturn(true); + when(mHolder.automaticBrightnessController.getMode()).thenReturn(AUTO_BRIGHTNESS_MODE_IDLE); when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness( any(BrightnessEvent.class))).thenReturn(brightness); @@ -1669,7 +1669,7 @@ public final class DisplayPowerControllerTest { } @Override - BrightnessMappingStrategy getDefaultModeBrightnessMapper(Resources resources, + BrightnessMappingStrategy getDefaultModeBrightnessMapper(Context context, DisplayDeviceConfig displayDeviceConfig, DisplayWhiteBalanceController displayWhiteBalanceController) { return mBrightnessMappingStrategy; diff --git a/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java index a63d01b6225a..e7da26ea38b1 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java @@ -456,6 +456,14 @@ public abstract class BaseAbsoluteVolumeBehaviorTest { } @Test + public void avbEnabled_standby_avbDisabled() { + enableAbsoluteVolumeBehavior(); + mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF); + assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo( + AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL); + } + + @Test public void avbEnabled_cecVolumeDisabled_avbDisabled() { enableAbsoluteVolumeBehavior(); 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 a0e49a616a28..884ea31504bc 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -13616,7 +13616,31 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES) - public void setNotificationPolicy_watchCompanionApp_setsGlobalPolicy() throws RemoteException { + public void setNotificationPolicy_watchCompanionApp_setsGlobalPolicy() + throws RemoteException { + setNotificationPolicy_dependingOnCompanionAppDevice_maySetGlobalPolicy( + AssociationRequest.DEVICE_PROFILE_WATCH, true); + } + + @Test + @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES) + public void setNotificationPolicy_autoCompanionApp_setsGlobalPolicy() + throws RemoteException { + setNotificationPolicy_dependingOnCompanionAppDevice_maySetGlobalPolicy( + AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION, true); + } + + @Test + @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES) + public void setNotificationPolicy_otherCompanionApp_doesNotSetGlobalPolicy() + throws RemoteException { + setNotificationPolicy_dependingOnCompanionAppDevice_maySetGlobalPolicy( + AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING, false); + } + + private void setNotificationPolicy_dependingOnCompanionAppDevice_maySetGlobalPolicy( + @AssociationRequest.DeviceProfile String deviceProfile, boolean canSetGlobalPolicy) + throws RemoteException { mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mService.setCallerIsNormalPackage(); ZenModeHelper zenModeHelper = mock(ZenModeHelper.class); @@ -13626,14 +13650,19 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mCompanionMgr.getAssociations(anyString(), anyInt())) .thenReturn(ImmutableList.of( new AssociationInfo.Builder(1, mUserId, "package") - .setDisplayName("My watch") - .setDeviceProfile(AssociationRequest.DEVICE_PROFILE_WATCH) + .setDisplayName("My connected device") + .setDeviceProfile(deviceProfile) .build())); NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0); mBinderService.setNotificationPolicy("package", policy, false); - verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyInt()); + if (canSetGlobalPolicy) { + verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyInt()); + } else { + verify(zenModeHelper).applyGlobalPolicyAsImplicitZenRule(anyString(), anyInt(), + eq(policy), anyInt()); + } } @Test @@ -13703,7 +13732,29 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES) - public void setInterruptionFilter_watchCompanionApp_setsGlobalPolicy() throws RemoteException { + public void setInterruptionFilter_watchCompanionApp_setsGlobalZen() throws RemoteException { + setInterruptionFilter_dependingOnCompanionAppDevice_maySetGlobalZen( + AssociationRequest.DEVICE_PROFILE_WATCH, true); + } + + @Test + @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES) + public void setInterruptionFilter_autoCompanionApp_setsGlobalZen() throws RemoteException { + setInterruptionFilter_dependingOnCompanionAppDevice_maySetGlobalZen( + AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION, true); + } + + @Test + @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES) + public void setInterruptionFilter_otherCompanionApp_doesNotSetGlobalZen() + throws RemoteException { + setInterruptionFilter_dependingOnCompanionAppDevice_maySetGlobalZen( + AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING, false); + } + + private void setInterruptionFilter_dependingOnCompanionAppDevice_maySetGlobalZen( + @AssociationRequest.DeviceProfile String deviceProfile, boolean canSetGlobalPolicy) + throws RemoteException { mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); ZenModeHelper zenModeHelper = mock(ZenModeHelper.class); mService.mZenModeHelper = zenModeHelper; @@ -13713,14 +13764,19 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mCompanionMgr.getAssociations(anyString(), anyInt())) .thenReturn(ImmutableList.of( new AssociationInfo.Builder(1, mUserId, "package") - .setDisplayName("My watch") - .setDeviceProfile(AssociationRequest.DEVICE_PROFILE_WATCH) + .setDisplayName("My connected device") + .setDeviceProfile(deviceProfile) .build())); mBinderService.setInterruptionFilter("package", INTERRUPTION_FILTER_PRIORITY, false); - verify(zenModeHelper).setManualZenMode(eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS), eq(null), - eq(ZenModeConfig.UPDATE_ORIGIN_APP), anyString(), eq("package"), anyInt()); + if (canSetGlobalPolicy) { + verify(zenModeHelper).setManualZenMode(eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS), eq(null), + eq(ZenModeConfig.UPDATE_ORIGIN_APP), anyString(), eq("package"), anyInt()); + } else { + verify(zenModeHelper).applyGlobalZenModeAsImplicitZenRule(anyString(), anyInt(), + eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS)); + } } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 85c6f9e9b2fe..718c59875c1a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -202,8 +202,7 @@ public class ActivityRecordTests extends WindowTestsBase { } private TestStartingWindowOrganizer registerTestStartingWindowOrganizer() { - return new TestStartingWindowOrganizer(mAtm, - mSystemServicesTestRule.getPowerManagerWrapper()); + return new TestStartingWindowOrganizer(mAtm); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java index 51f0404e2396..8cd9ff359111 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java +++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java @@ -134,7 +134,6 @@ public class SystemServicesTestRule implements TestRule { private StaticMockitoSession mMockitoSession; private ActivityTaskManagerService mAtmService; private WindowManagerService mWmService; - private WindowState.PowerManagerWrapper mPowerManagerWrapper; private InputManagerService mImService; private InputChannel mInputChannel; private Runnable mOnBeforeServicesCreated; @@ -360,7 +359,6 @@ public class SystemServicesTestRule implements TestRule { } private void setUpWindowManagerService() { - mPowerManagerWrapper = mock(WindowState.PowerManagerWrapper.class); TestWindowManagerPolicy wmPolicy = new TestWindowManagerPolicy(); TestDisplayWindowSettingsProvider testDisplayWindowSettingsProvider = new TestDisplayWindowSettingsProvider(); @@ -485,10 +483,6 @@ public class SystemServicesTestRule implements TestRule { return mAtmService; } - WindowState.PowerManagerWrapper getPowerManagerWrapper() { - return mPowerManagerWrapper; - } - /** Creates a no-op wakelock object. */ PowerManager.WakeLock createStubbedWakeLock(boolean needVerification) { if (needVerification) { diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index 2007f680f1ae..75e252f9a415 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -52,7 +52,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doThrow; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; @@ -368,28 +367,26 @@ public class WindowStateTests extends WindowTestsBase { firstWindow.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; secondWindow.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; - final WindowState.PowerManagerWrapper powerManagerWrapper = - mSystemServicesTestRule.getPowerManagerWrapper(); - reset(powerManagerWrapper); + final var powerManager = mWm.mPowerManager; + clearInvocations(powerManager); firstWindow.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/); - verify(powerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString()); + verify(powerManager).wakeUp(anyLong(), anyInt(), anyString()); - reset(powerManagerWrapper); + clearInvocations(powerManager); secondWindow.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/); - verify(powerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString()); + verify(powerManager).wakeUp(anyLong(), anyInt(), anyString()); } private void testPrepareWindowToDisplayDuringRelayout(WindowState appWindow, boolean expectedWakeupCalled, boolean expectedCurrentLaunchCanTurnScreenOn) { - final WindowState.PowerManagerWrapper powerManagerWrapper = - mSystemServicesTestRule.getPowerManagerWrapper(); - reset(powerManagerWrapper); + final var powerManager = mWm.mPowerManager; + clearInvocations(powerManager); appWindow.prepareWindowToDisplayDuringRelayout(false /* wasVisible */); if (expectedWakeupCalled) { - verify(powerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString()); + verify(powerManager).wakeUp(anyLong(), anyInt(), anyString()); } else { - verify(powerManagerWrapper, never()).wakeUp(anyLong(), anyInt(), anyString()); + verify(powerManager, never()).wakeUp(anyLong(), anyInt(), anyString()); } // If wakeup is expected to be called, the currentLaunchCanTurnScreenOn should be false // because the state will be consumed. diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 616a23e7ab5b..a5f6190f2d51 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -637,14 +637,12 @@ class WindowTestsBase extends SystemServiceTestsBase { WindowState createWindow(WindowState parent, int type, WindowToken token, String name, int ownerId, boolean ownerCanAddInternalSystemWindow, IWindow iwindow) { return createWindow(parent, type, token, name, ownerId, UserHandle.getUserId(ownerId), - ownerCanAddInternalSystemWindow, mWm, getTestSession(token), iwindow, - mSystemServicesTestRule.getPowerManagerWrapper()); + ownerCanAddInternalSystemWindow, mWm, getTestSession(token), iwindow); } static WindowState createWindow(WindowState parent, int type, WindowToken token, String name, int ownerId, int userId, boolean ownerCanAddInternalSystemWindow, - WindowManagerService service, Session session, IWindow iWindow, - WindowState.PowerManagerWrapper powerManagerWrapper) { + WindowManagerService service, Session session, IWindow iWindow) { SystemServicesTestRule.checkHoldsLock(service.mGlobalLock); final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type); @@ -652,9 +650,7 @@ class WindowTestsBase extends SystemServiceTestsBase { attrs.packageName = "test"; final WindowState w = new WindowState(service, session, iWindow, token, parent, - OP_NONE, attrs, VISIBLE, ownerId, userId, - ownerCanAddInternalSystemWindow, - powerManagerWrapper); + OP_NONE, attrs, VISIBLE, ownerId, userId, ownerCanAddInternalSystemWindow); // TODO: Probably better to make this call in the WindowState ctor to avoid errors with // adding it to the token... token.addWindow(w); @@ -1738,17 +1734,14 @@ class WindowTestsBase extends SystemServiceTestsBase { static class TestStartingWindowOrganizer extends WindowOrganizerTests.StubOrganizer { private final ActivityTaskManagerService mAtm; private final WindowManagerService mWMService; - private final WindowState.PowerManagerWrapper mPowerManagerWrapper; private Runnable mRunnableWhenAddingSplashScreen; private final SparseArray<IBinder> mTaskAppMap = new SparseArray<>(); private final HashMap<IBinder, WindowState> mAppWindowMap = new HashMap<>(); - TestStartingWindowOrganizer(ActivityTaskManagerService service, - WindowState.PowerManagerWrapper powerManagerWrapper) { + TestStartingWindowOrganizer(ActivityTaskManagerService service) { mAtm = service; mWMService = mAtm.mWindowManager; - mPowerManagerWrapper = powerManagerWrapper; mAtm.mTaskOrganizerController.setDeferTaskOrgCallbacksConsumer(Runnable::run); mAtm.mTaskOrganizerController.registerTaskOrganizer(this); } @@ -1767,8 +1760,7 @@ class WindowTestsBase extends SystemServiceTestsBase { final WindowState window = WindowTestsBase.createWindow(null, TYPE_APPLICATION_STARTING, activity, "Starting window", 0 /* ownerId */, 0 /* userId*/, - false /* internalWindows */, mWMService, createTestSession(mAtm), iWindow, - mPowerManagerWrapper); + false /* internalWindows */, mWMService, createTestSession(mAtm), iWindow); activity.mStartingWindow = window; mAppWindowMap.put(info.appToken, window); mTaskAppMap.put(info.taskInfo.taskId, info.appToken); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index d38e3217852d..c7b84a3b9530 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -3721,19 +3721,19 @@ public class CarrierConfigManager { * This configuration allows the system UI to display different 5G icons for different 5G * scenarios. * - * There are five 5G scenarios: - * 1. connected_mmwave: device currently connected to 5G cell as the secondary cell and using - * millimeter wave. - * 2. connected: device currently connected to 5G cell as the secondary cell but not using - * millimeter wave. - * 3. not_restricted_rrc_idle: device camped on a network that has 5G capability(not necessary - * to connect a 5G cell as a secondary cell) and the use of 5G is not restricted and RRC - * currently in IDLE state. - * 4. not_restricted_rrc_con: device camped on a network that has 5G capability(not necessary - * to connect a 5G cell as a secondary cell) and the use of 5G is not restricted and RRC - * currently in CONNECTED state. - * 5. restricted: device camped on a network that has 5G capability(not necessary to connect a - * 5G cell as a secondary cell) but the use of 5G is restricted. + * There are six 5G scenarios for icon configuration: + * 1. connected_mmwave: device currently connected to 5G cell as the primary or secondary cell + * and considered NR advanced. + * 2. connected: device currently connected to 5G cell as the primary or secondary cell but not + * considered NR advanced. + * 3. connected_rrc_idle: device currently connected to 5G cell as the primary or secondary cell + * and RRC currently in IDLE state. + * 4. not_restricted_rrc_idle: device camped on a network that has 5G capability and the use of + * 5G is not restricted and RRC currently in IDLE state. + * 5. not_restricted_rrc_con: device camped on a network that has 5G capability and the use of + * 5G is not restricted and RRC currently in CONNECTED state. + * 6. restricted: device camped on a network that has 5G capability but the use of 5G is + * restricted. * * The configured string contains multiple key-value pairs separated by comma. For each pair, * the key and value are separated by a colon. The key corresponds to a 5G status above and @@ -3754,21 +3754,21 @@ public class CarrierConfigManager { * This configuration allows the system UI to determine how long to continue to display 5G icons * when the device switches between different 5G scenarios. * - * There are seven 5G scenarios: - * 1. connected_mmwave: device currently connected to 5G cell as the secondary cell and using - * millimeter wave. - * 2. connected: device currently connected to 5G cell as the secondary cell but not using - * millimeter wave. - * 3. not_restricted_rrc_idle: device camped on a network that has 5G capability (not necessary - * to connect a 5G cell as a secondary cell) and the use of 5G is not restricted and RRC - * currently in IDLE state. - * 4. not_restricted_rrc_con: device camped on a network that has 5G capability (not necessary - * to connect a 5G cell as a secondary cell) and the use of 5G is not restricted and RRC - * currently in CONNECTED state. - * 5. restricted: device camped on a network that has 5G capability (not necessary to connect a - * 5G cell as a secondary cell) but the use of 5G is restricted. - * 6. legacy: device is not camped on a network that has 5G capability - * 7. any: any of the above scenarios + * There are eight 5G scenarios: + * 1. connected_mmwave: device currently connected to 5G cell as the primary or secondary cell + * and considered NR advanced. + * 2. connected: device currently connected to 5G cell as the primary or secondary cell but not + * considered NR advanced. + * 3. connected_rrc_idle: device currently connected to 5G cell as the primary or secondary cell + * and RRC currently in IDLE state. + * 4. not_restricted_rrc_idle: device camped on a network that has 5G capability and the use of + * 5G is not restricted and RRC currently in IDLE state. + * 5. not_restricted_rrc_con: device camped on a network that has 5G capability and the use of + * 5G is not restricted and RRC currently in CONNECTED state. + * 6. restricted: device camped on a network that has 5G capability but the use of 5G is + * restricted. + * 7. legacy: device is not camped on a network that has 5G capability + * 8. any: any of the above scenarios * * The configured string contains various timer rules separated by a semicolon. * Each rule will have three items: prior 5G scenario, current 5G scenario, and grace period @@ -3776,8 +3776,8 @@ public class CarrierConfigManager { * 5G scenario, the system UI will continue to show the icon for the prior 5G scenario (defined * in {@link #KEY_5G_ICON_CONFIGURATION_STRING}) for the amount of time specified by the grace * period. If the prior 5G scenario is reestablished, the timer will reset and start again if - * the UE changes 5G scenarios again. Defined states (5G scenarios #1-5) take precedence over - * 'any' (5G scenario #6), and unspecified transitions have a default grace period of 0. + * the UE changes 5G scenarios again. Defined states (5G scenarios #1-7) take precedence over + * 'any' (5G scenario #8), and unspecified transitions have a default grace period of 0. * The order of rules in the configuration determines the priority (the first applicable timer * rule will be used). * @@ -3800,21 +3800,21 @@ public class CarrierConfigManager { * This configuration extends {@link #KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING} to allow the * system UI to continue displaying 5G icons after the initial timer expires. * - * There are seven 5G scenarios: - * 1. connected_mmwave: device currently connected to 5G cell as the secondary cell and using - * millimeter wave. - * 2. connected: device currently connected to 5G cell as the secondary cell but not using - * millimeter wave. - * 3. not_restricted_rrc_idle: device camped on a network that has 5G capability (not necessary - * to connect a 5G cell as a secondary cell) and the use of 5G is not restricted and RRC - * currently in IDLE state. - * 4. not_restricted_rrc_con: device camped on a network that has 5G capability (not necessary - * to connect a 5G cell as a secondary cell) and the use of 5G is not restricted and RRC - * currently in CONNECTED state. - * 5. restricted: device camped on a network that has 5G capability (not necessary to connect a - * 5G cell as a secondary cell) but the use of 5G is restricted. - * 6. legacy: device is not camped on a network that has 5G capability - * 7. any: any of the above scenarios + * There are eight 5G scenarios: + * 1. connected_mmwave: device currently connected to 5G cell as the primary or secondary cell + * and considered NR advanced. + * 2. connected: device currently connected to 5G cell as the primary or secondary cell but not + * considered NR advanced. + * 3. connected_rrc_idle: device currently connected to 5G cell as the primary or secondary cell + * and RRC currently in IDLE state. + * 4. not_restricted_rrc_idle: device camped on a network that has 5G capability and the use of + * 5G is not restricted and RRC currently in IDLE state. + * 5. not_restricted_rrc_con: device camped on a network that has 5G capability and the use of + * 5G is not restricted and RRC currently in CONNECTED state. + * 6. restricted: device camped on a network that has 5G capability but the use of 5G is + * restricted. + * 7. legacy: device is not camped on a network that has 5G capability + * 8. any: any of the above scenarios * * The configured string contains various timer rules separated by a semicolon. * Each rule will have three items: primary 5G scenario, secondary 5G scenario, and @@ -3824,7 +3824,7 @@ public class CarrierConfigManager { * period. If the primary 5G scenario is reestablished, the timers will reset and the system UI * will continue to display the icon for the primary 5G scenario without interruption. If the * secondary 5G scenario is lost, the timer will reset and the icon will reflect the true state. - * Defined states (5G scenarios #1-5) take precedence over 'any' (5G scenario #6), and + * Defined states (5G scenarios #1-7) take precedence over 'any' (5G scenario #8), and * unspecified transitions have a default grace period of 0. The order of rules in the * configuration determines the priority (the first applicable timer rule will be used). * @@ -10608,7 +10608,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_USE_CALL_WAITING_USSD_BOOL, false); sDefaults.putInt(KEY_CALL_WAITING_SERVICE_CLASS_INT, 1 /* SERVICE_CLASS_VOICE */); sDefaults.putString(KEY_5G_ICON_CONFIGURATION_STRING, - "connected_mmwave:5G,connected:5G,not_restricted_rrc_idle:5G," + "connected_mmwave:5G,connected:5G,connected_rrc_idle:5G,not_restricted_rrc_idle:5G," + "not_restricted_rrc_con:5G"); sDefaults.putString(KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING, ""); sDefaults.putString(KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING, ""); diff --git a/telephony/java/android/telephony/SecurityAlgorithmUpdate.java b/telephony/java/android/telephony/SecurityAlgorithmUpdate.java new file mode 100644 index 000000000000..61d7ead67a21 --- /dev/null +++ b/telephony/java/android/telephony/SecurityAlgorithmUpdate.java @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + +/** + * A single occurrence capturing a notable change to previously reported + * cryptography algorithms for a given network and network event. + * + * @hide + */ +public final class SecurityAlgorithmUpdate implements Parcelable { + private static final String TAG = "SecurityAlgorithmUpdate"; + + private @ConnectionEvent int mConnectionEvent; + private @SecurityAlgorithm int mEncryption; + private @SecurityAlgorithm int mIntegrity; + private boolean mIsUnprotectedEmergency; + + public SecurityAlgorithmUpdate(@ConnectionEvent int connectionEvent, + @SecurityAlgorithm int encryption, @SecurityAlgorithm int integrity, + boolean isUnprotectedEmergency) { + mConnectionEvent = connectionEvent; + mEncryption = encryption; + mIntegrity = integrity; + mIsUnprotectedEmergency = isUnprotectedEmergency; + } + + private SecurityAlgorithmUpdate(Parcel in) { + readFromParcel(in); + } + + public @ConnectionEvent int getConnectionEvent() { + return mConnectionEvent; + } + + public @SecurityAlgorithm int getEncryption() { + return mEncryption; + } + + public @SecurityAlgorithm int getIntegrity() { + return mIntegrity; + } + + public boolean isUnprotectedEmergency() { + return mIsUnprotectedEmergency; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(mConnectionEvent); + out.writeInt(mEncryption); + out.writeInt(mIntegrity); + out.writeBoolean(mIsUnprotectedEmergency); + } + + private void readFromParcel(@NonNull Parcel in) { + mConnectionEvent = in.readInt(); + mEncryption = in.readInt(); + mIntegrity = in.readInt(); + mIsUnprotectedEmergency = in.readBoolean(); + } + + public static final Parcelable.Creator<SecurityAlgorithmUpdate> CREATOR = + new Parcelable.Creator<SecurityAlgorithmUpdate>() { + public SecurityAlgorithmUpdate createFromParcel(Parcel in) { + return new SecurityAlgorithmUpdate(in); + } + + public SecurityAlgorithmUpdate[] newArray(int size) { + return new SecurityAlgorithmUpdate[size]; + } + }; + + @Override + public String toString() { + return TAG + ":{ mConnectionEvent = " + mConnectionEvent + " mEncryption = " + mEncryption + + " mIntegrity = " + mIntegrity + " mIsUnprotectedEmergency = " + + mIsUnprotectedEmergency; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof SecurityAlgorithmUpdate)) return false; + SecurityAlgorithmUpdate that = (SecurityAlgorithmUpdate) o; + return mConnectionEvent == that.mConnectionEvent + && mEncryption == that.mEncryption + && mIntegrity == that.mIntegrity + && mIsUnprotectedEmergency == that.mIsUnprotectedEmergency; + } + + @Override + public int hashCode() { + return Objects.hash(mConnectionEvent, mEncryption, mIntegrity, mIsUnprotectedEmergency); + } + + public static final int CONNECTION_EVENT_CS_SIGNALLING_GSM = 0; + public static final int CONNECTION_EVENT_PS_SIGNALLING_GPRS = 1; + public static final int CONNECTION_EVENT_CS_SIGNALLING_3G = 2; + public static final int CONNECTION_EVENT_PS_SIGNALLING_3G = 3; + public static final int CONNECTION_EVENT_NAS_SIGNALLING_LTE = 4; + public static final int CONNECTION_EVENT_AS_SIGNALLING_LTE = 5; + public static final int CONNECTION_EVENT_VOLTE_SIP = 6; + public static final int CONNECTION_EVENT_VOLTE_RTP = 7; + public static final int CONNECTION_EVENT_NAS_SIGNALLING_5G = 8; + public static final int CONNECTION_EVENT_AS_SIGNALLING_5G = 9; + public static final int CONNECTION_EVENT_VONR_SIP = 10; + public static final int CONNECTION_EVENT_VONR_RTP = 11; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"CONNECTION_EVENT_"}, value = {CONNECTION_EVENT_CS_SIGNALLING_GSM, + CONNECTION_EVENT_PS_SIGNALLING_GPRS, CONNECTION_EVENT_CS_SIGNALLING_3G, + CONNECTION_EVENT_PS_SIGNALLING_3G, CONNECTION_EVENT_NAS_SIGNALLING_LTE, + CONNECTION_EVENT_AS_SIGNALLING_LTE, CONNECTION_EVENT_VOLTE_SIP, + CONNECTION_EVENT_VOLTE_RTP, CONNECTION_EVENT_NAS_SIGNALLING_5G, + CONNECTION_EVENT_AS_SIGNALLING_5G, CONNECTION_EVENT_VONR_SIP, + CONNECTION_EVENT_VONR_RTP}) + public @interface ConnectionEvent { + } + + public static final int SECURITY_ALGORITHM_A50 = 0; + public static final int SECURITY_ALGORITHM_A51 = 1; + public static final int SECURITY_ALGORITHM_A52 = 2; + public static final int SECURITY_ALGORITHM_A53 = 3; + public static final int SECURITY_ALGORITHM_A54 = 4; + public static final int SECURITY_ALGORITHM_GEA0 = 14; + public static final int SECURITY_ALGORITHM_GEA1 = 15; + public static final int SECURITY_ALGORITHM_GEA2 = 16; + public static final int SECURITY_ALGORITHM_GEA3 = 17; + public static final int SECURITY_ALGORITHM_GEA4 = 18; + public static final int SECURITY_ALGORITHM_GEA5 = 19; + public static final int SECURITY_ALGORITHM_UEA0 = 29; + public static final int SECURITY_ALGORITHM_UEA1 = 30; + public static final int SECURITY_ALGORITHM_UEA2 = 31; + public static final int SECURITY_ALGORITHM_EEA0 = 41; + public static final int SECURITY_ALGORITHM_EEA1 = 42; + public static final int SECURITY_ALGORITHM_EEA2 = 43; + public static final int SECURITY_ALGORITHM_EEA3 = 44; + public static final int SECURITY_ALGORITHM_NEA0 = 55; + public static final int SECURITY_ALGORITHM_NEA1 = 56; + public static final int SECURITY_ALGORITHM_NEA2 = 57; + public static final int SECURITY_ALGORITHM_NEA3 = 58; + public static final int SECURITY_ALGORITHM_SIP_NULL = 68; + public static final int SECURITY_ALGORITHM_AES_GCM = 69; + public static final int SECURITY_ALGORITHM_AES_GMAC = 70; + public static final int SECURITY_ALGORITHM_AES_CBC = 71; + public static final int SECURITY_ALGORITHM_DES_EDE3_CBC = 72; + public static final int SECURITY_ALGORITHM_AES_EDE3_CBC = 73; + public static final int SECURITY_ALGORITHM_HMAC_SHA1_96 = 74; + public static final int SECURITY_ALGORITHM_HMAC_SHA1_96_NULL = 75; + public static final int SECURITY_ALGORITHM_HMAC_MD5_96 = 76; + public static final int SECURITY_ALGORITHM_HMAC_MD5_96_NULL = 77; + public static final int SECURITY_ALGORITHM_SRTP_AES_COUNTER = 87; + public static final int SECURITY_ALGORITHM_SRTP_AES_F8 = 88; + public static final int SECURITY_ALGORITHM_SRTP_HMAC_SHA1 = 89; + public static final int SECURITY_ALGORITHM_ENCR_AES_GCM_16 = 99; + public static final int SECURITY_ALGORITHM_ENCR_AES_CBC = 100; + public static final int SECURITY_ALGORITHM_AUTH_HMAC_SHA2_256_128 = 101; + public static final int SECURITY_ALGORITHM_UNKNOWN = 113; + public static final int SECURITY_ALGORITHM_OTHER = 114; + public static final int SECURITY_ALGORITHM_ORYX = 124; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"CONNECTION_EVENT_"}, value = {SECURITY_ALGORITHM_A50, SECURITY_ALGORITHM_A51, + SECURITY_ALGORITHM_A52, SECURITY_ALGORITHM_A53, + SECURITY_ALGORITHM_A54, SECURITY_ALGORITHM_GEA0, SECURITY_ALGORITHM_GEA1, + SECURITY_ALGORITHM_GEA2, SECURITY_ALGORITHM_GEA3, SECURITY_ALGORITHM_GEA4, + SECURITY_ALGORITHM_GEA5, SECURITY_ALGORITHM_UEA0, SECURITY_ALGORITHM_UEA1, + SECURITY_ALGORITHM_UEA2, SECURITY_ALGORITHM_EEA0, SECURITY_ALGORITHM_EEA1, + SECURITY_ALGORITHM_EEA2, SECURITY_ALGORITHM_EEA3, SECURITY_ALGORITHM_NEA0, + SECURITY_ALGORITHM_NEA1, SECURITY_ALGORITHM_NEA2, SECURITY_ALGORITHM_NEA3, + SECURITY_ALGORITHM_SIP_NULL, SECURITY_ALGORITHM_AES_GCM, + SECURITY_ALGORITHM_AES_GMAC, SECURITY_ALGORITHM_AES_CBC, + SECURITY_ALGORITHM_DES_EDE3_CBC, SECURITY_ALGORITHM_AES_EDE3_CBC, + SECURITY_ALGORITHM_HMAC_SHA1_96, SECURITY_ALGORITHM_HMAC_SHA1_96_NULL, + SECURITY_ALGORITHM_HMAC_MD5_96, SECURITY_ALGORITHM_HMAC_MD5_96_NULL, + SECURITY_ALGORITHM_SRTP_AES_COUNTER, SECURITY_ALGORITHM_SRTP_AES_F8, + SECURITY_ALGORITHM_SRTP_HMAC_SHA1, SECURITY_ALGORITHM_ENCR_AES_GCM_16, + SECURITY_ALGORITHM_ENCR_AES_CBC, SECURITY_ALGORITHM_AUTH_HMAC_SHA2_256_128, + SECURITY_ALGORITHM_UNKNOWN, SECURITY_ALGORITHM_OTHER, SECURITY_ALGORITHM_ORYX}) + public @interface SecurityAlgorithm { + } + +} |