diff options
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> <print-service * android:vendor="SomeVendor" * android:settingsActivity="foo.bar.MySettingsActivity" - * andorid:addPrintersActivity="foo.bar.MyAddPrintersActivity." + * android:addPrintersActivity="foo.bar.MyAddPrintersActivity." * . . . * /></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; + } + } } |