summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/ActivityManager.java12
-rw-r--r--core/java/android/app/ActivityThread.java23
-rw-r--r--core/java/android/app/ConfigurationChangedListenerController.java116
-rw-r--r--core/java/android/printservice/PrintService.java2
-rw-r--r--core/java/android/view/inputmethod/RemoteInputConnectionImpl.java4
-rw-r--r--core/res/res/values/config.xml3
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--core/tests/GameManagerTests/src/android/app/GameManagerTests.java10
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java9
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java82
-rw-r--r--libs/hwui/Android.bp1
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp13
-rw-r--r--libs/hwui/renderthread/CanvasContext.h2
-rw-r--r--libs/hwui/renderthread/HintSessionWrapper.cpp121
-rw-r--r--libs/hwui/renderthread/HintSessionWrapper.h34
-rw-r--r--libs/hwui/tests/unit/HintSessionWrapperTests.cpp287
-rw-r--r--services/autofill/java/com/android/server/autofill/FieldClassificationEventLogger.java120
-rw-r--r--services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java33
-rw-r--r--services/autofill/java/com/android/server/autofill/RemoteFieldClassificationService.java52
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java56
-rw-r--r--services/core/java/com/android/server/BatteryService.java21
-rw-r--r--services/core/java/com/android/server/app/GameManagerService.java67
-rw-r--r--services/core/java/com/android/server/app/GameManagerShellCommand.java10
-rw-r--r--services/core/java/com/android/server/devicestate/DeviceStateManagerService.java48
-rw-r--r--services/core/java/com/android/server/devicestate/DeviceStateNotificationController.java9
-rw-r--r--services/core/java/com/android/server/display/BrightnessRangeController.java14
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java2
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController2.java2
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java4
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java14
-rw-r--r--services/tests/servicestests/src/com/android/server/devicestate/DeviceStateNotificationControllerTest.java17
32 files changed, 937 insertions, 258 deletions
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index ff0f437ea8a1..66d04a3132eb 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -5167,11 +5167,21 @@ public class ActivityManager {
* @hide
*/
public static void broadcastStickyIntent(Intent intent, int appOp, Bundle options, int userId) {
+ broadcastStickyIntent(intent, null, appOp, options, userId);
+ }
+
+ /**
+ * Convenience for sending a sticky broadcast. For internal use only.
+ *
+ * @hide
+ */
+ public static void broadcastStickyIntent(Intent intent, String[] excludedPackages,
+ int appOp, Bundle options, int userId) {
try {
getService().broadcastIntentWithFeature(
null, null, intent, null, null, Activity.RESULT_OK, null, null,
null /*requiredPermissions*/, null /*excludedPermissions*/,
- null /*excludedPackages*/, appOp, options, false, true, userId);
+ excludedPackages, appOp, options, false, true, userId);
} catch (RemoteException ex) {
}
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 1571fddb012f..f4caef0f2553 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -255,6 +255,7 @@ import java.util.Objects;
import java.util.TimeZone;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
/**
* This manages the execution of the main thread in an
@@ -370,6 +371,11 @@ public final class ActivityThread extends ClientTransactionHandler
@GuardedBy("mAppThread")
private int mLastProcessState = PROCESS_STATE_UNKNOWN;
ArrayList<WeakReference<AssistStructure>> mLastAssistStructures = new ArrayList<>();
+
+ @NonNull
+ private final ConfigurationChangedListenerController mConfigurationChangedListenerController =
+ new ConfigurationChangedListenerController();
+
private int mLastSessionId;
// Holds the value of the last reported device ID value from the server for the top activity.
int mLastReportedDeviceId;
@@ -3540,6 +3546,21 @@ public final class ActivityThread extends ClientTransactionHandler
return mConfigurationController.getConfiguration();
}
+ /**
+ * @hide
+ */
+ public void addConfigurationChangedListener(Executor executor,
+ Consumer<IBinder> consumer) {
+ mConfigurationChangedListenerController.addListener(executor, consumer);
+ }
+
+ /**
+ * @hide
+ */
+ public void removeConfigurationChangedListener(Consumer<IBinder> consumer) {
+ mConfigurationChangedListenerController.removeListener(consumer);
+ }
+
@Override
public void updatePendingConfiguration(Configuration config) {
final Configuration updatedConfig =
@@ -6098,6 +6119,8 @@ public final class ActivityThread extends ClientTransactionHandler
" did not call through to super.onConfigurationChanged()");
}
}
+ mConfigurationChangedListenerController
+ .dispatchOnConfigurationChanged(activity.getActivityToken());
return configToReport;
}
diff --git a/core/java/android/app/ConfigurationChangedListenerController.java b/core/java/android/app/ConfigurationChangedListenerController.java
new file mode 100644
index 000000000000..c644d57fb9a7
--- /dev/null
+++ b/core/java/android/app/ConfigurationChangedListenerController.java
@@ -0,0 +1,116 @@
+/*
+ * 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.app;
+
+import android.annotation.NonNull;
+import android.os.IBinder;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * Manages listeners for unfiltered configuration changes.
+ * @hide
+ */
+class ConfigurationChangedListenerController {
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private final List<ListenerContainer> mListenerContainers = new ArrayList<>();
+
+ /**
+ * Adds a listener to receive updates when they are dispatched. This only dispatches updates and
+ * does not relay the last emitted value. If called with the same listener then this method does
+ * not have any effect.
+ * @param executor an executor that is used to dispatch the updates.
+ * @param consumer a listener interested in receiving updates.
+ */
+ void addListener(@NonNull Executor executor,
+ @NonNull Consumer<IBinder> consumer) {
+ synchronized (mLock) {
+ if (indexOf(consumer) > -1) {
+ return;
+ }
+ mListenerContainers.add(new ListenerContainer(executor, consumer));
+ }
+ }
+
+ /**
+ * Removes the listener that was previously registered. If the listener was not registered this
+ * method does not have any effect.
+ */
+ void removeListener(@NonNull Consumer<IBinder> consumer) {
+ synchronized (mLock) {
+ final int index = indexOf(consumer);
+ if (index > -1) {
+ mListenerContainers.remove(index);
+ }
+ }
+ }
+
+ /**
+ * Dispatches the update to all registered listeners
+ * @param activityToken a token for the {@link Activity} that received a configuration update.
+ */
+ void dispatchOnConfigurationChanged(@NonNull IBinder activityToken) {
+ final List<ListenerContainer> consumers;
+ synchronized (mLock) {
+ consumers = new ArrayList<>(mListenerContainers);
+ }
+ for (int i = 0; i < consumers.size(); i++) {
+ consumers.get(i).accept(activityToken);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private int indexOf(Consumer<IBinder> consumer) {
+ for (int i = 0; i < mListenerContainers.size(); i++) {
+ if (mListenerContainers.get(i).isMatch(consumer)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private static final class ListenerContainer {
+
+ @NonNull
+ private final Executor mExecutor;
+ @NonNull
+ private final Consumer<IBinder> mConsumer;
+
+ ListenerContainer(@NonNull Executor executor,
+ @NonNull Consumer<IBinder> consumer) {
+ mExecutor = executor;
+ mConsumer = consumer;
+ }
+
+ public boolean isMatch(@NonNull Consumer<IBinder> consumer) {
+ return mConsumer.equals(consumer);
+ }
+
+ public void accept(@NonNull IBinder activityToken) {
+ mExecutor.execute(() -> mConsumer.accept(activityToken));
+ }
+
+ }
+}
diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java
index ecf17707a5c8..dc024701fbef 100644
--- a/core/java/android/printservice/PrintService.java
+++ b/core/java/android/printservice/PrintService.java
@@ -178,7 +178,7 @@ public abstract class PrintService extends Service {
* <pre> &lt;print-service
* android:vendor="SomeVendor"
* android:settingsActivity="foo.bar.MySettingsActivity"
- * andorid:addPrintersActivity="foo.bar.MyAddPrintersActivity."
+ * android:addPrintersActivity="foo.bar.MyAddPrintersActivity."
* . . .
* /&gt;</pre>
* <p>
diff --git a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
index 3ad49afb1575..7694754de6ff 100644
--- a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
@@ -451,7 +451,9 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
if (mBeamer == null) {
return;
}
- mBeamer.forget(token);
+ dispatch(() -> {
+ mBeamer.forget(token);
+ });
}
@Override
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 24dbc5eaed41..e77d8537bba0 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1803,6 +1803,9 @@
<array name="config_screenThresholdLevels">
</array>
+ <!-- Allow normal brightness controller feature. -->
+ <bool name="config_allowNormalBrightnessControllerFeature">false</bool>
+
<!-- Array of hysteresis constraint values for brightening, represented as tenths of a
percent. The length of this array is assumed to be one greater than
config_screenThresholdLevels. The brightening threshold is calculated as
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e661eda96a02..bee5799938f2 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1948,6 +1948,7 @@
<java-symbol type="bool" name="config_usbChargingMessage" />
<java-symbol type="bool" name="config_skipScreenOnBrightnessRamp" />
<java-symbol type="bool" name="config_allowAutoBrightnessWhileDozing" />
+ <java-symbol type="bool" name="config_allowNormalBrightnessControllerFeature" />
<java-symbol type="bool" name="config_allowTheaterModeWakeFromUnplug" />
<java-symbol type="bool" name="config_allowTheaterModeWakeFromGesture" />
<java-symbol type="bool" name="config_allowTheaterModeWakeFromCameraLens" />
diff --git a/core/tests/GameManagerTests/src/android/app/GameManagerTests.java b/core/tests/GameManagerTests/src/android/app/GameManagerTests.java
index fac3a0ecdec2..d34c91ee48ba 100644
--- a/core/tests/GameManagerTests/src/android/app/GameManagerTests.java
+++ b/core/tests/GameManagerTests/src/android/app/GameManagerTests.java
@@ -86,16 +86,6 @@ public final class GameManagerTests {
GameModeInfo gameModeInfo = mGameManager.getGameModeInfo(mPackageName);
assertNotNull(gameModeInfo);
assertNull(gameModeInfo.getGameModeConfiguration(GameManager.GAME_MODE_CUSTOM));
- GameModeConfiguration unsupportedFpsConfig =
- new GameModeConfiguration.Builder().setFpsOverride(
- 70).setScalingFactor(0.5f).build();
- mGameManager.updateCustomGameModeConfiguration(mPackageName, unsupportedFpsConfig);
- gameModeInfo = mGameManager.getGameModeInfo(mPackageName);
- assertNotNull(gameModeInfo);
- // TODO(b/243448953): update to non-zero FPS when matching is implemented
- assertEquals(new GameModeConfiguration.Builder().setFpsOverride(
- GameModeConfiguration.FPS_OVERRIDE_NONE).setScalingFactor(0.5f).build(),
- gameModeInfo.getGameModeConfiguration(GameManager.GAME_MODE_CUSTOM));
GameModeConfiguration supportedFpsConfig =
new GameModeConfiguration.Builder().setFpsOverride(
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
index 55eabb039c01..9da6c10c6d74 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
@@ -19,7 +19,6 @@ package androidx.window.extensions;
import android.app.ActivityThread;
import android.app.Application;
import android.content.Context;
-import android.window.TaskFragmentOrganizer;
import androidx.annotation.NonNull;
import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
@@ -81,13 +80,7 @@ public class WindowExtensionsImpl implements WindowExtensions {
Context context = getApplication();
DeviceStateManagerFoldingFeatureProducer producer =
getFoldingFeatureProducer();
- // TODO(b/263263909) Use the organizer to tell if an Activity is embededed.
- // Need to improve our Dependency Injection and centralize the logic.
- TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(command -> {
- throw new RuntimeException("Not allowed!");
- });
- mWindowLayoutComponent = new WindowLayoutComponentImpl(context, organizer,
- producer);
+ mWindowLayoutComponent = new WindowLayoutComponentImpl(context, producer);
}
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
index 6db2ae4d2141..a1fe7f75a826 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
@@ -24,7 +24,7 @@ import static androidx.window.util.ExtensionHelper.rotateRectToDisplayRotation;
import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect;
import android.app.Activity;
-import android.app.ActivityClient;
+import android.app.ActivityThread;
import android.app.Application;
import android.app.WindowConfiguration;
import android.content.ComponentCallbacks;
@@ -34,8 +34,7 @@ import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
import android.util.ArrayMap;
-import android.view.WindowManager;
-import android.window.TaskFragmentOrganizer;
+import android.util.Log;
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
@@ -51,7 +50,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.Set;
/**
@@ -63,7 +61,7 @@ import java.util.Set;
* Please refer to {@link androidx.window.sidecar.SampleSidecarImpl} instead.
*/
public class WindowLayoutComponentImpl implements WindowLayoutComponent {
- private static final String TAG = "SampleExtension";
+ private static final String TAG = WindowLayoutComponentImpl.class.getSimpleName();
private final Object mLock = new Object();
@@ -85,16 +83,15 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
private final Map<java.util.function.Consumer<WindowLayoutInfo>, Consumer<WindowLayoutInfo>>
mJavaToExtConsumers = new ArrayMap<>();
- private final TaskFragmentOrganizer mTaskFragmentOrganizer;
+ private final RawConfigurationChangedListener mRawConfigurationChangedListener =
+ new RawConfigurationChangedListener();
public WindowLayoutComponentImpl(@NonNull Context context,
- @NonNull TaskFragmentOrganizer taskFragmentOrganizer,
@NonNull DeviceStateManagerFoldingFeatureProducer foldingFeatureProducer) {
((Application) context.getApplicationContext())
.registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged());
mFoldingFeatureProducer = foldingFeatureProducer;
mFoldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
- mTaskFragmentOrganizer = taskFragmentOrganizer;
}
/** Registers to listen to {@link CommonFoldingFeature} changes */
@@ -117,6 +114,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
final Consumer<WindowLayoutInfo> extConsumer = consumer::accept;
synchronized (mLock) {
mJavaToExtConsumers.put(consumer, extConsumer);
+ updateListenerRegistrations();
}
addWindowLayoutInfoListener(activity, extConsumer);
}
@@ -170,6 +168,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
final Consumer<WindowLayoutInfo> extConsumer;
synchronized (mLock) {
extConsumer = mJavaToExtConsumers.remove(consumer);
+ updateListenerRegistrations();
}
if (extConsumer != null) {
removeWindowLayoutInfoListener(extConsumer);
@@ -200,6 +199,17 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
}
@GuardedBy("mLock")
+ private void updateListenerRegistrations() {
+ ActivityThread currentThread = ActivityThread.currentActivityThread();
+ if (mJavaToExtConsumers.isEmpty()) {
+ currentThread.removeConfigurationChangedListener(mRawConfigurationChangedListener);
+ } else {
+ currentThread.addConfigurationChangedListener(Runnable::run,
+ mRawConfigurationChangedListener);
+ }
+ }
+
+ @GuardedBy("mLock")
@NonNull
private Set<Context> getContextsListeningForLayoutChanges() {
return mWindowLayoutChangeListeners.keySet();
@@ -344,25 +354,28 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
continue;
}
if (featureRect.left != 0 && featureRect.top != 0) {
- throw new IllegalArgumentException("Bounding rectangle must start at the top or "
+ Log.wtf(TAG, "Bounding rectangle must start at the top or "
+ "left of the window. BaseFeatureRect: " + baseFeature.getRect()
+ ", FeatureRect: " + featureRect
+ ", WindowConfiguration: " + windowConfiguration);
+ continue;
}
if (featureRect.left == 0
&& featureRect.width() != windowConfiguration.getBounds().width()) {
- throw new IllegalArgumentException("Horizontal FoldingFeature must have full width."
+ Log.wtf(TAG, "Horizontal FoldingFeature must have full width."
+ " BaseFeatureRect: " + baseFeature.getRect()
+ ", FeatureRect: " + featureRect
+ ", WindowConfiguration: " + windowConfiguration);
+ continue;
}
if (featureRect.top == 0
&& featureRect.height() != windowConfiguration.getBounds().height()) {
- throw new IllegalArgumentException("Vertical FoldingFeature must have full height."
+ Log.wtf(TAG, "Vertical FoldingFeature must have full height."
+ " BaseFeatureRect: " + baseFeature.getRect()
+ ", FeatureRect: " + featureRect
+ ", WindowConfiguration: " + windowConfiguration);
+ continue;
}
features.add(new FoldingFeature(featureRect, baseFeature.getType(), state));
}
@@ -382,38 +395,11 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
// Display features are not supported on secondary displays.
return false;
}
- final int windowingMode;
- IBinder activityToken = context.getActivityToken();
- if (activityToken != null) {
- final Configuration taskConfig = ActivityClient.getInstance().getTaskConfiguration(
- activityToken);
- if (taskConfig == null) {
- // If we cannot determine the task configuration for any reason, it is likely that
- // we won't be able to determine its position correctly as well. DisplayFeatures'
- // bounds in this case can't be computed correctly, so we should skip.
- return false;
- }
- final Rect taskBounds = taskConfig.windowConfiguration.getBounds();
- final WindowManager windowManager = Objects.requireNonNull(
- context.getSystemService(WindowManager.class));
- final Rect maxBounds = windowManager.getMaximumWindowMetrics().getBounds();
- boolean isTaskExpanded = maxBounds.equals(taskBounds);
- /*
- * We need to proxy being in full screen because when a user enters PiP and exits PiP
- * the task windowingMode will report multi-window/pinned until the transition is
- * finished in WM Shell.
- * maxBounds == taskWindowBounds is a proxy check to verify the window is full screen
- */
- return isTaskExpanded;
- } else {
- // TODO(b/242674941): use task windowing mode for window context that associates with
- // activity.
- windowingMode = context.getResources().getConfiguration().windowConfiguration
- .getWindowingMode();
- }
- // It is recommended not to report any display features in multi-window mode, since it
- // won't be possible to synchronize the display feature positions with window movement.
- return !WindowConfiguration.inMultiWindowMode(windowingMode);
+
+ // We do not report folding features for Activities in PiP because the bounds are
+ // not updated fast enough and the window is too small for the UI to adapt.
+ return context.getResources().getConfiguration().windowConfiguration
+ .getWindowingMode() != WindowConfiguration.WINDOWING_MODE_PINNED;
}
@GuardedBy("mLock")
@@ -442,6 +428,16 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
}
}
+ private final class RawConfigurationChangedListener implements
+ java.util.function.Consumer<IBinder> {
+ @Override
+ public void accept(IBinder activityToken) {
+ synchronized (mLock) {
+ onDisplayFeaturesChangedIfListening(activityToken);
+ }
+ }
+ }
+
private final class ConfigurationChangeListener implements ComponentCallbacks {
final IBinder mToken;
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index db581471e2ca..c87c15669a09 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -717,6 +717,7 @@ cc_test {
"tests/unit/EglManagerTests.cpp",
"tests/unit/FatVectorTests.cpp",
"tests/unit/GraphicsStatsServiceTests.cpp",
+ "tests/unit/HintSessionWrapperTests.cpp",
"tests/unit/JankTrackerTests.cpp",
"tests/unit/FrameMetricsReporterTests.cpp",
"tests/unit/LayerUpdateQueueTests.cpp",
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index f5b3ca602469..310e39e1d0a2 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -123,7 +123,7 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode*
, mProfiler(mJankTracker.frames(), thread.timeLord().frameIntervalNanos())
, mContentDrawBounds(0, 0, 0, 0)
, mRenderPipeline(std::move(renderPipeline))
- , mHintSessionWrapper(uiThreadId, renderThreadId) {
+ , mHintSessionWrapper(std::make_shared<HintSessionWrapper>(uiThreadId, renderThreadId)) {
mRenderThread.cacheManager().registerCanvasContext(this);
rootRenderNode->makeRoot();
mRenderNodes.emplace_back(rootRenderNode);
@@ -160,6 +160,7 @@ void CanvasContext::destroy() {
destroyHardwareResources();
mAnimationContext->destroy();
mRenderThread.cacheManager().onContextStopped(this);
+ mHintSessionWrapper->delayedDestroy(mRenderThread, 2_s, mHintSessionWrapper);
}
static void setBufferCount(ANativeWindow* window) {
@@ -739,7 +740,7 @@ void CanvasContext::draw(bool solelyTextureViewUpdates) {
int64_t frameDeadline = mCurrentFrameInfo->get(FrameInfoIndex::FrameDeadline);
int64_t dequeueBufferDuration = mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration);
- mHintSessionWrapper.updateTargetWorkDuration(frameDeadline - intendedVsync);
+ mHintSessionWrapper->updateTargetWorkDuration(frameDeadline - intendedVsync);
if (didDraw) {
int64_t frameStartTime = mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime);
@@ -747,7 +748,7 @@ void CanvasContext::draw(bool solelyTextureViewUpdates) {
int64_t actualDuration = frameDuration -
(std::min(syncDelayDuration, mLastDequeueBufferDuration)) -
dequeueBufferDuration - idleDuration;
- mHintSessionWrapper.reportActualWorkDuration(actualDuration);
+ mHintSessionWrapper->reportActualWorkDuration(actualDuration);
}
mLastDequeueBufferDuration = dequeueBufferDuration;
@@ -1081,11 +1082,11 @@ void CanvasContext::prepareSurfaceControlForWebview() {
}
void CanvasContext::sendLoadResetHint() {
- mHintSessionWrapper.sendLoadResetHint();
+ mHintSessionWrapper->sendLoadResetHint();
}
void CanvasContext::sendLoadIncreaseHint() {
- mHintSessionWrapper.sendLoadIncreaseHint();
+ mHintSessionWrapper->sendLoadIncreaseHint();
}
void CanvasContext::setSyncDelayDuration(nsecs_t duration) {
@@ -1093,7 +1094,7 @@ void CanvasContext::setSyncDelayDuration(nsecs_t duration) {
}
void CanvasContext::startHintSession() {
- mHintSessionWrapper.init();
+ mHintSessionWrapper->init();
}
bool CanvasContext::shouldDither() {
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 32ac5af94c14..10a4afb23d35 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -359,7 +359,7 @@ private:
std::function<bool(int64_t, int64_t, int64_t)> mASurfaceTransactionCallback;
std::function<void()> mPrepareSurfaceControlForWebviewCallback;
- HintSessionWrapper mHintSessionWrapper;
+ std::shared_ptr<HintSessionWrapper> mHintSessionWrapper;
nsecs_t mLastDequeueBufferDuration = 0;
nsecs_t mSyncDelayDuration = 0;
nsecs_t mIdleDuration = 0;
diff --git a/libs/hwui/renderthread/HintSessionWrapper.cpp b/libs/hwui/renderthread/HintSessionWrapper.cpp
index 1f338ee523eb..d1ebe6d9f576 100644
--- a/libs/hwui/renderthread/HintSessionWrapper.cpp
+++ b/libs/hwui/renderthread/HintSessionWrapper.cpp
@@ -24,6 +24,7 @@
#include <vector>
#include "../Properties.h"
+#include "RenderThread.h"
#include "thread/CommonPool.h"
using namespace std::chrono_literals;
@@ -32,76 +33,42 @@ namespace android {
namespace uirenderer {
namespace renderthread {
-namespace {
+#define BIND_APH_METHOD(name) \
+ name = (decltype(name))dlsym(handle_, "APerformanceHint_" #name); \
+ LOG_ALWAYS_FATAL_IF(name == nullptr, "Failed to find required symbol APerformanceHint_" #name)
-typedef APerformanceHintManager* (*APH_getManager)();
-typedef APerformanceHintSession* (*APH_createSession)(APerformanceHintManager*, const int32_t*,
- size_t, int64_t);
-typedef void (*APH_closeSession)(APerformanceHintSession* session);
-typedef void (*APH_updateTargetWorkDuration)(APerformanceHintSession*, int64_t);
-typedef void (*APH_reportActualWorkDuration)(APerformanceHintSession*, int64_t);
-typedef void (*APH_sendHint)(APerformanceHintSession* session, int32_t);
-
-bool gAPerformanceHintBindingInitialized = false;
-APH_getManager gAPH_getManagerFn = nullptr;
-APH_createSession gAPH_createSessionFn = nullptr;
-APH_closeSession gAPH_closeSessionFn = nullptr;
-APH_updateTargetWorkDuration gAPH_updateTargetWorkDurationFn = nullptr;
-APH_reportActualWorkDuration gAPH_reportActualWorkDurationFn = nullptr;
-APH_sendHint gAPH_sendHintFn = nullptr;
-
-void ensureAPerformanceHintBindingInitialized() {
- if (gAPerformanceHintBindingInitialized) return;
+void HintSessionWrapper::HintSessionBinding::init() {
+ if (mInitialized) return;
void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
LOG_ALWAYS_FATAL_IF(handle_ == nullptr, "Failed to dlopen libandroid.so!");
- gAPH_getManagerFn = (APH_getManager)dlsym(handle_, "APerformanceHint_getManager");
- LOG_ALWAYS_FATAL_IF(gAPH_getManagerFn == nullptr,
- "Failed to find required symbol APerformanceHint_getManager!");
-
- gAPH_createSessionFn = (APH_createSession)dlsym(handle_, "APerformanceHint_createSession");
- LOG_ALWAYS_FATAL_IF(gAPH_createSessionFn == nullptr,
- "Failed to find required symbol APerformanceHint_createSession!");
-
- gAPH_closeSessionFn = (APH_closeSession)dlsym(handle_, "APerformanceHint_closeSession");
- LOG_ALWAYS_FATAL_IF(gAPH_closeSessionFn == nullptr,
- "Failed to find required symbol APerformanceHint_closeSession!");
-
- gAPH_updateTargetWorkDurationFn = (APH_updateTargetWorkDuration)dlsym(
- handle_, "APerformanceHint_updateTargetWorkDuration");
- LOG_ALWAYS_FATAL_IF(
- gAPH_updateTargetWorkDurationFn == nullptr,
- "Failed to find required symbol APerformanceHint_updateTargetWorkDuration!");
+ BIND_APH_METHOD(getManager);
+ BIND_APH_METHOD(createSession);
+ BIND_APH_METHOD(closeSession);
+ BIND_APH_METHOD(updateTargetWorkDuration);
+ BIND_APH_METHOD(reportActualWorkDuration);
+ BIND_APH_METHOD(sendHint);
- gAPH_reportActualWorkDurationFn = (APH_reportActualWorkDuration)dlsym(
- handle_, "APerformanceHint_reportActualWorkDuration");
- LOG_ALWAYS_FATAL_IF(
- gAPH_reportActualWorkDurationFn == nullptr,
- "Failed to find required symbol APerformanceHint_reportActualWorkDuration!");
-
- gAPH_sendHintFn = (APH_sendHint)dlsym(handle_, "APerformanceHint_sendHint");
- LOG_ALWAYS_FATAL_IF(gAPH_sendHintFn == nullptr,
- "Failed to find required symbol APerformanceHint_sendHint!");
-
- gAPerformanceHintBindingInitialized = true;
+ mInitialized = true;
}
-} // namespace
-
HintSessionWrapper::HintSessionWrapper(pid_t uiThreadId, pid_t renderThreadId)
- : mUiThreadId(uiThreadId), mRenderThreadId(renderThreadId) {}
+ : mUiThreadId(uiThreadId)
+ , mRenderThreadId(renderThreadId)
+ , mBinding(std::make_shared<HintSessionBinding>()) {}
HintSessionWrapper::~HintSessionWrapper() {
destroy();
}
void HintSessionWrapper::destroy() {
- if (mHintSessionFuture.valid()) {
- mHintSession = mHintSessionFuture.get();
+ if (mHintSessionFuture.has_value()) {
+ mHintSession = mHintSessionFuture->get();
+ mHintSessionFuture = std::nullopt;
}
if (mHintSession) {
- gAPH_closeSessionFn(mHintSession);
+ mBinding->closeSession(mHintSession);
mSessionValid = true;
mHintSession = nullptr;
}
@@ -109,12 +76,12 @@ void HintSessionWrapper::destroy() {
bool HintSessionWrapper::init() {
if (mHintSession != nullptr) return true;
-
// If we're waiting for the session
- if (mHintSessionFuture.valid()) {
+ if (mHintSessionFuture.has_value()) {
// If the session is here
- if (mHintSessionFuture.wait_for(0s) == std::future_status::ready) {
- mHintSession = mHintSessionFuture.get();
+ if (mHintSessionFuture->wait_for(0s) == std::future_status::ready) {
+ mHintSession = mHintSessionFuture->get();
+ mHintSessionFuture = std::nullopt;
if (mHintSession != nullptr) {
mSessionValid = true;
return true;
@@ -133,9 +100,9 @@ bool HintSessionWrapper::init() {
// Assume that if we return before the end, it broke
mSessionValid = false;
- ensureAPerformanceHintBindingInitialized();
+ mBinding->init();
- APerformanceHintManager* manager = gAPH_getManagerFn();
+ APerformanceHintManager* manager = mBinding->getManager();
if (!manager) return false;
std::vector<pid_t> tids = CommonPool::getThreadIds();
@@ -145,8 +112,9 @@ bool HintSessionWrapper::init() {
// Use a placeholder target value to initialize,
// this will always be replaced elsewhere before it gets used
int64_t defaultTargetDurationNanos = 16666667;
- mHintSessionFuture = CommonPool::async([=, tids = std::move(tids)] {
- return gAPH_createSessionFn(manager, tids.data(), tids.size(), defaultTargetDurationNanos);
+ mHintSessionFuture = CommonPool::async([=, this, tids = std::move(tids)] {
+ return mBinding->createSession(manager, tids.data(), tids.size(),
+ defaultTargetDurationNanos);
});
return false;
}
@@ -158,7 +126,7 @@ void HintSessionWrapper::updateTargetWorkDuration(long targetWorkDurationNanos)
targetWorkDurationNanos > kSanityCheckLowerBound &&
targetWorkDurationNanos < kSanityCheckUpperBound) {
mLastTargetWorkDuration = targetWorkDurationNanos;
- gAPH_updateTargetWorkDurationFn(mHintSession, targetWorkDurationNanos);
+ mBinding->updateTargetWorkDuration(mHintSession, targetWorkDurationNanos);
}
mLastFrameNotification = systemTime();
}
@@ -168,8 +136,9 @@ void HintSessionWrapper::reportActualWorkDuration(long actualDurationNanos) {
mResetsSinceLastReport = 0;
if (actualDurationNanos > kSanityCheckLowerBound &&
actualDurationNanos < kSanityCheckUpperBound) {
- gAPH_reportActualWorkDurationFn(mHintSession, actualDurationNanos);
+ mBinding->reportActualWorkDuration(mHintSession, actualDurationNanos);
}
+ mLastFrameNotification = systemTime();
}
void HintSessionWrapper::sendLoadResetHint() {
@@ -179,14 +148,36 @@ void HintSessionWrapper::sendLoadResetHint() {
if (now - mLastFrameNotification > kResetHintTimeout &&
mResetsSinceLastReport <= kMaxResetsSinceLastReport) {
++mResetsSinceLastReport;
- gAPH_sendHintFn(mHintSession, static_cast<int>(SessionHint::CPU_LOAD_RESET));
+ mBinding->sendHint(mHintSession, static_cast<int32_t>(SessionHint::CPU_LOAD_RESET));
}
mLastFrameNotification = now;
}
void HintSessionWrapper::sendLoadIncreaseHint() {
if (!init()) return;
- gAPH_sendHintFn(mHintSession, static_cast<int>(SessionHint::CPU_LOAD_UP));
+ mBinding->sendHint(mHintSession, static_cast<int32_t>(SessionHint::CPU_LOAD_UP));
+ mLastFrameNotification = systemTime();
+}
+
+bool HintSessionWrapper::alive() {
+ return mHintSession != nullptr;
+}
+
+nsecs_t HintSessionWrapper::getLastUpdate() {
+ return mLastFrameNotification;
+}
+
+// Requires passing in its shared_ptr since it shouldn't own a shared_ptr to itself
+void HintSessionWrapper::delayedDestroy(RenderThread& rt, nsecs_t delay,
+ std::shared_ptr<HintSessionWrapper> wrapperPtr) {
+ nsecs_t lastUpdate = wrapperPtr->getLastUpdate();
+ rt.queue().postDelayed(delay, [lastUpdate = lastUpdate, wrapper = wrapperPtr]() mutable {
+ if (wrapper->getLastUpdate() == lastUpdate) {
+ wrapper->destroy();
+ }
+ // Ensure the shared_ptr is killed at the end of the method
+ wrapper = nullptr;
+ });
}
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/HintSessionWrapper.h b/libs/hwui/renderthread/HintSessionWrapper.h
index bdb9959b1ca7..36e91ea33c75 100644
--- a/libs/hwui/renderthread/HintSessionWrapper.h
+++ b/libs/hwui/renderthread/HintSessionWrapper.h
@@ -19,6 +19,7 @@
#include <android/performance_hint.h>
#include <future>
+#include <optional>
#include "utils/TimeUtils.h"
@@ -27,8 +28,12 @@ namespace uirenderer {
namespace renderthread {
+class RenderThread;
+
class HintSessionWrapper {
public:
+ friend class HintSessionWrapperTests;
+
HintSessionWrapper(pid_t uiThreadId, pid_t renderThreadId);
~HintSessionWrapper();
@@ -38,10 +43,15 @@ public:
void sendLoadIncreaseHint();
bool init();
void destroy();
+ bool alive();
+ nsecs_t getLastUpdate();
+ void delayedDestroy(renderthread::RenderThread& rt, nsecs_t delay,
+ std::shared_ptr<HintSessionWrapper> wrapperPtr);
private:
APerformanceHintSession* mHintSession = nullptr;
- std::future<APerformanceHintSession*> mHintSessionFuture;
+ // This needs to work concurrently for testing
+ std::optional<std::shared_future<APerformanceHintSession*>> mHintSessionFuture;
int mResetsSinceLastReport = 0;
nsecs_t mLastFrameNotification = 0;
@@ -55,6 +65,28 @@ private:
static constexpr nsecs_t kResetHintTimeout = 100_ms;
static constexpr int64_t kSanityCheckLowerBound = 100_us;
static constexpr int64_t kSanityCheckUpperBound = 10_s;
+
+ // Allows easier stub when testing
+ class HintSessionBinding {
+ public:
+ virtual ~HintSessionBinding() = default;
+ virtual void init();
+ APerformanceHintManager* (*getManager)();
+ APerformanceHintSession* (*createSession)(APerformanceHintManager* manager,
+ const int32_t* tids, size_t tidCount,
+ int64_t defaultTarget) = nullptr;
+ void (*closeSession)(APerformanceHintSession* session) = nullptr;
+ void (*updateTargetWorkDuration)(APerformanceHintSession* session,
+ int64_t targetDuration) = nullptr;
+ void (*reportActualWorkDuration)(APerformanceHintSession* session,
+ int64_t actualDuration) = nullptr;
+ void (*sendHint)(APerformanceHintSession* session, int32_t hintId) = nullptr;
+
+ private:
+ bool mInitialized = false;
+ };
+
+ std::shared_ptr<HintSessionBinding> mBinding;
};
} /* namespace renderthread */
diff --git a/libs/hwui/tests/unit/HintSessionWrapperTests.cpp b/libs/hwui/tests/unit/HintSessionWrapperTests.cpp
new file mode 100644
index 000000000000..5a10f4f959aa
--- /dev/null
+++ b/libs/hwui/tests/unit/HintSessionWrapperTests.cpp
@@ -0,0 +1,287 @@
+/*
+ * 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.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <private/performance_hint_private.h>
+#include <renderthread/HintSessionWrapper.h>
+#include <utils/Log.h>
+
+#include <chrono>
+
+#include "Properties.h"
+#include "tests/common/TestUtils.h"
+
+using namespace testing;
+using namespace std::chrono_literals;
+using namespace android::uirenderer::renderthread;
+
+APerformanceHintManager* managerPtr = reinterpret_cast<APerformanceHintManager*>(123);
+APerformanceHintSession* sessionPtr = reinterpret_cast<APerformanceHintSession*>(456);
+int uiThreadId = 1;
+int renderThreadId = 2;
+
+namespace android::uirenderer::renderthread {
+
+class HintSessionWrapperTests : public testing::Test {
+public:
+ void SetUp() override;
+ void TearDown() override;
+
+protected:
+ std::shared_ptr<HintSessionWrapper> mWrapper;
+
+ std::promise<int> blockDestroyCallUntil;
+ std::promise<int> waitForDestroyFinished;
+
+ class MockHintSessionBinding : public HintSessionWrapper::HintSessionBinding {
+ public:
+ void init() override;
+
+ MOCK_METHOD(APerformanceHintManager*, fakeGetManager, ());
+ MOCK_METHOD(APerformanceHintSession*, fakeCreateSession,
+ (APerformanceHintManager*, const int32_t*, size_t, int64_t));
+ MOCK_METHOD(void, fakeCloseSession, (APerformanceHintSession*));
+ MOCK_METHOD(void, fakeUpdateTargetWorkDuration, (APerformanceHintSession*, int64_t));
+ MOCK_METHOD(void, fakeReportActualWorkDuration, (APerformanceHintSession*, int64_t));
+ MOCK_METHOD(void, fakeSendHint, (APerformanceHintSession*, int32_t));
+ // Needs to be on the binding so it can be accessed from static methods
+ std::promise<int> allowCreationToFinish;
+ };
+
+ // Must be static so it can have function pointers we can point to with static methods
+ static std::shared_ptr<MockHintSessionBinding> sMockBinding;
+
+ static void allowCreationToFinish() { sMockBinding->allowCreationToFinish.set_value(1); }
+ void allowDelayedDestructionToStart() { blockDestroyCallUntil.set_value(1); }
+ void waitForDelayedDestructionToFinish() { waitForDestroyFinished.get_future().wait(); }
+
+ // Must be static so we can point to them as normal fn pointers with HintSessionBinding
+ static APerformanceHintManager* stubGetManager() { return sMockBinding->fakeGetManager(); };
+ static APerformanceHintSession* stubCreateSession(APerformanceHintManager* manager,
+ const int32_t* ids, size_t idsSize,
+ int64_t initialTarget) {
+ return sMockBinding->fakeCreateSession(manager, ids, idsSize, initialTarget);
+ }
+ static APerformanceHintSession* stubManagedCreateSession(APerformanceHintManager* manager,
+ const int32_t* ids, size_t idsSize,
+ int64_t initialTarget) {
+ sMockBinding->allowCreationToFinish.get_future().wait();
+ return sMockBinding->fakeCreateSession(manager, ids, idsSize, initialTarget);
+ }
+ static APerformanceHintSession* stubSlowCreateSession(APerformanceHintManager* manager,
+ const int32_t* ids, size_t idsSize,
+ int64_t initialTarget) {
+ std::this_thread::sleep_for(50ms);
+ return sMockBinding->fakeCreateSession(manager, ids, idsSize, initialTarget);
+ }
+ static void stubCloseSession(APerformanceHintSession* session) {
+ sMockBinding->fakeCloseSession(session);
+ };
+ static void stubUpdateTargetWorkDuration(APerformanceHintSession* session,
+ int64_t workDuration) {
+ sMockBinding->fakeUpdateTargetWorkDuration(session, workDuration);
+ }
+ static void stubReportActualWorkDuration(APerformanceHintSession* session,
+ int64_t workDuration) {
+ sMockBinding->fakeReportActualWorkDuration(session, workDuration);
+ }
+ static void stubSendHint(APerformanceHintSession* session, int32_t hintId) {
+ sMockBinding->fakeSendHint(session, hintId);
+ };
+ void waitForWrapperReady() {
+ if (mWrapper->mHintSessionFuture.has_value()) {
+ mWrapper->mHintSessionFuture->wait();
+ }
+ }
+ void scheduleDelayedDestroyManaged() {
+ TestUtils::runOnRenderThread([&](renderthread::RenderThread& rt) {
+ // Guaranteed to be scheduled first, allows destruction to start
+ rt.queue().postDelayed(0_ms, [&] { blockDestroyCallUntil.get_future().wait(); });
+ // Guaranteed to be scheduled second, destroys the session
+ mWrapper->delayedDestroy(rt, 1_ms, mWrapper);
+ // This is guaranteed to be queued after the destroy, signals that destruction is done
+ rt.queue().postDelayed(1_ms, [&] { waitForDestroyFinished.set_value(1); });
+ });
+ }
+};
+
+std::shared_ptr<HintSessionWrapperTests::MockHintSessionBinding>
+ HintSessionWrapperTests::sMockBinding;
+
+void HintSessionWrapperTests::SetUp() {
+ // Pretend it's supported even if we're in an emulator
+ Properties::useHintManager = true;
+ sMockBinding = std::make_shared<NiceMock<MockHintSessionBinding>>();
+ mWrapper = std::make_shared<HintSessionWrapper>(uiThreadId, renderThreadId);
+ mWrapper->mBinding = sMockBinding;
+ EXPECT_CALL(*sMockBinding, fakeGetManager).WillOnce(Return(managerPtr));
+ ON_CALL(*sMockBinding, fakeCreateSession).WillByDefault(Return(sessionPtr));
+}
+
+void HintSessionWrapperTests::MockHintSessionBinding::init() {
+ sMockBinding->getManager = &stubGetManager;
+ if (sMockBinding->createSession == nullptr) {
+ sMockBinding->createSession = &stubCreateSession;
+ }
+ sMockBinding->closeSession = &stubCloseSession;
+ sMockBinding->updateTargetWorkDuration = &stubUpdateTargetWorkDuration;
+ sMockBinding->reportActualWorkDuration = &stubReportActualWorkDuration;
+ sMockBinding->sendHint = &stubSendHint;
+}
+
+void HintSessionWrapperTests::TearDown() {
+ // Ensure that anything running on RT is completely finished
+ mWrapper = nullptr;
+ sMockBinding = nullptr;
+}
+
+TEST_F(HintSessionWrapperTests, destructorClosesBackgroundSession) {
+ EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
+ sMockBinding->createSession = stubSlowCreateSession;
+ mWrapper->init();
+ mWrapper = nullptr;
+ Mock::VerifyAndClearExpectations(sMockBinding.get());
+}
+
+TEST_F(HintSessionWrapperTests, sessionInitializesCorrectly) {
+ EXPECT_CALL(*sMockBinding, fakeCreateSession(managerPtr, _, Gt(1), _)).Times(1);
+ mWrapper->init();
+ waitForWrapperReady();
+}
+
+TEST_F(HintSessionWrapperTests, loadUpHintsSendCorrectly) {
+ EXPECT_CALL(*sMockBinding,
+ fakeSendHint(sessionPtr, static_cast<int32_t>(SessionHint::CPU_LOAD_UP)))
+ .Times(1);
+ mWrapper->init();
+ waitForWrapperReady();
+ mWrapper->sendLoadIncreaseHint();
+}
+
+TEST_F(HintSessionWrapperTests, loadResetHintsSendCorrectly) {
+ EXPECT_CALL(*sMockBinding,
+ fakeSendHint(sessionPtr, static_cast<int32_t>(SessionHint::CPU_LOAD_RESET)))
+ .Times(1);
+ mWrapper->init();
+ waitForWrapperReady();
+ mWrapper->sendLoadResetHint();
+}
+
+TEST_F(HintSessionWrapperTests, delayedDeletionWorksCorrectlyAndOnlyClosesOnce) {
+ EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
+ mWrapper->init();
+ waitForWrapperReady();
+ // Init a second time just to ensure the wrapper grabs the promise value
+ mWrapper->init();
+
+ EXPECT_EQ(mWrapper->alive(), true);
+
+ // Schedule delayed destruction, allow it to run, and check when it's done
+ scheduleDelayedDestroyManaged();
+ allowDelayedDestructionToStart();
+ waitForDelayedDestructionToFinish();
+
+ // Ensure it closed within the timeframe of the test
+ Mock::VerifyAndClearExpectations(sMockBinding.get());
+ EXPECT_EQ(mWrapper->alive(), false);
+ // If we then delete the wrapper, it shouldn't close the session again
+ EXPECT_CALL(*sMockBinding, fakeCloseSession(_)).Times(0);
+ mWrapper = nullptr;
+}
+
+TEST_F(HintSessionWrapperTests, delayedDeletionResolvesBeforeAsyncCreationFinishes) {
+ // Here we test whether queueing delayedDestroy works while creation is still happening, if
+ // creation happens after
+ EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
+ sMockBinding->createSession = &stubManagedCreateSession;
+
+ // Start creating the session and destroying it at the same time
+ mWrapper->init();
+ scheduleDelayedDestroyManaged();
+
+ // Allow destruction to happen first
+ allowDelayedDestructionToStart();
+
+ // Make sure destruction has had time to happen
+ std::this_thread::sleep_for(50ms);
+
+ // Then, allow creation to finish after delayed destroy runs
+ allowCreationToFinish();
+
+ // Wait for destruction to finish
+ waitForDelayedDestructionToFinish();
+
+ // Ensure it closed within the timeframe of the test
+ Mock::VerifyAndClearExpectations(sMockBinding.get());
+ EXPECT_EQ(mWrapper->alive(), false);
+}
+
+TEST_F(HintSessionWrapperTests, delayedDeletionResolvesAfterAsyncCreationFinishes) {
+ // Here we test whether queueing delayedDestroy works while creation is still happening, if
+ // creation happens before
+ EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
+ sMockBinding->createSession = &stubManagedCreateSession;
+
+ // Start creating the session and destroying it at the same time
+ mWrapper->init();
+ scheduleDelayedDestroyManaged();
+
+ // Allow creation to happen first
+ allowCreationToFinish();
+
+ // Make sure creation has had time to happen
+ waitForWrapperReady();
+
+ // Then allow destruction to happen after creation is done
+ allowDelayedDestructionToStart();
+
+ // Wait for it to finish
+ waitForDelayedDestructionToFinish();
+
+ // Ensure it closed within the timeframe of the test
+ Mock::VerifyAndClearExpectations(sMockBinding.get());
+ EXPECT_EQ(mWrapper->alive(), false);
+}
+
+TEST_F(HintSessionWrapperTests, delayedDeletionDoesNotKillReusedSession) {
+ EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(0);
+ EXPECT_CALL(*sMockBinding,
+ fakeSendHint(sessionPtr, static_cast<int32_t>(SessionHint::CPU_LOAD_UP)))
+ .Times(1);
+
+ mWrapper->init();
+ waitForWrapperReady();
+ // Init a second time just to grab the wrapper from the promise
+ mWrapper->init();
+ EXPECT_EQ(mWrapper->alive(), true);
+
+ // First schedule the deletion
+ scheduleDelayedDestroyManaged();
+
+ // Then, send a hint to update the timestamp
+ mWrapper->sendLoadIncreaseHint();
+
+ // Then, run the delayed deletion after sending the update
+ allowDelayedDestructionToStart();
+ waitForDelayedDestructionToFinish();
+
+ // Ensure it didn't close within the timeframe of the test
+ Mock::VerifyAndClearExpectations(sMockBinding.get());
+ EXPECT_EQ(mWrapper->alive(), true);
+}
+
+} // namespace android::uirenderer::renderthread
diff --git a/services/autofill/java/com/android/server/autofill/FieldClassificationEventLogger.java b/services/autofill/java/com/android/server/autofill/FieldClassificationEventLogger.java
index ffb4632164a3..db2e18a0fd57 100644
--- a/services/autofill/java/com/android/server/autofill/FieldClassificationEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/FieldClassificationEventLogger.java
@@ -17,12 +17,19 @@
package com.android.server.autofill;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED__STATUS__STATUS_CANCELLED;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED__STATUS__STATUS_FAIL;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED__STATUS__STATUS_SUCCESS;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED__STATUS__STATUS_UNKNOWN;
import static com.android.server.autofill.Helper.sVerbose;
+import android.annotation.IntDef;
import android.util.Slog;
import com.android.internal.util.FrameworkStatsLog;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Optional;
/**
@@ -36,6 +43,29 @@ public final class FieldClassificationEventLogger {
mEventInternal = Optional.empty();
}
+ public static final int STATUS_SUCCESS =
+ AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED__STATUS__STATUS_SUCCESS;
+ public static final int STATUS_UNKNOWN =
+ AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED__STATUS__STATUS_UNKNOWN;
+ public static final int STATUS_FAIL =
+ AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED__STATUS__STATUS_FAIL;
+ public static final int STATUS_CANCELLED =
+ AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED__STATUS__STATUS_CANCELLED;
+
+ /**
+ * Status of the FieldClassification IPC request. These are wrappers around
+ * {@link com.android.os.AtomsProto.AutofillFieldClassificationEventReported.FieldClassificationRequestStatus}.
+ */
+ @IntDef(prefix = {"STATUS"}, value = {
+ STATUS_UNKNOWN,
+ STATUS_SUCCESS,
+ STATUS_FAIL,
+ STATUS_CANCELLED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FieldClassificationStatus {
+ }
+
/**
* A factory constructor to create FieldClassificationEventLogger.
*/
@@ -56,7 +86,7 @@ public final class FieldClassificationEventLogger {
}
/**
- * Set latency as long as mEventInternal presents.
+ * Set latency_millis as long as mEventInternal presents.
*/
public void maybeSetLatencyMillis(long timestamp) {
mEventInternal.ifPresent(event -> {
@@ -65,6 +95,69 @@ public final class FieldClassificationEventLogger {
}
/**
+ * Set count_classifications as long as mEventInternal presents.
+ */
+ public void maybeSetCountClassifications(int countClassifications) {
+ mEventInternal.ifPresent(event -> {
+ event.mCountClassifications = countClassifications;
+ });
+ }
+
+ /**
+ * Set session_id as long as mEventInternal presents.
+ */
+ public void maybeSetSessionId(int sessionId) {
+ mEventInternal.ifPresent(event -> {
+ event.mSessionId = sessionId;
+ });
+ }
+
+ /**
+ * Set request_id as long as mEventInternal presents.
+ */
+ public void maybeSetRequestId(int requestId) {
+ mEventInternal.ifPresent(event -> {
+ event.mRequestId = requestId;
+ });
+ }
+
+ /**
+ * Set next_fill_request_id as long as mEventInternal presents.
+ */
+ public void maybeSetNextFillRequestId(int nextFillRequestId) {
+ mEventInternal.ifPresent(event -> {
+ event.mNextFillRequestId = nextFillRequestId;
+ });
+ }
+
+ /**
+ * Set app_package_uid as long as mEventInternal presents.
+ */
+ public void maybeSetAppPackageUid(int uid) {
+ mEventInternal.ifPresent(event -> {
+ event.mAppPackageUid = uid;
+ });
+ }
+
+ /**
+ * Set status as long as mEventInternal presents.
+ */
+ public void maybeSetRequestStatus(@FieldClassificationStatus int status) {
+ mEventInternal.ifPresent(event -> {
+ event.mStatus = status;
+ });
+ }
+
+ /**
+ * Set is_session_gc as long as mEventInternal presents.
+ */
+ public void maybeSetSessionGc(boolean isSessionGc) {
+ mEventInternal.ifPresent(event -> {
+ event.mIsSessionGc = isSessionGc;
+ });
+ }
+
+ /**
* Log an AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED event.
*/
public void logAndEndEvent() {
@@ -77,16 +170,37 @@ public final class FieldClassificationEventLogger {
if (sVerbose) {
Slog.v(TAG, "Log AutofillFieldClassificationEventReported:"
+ " mLatencyClassificationRequestMillis="
- + event.mLatencyClassificationRequestMillis);
+ + event.mLatencyClassificationRequestMillis
+ + " mCountClassifications=" + event.mCountClassifications
+ + " mSessionId=" + event.mSessionId
+ + " mRequestId=" + event.mRequestId
+ + " mNextFillRequestId=" + event.mNextFillRequestId
+ + " mAppPackageUid=" + event.mAppPackageUid
+ + " mStatus=" + event.mStatus
+ + " mIsSessionGc=" + event.mIsSessionGc);
}
FrameworkStatsLog.write(
AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED,
- event.mLatencyClassificationRequestMillis);
+ event.mLatencyClassificationRequestMillis,
+ event.mCountClassifications,
+ event.mSessionId,
+ event.mRequestId,
+ event.mNextFillRequestId,
+ event.mAppPackageUid,
+ event.mStatus,
+ event.mIsSessionGc);
mEventInternal = Optional.empty();
}
private static final class FieldClassificationEventInternal {
long mLatencyClassificationRequestMillis = -1;
+ int mCountClassifications = -1;
+ int mSessionId = -1;
+ int mRequestId = -1;
+ int mNextFillRequestId = -1;
+ int mAppPackageUid = -1;
+ int mStatus;
+ boolean mIsSessionGc;
FieldClassificationEventInternal() {
}
diff --git a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
index 11b45db29d5d..6b0fdb5f7fe0 100644
--- a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
@@ -211,15 +211,25 @@ public final class PresentationStatsEventLogger {
public static final int DETECTION_PREFER_PCC =
AUTOFILL_FILL_RESPONSE_REPORTED__DETECTION_PREFERENCE__DETECTION_PREFER_PCC;
private final int mSessionId;
+
+ /**
+ * For app_package_uid.
+ */
+ private final int mCallingAppUid;
private Optional<PresentationStatsEventInternal> mEventInternal;
- private PresentationStatsEventLogger(int sessionId) {
+ private PresentationStatsEventLogger(int sessionId, int callingAppUid) {
mSessionId = sessionId;
+ mCallingAppUid = callingAppUid;
mEventInternal = Optional.empty();
}
- public static PresentationStatsEventLogger forSessionId(int sessionId) {
- return new PresentationStatsEventLogger(sessionId);
+ /**
+ * Create PresentationStatsEventLogger, populated with sessionId and the callingAppUid
+ */
+ public static PresentationStatsEventLogger createPresentationLog(
+ int sessionId, int callingAppUid) {
+ return new PresentationStatsEventLogger(sessionId, callingAppUid);
}
public void startNewEvent() {
@@ -508,6 +518,14 @@ public final class PresentationStatsEventLogger {
return PICK_REASON_UNKNOWN;
}
+ /**
+ * Set field_classification_request_id as long as mEventInternal presents.
+ */
+ public void maybeSetFieldClassificationRequestId(int requestId) {
+ mEventInternal.ifPresent(event -> {
+ event.mFieldClassificationRequestId = requestId;
+ });
+ }
public void logAndEndEvent() {
if (!mEventInternal.isPresent()) {
@@ -547,7 +565,9 @@ public final class PresentationStatsEventLogger {
+ " mAvailablePccCount=" + event.mAvailablePccCount
+ " mAvailablePccOnlyCount=" + event.mAvailablePccOnlyCount
+ " mSelectedDatasetPickedReason=" + event.mSelectedDatasetPickedReason
- + " mDetectionPreference=" + event.mDetectionPreference);
+ + " mDetectionPreference=" + event.mDetectionPreference
+ + " mFieldClassificationRequestId=" + event.mFieldClassificationRequestId
+ + " mAppPackageUid=" + mCallingAppUid);
}
// TODO(b/234185326): Distinguish empty responses from other no presentation reasons.
@@ -584,7 +604,9 @@ public final class PresentationStatsEventLogger {
event.mAvailablePccCount,
event.mAvailablePccOnlyCount,
event.mSelectedDatasetPickedReason,
- event.mDetectionPreference);
+ event.mDetectionPreference,
+ event.mFieldClassificationRequestId,
+ mCallingAppUid);
mEventInternal = Optional.empty();
}
@@ -617,6 +639,7 @@ public final class PresentationStatsEventLogger {
int mAvailablePccOnlyCount = -1;
@DatasetPickedReason int mSelectedDatasetPickedReason = PICK_REASON_UNKNOWN;
@DetectionPreference int mDetectionPreference = DETECTION_PREFER_UNKNOWN;
+ int mFieldClassificationRequestId = -1;
PresentationStatsEventInternal() {}
}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFieldClassificationService.java b/services/autofill/java/com/android/server/autofill/RemoteFieldClassificationService.java
index bcca0069eb34..50fabfdaef83 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFieldClassificationService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFieldClassificationService.java
@@ -69,6 +69,9 @@ final class RemoteFieldClassificationService
void onClassificationRequestFailure(int requestId, @Nullable CharSequence message);
void onClassificationRequestTimeout(int requestId);
void onServiceDied(@NonNull RemoteFieldClassificationService service);
+ void logFieldClassificationEvent(
+ long startTime, @NonNull FieldClassificationResponse response,
+ @FieldClassificationEventLogger.FieldClassificationStatus int status);
}
RemoteFieldClassificationService(Context context, ComponentName serviceName,
@@ -149,15 +152,24 @@ final class RemoteFieldClassificationService
new IFieldClassificationCallback.Stub() {
@Override
public void onCancellable(ICancellationSignal cancellation) {
- logLatency(startTime);
if (sDebug) {
Log.d(TAG, "onCancellable");
}
+ FieldClassificationServiceCallbacks
+ fieldClassificationServiceCallbacks =
+ Helper.weakDeref(
+ fieldClassificationServiceCallbacksWeakRef,
+ TAG, "onCancellable "
+ );
+ logFieldClassificationEvent(
+ startTime,
+ fieldClassificationServiceCallbacks,
+ FieldClassificationEventLogger.STATUS_CANCELLED,
+ null);
}
@Override
public void onSuccess(FieldClassificationResponse response) {
- logLatency(startTime);
if (sDebug) {
if (Build.IS_DEBUGGABLE) {
Slog.d(TAG, "onSuccess Response: " + response);
@@ -179,6 +191,11 @@ final class RemoteFieldClassificationService
fieldClassificationServiceCallbacksWeakRef,
TAG, "onSuccess "
);
+ logFieldClassificationEvent(
+ startTime,
+ fieldClassificationServiceCallbacks,
+ FieldClassificationEventLogger.STATUS_SUCCESS,
+ response);
if (fieldClassificationServiceCallbacks == null) {
return;
}
@@ -188,7 +205,6 @@ final class RemoteFieldClassificationService
@Override
public void onFailure() {
- logLatency(startTime);
if (sDebug) {
Slog.d(TAG, "onFailure");
}
@@ -198,6 +214,11 @@ final class RemoteFieldClassificationService
fieldClassificationServiceCallbacksWeakRef,
TAG, "onFailure "
);
+ logFieldClassificationEvent(
+ startTime,
+ fieldClassificationServiceCallbacks,
+ FieldClassificationEventLogger.STATUS_FAIL,
+ null);
if (fieldClassificationServiceCallbacks == null) {
return;
}
@@ -215,11 +236,24 @@ final class RemoteFieldClassificationService
}));
}
- private void logLatency(long startTime) {
- final FieldClassificationEventLogger logger = FieldClassificationEventLogger.createLogger();
- logger.startNewLogForRequest();
- logger.maybeSetLatencyMillis(
- SystemClock.elapsedRealtime() - startTime);
- logger.logAndEndEvent();
+ private void logFieldClassificationEvent(
+ long startTime,
+ @Nullable FieldClassificationServiceCallbacks fieldClassificationServiceCallbacks,
+ @FieldClassificationEventLogger.FieldClassificationStatus int status,
+ FieldClassificationResponse response) {
+ if (fieldClassificationServiceCallbacks == null) {
+ final FieldClassificationEventLogger logger =
+ FieldClassificationEventLogger.createLogger();
+ logger.startNewLogForRequest();
+ logger.maybeSetLatencyMillis(
+ SystemClock.elapsedRealtime() - startTime);
+ logger.maybeSetSessionGc(true);
+ logger.maybeSetRequestStatus(status);
+ logger.logAndEndEvent();
+ } else {
+ fieldClassificationServiceCallbacks.logFieldClassificationEvent(
+ startTime, response, status);
+ }
+
}
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index d6702b7ecf9d..da9235f0e257 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -53,8 +53,8 @@ import static com.android.server.autofill.FillRequestEventLogger.TRIGGER_REASON_
import static com.android.server.autofill.FillRequestEventLogger.TRIGGER_REASON_SERVED_FROM_CACHED_RESPONSE;
import static com.android.server.autofill.FillResponseEventLogger.AVAILABLE_COUNT_WHEN_FILL_REQUEST_FAILED_OR_TIMEOUT;
import static com.android.server.autofill.FillResponseEventLogger.DETECTION_PREFER_AUTOFILL_PROVIDER;
-import static com.android.server.autofill.FillResponseEventLogger.DETECTION_PREFER_UNKNOWN;
import static com.android.server.autofill.FillResponseEventLogger.DETECTION_PREFER_PCC;
+import static com.android.server.autofill.FillResponseEventLogger.DETECTION_PREFER_UNKNOWN;
import static com.android.server.autofill.FillResponseEventLogger.HAVE_SAVE_TRIGGER_ID;
import static com.android.server.autofill.FillResponseEventLogger.RESPONSE_STATUS_FAILURE;
import static com.android.server.autofill.FillResponseEventLogger.RESPONSE_STATUS_SESSION_DESTROYED;
@@ -228,6 +228,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
private static final String PCC_HINTS_DELIMITER = ",";
public static final String EXTRA_KEY_DETECTIONS = "detections";
+ private static final int DEFAULT__FILL_REQUEST_ID_SNAPSHOT = -2;
+ private static final int DEFAULT__FIELD_CLASSIFICATION_REQUEST_ID_SNAPSHOT = -2;
final Object mLock;
@@ -411,6 +413,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@GuardedBy("mLock")
private long mUiShownTime;
+ /**
+ * Tracks the value of the fill request id at the time of issuing request for field
+ * classification.
+ */
+ @GuardedBy("mLock")
+ private int mFillRequestIdSnapshot = DEFAULT__FILL_REQUEST_ID_SNAPSHOT;
+
+ /**
+ * Tracks the value of the field classification id at the time of issuing request for fill
+ * request.
+ */
+ @GuardedBy("mLock")
+ private int mFieldClassificationIdSnapshot = DEFAULT__FIELD_CLASSIFICATION_REQUEST_ID_SNAPSHOT;
+
@GuardedBy("mLock")
private final LocalLog mUiLatencyHistory;
@@ -660,6 +676,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (mPendingFillRequest == null) {
return;
}
+ mFieldClassificationIdSnapshot = sIdCounterForPcc.get();
if (mWaitForInlineRequest) {
if (mPendingInlineSuggestionsRequest == null) {
@@ -1215,6 +1232,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
+ ", flags=" + flags);
}
mPresentationStatsEventLogger.maybeSetRequestId(requestId);
+ mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId(
+ mFieldClassificationIdSnapshot);
mFillRequestEventLogger.maybeSetRequestId(requestId);
mFillRequestEventLogger.maybeSetAutofillServiceUid(getAutofillServiceUid());
if (mSessionFlags.mInlineSupportedByService) {
@@ -1284,6 +1303,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@GuardedBy("mLock")
private void requestAssistStructureForPccLocked(int flags) {
if (!mClassificationState.shouldTriggerRequest()) return;
+ mFillRequestIdSnapshot = sIdCounter.get();
mClassificationState.updatePendingRequest();
// Get request id
int requestId;
@@ -1368,7 +1388,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mStartTime = SystemClock.elapsedRealtime();
mLatencyBaseTime = mStartTime;
mRequestCount = 0;
- mPresentationStatsEventLogger = PresentationStatsEventLogger.forSessionId(sessionId);
+ mPresentationStatsEventLogger = PresentationStatsEventLogger.createPresentationLog(
+ sessionId, uid);
mFillRequestEventLogger = FillRequestEventLogger.forSessionId(sessionId);
mFillResponseEventLogger = FillResponseEventLogger.forSessionId(sessionId);
mSessionCommittedEventLogger = SessionCommittedEventLogger.forSessionId(sessionId);
@@ -4206,6 +4227,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (viewState.getResponse() != null) {
FillResponse response = viewState.getResponse();
mPresentationStatsEventLogger.maybeSetRequestId(response.getRequestId());
+ mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId(
+ mFieldClassificationIdSnapshot);
mPresentationStatsEventLogger.maybeSetAvailableCount(
response.getDatasets(), mCurrentViewId);
}
@@ -4375,6 +4398,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
public void onFillReady(@NonNull FillResponse response, @NonNull AutofillId filledId,
@Nullable AutofillValue value, int flags) {
synchronized (mLock) {
+ mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId(
+ mFieldClassificationIdSnapshot);
if (mDestroyed) {
Slog.w(TAG, "Call to Session#onFillReady() rejected - session: "
+ id + " destroyed");
@@ -5266,6 +5291,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
List<Dataset> datasetList = newResponse.getDatasets();
+ mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId(sIdCounterForPcc.get());
mPresentationStatsEventLogger.maybeSetAvailableCount(datasetList, mCurrentViewId);
mFillResponseEventLogger.maybeSetDatasetsCountAfterPotentialPccFiltering(datasetList);
@@ -6308,7 +6334,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return serviceInfo == null ? Process.INVALID_UID : serviceInfo.applicationInfo.uid;
}
- // FieldClassificationServiceCallbacks
+ // FieldClassificationServiceCallbacks start
public void onClassificationRequestSuccess(@Nullable FieldClassificationResponse response) {
mClassificationState.updateResponseReceived(response);
}
@@ -6329,6 +6355,28 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// forceRemoveFromServiceLocked();
}
}
- // DetectionServiceCallbacks end
+
+ @Override
+ public void logFieldClassificationEvent(
+ long startTime, FieldClassificationResponse response,
+ @FieldClassificationEventLogger.FieldClassificationStatus int status) {
+ final FieldClassificationEventLogger logger = FieldClassificationEventLogger.createLogger();
+ logger.startNewLogForRequest();
+ logger.maybeSetLatencyMillis(
+ SystemClock.elapsedRealtime() - startTime);
+ logger.maybeSetAppPackageUid(uid);
+ logger.maybeSetNextFillRequestId(mFillRequestIdSnapshot + 1);
+ logger.maybeSetRequestId(sIdCounterForPcc.get());
+ logger.maybeSetSessionId(id);
+ int count = -1;
+ if (response != null) {
+ count = response.getClassifications().size();
+ }
+ logger.maybeSetRequestStatus(status);
+ logger.maybeSetCountClassifications(count);
+ logger.logAndEndEvent();
+ mFillRequestIdSnapshot = DEFAULT__FILL_REQUEST_ID_SNAPSHOT;
+ }
+ // FieldClassificationServiceCallbacks end
}
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index d94f4f22f2c9..556eba6ced76 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -167,6 +167,8 @@ public final class BatteryService extends SystemService {
private int mBatteryNearlyFullLevel;
private int mShutdownBatteryTemperature;
+ private static String sSystemUiPackage;
+
private int mPlugType;
private int mLastPlugType = -1; // Extra state so we can detect first run
@@ -228,6 +230,8 @@ public final class BatteryService extends SystemService {
com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
mShutdownBatteryTemperature = mContext.getResources().getInteger(
com.android.internal.R.integer.config_shutdownBatteryTemperature);
+ sSystemUiPackage = mContext.getResources().getString(
+ com.android.internal.R.string.config_systemUi);
mBatteryLevelsEventQueue = new ArrayDeque<>();
mMetricsLogger = new MetricsLogger();
@@ -750,8 +754,21 @@ public final class BatteryService extends SystemService {
+ ", info:" + mHealthInfo.toString());
}
- mHandler.post(() -> ActivityManager.broadcastStickyIntent(intent, AppOpsManager.OP_NONE,
- mBatteryChangedOptions, UserHandle.USER_ALL));
+ mHandler.post(() -> broadcastBatteryChangedIntent(intent, mBatteryChangedOptions));
+ }
+
+ private static void broadcastBatteryChangedIntent(Intent intent, Bundle options) {
+ // TODO (293959093): It is important that SystemUI receives this broadcast as soon as
+ // possible. Ideally, it should be using binder callbacks but until then, dispatch this
+ // as a foreground broadcast to SystemUI.
+ final Intent fgIntent = new Intent(intent);
+ fgIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ fgIntent.setPackage(sSystemUiPackage);
+ ActivityManager.broadcastStickyIntent(fgIntent, AppOpsManager.OP_NONE,
+ options, UserHandle.USER_ALL);
+
+ ActivityManager.broadcastStickyIntent(intent, new String[] {sSystemUiPackage},
+ AppOpsManager.OP_NONE, options, UserHandle.USER_ALL);
}
private void sendBatteryLevelChangedIntentLocked() {
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index 0c9cb3bd00f8..675bb87b58cd 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -418,59 +418,6 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
}
- public enum FrameRate {
- FPS_DEFAULT(0),
- FPS_30(30),
- FPS_36(36),
- FPS_40(40),
- FPS_45(45),
- FPS_48(48),
- FPS_60(60),
- FPS_72(72),
- FPS_90(90),
- FPS_120(120),
- FPS_144(144),
- FPS_INVALID(-1);
-
- public final int fps;
-
- FrameRate(int fps) {
- this.fps = fps;
- }
- }
-
- // Turn the raw string to the corresponding fps int.
- // Return 0 when disabling, -1 for invalid fps.
- static int getFpsInt(String raw) {
- // TODO(b/243448953): make sure this translates to proper values based on current display
- switch (raw) {
- case "30":
- return FrameRate.FPS_30.fps;
- case "36":
- return FrameRate.FPS_36.fps;
- case "40":
- return FrameRate.FPS_40.fps;
- case "45":
- return FrameRate.FPS_45.fps;
- case "48":
- return FrameRate.FPS_48.fps;
- case "60":
- return FrameRate.FPS_60.fps;
- case "72":
- return FrameRate.FPS_72.fps;
- case "90":
- return FrameRate.FPS_90.fps;
- case "120":
- return FrameRate.FPS_120.fps;
- case "144":
- return FrameRate.FPS_144.fps;
- case "disable":
- case "":
- return FrameRate.FPS_DEFAULT.fps;
- }
- return FrameRate.FPS_INVALID.fps;
- }
-
/**
* Called by games to communicate the current state to the platform.
*
@@ -718,7 +665,12 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
public synchronized int getFps() {
- return GameManagerService.getFpsInt(mFps);
+ try {
+ final int fpsInt = Integer.parseInt(mFps);
+ return fpsInt;
+ } catch (NumberFormatException e) {
+ return 0;
+ }
}
synchronized String getFpsStr() {
@@ -758,7 +710,12 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
android.app.GameModeConfiguration toPublicGameModeConfig() {
- int fpsOverride = getFpsInt(mFps);
+ int fpsOverride;
+ try {
+ fpsOverride = Integer.parseInt(mFps);
+ } catch (NumberFormatException e) {
+ fpsOverride = 0;
+ }
// TODO(b/243448953): match to proper value in case of display change?
fpsOverride = fpsOverride > 0 ? fpsOverride
: android.app.GameModeConfiguration.FPS_OVERRIDE_NONE;
diff --git a/services/core/java/com/android/server/app/GameManagerShellCommand.java b/services/core/java/com/android/server/app/GameManagerShellCommand.java
index 00ff489ee0ff..ab57c4fe837e 100644
--- a/services/core/java/com/android/server/app/GameManagerShellCommand.java
+++ b/services/core/java/com/android/server/app/GameManagerShellCommand.java
@@ -241,8 +241,10 @@ public class GameManagerShellCommand extends ShellCommand {
case "--fps":
if (fpsStr == null) {
fpsStr = getNextArgRequired();
- if (fpsStr != null && GameManagerService.getFpsInt(fpsStr) == -1) {
- pw.println("Invalid frame rate '" + fpsStr + "'");
+ try {
+ Integer.parseInt(fpsStr);
+ } catch (NumberFormatException e) {
+ pw.println("Invalid frame rate: '" + fpsStr + "'");
return -1;
}
} else {
@@ -375,8 +377,8 @@ public class GameManagerShellCommand extends ShellCommand {
pw.println(" --downscale [0.3|0.35|0.4|0.45|0.5|0.55|0.6|0.65");
pw.println(" |0.7|0.75|0.8|0.85|0.9|disable]: Set app to run at the");
pw.println(" specified scaling ratio.");
- pw.println(" --fps [30|45|60|90|120|disable]: Set app to run at the specified fps,");
- pw.println(" if supported.");
+ pw.println(" --fps: Integer value to set app to run at the specified fps,");
+ pw.println(" if supported. 0 to disable.");
pw.println(" reset [--mode [2|3|performance|battery] --user <USER_ID>] <PACKAGE_NAME>");
pw.println(" Resets the game mode of the app to device configuration.");
pw.println(" This should only be used to reset any override to non custom game mode");
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index add94b1bf937..3aa087acf336 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -37,7 +37,8 @@ import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
-import android.app.TaskStackListener;
+import android.app.ActivityManagerInternal;
+import android.app.IProcessObserver;
import android.content.Context;
import android.hardware.devicestate.DeviceStateInfo;
import android.hardware.devicestate.DeviceStateManager;
@@ -184,7 +185,30 @@ public final class DeviceStateManagerService extends SystemService {
private final SystemPropertySetter mSystemPropertySetter;
@VisibleForTesting
- TaskStackListener mOverrideRequestTaskStackListener = new OverrideRequestTaskStackListener();
+ final IProcessObserver mProcessObserver = new IProcessObserver.Stub() {
+ @Override
+ public void onForegroundActivitiesChanged(int pid, int uid, boolean fg) {
+ synchronized (mLock) {
+ if (!shouldCancelOverrideRequestWhenRequesterNotOnTop()) {
+ return;
+ }
+
+ OverrideRequest request = mActiveOverride.get();
+ if (pid != request.getPid() || uid != request.getUid()) {
+ return;
+ }
+ if (!fg) {
+ mOverrideRequestController.cancelRequest(request);
+ }
+ }
+ }
+
+ @Override
+ public void onProcessDied(int pid, int uid) {}
+
+ @Override
+ public void onForegroundServicesChanged(int pid, int uid, int serviceTypes) {}
+ };
@VisibleForTesting
ActivityTaskManagerInternal.ScreenObserver mOverrideRequestScreenObserver =
new OverrideRequestScreenObserver();
@@ -239,8 +263,9 @@ public final class DeviceStateManagerService extends SystemService {
mFoldedDeviceStates = readFoldedStates();
}
- mActivityTaskManagerInternal.registerTaskStackListener(mOverrideRequestTaskStackListener);
mActivityTaskManagerInternal.registerScreenObserver(mOverrideRequestScreenObserver);
+ LocalServices.getService(ActivityManagerInternal.class).registerProcessObserver(
+ mProcessObserver);
}
@VisibleForTesting
@@ -1289,23 +1314,6 @@ public final class DeviceStateManagerService extends SystemService {
return deviceState.hasFlag(DeviceState.FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP);
}
- private class OverrideRequestTaskStackListener extends TaskStackListener {
- @Override
- public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo)
- throws RemoteException {
- synchronized (mLock) {
- if (!shouldCancelOverrideRequestWhenRequesterNotOnTop()) {
- return;
- }
-
- OverrideRequest request = mActiveOverride.get();
- if (!isTopApp(request.getPid())) {
- mOverrideRequestController.cancelRequest(request);
- }
- }
- }
- }
-
private class OverrideRequestScreenObserver implements
ActivityTaskManagerInternal.ScreenObserver {
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateNotificationController.java b/services/core/java/com/android/server/devicestate/DeviceStateNotificationController.java
index f4c84e7fe464..f9aefd0535d0 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateNotificationController.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateNotificationController.java
@@ -109,6 +109,7 @@ class DeviceStateNotificationController extends BroadcastReceiver {
.setPackage(mContext.getPackageName());
final PendingIntent pendingIntent = PendingIntent.getBroadcast(
mContext, 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE);
+
showNotification(
info.name, info.activeNotificationTitle,
String.format(info.activeNotificationContent, requesterApplicationLabel),
@@ -175,7 +176,7 @@ class DeviceStateNotificationController extends BroadcastReceiver {
if (getNotificationInfos().get(state) == null) {
return;
}
- mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID);
+ mHandler.post(() -> mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID));
}
@Override
@@ -219,8 +220,10 @@ class DeviceStateNotificationController extends BroadcastReceiver {
builder.addAction(action);
}
- mNotificationManager.createNotificationChannel(channel);
- mNotificationManager.notify(NOTIFICATION_TAG, NOTIFICATION_ID, builder.build());
+ mHandler.post(() -> {
+ mNotificationManager.createNotificationChannel(channel);
+ mNotificationManager.notify(NOTIFICATION_TAG, NOTIFICATION_ID, builder.build());
+ });
}
private SparseArray<NotificationInfo> getNotificationInfos() {
diff --git a/services/core/java/com/android/server/display/BrightnessRangeController.java b/services/core/java/com/android/server/display/BrightnessRangeController.java
index 4bfc09075448..7b8b4610112d 100644
--- a/services/core/java/com/android/server/display/BrightnessRangeController.java
+++ b/services/core/java/com/android/server/display/BrightnessRangeController.java
@@ -16,10 +16,12 @@
package com.android.server.display;
+import android.content.res.Resources;
import android.hardware.display.BrightnessInfo;
import android.os.IBinder;
import android.provider.DeviceConfigInterface;
+import com.android.internal.R;
import com.android.server.display.feature.DeviceConfigParameterProvider;
import java.io.PrintWriter;
@@ -33,20 +35,24 @@ class BrightnessRangeController {
private final Runnable mModeChangeCallback;
private final boolean mUseNbmController;
+ private Resources mResources;
BrightnessRangeController(HighBrightnessModeController hbmController,
- Runnable modeChangeCallback, DisplayDeviceConfig displayDeviceConfig) {
+ Runnable modeChangeCallback, DisplayDeviceConfig displayDeviceConfig,
+ Resources resources) {
this(hbmController, modeChangeCallback, displayDeviceConfig,
- new DeviceConfigParameterProvider(DeviceConfigInterface.REAL));
+ new DeviceConfigParameterProvider(DeviceConfigInterface.REAL), resources);
}
BrightnessRangeController(HighBrightnessModeController hbmController,
Runnable modeChangeCallback, DisplayDeviceConfig displayDeviceConfig,
- DeviceConfigParameterProvider configParameterProvider) {
+ DeviceConfigParameterProvider configParameterProvider, Resources resources) {
mHbmController = hbmController;
mModeChangeCallback = modeChangeCallback;
- mUseNbmController = configParameterProvider.isNormalBrightnessControllerFeatureEnabled();
+ mResources = resources;
+ mUseNbmController = configParameterProvider.isNormalBrightnessControllerFeatureEnabled() ||
+ resources.getBoolean(R.bool.config_allowNormalBrightnessControllerFeature);
mNormalBrightnessModeController.resetNbmData(displayDeviceConfig.getLuxThrottlingData());
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index e8a954ac8fc6..94904caf5751 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -675,7 +675,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
HighBrightnessModeController hbmController = createHbmControllerLocked(modeChangeCallback);
mBrightnessRangeController = new BrightnessRangeController(hbmController,
- modeChangeCallback, mDisplayDeviceConfig);
+ modeChangeCallback, mDisplayDeviceConfig, resources);
mBrightnessThrottler = createBrightnessThrottlerLocked();
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 7558c6ab53c2..e849f7c97236 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -548,7 +548,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
mBrightnessThrottler = createBrightnessThrottlerLocked();
mBrightnessRangeController = new BrightnessRangeController(hbmController,
- modeChangeCallback, mDisplayDeviceConfig);
+ modeChangeCallback, mDisplayDeviceConfig, resources);
mDisplayBrightnessController =
new DisplayBrightnessController(context, null,
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index a1d28da5f65e..999e30a72f7a 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -3387,6 +3387,10 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
// Changing to a different IME.
+ IInputMethodInvoker curMethod = getCurMethodLocked();
+ if (curMethod != null) {
+ curMethod.removeStylusHandwritingWindow();
+ }
final long ident = Binder.clearCallingIdentity();
try {
// Set a subtype to this input method.
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index d1a4e6008c2a..339428260800 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3740,15 +3740,15 @@ public class PhoneWindowManager implements WindowManagerPolicy {
*/
private int handleTransitionForKeyguardLw(boolean startKeyguardExitAnimation,
boolean notifyOccluded) {
+ int redoLayout = 0;
if (notifyOccluded) {
- final int redoLayout = applyKeyguardOcclusionChange();
- if (redoLayout != 0) return redoLayout;
+ redoLayout = applyKeyguardOcclusionChange();
}
if (startKeyguardExitAnimation) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "Starting keyguard exit animation");
startKeyguardExitAnimation(SystemClock.uptimeMillis());
}
- return 0;
+ return redoLayout;
}
// There are several different flavors of "assistant" that can be launched from
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index 668415041129..a22a20ece03c 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -28,7 +28,6 @@ import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertThrows;
-import android.app.ActivityManager;
import android.hardware.devicestate.DeviceStateInfo;
import android.hardware.devicestate.DeviceStateRequest;
import android.hardware.devicestate.IDeviceStateManagerCallback;
@@ -582,10 +581,10 @@ public final class DeviceStateManagerServiceTest {
// When the app is foreground, the state should not change
() -> {
int pid = Binder.getCallingPid();
- when(mWindowProcessController.getPid()).thenReturn(pid);
+ int uid = Binder.getCallingUid();
try {
- mService.mOverrideRequestTaskStackListener.onTaskMovedToFront(
- new ActivityManager.RunningTaskInfo());
+ mService.mProcessObserver.onForegroundActivitiesChanged(pid, uid,
+ true /* foregroundActivities */);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
@@ -594,8 +593,11 @@ public final class DeviceStateManagerServiceTest {
() -> {
when(mWindowProcessController.getPid()).thenReturn(FAKE_PROCESS_ID);
try {
- mService.mOverrideRequestTaskStackListener.onTaskMovedToFront(
- new ActivityManager.RunningTaskInfo());
+ int pid = Binder.getCallingPid();
+ int uid = Binder.getCallingUid();
+ mService.mProcessObserver.onForegroundActivitiesChanged(pid, uid,
+ false /* foregroundActivities */);
+
} catch (RemoteException e) {
throw new RuntimeException(e);
}
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateNotificationControllerTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateNotificationControllerTest.java
index 728606f9f628..bb0de032d987 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateNotificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateNotificationControllerTest.java
@@ -33,6 +33,8 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
import android.platform.test.annotations.Presubmit;
import android.util.SparseArray;
@@ -89,11 +91,12 @@ public class DeviceStateNotificationControllerTest {
@Before
public void setup() throws Exception {
Context context = InstrumentationRegistry.getInstrumentation().getContext();
- Handler handler = mock(Handler.class);
PackageManager packageManager = mock(PackageManager.class);
Runnable cancelStateRunnable = mock(Runnable.class);
ApplicationInfo applicationInfo = mock(ApplicationInfo.class);
+ Handler handler = new DeviceStateNotificationControllerTestHandler(Looper.getMainLooper());
+
final SparseArray<DeviceStateNotificationController.NotificationInfo> notificationInfos =
new SparseArray<>();
notificationInfos.put(STATE_WITH_ACTIVE_NOTIFICATION,
@@ -259,4 +262,16 @@ public class DeviceStateNotificationControllerTest {
assertEquals(Locale.ITALY, mNotificationInfoProvider.getCachedLocale());
clearInvocations(mNotificationInfoProvider);
}
+
+ private static class DeviceStateNotificationControllerTestHandler extends Handler {
+ DeviceStateNotificationControllerTestHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+ msg.getCallback().run();
+ return true;
+ }
+ }
}