Merge "Replace default cross activity runner in registerAnimation." into main
diff --git a/libs/hostgraphics/ANativeWindow.cpp b/libs/hostgraphics/ANativeWindow.cpp
new file mode 100644
index 0000000..fcfaf02
--- /dev/null
+++ b/libs/hostgraphics/ANativeWindow.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2024 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 <system/window.h>
+
+static int32_t query(ANativeWindow* window, int what) {
+ int value;
+ int res = window->query(window, what, &value);
+ return res < 0 ? res : value;
+}
+
+static int64_t query64(ANativeWindow* window, int what) {
+ int64_t value;
+ int res = window->perform(window, what, &value);
+ return res < 0 ? res : value;
+}
+
+int ANativeWindow_setCancelBufferInterceptor(ANativeWindow* window,
+ ANativeWindow_cancelBufferInterceptor interceptor,
+ void* data) {
+ return window->perform(window, NATIVE_WINDOW_SET_CANCEL_INTERCEPTOR, interceptor, data);
+}
+
+int ANativeWindow_setDequeueBufferInterceptor(ANativeWindow* window,
+ ANativeWindow_dequeueBufferInterceptor interceptor,
+ void* data) {
+ return window->perform(window, NATIVE_WINDOW_SET_DEQUEUE_INTERCEPTOR, interceptor, data);
+}
+
+int ANativeWindow_setQueueBufferInterceptor(ANativeWindow* window,
+ ANativeWindow_queueBufferInterceptor interceptor,
+ void* data) {
+ return window->perform(window, NATIVE_WINDOW_SET_QUEUE_INTERCEPTOR, interceptor, data);
+}
+
+int ANativeWindow_setPerformInterceptor(ANativeWindow* window,
+ ANativeWindow_performInterceptor interceptor, void* data) {
+ return window->perform(window, NATIVE_WINDOW_SET_PERFORM_INTERCEPTOR, interceptor, data);
+}
+
+int ANativeWindow_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer, int* fenceFd) {
+ return window->dequeueBuffer(window, buffer, fenceFd);
+}
+
+int ANativeWindow_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd) {
+ return window->cancelBuffer(window, buffer, fenceFd);
+}
+
+int ANativeWindow_setDequeueTimeout(ANativeWindow* window, int64_t timeout) {
+ return window->perform(window, NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT, timeout);
+}
+
+// extern "C", so that it can be used outside libhostgraphics (in host hwui/.../CanvasContext.cpp)
+extern "C" void ANativeWindow_tryAllocateBuffers(ANativeWindow* window) {
+ if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) {
+ return;
+ }
+ window->perform(window, NATIVE_WINDOW_ALLOCATE_BUFFERS);
+}
+
+int64_t ANativeWindow_getLastDequeueStartTime(ANativeWindow* window) {
+ return query64(window, NATIVE_WINDOW_GET_LAST_DEQUEUE_START);
+}
+
+int64_t ANativeWindow_getLastDequeueDuration(ANativeWindow* window) {
+ return query64(window, NATIVE_WINDOW_GET_LAST_DEQUEUE_DURATION);
+}
+
+int64_t ANativeWindow_getLastQueueDuration(ANativeWindow* window) {
+ return query64(window, NATIVE_WINDOW_GET_LAST_QUEUE_DURATION);
+}
+
+int32_t ANativeWindow_getWidth(ANativeWindow* window) {
+ return query(window, NATIVE_WINDOW_WIDTH);
+}
+
+int32_t ANativeWindow_getHeight(ANativeWindow* window) {
+ return query(window, NATIVE_WINDOW_HEIGHT);
+}
+
+int32_t ANativeWindow_getFormat(ANativeWindow* window) {
+ return query(window, NATIVE_WINDOW_FORMAT);
+}
+
+void ANativeWindow_acquire(ANativeWindow* window) {
+ // incStrong/decStrong token must be the same, doesn't matter what it is
+ window->incStrong((void*)ANativeWindow_acquire);
+}
+
+void ANativeWindow_release(ANativeWindow* window) {
+ // incStrong/decStrong token must be the same, doesn't matter what it is
+ window->decStrong((void*)ANativeWindow_acquire);
+}
diff --git a/libs/hostgraphics/Android.bp b/libs/hostgraphics/Android.bp
index 4407af6..09232b6 100644
--- a/libs/hostgraphics/Android.bp
+++ b/libs/hostgraphics/Android.bp
@@ -17,26 +17,18 @@
static_libs: [
"libbase",
"libmath",
+ "libui-types",
"libutils",
],
srcs: [
- ":libui_host_common",
"ADisplay.cpp",
+ "ANativeWindow.cpp",
"Fence.cpp",
"HostBufferQueue.cpp",
"PublicFormat.cpp",
],
- include_dirs: [
- // Here we override all the headers automatically included with frameworks/native/include.
- // When frameworks/native/include will be removed from the list of automatic includes.
- // We will have to copy necessary headers with a pre-build step (generated headers).
- ".",
- "frameworks/native/libs/arect/include",
- "frameworks/native/libs/ui/include_private",
- ],
-
header_libs: [
"libnativebase_headers",
"libnativedisplay_headers",
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 4706699..7439fbc 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -96,6 +96,7 @@
],
cflags: [
"-Wno-unused-variable",
+ "-D__INTRODUCED_IN(n)=",
],
},
},
@@ -539,7 +540,10 @@
"pipeline/skia/ReorderBarrierDrawables.cpp",
"pipeline/skia/TransformCanvas.cpp",
"renderstate/RenderState.cpp",
+ "renderthread/CanvasContext.cpp",
+ "renderthread/DrawFrameTask.cpp",
"renderthread/Frame.cpp",
+ "renderthread/RenderProxy.cpp",
"renderthread/RenderTask.cpp",
"renderthread/TimeLord.cpp",
"hwui/AnimatedImageDrawable.cpp",
@@ -589,6 +593,7 @@
"SkiaCanvas.cpp",
"SkiaInterpolator.cpp",
"Tonemapper.cpp",
+ "TreeInfo.cpp",
"VectorDrawable.cpp",
],
@@ -617,14 +622,11 @@
"pipeline/skia/VkFunctorDrawable.cpp",
"pipeline/skia/VkInteropFunctorDrawable.cpp",
"renderthread/CacheManager.cpp",
- "renderthread/CanvasContext.cpp",
- "renderthread/DrawFrameTask.cpp",
"renderthread/EglManager.cpp",
"renderthread/ReliableSurface.cpp",
"renderthread/RenderEffectCapabilityQuery.cpp",
"renderthread/VulkanManager.cpp",
"renderthread/VulkanSurface.cpp",
- "renderthread/RenderProxy.cpp",
"renderthread/RenderThread.cpp",
"renderthread/HintSessionWrapper.cpp",
"service/GraphicsStatsService.cpp",
@@ -636,7 +638,6 @@
"Layer.cpp",
"ProfileDataContainer.cpp",
"Readback.cpp",
- "TreeInfo.cpp",
"WebViewFunctorManager.cpp",
"protos/graphicsstats.proto",
],
@@ -654,6 +655,8 @@
srcs: [
"platform/host/renderthread/CacheManager.cpp",
+ "platform/host/renderthread/HintSessionWrapper.cpp",
+ "platform/host/renderthread/ReliableSurface.cpp",
"platform/host/renderthread/RenderThread.cpp",
"platform/host/ProfileDataContainer.cpp",
"platform/host/Readback.cpp",
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index f526a28..589abb4 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -16,18 +16,6 @@
#include "RenderNode.h"
-#include "DamageAccumulator.h"
-#include "Debug.h"
-#include "Properties.h"
-#include "TreeInfo.h"
-#include "VectorDrawable.h"
-#include "private/hwui/WebViewFunctor.h"
-#ifdef __ANDROID__
-#include "renderthread/CanvasContext.h"
-#else
-#include "DamageAccumulator.h"
-#include "pipeline/skia/SkiaDisplayList.h"
-#endif
#include <SkPathOps.h>
#include <gui/TraceUtils.h>
#include <ui/FatVector.h>
@@ -37,6 +25,14 @@
#include <sstream>
#include <string>
+#include "DamageAccumulator.h"
+#include "Debug.h"
+#include "Properties.h"
+#include "TreeInfo.h"
+#include "VectorDrawable.h"
+#include "private/hwui/WebViewFunctor.h"
+#include "renderthread/CanvasContext.h"
+
#ifdef __ANDROID__
#include "include/gpu/ganesh/SkImageGanesh.h"
#endif
@@ -186,7 +182,6 @@
}
void RenderNode::pushLayerUpdate(TreeInfo& info) {
-#ifdef __ANDROID__ // Layoutlib does not support CanvasContext and Layers
LayerType layerType = properties().effectiveLayerType();
// If we are not a layer OR we cannot be rendered (eg, view was detached)
// we need to destroy any Layers we may have had previously
@@ -218,7 +213,6 @@
// That might be us, so tell CanvasContext that this layer is in the
// tree and should not be destroyed.
info.canvasContext.markLayerInUse(this);
-#endif
}
/**
diff --git a/libs/hwui/RootRenderNode.cpp b/libs/hwui/RootRenderNode.cpp
index ddbbf58..5174e27 100644
--- a/libs/hwui/RootRenderNode.cpp
+++ b/libs/hwui/RootRenderNode.cpp
@@ -18,11 +18,12 @@
#ifdef __ANDROID__ // Layoutlib does not support Looper (windows)
#include <utils/Looper.h>
+#else
+#include "utils/MessageHandler.h"
#endif
namespace android::uirenderer {
-#ifdef __ANDROID__ // Layoutlib does not support Looper
class FinishAndInvokeListener : public MessageHandler {
public:
explicit FinishAndInvokeListener(PropertyValuesAnimatorSet* anim) : mAnimator(anim) {
@@ -237,9 +238,13 @@
// user events, in which case the already posted listener's id will become stale, and
// the onFinished callback will then be ignored.
sp<FinishAndInvokeListener> message = new FinishAndInvokeListener(anim);
+#ifdef __ANDROID__ // Layoutlib does not support Looper
auto looper = Looper::getForThread();
LOG_ALWAYS_FATAL_IF(looper == nullptr, "Not on a looper thread?");
looper->sendMessageDelayed(ms2ns(remainingTimeInMs), message, 0);
+#else
+ message->handleMessage(0);
+#endif
anim->clearOneShotListener();
}
}
@@ -285,22 +290,5 @@
AnimationContext* ContextFactoryImpl::createAnimationContext(renderthread::TimeLord& clock) {
return new AnimationContextBridge(clock, mRootNode);
}
-#else
-
-void RootRenderNode::prepareTree(TreeInfo& info) {
- info.errorHandler = mErrorHandler.get();
- info.updateWindowPositions = true;
- RenderNode::prepareTree(info);
- info.updateWindowPositions = false;
- info.errorHandler = nullptr;
-}
-
-void RootRenderNode::attachAnimatingNode(RenderNode* animatingNode) { }
-
-void RootRenderNode::destroy() { }
-
-void RootRenderNode::addVectorDrawableAnimator(PropertyValuesAnimatorSet* anim) { }
-
-#endif
} // namespace android::uirenderer
diff --git a/libs/hwui/RootRenderNode.h b/libs/hwui/RootRenderNode.h
index 1d3f5a8..7a5cda7 100644
--- a/libs/hwui/RootRenderNode.h
+++ b/libs/hwui/RootRenderNode.h
@@ -74,7 +74,6 @@
void detachVectorDrawableAnimator(PropertyValuesAnimatorSet* anim);
};
-#ifdef __ANDROID__ // Layoutlib does not support Animations
class ContextFactoryImpl : public IContextFactory {
public:
explicit ContextFactoryImpl(RootRenderNode* rootNode) : mRootNode(rootNode) {}
@@ -84,6 +83,5 @@
private:
RootRenderNode* mRootNode;
};
-#endif
} // namespace android::uirenderer
diff --git a/libs/hwui/WebViewFunctorManager.cpp b/libs/hwui/WebViewFunctorManager.cpp
index efa9b11..9d16ee8 100644
--- a/libs/hwui/WebViewFunctorManager.cpp
+++ b/libs/hwui/WebViewFunctorManager.cpp
@@ -87,7 +87,7 @@
WebViewFunctorManager::instance().releaseFunctor(functor);
}
-void WebViewFunctor_reportRenderingThreads(int functor, const int32_t* thread_ids, size_t size) {
+void WebViewFunctor_reportRenderingThreads(int functor, const pid_t* thread_ids, size_t size) {
WebViewFunctorManager::instance().reportRenderingThreads(functor, thread_ids, size);
}
@@ -265,8 +265,8 @@
funcs.transactionDeleteFunc(transaction);
}
-void WebViewFunctor::reportRenderingThreads(const int32_t* thread_ids, size_t size) {
- mRenderingThreads = std::vector<int32_t>(thread_ids, thread_ids + size);
+void WebViewFunctor::reportRenderingThreads(const pid_t* thread_ids, size_t size) {
+ mRenderingThreads = std::vector<pid_t>(thread_ids, thread_ids + size);
}
WebViewFunctorManager& WebViewFunctorManager::instance() {
@@ -355,7 +355,7 @@
}
}
-void WebViewFunctorManager::reportRenderingThreads(int functor, const int32_t* thread_ids,
+void WebViewFunctorManager::reportRenderingThreads(int functor, const pid_t* thread_ids,
size_t size) {
std::lock_guard _lock{mLock};
for (auto& iter : mFunctors) {
@@ -366,8 +366,8 @@
}
}
-std::vector<int32_t> WebViewFunctorManager::getRenderingThreadsForActiveFunctors() {
- std::vector<int32_t> renderingThreads;
+std::vector<pid_t> WebViewFunctorManager::getRenderingThreadsForActiveFunctors() {
+ std::vector<pid_t> renderingThreads;
std::lock_guard _lock{mLock};
for (const auto& iter : mActiveFunctors) {
const auto& functorThreads = iter->getRenderingThreads();
diff --git a/libs/hwui/WebViewFunctorManager.h b/libs/hwui/WebViewFunctorManager.h
index 2d77dd8..ec17640 100644
--- a/libs/hwui/WebViewFunctorManager.h
+++ b/libs/hwui/WebViewFunctorManager.h
@@ -17,13 +17,11 @@
#pragma once
#include <private/hwui/WebViewFunctor.h>
-#ifdef __ANDROID__ // Layoutlib does not support render thread
#include <renderthread/RenderProxy.h>
-#endif
-
#include <utils/LightRefBase.h>
#include <utils/Log.h>
#include <utils/StrongPointer.h>
+
#include <mutex>
#include <vector>
@@ -38,11 +36,7 @@
class Handle : public LightRefBase<Handle> {
public:
- ~Handle() {
-#ifdef __ANDROID__ // Layoutlib does not support render thread
- renderthread::RenderProxy::destroyFunctor(id());
-#endif
- }
+ ~Handle() { renderthread::RenderProxy::destroyFunctor(id()); }
int id() const { return mReference.id(); }
@@ -60,7 +54,7 @@
void onRemovedFromTree() { mReference.onRemovedFromTree(); }
- const std::vector<int32_t>& getRenderingThreads() const {
+ const std::vector<pid_t>& getRenderingThreads() const {
return mReference.getRenderingThreads();
}
@@ -85,8 +79,8 @@
ASurfaceControl* getSurfaceControl();
void mergeTransaction(ASurfaceTransaction* transaction);
- void reportRenderingThreads(const int32_t* thread_ids, size_t size);
- const std::vector<int32_t>& getRenderingThreads() const { return mRenderingThreads; }
+ void reportRenderingThreads(const pid_t* thread_ids, size_t size);
+ const std::vector<pid_t>& getRenderingThreads() const { return mRenderingThreads; }
sp<Handle> createHandle() {
LOG_ALWAYS_FATAL_IF(mCreatedHandle);
@@ -107,7 +101,7 @@
bool mCreatedHandle = false;
int32_t mParentSurfaceControlGenerationId = 0;
ASurfaceControl* mSurfaceControl = nullptr;
- std::vector<int32_t> mRenderingThreads;
+ std::vector<pid_t> mRenderingThreads;
};
class WebViewFunctorManager {
@@ -118,8 +112,8 @@
void releaseFunctor(int functor);
void onContextDestroyed();
void destroyFunctor(int functor);
- void reportRenderingThreads(int functor, const int32_t* thread_ids, size_t size);
- std::vector<int32_t> getRenderingThreadsForActiveFunctors();
+ void reportRenderingThreads(int functor, const pid_t* thread_ids, size_t size);
+ std::vector<pid_t> getRenderingThreadsForActiveFunctors();
sp<WebViewFunctor::Handle> handleFor(int functor);
diff --git a/libs/hwui/apex/LayoutlibLoader.cpp b/libs/hwui/apex/LayoutlibLoader.cpp
index 770822a..fd9915a 100644
--- a/libs/hwui/apex/LayoutlibLoader.cpp
+++ b/libs/hwui/apex/LayoutlibLoader.cpp
@@ -164,8 +164,10 @@
} // namespace android
using namespace android;
+using namespace android::uirenderer;
void init_android_graphics() {
+ Properties::overrideRenderPipelineType(RenderPipelineType::SkiaCpu);
SkGraphics::Init();
}
diff --git a/libs/hwui/jni/AnimatedImageDrawable.cpp b/libs/hwui/jni/AnimatedImageDrawable.cpp
index 0f80c55..b01e38d 100644
--- a/libs/hwui/jni/AnimatedImageDrawable.cpp
+++ b/libs/hwui/jni/AnimatedImageDrawable.cpp
@@ -27,6 +27,8 @@
#include <hwui/ImageDecoder.h>
#ifdef __ANDROID__
#include <utils/Looper.h>
+#else
+#include "utils/MessageHandler.h"
#endif
#include "ColorFilter.h"
@@ -182,23 +184,6 @@
drawable->setRepetitionCount(loopCount);
}
-#ifndef __ANDROID__
-struct Message {
- Message(int w) {}
-};
-
-class MessageHandler : public virtual RefBase {
-protected:
- virtual ~MessageHandler() override {}
-
-public:
- /**
- * Handles a message.
- */
- virtual void handleMessage(const Message& message) = 0;
-};
-#endif
-
class InvokeListener : public MessageHandler {
public:
InvokeListener(JNIEnv* env, jobject javaObject) {
diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp
index 9e21f86..d415700 100644
--- a/libs/hwui/jni/Bitmap.cpp
+++ b/libs/hwui/jni/Bitmap.cpp
@@ -1,8 +1,14 @@
// #define LOG_NDEBUG 0
#include "Bitmap.h"
+#include <android-base/unique_fd.h>
#include <hwui/Bitmap.h>
#include <hwui/Paint.h>
+#include <inttypes.h>
+#include <renderthread/RenderProxy.h>
+#include <string.h>
+
+#include <memory>
#include "CreateJavaOutputStreamAdaptor.h"
#include "Gainmap.h"
@@ -24,16 +30,6 @@
#include "SkTypes.h"
#include "android_nio_utils.h"
-#ifdef __ANDROID__ // Layoutlib does not support graphic buffer, parcel or render thread
-#include <android-base/unique_fd.h>
-#include <renderthread/RenderProxy.h>
-#endif
-
-#include <inttypes.h>
-#include <string.h>
-
-#include <memory>
-
#define DEBUG_PARCEL 0
static jclass gBitmap_class;
@@ -1105,11 +1101,9 @@
}
static void Bitmap_prepareToDraw(JNIEnv* env, jobject, jlong bitmapPtr) {
-#ifdef __ANDROID__ // Layoutlib does not support render thread
LocalScopedBitmap bitmapHandle(bitmapPtr);
if (!bitmapHandle.valid()) return;
android::uirenderer::renderthread::RenderProxy::prepareToDraw(bitmapHandle->bitmap());
-#endif
}
static jint Bitmap_getAllocationByteCount(JNIEnv* env, jobject, jlong bitmapPtr) {
diff --git a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp
index 426644e..948362c 100644
--- a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp
+++ b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp
@@ -16,22 +16,19 @@
#include "GraphicsJNI.h"
-#ifdef __ANDROID__ // Layoutlib does not support Looper and device properties
+#ifdef __ANDROID__ // Layoutlib does not support Looper
#include <utils/Looper.h>
#endif
-#include <SkRegion.h>
-#include <SkRuntimeEffect.h>
-
+#include <CanvasProperty.h>
#include <Rect.h>
#include <RenderNode.h>
-#include <CanvasProperty.h>
+#include <SkRegion.h>
+#include <SkRuntimeEffect.h>
#include <hwui/Canvas.h>
#include <hwui/Paint.h>
#include <minikin/Layout.h>
-#ifdef __ANDROID__ // Layoutlib does not support RenderThread
#include <renderthread/RenderProxy.h>
-#endif
namespace android {
@@ -85,11 +82,7 @@
}
static jint android_view_DisplayListCanvas_getMaxTextureSize(JNIEnv*, jobject) {
-#ifdef __ANDROID__ // Layoutlib does not support RenderProxy (RenderThread)
return android::uirenderer::renderthread::RenderProxy::maxTextureSize();
-#else
- return 4096;
-#endif
}
static void android_view_DisplayListCanvas_enableZ(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr,
diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp
index a7d6423..6e03bbd 100644
--- a/libs/hwui/jni/android_graphics_RenderNode.cpp
+++ b/libs/hwui/jni/android_graphics_RenderNode.cpp
@@ -15,19 +15,17 @@
*/
#define ATRACE_TAG ATRACE_TAG_VIEW
-#include "GraphicsJNI.h"
-
#include <Animator.h>
#include <DamageAccumulator.h>
#include <Matrix.h>
#include <RenderNode.h>
-#ifdef __ANDROID__ // Layoutlib does not support CanvasContext
-#include <renderthread/CanvasContext.h>
-#endif
#include <TreeInfo.h>
#include <effects/StretchEffect.h>
#include <gui/TraceUtils.h>
#include <hwui/Paint.h>
+#include <renderthread/CanvasContext.h>
+
+#include "GraphicsJNI.h"
namespace android {
@@ -640,7 +638,6 @@
ATRACE_NAME("Update SurfaceView position");
-#ifdef __ANDROID__ // Layoutlib does not support CanvasContext
JNIEnv* env = jnienv();
// Update the new position synchronously. We cannot defer this to
// a worker pool to process asynchronously because the UI thread
@@ -669,7 +666,6 @@
env->DeleteGlobalRef(mListener);
mListener = nullptr;
}
-#endif
}
virtual void onPositionLost(RenderNode& node, const TreeInfo* info) override {
@@ -682,7 +678,6 @@
ATRACE_NAME("SurfaceView position lost");
JNIEnv* env = jnienv();
-#ifdef __ANDROID__ // Layoutlib does not support CanvasContext
// Update the lost position synchronously. We cannot defer this to
// a worker pool to process asynchronously because the UI thread
// may be unblocked by the time a worker thread can process this,
@@ -698,7 +693,6 @@
env->DeleteGlobalRef(mListener);
mListener = nullptr;
}
-#endif
}
private:
@@ -750,7 +744,6 @@
StretchEffectBehavior::Shader) {
JNIEnv* env = jnienv();
-#ifdef __ANDROID__ // Layoutlib does not support CanvasContext
SkVector stretchDirection = effect->getStretchDirection();
jboolean keepListening = env->CallStaticBooleanMethod(
gPositionListener.clazz, gPositionListener.callApplyStretch, mListener,
@@ -762,7 +755,6 @@
env->DeleteGlobalRef(mListener);
mListener = nullptr;
}
-#endif
}
}
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index e0216b6..36dc933 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -15,23 +15,19 @@
*/
#include "SkiaDisplayList.h"
-#include "FunctorDrawable.h"
-
-#include "DumpOpsCanvas.h"
-#ifdef __ANDROID__ // Layoutlib does not support SkiaPipeline
-#include "SkiaPipeline.h"
-#else
-#include "DamageAccumulator.h"
-#endif
-#include "TreeInfo.h"
-#include "VectorDrawable.h"
-#ifdef __ANDROID__
-#include "renderthread/CanvasContext.h"
-#endif
#include <SkImagePriv.h>
#include <SkPathOps.h>
+// clang-format off
+#include "FunctorDrawable.h" // Must be included before DumpOpsCanvas.h
+#include "DumpOpsCanvas.h"
+// clang-format on
+#include "SkiaPipeline.h"
+#include "TreeInfo.h"
+#include "VectorDrawable.h"
+#include "renderthread/CanvasContext.h"
+
namespace android {
namespace uirenderer {
namespace skiapipeline {
@@ -101,7 +97,6 @@
// If the prepare tree is triggered by the UI thread and no previous call to
// pinImages has failed then we must pin all mutable images in the GPU cache
// until the next UI thread draw.
-#ifdef __ANDROID__ // Layoutlib does not support CanvasContext
if (info.prepareTextures && !info.canvasContext.pinImages(mMutableImages)) {
// In the event that pinning failed we prevent future pinImage calls for the
// remainder of this tree traversal and also unpin any currently pinned images
@@ -110,11 +105,11 @@
info.canvasContext.unpinImages();
}
+#ifdef __ANDROID__
auto grContext = info.canvasContext.getGrContext();
for (const auto& bufferData : mMeshBufferData) {
bufferData->updateBuffers(grContext);
}
-
#endif
bool hasBackwardProjectedNodesHere = false;
diff --git a/libs/hwui/platform/host/WebViewFunctorManager.cpp b/libs/hwui/platform/host/WebViewFunctorManager.cpp
index 1d16655..4ba206b 100644
--- a/libs/hwui/platform/host/WebViewFunctorManager.cpp
+++ b/libs/hwui/platform/host/WebViewFunctorManager.cpp
@@ -50,6 +50,8 @@
void WebViewFunctor::mergeTransaction(ASurfaceTransaction* transaction) {}
+void WebViewFunctor::reportRenderingThreads(const pid_t* thread_ids, size_t size) {}
+
void WebViewFunctor::reparentSurfaceControl(ASurfaceControl* parent) {}
WebViewFunctorManager& WebViewFunctorManager::instance() {
@@ -68,6 +70,13 @@
void WebViewFunctorManager::destroyFunctor(int functor) {}
+void WebViewFunctorManager::reportRenderingThreads(int functor, const pid_t* thread_ids,
+ size_t size) {}
+
+std::vector<pid_t> WebViewFunctorManager::getRenderingThreadsForActiveFunctors() {
+ return {};
+}
+
sp<WebViewFunctor::Handle> WebViewFunctorManager::handleFor(int functor) {
return nullptr;
}
diff --git a/libs/hwui/platform/host/renderthread/HintSessionWrapper.cpp b/libs/hwui/platform/host/renderthread/HintSessionWrapper.cpp
new file mode 100644
index 0000000..b1b1d58
--- /dev/null
+++ b/libs/hwui/platform/host/renderthread/HintSessionWrapper.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "renderthread/HintSessionWrapper.h"
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+void HintSessionWrapper::HintSessionBinding::init() {}
+
+HintSessionWrapper::HintSessionWrapper(pid_t uiThreadId, pid_t renderThreadId)
+ : mUiThreadId(uiThreadId)
+ , mRenderThreadId(renderThreadId)
+ , mBinding(std::make_shared<HintSessionBinding>()) {}
+
+HintSessionWrapper::~HintSessionWrapper() {}
+
+void HintSessionWrapper::destroy() {}
+
+bool HintSessionWrapper::init() {
+ return false;
+}
+
+void HintSessionWrapper::updateTargetWorkDuration(long targetWorkDurationNanos) {}
+
+void HintSessionWrapper::reportActualWorkDuration(long actualDurationNanos) {}
+
+void HintSessionWrapper::sendLoadResetHint() {}
+
+void HintSessionWrapper::sendLoadIncreaseHint() {}
+
+bool HintSessionWrapper::alive() {
+ return false;
+}
+
+nsecs_t HintSessionWrapper::getLastUpdate() {
+ return -1;
+}
+
+void HintSessionWrapper::delayedDestroy(RenderThread& rt, nsecs_t delay,
+ std::shared_ptr<HintSessionWrapper> wrapperPtr) {}
+
+void HintSessionWrapper::setActiveFunctorThreads(std::vector<pid_t> threadIds) {}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/platform/host/renderthread/ReliableSurface.cpp b/libs/hwui/platform/host/renderthread/ReliableSurface.cpp
new file mode 100644
index 0000000..2deaaf3
--- /dev/null
+++ b/libs/hwui/platform/host/renderthread/ReliableSurface.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "renderthread/ReliableSurface.h"
+
+#include <log/log_main.h>
+#include <system/window.h>
+
+namespace android::uirenderer::renderthread {
+
+ReliableSurface::ReliableSurface(ANativeWindow* window) : mWindow(window) {
+ LOG_ALWAYS_FATAL_IF(!mWindow, "Error, unable to wrap a nullptr");
+ ANativeWindow_acquire(mWindow);
+}
+
+ReliableSurface::~ReliableSurface() {
+ ANativeWindow_release(mWindow);
+}
+
+void ReliableSurface::init() {}
+
+int ReliableSurface::reserveNext() {
+ return OK;
+}
+
+}; // namespace android::uirenderer::renderthread
diff --git a/libs/hwui/platform/host/utils/MessageHandler.h b/libs/hwui/platform/host/utils/MessageHandler.h
new file mode 100644
index 0000000..51ee48e
--- /dev/null
+++ b/libs/hwui/platform/host/utils/MessageHandler.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+#pragma once
+
+#include <utils/RefBase.h>
+
+struct Message {
+ Message(int w) {}
+};
+
+class MessageHandler : public virtual android::RefBase {
+protected:
+ virtual ~MessageHandler() override {}
+
+public:
+ /**
+ * Handles a message.
+ */
+ virtual void handleMessage(const Message& message) = 0;
+};
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 22de2f2..66e0896 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -35,6 +35,7 @@
#include "Properties.h"
#include "RenderThread.h"
#include "hwui/Canvas.h"
+#include "pipeline/skia/SkiaCpuPipeline.h"
#include "pipeline/skia/SkiaGpuPipeline.h"
#include "pipeline/skia/SkiaOpenGLPipeline.h"
#include "pipeline/skia/SkiaVulkanPipeline.h"
@@ -72,7 +73,7 @@
CanvasContext* CanvasContext::create(RenderThread& thread, bool translucent,
RenderNode* rootRenderNode, IContextFactory* contextFactory,
- int32_t uiThreadId, int32_t renderThreadId) {
+ pid_t uiThreadId, pid_t renderThreadId) {
auto renderType = Properties::getRenderPipelineType();
switch (renderType) {
@@ -84,6 +85,12 @@
return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
std::make_unique<skiapipeline::SkiaVulkanPipeline>(thread),
uiThreadId, renderThreadId);
+#ifndef __ANDROID__
+ case RenderPipelineType::SkiaCpu:
+ return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
+ std::make_unique<skiapipeline::SkiaCpuPipeline>(thread),
+ uiThreadId, renderThreadId);
+#endif
default:
LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t)renderType);
break;
@@ -182,6 +189,7 @@
}
void CanvasContext::setHardwareBuffer(AHardwareBuffer* buffer) {
+#ifdef __ANDROID__
if (mHardwareBuffer) {
AHardwareBuffer_release(mHardwareBuffer);
mHardwareBuffer = nullptr;
@@ -192,6 +200,7 @@
mHardwareBuffer = buffer;
}
mRenderPipeline->setHardwareBuffer(mHardwareBuffer);
+#endif
}
void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) {
@@ -561,6 +570,7 @@
}
void CanvasContext::draw(bool solelyTextureViewUpdates) {
+#ifdef __ANDROID__
if (auto grContext = getGrContext()) {
if (grContext->abandoned()) {
if (grContext->isDeviceLost()) {
@@ -571,6 +581,7 @@
return;
}
}
+#endif
SkRect dirty;
mDamageAccumulator.finish(&dirty);
@@ -594,11 +605,13 @@
if (skippedFrameReason) {
mCurrentFrameInfo->setSkippedFrameReason(*skippedFrameReason);
+#ifdef __ANDROID__
if (auto grContext = getGrContext()) {
// Submit to ensure that any texture uploads complete and Skia can
// free its staging buffers.
grContext->flushAndSubmit();
}
+#endif
// Notify the callbacks, even if there's nothing to draw so they aren't waiting
// indefinitely
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 1b333bf..826d00e 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -140,12 +140,14 @@
if (CC_LIKELY(canDrawThisFrame)) {
context->draw(solelyTextureViewUpdates);
} else {
+#ifdef __ANDROID__
// Do a flush in case syncFrameState performed any texture uploads. Since we skipped
// the draw() call, those uploads (or deletes) will end up sitting in the queue.
// Do them now
if (GrDirectContext* grContext = mRenderThread->getGrContext()) {
grContext->flushAndSubmit();
}
+#endif
// wait on fences so tasks don't overlap next frame
context->waitOnFences();
}
@@ -176,11 +178,13 @@
bool canDraw = mContext->makeCurrent();
mContext->unpinImages();
+#ifdef __ANDROID__
for (size_t i = 0; i < mLayers.size(); i++) {
if (mLayers[i]) {
mLayers[i]->apply();
}
}
+#endif
mLayers.clear();
mContext->setContentDrawBounds(mContentDrawBounds);
diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h
index 5959647..d6a4d50 100644
--- a/libs/hwui/renderthread/ReliableSurface.h
+++ b/libs/hwui/renderthread/ReliableSurface.h
@@ -21,7 +21,9 @@
#include <apex/window.h>
#include <utils/Errors.h>
#include <utils/Macros.h>
+#ifdef __ANDROID__
#include <utils/NdkUtils.h>
+#endif
#include <utils/StrongPointer.h>
#include <memory>
@@ -62,9 +64,11 @@
mutable std::mutex mMutex;
uint64_t mUsage = AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER;
+#ifdef __ANDROID__
AHardwareBuffer_Format mFormat = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
UniqueAHardwareBuffer mScratchBuffer;
ANativeWindowBuffer* mReservedBuffer = nullptr;
+#endif
base::unique_fd mReservedFenceFd;
bool mHasDequeuedBuffer = false;
int mBufferQueueState = OK;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index eab3605..715153b 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -42,7 +42,11 @@
RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode,
IContextFactory* contextFactory)
: mRenderThread(RenderThread::getInstance()), mContext(nullptr) {
+#ifdef __ANDROID__
pid_t uiThreadId = pthread_gettid_np(pthread_self());
+#else
+ pid_t uiThreadId = 0;
+#endif
pid_t renderThreadId = getRenderThreadTid();
mContext = mRenderThread.queue().runSync([=, this]() -> CanvasContext* {
CanvasContext* context = CanvasContext::create(mRenderThread, translucent, rootRenderNode,
@@ -90,6 +94,7 @@
}
void RenderProxy::setHardwareBuffer(AHardwareBuffer* buffer) {
+#ifdef __ANDROID__
if (buffer) {
AHardwareBuffer_acquire(buffer);
}
@@ -99,6 +104,7 @@
AHardwareBuffer_release(hardwareBuffer);
}
});
+#endif
}
void RenderProxy::setSurface(ANativeWindow* window, bool enableTimeout) {
@@ -216,7 +222,9 @@
}
void RenderProxy::detachSurfaceTexture(DeferredLayerUpdater* layer) {
+#ifdef __ANDROID__
return mRenderThread.queue().runSync([&]() { layer->detachSurfaceTexture(); });
+#endif
}
void RenderProxy::destroyHardwareResources() {
@@ -324,11 +332,13 @@
}
});
}
+#ifdef __ANDROID__
if (!Properties::isolatedProcess) {
std::string grallocInfo;
GraphicBufferAllocator::getInstance().dump(grallocInfo);
dprintf(fd, "%s\n", grallocInfo.c_str());
}
+#endif
}
void RenderProxy::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) {
@@ -352,7 +362,11 @@
}
int RenderProxy::getRenderThreadTid() {
+#ifdef __ANDROID__
return mRenderThread.getTid();
+#else
+ return 0;
+#endif
}
void RenderProxy::addRenderNode(RenderNode* node, bool placeFront) {
@@ -461,7 +475,7 @@
int RenderProxy::copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap) {
ATRACE_NAME("HardwareBitmap readback");
RenderThread& thread = RenderThread::getInstance();
- if (gettid() == thread.getTid()) {
+ if (RenderThread::isCurrent()) {
// TODO: fix everything that hits this. We should never be triggering a readback ourselves.
return (int)thread.readback().copyHWBitmapInto(hwBitmap, bitmap);
} else {
@@ -472,7 +486,7 @@
int RenderProxy::copyImageInto(const sk_sp<SkImage>& image, SkBitmap* bitmap) {
RenderThread& thread = RenderThread::getInstance();
- if (gettid() == thread.getTid()) {
+ if (RenderThread::isCurrent()) {
// TODO: fix everything that hits this. We should never be triggering a readback ourselves.
return (int)thread.readback().copyImageInto(image, bitmap);
} else {
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index 207ccbe..871e9ab 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -80,4 +80,7 @@
boolean hasCustomMediaSessionPolicyProvider(String componentName);
int getSessionPolicies(in MediaSession.Token token);
void setSessionPolicies(in MediaSession.Token token, int policies);
+
+ // For testing of temporarily engaged sessions.
+ void expireTempEngagedSessions();
}
diff --git a/packages/SettingsLib/Spa/gradle/libs.versions.toml b/packages/SettingsLib/Spa/gradle/libs.versions.toml
index 0ee9d59..85ad160 100644
--- a/packages/SettingsLib/Spa/gradle/libs.versions.toml
+++ b/packages/SettingsLib/Spa/gradle/libs.versions.toml
@@ -15,7 +15,7 @@
#
[versions]
-agp = "8.3.1"
+agp = "8.3.2"
compose-compiler = "1.5.11"
dexmaker-mockito = "2.28.3"
jvm = "17"
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.6-bin.zip b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.6-bin.zip
deleted file mode 100644
index 5c96347..0000000
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.6-bin.zip
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.7-bin.zip b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.7-bin.zip
new file mode 100644
index 0000000..7a9ac5a
--- /dev/null
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.7-bin.zip
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
index 50ff9df..182095e 100644
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
@@ -16,6 +16,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=gradle-8.6-bin.zip
+distributionUrl=gradle-8.7-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index 2f2ac24..6344501 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -65,7 +65,7 @@
api("androidx.lifecycle:lifecycle-runtime-compose")
api("androidx.navigation:navigation-compose:2.8.0-alpha05")
api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha")
- api("com.google.android.material:material:1.7.0-alpha03")
+ api("com.google.android.material:material:1.11.0")
debugApi("androidx.compose.ui:ui-tooling:$jetpackComposeVersion")
implementation("com.airbnb.android:lottie-compose:5.2.0")
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncPopup.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncPopup.kt
index 2af042a..e1ee01e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncPopup.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncPopup.kt
@@ -28,11 +28,13 @@
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.slice.Slice
+import com.android.internal.logging.UiEventLogger
import com.android.systemui.animation.Expandable
import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.volume.panel.component.anc.ui.viewmodel.AncViewModel
import com.android.systemui.volume.panel.component.popup.ui.composable.VolumePanelPopup
+import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
import javax.inject.Inject
/** ANC popup up displaying ANC control [Slice]. */
@@ -41,10 +43,12 @@
constructor(
private val volumePanelPopup: VolumePanelPopup,
private val viewModel: AncViewModel,
+ private val uiEventLogger: UiEventLogger,
) {
/** Shows a popup with the [expandable] animation. */
fun show(expandable: Expandable?) {
+ uiEventLogger.log(VolumePanelUiEvent.VOLUME_PANEL_ANC_POPUP_SHOWN)
volumePanelPopup.show(expandable, { Title() }, { Content(it) })
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
index eed54da..9a98bde 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
@@ -26,6 +26,7 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
+import com.android.internal.logging.UiEventLogger
import com.android.systemui.animation.Expandable
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.common.ui.compose.toColor
@@ -34,6 +35,7 @@
import com.android.systemui.volume.panel.component.popup.ui.composable.VolumePanelPopup
import com.android.systemui.volume.panel.component.selector.ui.composable.VolumePanelRadioButtonBar
import com.android.systemui.volume.panel.component.spatial.ui.viewmodel.SpatialAudioViewModel
+import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
import javax.inject.Inject
class SpatialAudioPopup
@@ -41,10 +43,17 @@
constructor(
private val viewModel: SpatialAudioViewModel,
private val volumePanelPopup: VolumePanelPopup,
+ private val uiEventLogger: UiEventLogger,
) {
/** Shows a popup with the [expandable] animation. */
fun show(expandable: Expandable) {
+ uiEventLogger.logWithPosition(
+ VolumePanelUiEvent.VOLUME_PANEL_SPATIAL_AUDIO_POP_UP_SHOWN,
+ 0,
+ null,
+ viewModel.spatialAudioButtons.value.indexOfFirst { it.button.isChecked }
+ )
volumePanelPopup.show(expandable, { Title() }, { Content(it) })
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
index f89669c..a54d005 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
@@ -85,6 +85,7 @@
onValueChange = { newValue: Float ->
sliderViewModel.onValueChanged(sliderState, newValue)
},
+ onValueChangeFinished = { sliderViewModel.onValueChangeFinished() },
onIconTapped = { sliderViewModel.toggleMuted(sliderState) },
sliderColors = sliderColors,
)
@@ -131,6 +132,7 @@
onValueChange = { newValue: Float ->
sliderViewModel.onValueChanged(sliderState, newValue)
},
+ onValueChangeFinished = { sliderViewModel.onValueChangeFinished() },
onIconTapped = { sliderViewModel.toggleMuted(sliderState) },
sliderColors = sliderColors,
)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/GridVolumeSliders.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/GridVolumeSliders.kt
index b284c69..bb17499 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/GridVolumeSliders.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/GridVolumeSliders.kt
@@ -46,6 +46,7 @@
onValueChange = { newValue: Float ->
sliderViewModel.onValueChanged(sliderState, newValue)
},
+ onValueChangeFinished = { sliderViewModel.onValueChangeFinished() },
onIconTapped = { sliderViewModel.toggleMuted(sliderState) },
sliderColors = sliderColors,
)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
index 28cd37e..228d292 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
@@ -51,6 +51,7 @@
fun VolumeSlider(
state: SliderState,
onValueChange: (newValue: Float) -> Unit,
+ onValueChangeFinished: (() -> Unit)? = null,
onIconTapped: () -> Unit,
modifier: Modifier = Modifier,
sliderColors: PlatformSliderColors,
@@ -85,6 +86,7 @@
value = value,
valueRange = state.valueRange,
onValueChange = onValueChange,
+ onValueChangeFinished = onValueChangeFinished,
enabled = state.isEnabled,
icon = {
state.icon?.let {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/startable/AudioModeLoggerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/startable/AudioModeLoggerStartableTest.kt
new file mode 100644
index 0000000..8bb3672
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/startable/AudioModeLoggerStartableTest.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.domain.startable
+
+import android.media.AudioManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.uiEventLogger
+import com.android.internal.logging.uiEventLoggerFake
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.volume.audioModeInteractor
+import com.android.systemui.volume.audioRepository
+import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class AudioModeLoggerStartableTest : SysuiTestCase() {
+ @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
+
+ private val kosmos = testKosmos()
+
+ private lateinit var underTest: AudioModeLoggerStartable
+
+ @Before
+ fun setUp() {
+ with(kosmos) {
+ underTest =
+ AudioModeLoggerStartable(
+ applicationCoroutineScope,
+ uiEventLogger,
+ audioModeInteractor
+ )
+ }
+ }
+
+ @Test
+ fun audioMode_inCall() {
+ with(kosmos) {
+ testScope.runTest {
+ audioRepository.setMode(AudioManager.MODE_IN_CALL)
+
+ underTest.start()
+ runCurrent()
+
+ assertThat(uiEventLoggerFake.eventId(0))
+ .isEqualTo(VolumePanelUiEvent.VOLUME_PANEL_AUDIO_MODE_CHANGE_TO_CALLING.id)
+ }
+ }
+ }
+
+ @Test
+ fun audioMode_notInCall() {
+ with(kosmos) {
+ testScope.runTest {
+ audioRepository.setMode(AudioManager.MODE_NORMAL)
+
+ underTest.start()
+ runCurrent()
+
+ assertThat(uiEventLoggerFake.eventId(0))
+ .isEqualTo(VolumePanelUiEvent.VOLUME_PANEL_AUDIO_MODE_CHANGE_TO_NORMAL.id)
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/bottombar/ui/viewmodel/BottomBarViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/bottombar/ui/viewmodel/BottomBarViewModelTest.kt
index 2cc1ad3..27a813f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/bottombar/ui/viewmodel/BottomBarViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/bottombar/ui/viewmodel/BottomBarViewModelTest.kt
@@ -21,6 +21,8 @@
import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.internal.logging.uiEventLogger
+import com.android.internal.logging.uiEventLoggerFake
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
@@ -29,6 +31,7 @@
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
+import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
import com.android.systemui.volume.panel.volumePanelViewModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -58,7 +61,10 @@
private lateinit var underTest: BottomBarViewModel
private fun initUnderTest() {
- underTest = with(kosmos) { BottomBarViewModel(activityStarter, volumePanelViewModel) }
+ underTest =
+ with(kosmos) {
+ BottomBarViewModel(activityStarter, volumePanelViewModel, uiEventLogger)
+ }
}
@Test
@@ -96,6 +102,8 @@
/* userHandle = */ eq(null),
)
assertThat(intentCaptor.value.action).isEqualTo(Settings.ACTION_SOUND_SETTINGS)
+ assertThat(uiEventLoggerFake.eventId(0))
+ .isEqualTo(VolumePanelUiEvent.VOLUME_PANEL_SOUND_SETTINGS_CLICKED.id)
activityStartedCaptor.value.onActivityStarted(ActivityManager.START_SUCCESS)
val volumePanelState by collectLastValue(volumePanelViewModel.volumePanelState)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelTest.kt
index 610195f..fdeded8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelTest.kt
@@ -18,6 +18,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.internal.logging.uiEventLogger
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
@@ -45,7 +46,12 @@
fun setup() {
underTest =
with(kosmos) {
- CaptioningViewModel(context, captioningInteractor, testScope.backgroundScope)
+ CaptioningViewModel(
+ context,
+ captioningInteractor,
+ testScope.backgroundScope,
+ uiEventLogger,
+ )
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteriaTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteriaTest.kt
index ec55c75..da0a229 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteriaTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteriaTest.kt
@@ -46,7 +46,10 @@
@Before
fun setup() {
- underTest = MediaOutputAvailabilityCriteria(kosmos.audioModeInteractor)
+ underTest =
+ MediaOutputAvailabilityCriteria(
+ kosmos.audioModeInteractor,
+ )
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt
index 462f36d..30524d9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt
@@ -22,6 +22,7 @@
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.internal.logging.uiEventLogger
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
@@ -64,6 +65,7 @@
mediaOutputActionsInteractor,
mediaDeviceSessionInteractor,
mediaOutputInteractor,
+ uiEventLogger,
)
with(context.orCreateTestableResources) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 5eaccd9..e980794 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -594,7 +594,11 @@
);
if (view instanceof FooterView) {
if (FooterViewRefactor.isEnabled()) {
- if (((FooterView) view).shouldBeHidden()) {
+ // TODO(b/333445519): shouldBeHidden should reflect whether the shade is closed
+ // already, so we shouldn't need to use ambientState here. However, currently it
+ // doesn't get updated quickly enough and can cause the footer to flash when
+ // closing the shade. As such, we temporarily also check the ambientState directly.
+ if (((FooterView) view).shouldBeHidden() || !ambientState.isShadeExpanded()) {
viewState.hidden = true;
} else {
final float footerEnd = algorithmState.mCurrentExpandedYPosition
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/UiEventLoggerStartableModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/UiEventLoggerStartableModule.kt
new file mode 100644
index 0000000..9b84090
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/UiEventLoggerStartableModule.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dagger
+
+import com.android.systemui.volume.domain.startable.AudioModeLoggerStartable
+import com.android.systemui.volume.panel.domain.VolumePanelStartable
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
+
+@Module
+interface UiEventLoggerStartableModule {
+
+ @Binds
+ @IntoSet
+ fun bindAudioModeLoggerStartable(
+ audioModeLoggerStartable: AudioModeLoggerStartable,
+ ): VolumePanelStartable
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/domain/startable/AudioModeLoggerStartable.kt b/packages/SystemUI/src/com/android/systemui/volume/domain/startable/AudioModeLoggerStartable.kt
new file mode 100644
index 0000000..1244757
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/domain/startable/AudioModeLoggerStartable.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.domain.startable
+
+import com.android.internal.logging.UiEventLogger
+import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import com.android.systemui.volume.panel.domain.VolumePanelStartable
+import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.launch
+
+/** Logger for audio mode */
+@VolumePanelScope
+class AudioModeLoggerStartable
+@Inject
+constructor(
+ @VolumePanelScope private val scope: CoroutineScope,
+ private val uiEventLogger: UiEventLogger,
+ private val audioModeInteractor: AudioModeInteractor,
+) : VolumePanelStartable {
+
+ override fun start() {
+ scope.launch {
+ audioModeInteractor.isOngoingCall.distinctUntilChanged().collect { ongoingCall ->
+ uiEventLogger.log(
+ if (ongoingCall) VolumePanelUiEvent.VOLUME_PANEL_AUDIO_MODE_CHANGE_TO_CALLING
+ else VolumePanelUiEvent.VOLUME_PANEL_AUDIO_MODE_CHANGE_TO_NORMAL
+ )
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/bottombar/ui/viewmodel/BottomBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/bottombar/ui/viewmodel/BottomBarViewModel.kt
index 04d7b1f..3ca9cdf 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/bottombar/ui/viewmodel/BottomBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/bottombar/ui/viewmodel/BottomBarViewModel.kt
@@ -18,8 +18,10 @@
import android.content.Intent
import android.provider.Settings
+import com.android.internal.logging.UiEventLogger
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel
import javax.inject.Inject
@@ -29,6 +31,7 @@
constructor(
private val activityStarter: ActivityStarter,
private val volumePanelViewModel: VolumePanelViewModel,
+ private val uiEventLogger: UiEventLogger,
) {
fun onDoneClicked() {
@@ -36,6 +39,7 @@
}
fun onSettingsClicked() {
+ uiEventLogger.log(VolumePanelUiEvent.VOLUME_PANEL_SOUND_SETTINGS_CLICKED)
activityStarter.startActivityDismissingKeyguard(
/* intent = */ Intent(Settings.ACTION_SOUND_SETTINGS),
/* onlyProvisioned = */ false,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/domain/CaptioningAvailabilityCriteria.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/domain/CaptioningAvailabilityCriteria.kt
index aab825f..85da1d0 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/domain/CaptioningAvailabilityCriteria.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/domain/CaptioningAvailabilityCriteria.kt
@@ -16,18 +16,36 @@
package com.android.systemui.volume.panel.component.captioning.domain
+import com.android.internal.logging.UiEventLogger
import com.android.settingslib.view.accessibility.domain.interactor.CaptioningInteractor
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
+import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.shareIn
@VolumePanelScope
class CaptioningAvailabilityCriteria
@Inject
-constructor(private val captioningInteractor: CaptioningInteractor) :
- ComponentAvailabilityCriteria {
+constructor(
+ captioningInteractor: CaptioningInteractor,
+ @VolumePanelScope private val scope: CoroutineScope,
+ private val uiEventLogger: UiEventLogger,
+) : ComponentAvailabilityCriteria {
- override fun isAvailable(): Flow<Boolean> =
+ private val availability =
captioningInteractor.isSystemAudioCaptioningUiEnabled
+ .onEach { visible ->
+ uiEventLogger.log(
+ if (visible) VolumePanelUiEvent.VOLUME_PANEL_LIVE_CAPTION_TOGGLE_SHOWN
+ else VolumePanelUiEvent.VOLUME_PANEL_LIVE_CAPTION_TOGGLE_GONE
+ )
+ }
+ .shareIn(scope, SharingStarted.WhileSubscribed(), replay = 1)
+
+ override fun isAvailable(): Flow<Boolean> = availability
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModel.kt
index 92f8f22..01421f8 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModel.kt
@@ -17,11 +17,13 @@
package com.android.systemui.volume.panel.component.captioning.ui.viewmodel
import android.content.Context
+import com.android.internal.logging.UiEventLogger
import com.android.settingslib.view.accessibility.domain.interactor.CaptioningInteractor
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.res.R
import com.android.systemui.volume.panel.component.button.ui.viewmodel.ToggleButtonViewModel
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
@@ -38,6 +40,7 @@
private val context: Context,
private val captioningInteractor: CaptioningInteractor,
@VolumePanelScope private val coroutineScope: CoroutineScope,
+ private val uiEventLogger: UiEventLogger,
) {
val buttonViewModel: StateFlow<ToggleButtonViewModel?> =
@@ -57,6 +60,13 @@
.stateIn(coroutineScope, SharingStarted.Eagerly, null)
fun setIsSystemAudioCaptioningEnabled(enabled: Boolean) {
+ uiEventLogger.logWithPosition(
+ VolumePanelUiEvent.VOLUME_PANEL_LIVE_CAPTION_TOGGLE_CLICKED,
+ 0,
+ null,
+ if (enabled) VolumePanelUiEvent.LIVE_CAPTION_TOGGLE_ENABLED
+ else VolumePanelUiEvent.LIVE_CAPTION_TOGGLE_DISABLED
+ )
coroutineScope.launch { captioningInteractor.setIsSystemAudioCaptioningEnabled(enabled) }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
index fc9602e..6b237f8e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.volume.panel.component.mediaoutput.ui.viewmodel
import android.content.Context
+import com.android.internal.logging.UiEventLogger
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Color
import com.android.systemui.common.shared.model.Icon
@@ -26,6 +27,7 @@
import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor
import com.android.systemui.volume.panel.component.mediaoutput.shared.model.SessionWithPlayback
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -48,6 +50,7 @@
private val actionsInteractor: MediaOutputActionsInteractor,
private val mediaDeviceSessionInteractor: MediaDeviceSessionInteractor,
interactor: MediaOutputInteractor,
+ private val uiEventLogger: UiEventLogger,
) {
private val sessionWithPlayback: StateFlow<SessionWithPlayback?> =
@@ -126,6 +129,7 @@
)
fun onBarClick(expandable: Expandable) {
+ uiEventLogger.log(VolumePanelUiEvent.VOLUME_PANEL_MEDIA_OUTPUT_CLICKED)
actionsInteractor.onBarClick(sessionWithPlayback.value, expandable)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt
index f022039..4ecdd46 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.volume.panel.component.spatial.ui.viewmodel
import android.content.Context
+import com.android.internal.logging.UiEventLogger
import com.android.systemui.common.shared.model.Color
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.qualifiers.Application
@@ -29,6 +30,7 @@
import com.android.systemui.volume.panel.component.spatial.domain.model.SpatialAudioAvailabilityModel
import com.android.systemui.volume.panel.component.spatial.domain.model.SpatialAudioEnabledModel
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
@@ -46,6 +48,7 @@
@VolumePanelScope private val scope: CoroutineScope,
availabilityCriteria: SpatialAudioAvailabilityCriteria,
private val interactor: SpatialAudioComponentInteractor,
+ private val uiEventLogger: UiEventLogger,
) {
val spatialAudioButton: StateFlow<ButtonViewModel?> =
@@ -101,6 +104,19 @@
.stateIn(scope, SharingStarted.Eagerly, emptyList())
fun setEnabled(model: SpatialAudioEnabledModel) {
+ uiEventLogger.logWithPosition(
+ VolumePanelUiEvent.VOLUME_PANEL_SPATIAL_AUDIO_TOGGLE_CLICKED,
+ 0,
+ null,
+ when (model) {
+ SpatialAudioEnabledModel.Disabled -> 0
+ SpatialAudioEnabledModel.SpatialAudioEnabled -> 1
+ SpatialAudioEnabledModel.HeadTrackingEnabled -> 2
+ else -> {
+ -1
+ }
+ }
+ )
scope.launch { interactor.setEnabled(model) }
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
index 1ae1ebb..c8cd6fd 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
@@ -18,12 +18,14 @@
import android.content.Context
import android.media.AudioManager
+import com.android.internal.logging.UiEventLogger
import com.android.settingslib.volume.domain.interactor.AudioVolumeInteractor
import com.android.settingslib.volume.shared.model.AudioStream
import com.android.settingslib.volume.shared.model.AudioStreamModel
import com.android.settingslib.volume.shared.model.RingerMode
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.res.R
+import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -43,6 +45,7 @@
@Assisted private val coroutineScope: CoroutineScope,
private val context: Context,
private val audioVolumeInteractor: AudioVolumeInteractor,
+ private val uiEventLogger: UiEventLogger,
) : SliderViewModel {
private val audioStream = audioStreamWrapper.audioStream
@@ -69,6 +72,19 @@
AudioStream(AudioManager.STREAM_ALARM) to R.string.stream_alarm_unavailable,
AudioStream(AudioManager.STREAM_MUSIC) to R.string.stream_media_unavailable,
)
+ private val uiEventByStream =
+ mapOf(
+ AudioStream(AudioManager.STREAM_MUSIC) to
+ VolumePanelUiEvent.VOLUME_PANEL_MUSIC_SLIDER_TOUCHED,
+ AudioStream(AudioManager.STREAM_VOICE_CALL) to
+ VolumePanelUiEvent.VOLUME_PANEL_VOICE_CALL_SLIDER_TOUCHED,
+ AudioStream(AudioManager.STREAM_RING) to
+ VolumePanelUiEvent.VOLUME_PANEL_RING_SLIDER_TOUCHED,
+ AudioStream(AudioManager.STREAM_NOTIFICATION) to
+ VolumePanelUiEvent.VOLUME_PANEL_NOTIFICATION_SLIDER_TOUCHED,
+ AudioStream(AudioManager.STREAM_ALARM) to
+ VolumePanelUiEvent.VOLUME_PANEL_ALARM_SLIDER_TOUCHED,
+ )
override val slider: StateFlow<SliderState> =
combine(
@@ -88,6 +104,10 @@
}
}
+ override fun onValueChangeFinished() {
+ uiEventByStream[audioStream]?.let { uiEventLogger.log(it) }
+ }
+
override fun toggleMuted(state: SliderState) {
val audioViewModel = state as? State
audioViewModel ?: return
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt
index 3689303..956ab66 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt
@@ -54,6 +54,8 @@
}
}
+ override fun onValueChangeFinished() {}
+
override fun toggleMuted(state: SliderState) {
// do nothing because this action isn't supported for Cast sliders.
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderViewModel.kt
index 74aee55..7ded8c5 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderViewModel.kt
@@ -25,5 +25,7 @@
fun onValueChanged(state: SliderState, newValue: Float)
+ fun onValueChangeFinished()
+
fun toggleMuted(state: SliderState)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/DefaultMultibindsModule.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/DefaultMultibindsModule.kt
index d1d5390..f889ed6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/DefaultMultibindsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/DefaultMultibindsModule.kt
@@ -17,6 +17,7 @@
package com.android.systemui.volume.panel.dagger
import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
+import com.android.systemui.volume.panel.domain.VolumePanelStartable
import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey
import com.android.systemui.volume.panel.shared.model.VolumePanelUiComponent
import dagger.Module
@@ -31,4 +32,6 @@
@Multibinds fun criteriaMap(): Map<VolumePanelComponentKey, ComponentAvailabilityCriteria>
@Multibinds fun components(): Map<VolumePanelComponentKey, VolumePanelUiComponent>
+
+ @Multibinds fun startables(): Set<VolumePanelStartable>
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/VolumePanelComponent.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/VolumePanelComponent.kt
index d868c33..ec64f3d9 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/VolumePanelComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/VolumePanelComponent.kt
@@ -16,6 +16,7 @@
package com.android.systemui.volume.panel.dagger
+import com.android.systemui.volume.dagger.UiEventLoggerStartableModule
import com.android.systemui.volume.panel.component.anc.AncModule
import com.android.systemui.volume.panel.component.bottombar.BottomBarModule
import com.android.systemui.volume.panel.component.captioning.CaptioningModule
@@ -25,6 +26,7 @@
import com.android.systemui.volume.panel.dagger.factory.VolumePanelComponentFactory
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
import com.android.systemui.volume.panel.domain.DomainModule
+import com.android.systemui.volume.panel.domain.VolumePanelStartable
import com.android.systemui.volume.panel.domain.interactor.ComponentsInteractor
import com.android.systemui.volume.panel.ui.UiModule
import com.android.systemui.volume.panel.ui.composable.ComponentsFactory
@@ -47,6 +49,7 @@
DefaultMultibindsModule::class,
DomainModule::class,
UiModule::class,
+ UiEventLoggerStartableModule::class,
// Components modules
BottomBarModule::class,
AncModule::class,
@@ -66,6 +69,8 @@
fun componentsLayoutManager(): ComponentsLayoutManager
+ fun volumePanelStartables(): Set<VolumePanelStartable>
+
@Subcomponent.Factory
interface Factory : VolumePanelComponentFactory {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/domain/VolumePanelStartable.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/VolumePanelStartable.kt
new file mode 100644
index 0000000..9c39f5e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/VolumePanelStartable.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.domain
+
+/** Code that needs to be run when Volume Panel is started.. */
+interface VolumePanelStartable {
+ fun start()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/VolumePanelUiEvent.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/VolumePanelUiEvent.kt
new file mode 100644
index 0000000..8b8714f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/VolumePanelUiEvent.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.ui
+
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+
+/** UI events for Volume Panel. */
+enum class VolumePanelUiEvent(val metricId: Int) : UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "The volume panel is shown") VOLUME_PANEL_SHOWN(1634),
+ @UiEvent(doc = "The volume panel is gone") VOLUME_PANEL_GONE(1635),
+ @UiEvent(doc = "Media output is clicked") VOLUME_PANEL_MEDIA_OUTPUT_CLICKED(1636),
+ @UiEvent(doc = "Audio mode changed to normal") VOLUME_PANEL_AUDIO_MODE_CHANGE_TO_NORMAL(1680),
+ @UiEvent(doc = "Audio mode changed to calling") VOLUME_PANEL_AUDIO_MODE_CHANGE_TO_CALLING(1681),
+ @UiEvent(doc = "Sound settings is clicked") VOLUME_PANEL_SOUND_SETTINGS_CLICKED(1638),
+ @UiEvent(doc = "The music volume slider is touched") VOLUME_PANEL_MUSIC_SLIDER_TOUCHED(1639),
+ @UiEvent(doc = "The voice call volume slider is touched")
+ VOLUME_PANEL_VOICE_CALL_SLIDER_TOUCHED(1640),
+ @UiEvent(doc = "The ring volume slider is touched") VOLUME_PANEL_RING_SLIDER_TOUCHED(1641),
+ @UiEvent(doc = "The notification volume slider is touched")
+ VOLUME_PANEL_NOTIFICATION_SLIDER_TOUCHED(1642),
+ @UiEvent(doc = "The alarm volume slider is touched") VOLUME_PANEL_ALARM_SLIDER_TOUCHED(1643),
+ @UiEvent(doc = "Live caption toggle is shown") VOLUME_PANEL_LIVE_CAPTION_TOGGLE_SHOWN(1644),
+ @UiEvent(doc = "Live caption toggle is gone") VOLUME_PANEL_LIVE_CAPTION_TOGGLE_GONE(1645),
+ @UiEvent(doc = "Live caption toggle is clicked") VOLUME_PANEL_LIVE_CAPTION_TOGGLE_CLICKED(1646),
+ @UiEvent(doc = "Spatial audio button is shown") VOLUME_PANEL_SPATIAL_AUDIO_BUTTON_SHOWN(1647),
+ @UiEvent(doc = "Spatial audio button is gone") VOLUME_PANEL_SPATIAL_AUDIO_BUTTON_GONE(1648),
+ @UiEvent(doc = "Spatial audio popup is shown") VOLUME_PANEL_SPATIAL_AUDIO_POP_UP_SHOWN(1649),
+ @UiEvent(doc = "Spatial audio toggle is clicked")
+ VOLUME_PANEL_SPATIAL_AUDIO_TOGGLE_CLICKED(1650),
+ @UiEvent(doc = "ANC button is shown") VOLUME_PANEL_ANC_BUTTON_SHOWN(1651),
+ @UiEvent(doc = "ANC button is gone") VOLUME_PANEL_ANC_BUTTON_GONE(1652),
+ @UiEvent(doc = "ANC popup is shown") VOLUME_PANEL_ANC_POPUP_SHOWN(1653),
+ @UiEvent(doc = "ANC toggle is clicked") VOLUME_PANEL_ANC_TOGGLE_CLICKED(1654);
+
+ override fun getId() = metricId
+
+ companion object {
+ const val LIVE_CAPTION_TOGGLE_DISABLED = 0
+ const val LIVE_CAPTION_TOGGLE_ENABLED = 1
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/activity/VolumePanelActivity.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/activity/VolumePanelActivity.kt
index c728fef..ccb91ac 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/activity/VolumePanelActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/activity/VolumePanelActivity.kt
@@ -21,8 +21,10 @@
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
+import com.android.internal.logging.UiEventLogger
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.volume.panel.shared.flag.VolumePanelFlag
+import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
import com.android.systemui.volume.panel.ui.composable.VolumePanelRoot
import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel
import javax.inject.Inject
@@ -34,6 +36,7 @@
private val volumePanelViewModelFactory: Provider<VolumePanelViewModel.Factory>,
private val volumePanelFlag: VolumePanelFlag,
private val configurationController: ConfigurationController,
+ private val uiEventLogger: UiEventLogger,
) : ComponentActivity() {
private val viewModel: VolumePanelViewModel by
@@ -43,8 +46,16 @@
enableEdgeToEdge()
super.onCreate(savedInstanceState)
volumePanelFlag.assertNewVolumePanel()
-
- setContent { VolumePanelRoot(viewModel = viewModel, onDismiss = ::finish) }
+ uiEventLogger.log(VolumePanelUiEvent.VOLUME_PANEL_SHOWN)
+ setContent {
+ VolumePanelRoot(
+ viewModel = viewModel,
+ onDismiss = {
+ uiEventLogger.log(VolumePanelUiEvent.VOLUME_PANEL_GONE)
+ finish()
+ }
+ )
+ }
}
override fun onContentChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt
index 5ae827f..1de4fd1 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt
@@ -26,6 +26,7 @@
import com.android.systemui.statusbar.policy.onConfigChanged
import com.android.systemui.volume.panel.dagger.VolumePanelComponent
import com.android.systemui.volume.panel.dagger.factory.VolumePanelComponentFactory
+import com.android.systemui.volume.panel.domain.VolumePanelStartable
import com.android.systemui.volume.panel.domain.interactor.ComponentsInteractor
import com.android.systemui.volume.panel.ui.composable.ComponentsFactory
import com.android.systemui.volume.panel.ui.layout.ComponentsLayout
@@ -109,6 +110,10 @@
replay = 1,
)
+ init {
+ volumePanelComponent.volumePanelStartables().onEach(VolumePanelStartable::start)
+ }
+
fun dismissPanel() {
mutablePanelVisibility.update { false }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/VolumePanelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/VolumePanelKosmos.kt
index d341073..348a02e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/VolumePanelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/VolumePanelKosmos.kt
@@ -24,6 +24,7 @@
import com.android.systemui.volume.panel.dagger.factory.KosmosVolumePanelComponentFactory
import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
import com.android.systemui.volume.panel.domain.TestComponentAvailabilityCriteria
+import com.android.systemui.volume.panel.domain.VolumePanelStartable
import com.android.systemui.volume.panel.domain.interactor.ComponentsInteractor
import com.android.systemui.volume.panel.domain.interactor.ComponentsInteractorImpl
import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey
@@ -44,6 +45,8 @@
var Kosmos.componentsLayoutManager: ComponentsLayoutManager by Kosmos.Fixture()
var Kosmos.enabledComponents: Collection<VolumePanelComponentKey> by
Kosmos.Fixture { componentByKey.keys }
+var Kosmos.volumePanelStartables: Set<VolumePanelStartable> by
+ Kosmos.Fixture { emptySet<VolumePanelStartable>() }
val Kosmos.unavailableCriteria: Provider<ComponentAvailabilityCriteria> by
Kosmos.Fixture { Provider { TestComponentAvailabilityCriteria(false) } }
val Kosmos.availableCriteria: Provider<ComponentAvailabilityCriteria> by
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/dagger/factory/KosmosVolumePanelComponentFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/dagger/factory/KosmosVolumePanelComponentFactory.kt
index 49041ed..e5f5d4e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/dagger/factory/KosmosVolumePanelComponentFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/dagger/factory/KosmosVolumePanelComponentFactory.kt
@@ -22,10 +22,12 @@
import com.android.systemui.volume.panel.componentsInteractor
import com.android.systemui.volume.panel.componentsLayoutManager
import com.android.systemui.volume.panel.dagger.VolumePanelComponent
+import com.android.systemui.volume.panel.domain.VolumePanelStartable
import com.android.systemui.volume.panel.domain.interactor.ComponentsInteractor
import com.android.systemui.volume.panel.ui.composable.ComponentsFactory
import com.android.systemui.volume.panel.ui.layout.ComponentsLayoutManager
import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel
+import com.android.systemui.volume.panel.volumePanelStartables
import kotlinx.coroutines.CoroutineScope
class KosmosVolumePanelComponentFactory(private val kosmos: Kosmos) : VolumePanelComponentFactory {
@@ -41,5 +43,8 @@
override fun componentsLayoutManager(): ComponentsLayoutManager =
kosmos.componentsLayoutManager
+
+ override fun volumePanelStartables(): Set<VolumePanelStartable> =
+ kosmos.volumePanelStartables
}
}
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java b/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java
new file mode 100644
index 0000000..7890fe0
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java
@@ -0,0 +1,464 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputmethod;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.Manifest;
+import android.annotation.BinderThread;
+import android.annotation.EnforcePermission;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+import android.view.inputmethod.CursorAnchorInfo;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.ImeTracker;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodSubtype;
+import android.window.ImeOnBackInvokedDispatcher;
+
+import com.android.internal.inputmethod.DirectBootAwareness;
+import com.android.internal.inputmethod.IBooleanListener;
+import com.android.internal.inputmethod.IConnectionlessHandwritingCallback;
+import com.android.internal.inputmethod.IImeTracker;
+import com.android.internal.inputmethod.IInputMethodClient;
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
+import com.android.internal.inputmethod.IRemoteInputConnection;
+import com.android.internal.inputmethod.InputBindResult;
+import com.android.internal.inputmethod.SoftInputShowHideReason;
+import com.android.internal.inputmethod.StartInputFlags;
+import com.android.internal.inputmethod.StartInputReason;
+import com.android.internal.view.IInputMethodManager;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.util.List;
+
+/**
+ * An actual implementation class of {@link IInputMethodManager.Stub} to allow other classes to
+ * focus on handling IPC callbacks.
+ */
+final class IInputMethodManagerImpl extends IInputMethodManager.Stub {
+
+ /**
+ * Tells that the given permission is already verified before the annotated method gets called.
+ */
+ @Retention(SOURCE)
+ @Target({METHOD})
+ @interface PermissionVerified {
+ String value() default "";
+ }
+
+ @BinderThread
+ interface Callback {
+ void addClient(IInputMethodClient client, IRemoteInputConnection inputConnection,
+ int selfReportedDisplayId);
+
+ InputMethodInfo getCurrentInputMethodInfoAsUser(@UserIdInt int userId);
+
+ List<InputMethodInfo> getInputMethodList(@UserIdInt int userId,
+ @DirectBootAwareness int directBootAwareness);
+
+ List<InputMethodInfo> getEnabledInputMethodList(@UserIdInt int userId);
+
+ List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String imiId,
+ boolean allowsImplicitlyEnabledSubtypes, @UserIdInt int userId);
+
+ InputMethodSubtype getLastInputMethodSubtype(@UserIdInt int userId);
+
+ boolean showSoftInput(IInputMethodClient client, IBinder windowToken,
+ @Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
+ @MotionEvent.ToolType int lastClickToolType, ResultReceiver resultReceiver,
+ @SoftInputShowHideReason int reason);
+
+ boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
+ @Nullable ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
+ ResultReceiver resultReceiver, @SoftInputShowHideReason int reason);
+
+ @PermissionVerified(Manifest.permission.TEST_INPUT_METHOD)
+ void hideSoftInputFromServerForTest();
+
+ void startInputOrWindowGainedFocusAsync(
+ @StartInputReason int startInputReason, IInputMethodClient client,
+ IBinder windowToken, @StartInputFlags int startInputFlags,
+ @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode, int windowFlags,
+ @Nullable EditorInfo editorInfo, IRemoteInputConnection inputConnection,
+ IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
+ int unverifiedTargetSdkVersion, @UserIdInt int userId,
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq);
+
+ InputBindResult startInputOrWindowGainedFocus(
+ @StartInputReason int startInputReason, IInputMethodClient client,
+ IBinder windowToken, @StartInputFlags int startInputFlags,
+ @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode, int windowFlags,
+ @Nullable EditorInfo editorInfo, IRemoteInputConnection inputConnection,
+ IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
+ int unverifiedTargetSdkVersion, @UserIdInt int userId,
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher);
+
+ void showInputMethodPickerFromClient(IInputMethodClient client, int auxiliarySubtypeMode);
+
+ @PermissionVerified(Manifest.permission.WRITE_SECURE_SETTINGS)
+ void showInputMethodPickerFromSystem(int auxiliarySubtypeMode, int displayId);
+
+ @PermissionVerified(Manifest.permission.TEST_INPUT_METHOD)
+ boolean isInputMethodPickerShownForTest();
+
+ InputMethodSubtype getCurrentInputMethodSubtype(@UserIdInt int userId);
+
+ void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes,
+ @UserIdInt int userId);
+
+ void setExplicitlyEnabledInputMethodSubtypes(String imeId,
+ @NonNull int[] subtypeHashCodes, @UserIdInt int userId);
+
+ int getInputMethodWindowVisibleHeight(IInputMethodClient client);
+
+ void reportPerceptibleAsync(IBinder windowToken, boolean perceptible);
+
+ @PermissionVerified(Manifest.permission.INTERNAL_SYSTEM_WINDOW)
+ void removeImeSurface();
+
+ void removeImeSurfaceFromWindowAsync(IBinder windowToken);
+
+ void startProtoDump(byte[] bytes, int i, String s);
+
+ boolean isImeTraceEnabled();
+
+ @PermissionVerified(Manifest.permission.CONTROL_UI_TRACING)
+ void startImeTrace();
+
+ @PermissionVerified(Manifest.permission.CONTROL_UI_TRACING)
+ void stopImeTrace();
+
+ void startStylusHandwriting(IInputMethodClient client);
+
+ void startConnectionlessStylusHandwriting(IInputMethodClient client, @UserIdInt int userId,
+ @Nullable CursorAnchorInfo cursorAnchorInfo, @Nullable String delegatePackageName,
+ @Nullable String delegatorPackageName,
+ @NonNull IConnectionlessHandwritingCallback callback);
+
+ boolean acceptStylusHandwritingDelegation(@NonNull IInputMethodClient client,
+ @UserIdInt int userId, @NonNull String delegatePackageName,
+ @NonNull String delegatorPackageName,
+ @InputMethodManager.HandwritingDelegateFlags int flags);
+
+ void acceptStylusHandwritingDelegationAsync(@NonNull IInputMethodClient client,
+ @UserIdInt int userId, @NonNull String delegatePackageName,
+ @NonNull String delegatorPackageName,
+ @InputMethodManager.HandwritingDelegateFlags int flags, IBooleanListener callback);
+
+ void prepareStylusHandwritingDelegation(@NonNull IInputMethodClient client,
+ @UserIdInt int userId, @NonNull String delegatePackageName,
+ @NonNull String delegatorPackageName);
+
+ boolean isStylusHandwritingAvailableAsUser(@UserIdInt int userId, boolean connectionless);
+
+ @PermissionVerified(Manifest.permission.TEST_INPUT_METHOD)
+ void addVirtualStylusIdForTestSession(IInputMethodClient client);
+
+ @PermissionVerified(Manifest.permission.TEST_INPUT_METHOD)
+ void setStylusWindowIdleTimeoutForTest(IInputMethodClient client, long timeout);
+
+ IImeTracker getImeTrackerService();
+
+ void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+ @Nullable FileDescriptor err, @NonNull String[] args,
+ @Nullable ShellCallback callback, @NonNull ResultReceiver resultReceiver,
+ @NonNull Binder self);
+
+ void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout, @Nullable String[] args);
+ }
+
+ @NonNull
+ private final Callback mCallback;
+
+ private IInputMethodManagerImpl(@NonNull Callback callback) {
+ mCallback = callback;
+ }
+
+ static IInputMethodManagerImpl create(@NonNull Callback callback) {
+ return new IInputMethodManagerImpl(callback);
+ }
+
+ @Override
+ public void addClient(IInputMethodClient client, IRemoteInputConnection inputmethod,
+ int untrustedDisplayId) {
+ mCallback.addClient(client, inputmethod, untrustedDisplayId);
+ }
+
+ @Override
+ public InputMethodInfo getCurrentInputMethodInfoAsUser(@UserIdInt int userId) {
+ return mCallback.getCurrentInputMethodInfoAsUser(userId);
+ }
+
+ @Override
+ public List<InputMethodInfo> getInputMethodList(@UserIdInt int userId,
+ int directBootAwareness) {
+ return mCallback.getInputMethodList(userId, directBootAwareness);
+ }
+
+ @Override
+ public List<InputMethodInfo> getEnabledInputMethodList(@UserIdInt int userId) {
+ return mCallback.getEnabledInputMethodList(userId);
+ }
+
+ @Override
+ public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String imiId,
+ boolean allowsImplicitlyEnabledSubtypes, @UserIdInt int userId) {
+ return mCallback.getEnabledInputMethodSubtypeList(imiId, allowsImplicitlyEnabledSubtypes,
+ userId);
+ }
+
+ @Override
+ public InputMethodSubtype getLastInputMethodSubtype(@UserIdInt int userId) {
+ return mCallback.getLastInputMethodSubtype(userId);
+ }
+
+ @Override
+ public boolean showSoftInput(IInputMethodClient client, IBinder windowToken,
+ @NonNull ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
+ @MotionEvent.ToolType int lastClickToolType, ResultReceiver resultReceiver,
+ @SoftInputShowHideReason int reason) {
+ return mCallback.showSoftInput(client, windowToken, statsToken, flags, lastClickToolType,
+ resultReceiver, reason);
+ }
+
+ @Override
+ public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
+ @NonNull ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
+ ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+ return mCallback.hideSoftInput(client, windowToken, statsToken, flags, resultReceiver,
+ reason);
+ }
+
+ @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
+ @Override
+ public void hideSoftInputFromServerForTest() {
+ super.hideSoftInputFromServerForTest_enforcePermission();
+
+ mCallback.hideSoftInputFromServerForTest();
+ }
+
+ @Override
+ public InputBindResult startInputOrWindowGainedFocus(
+ @StartInputReason int startInputReason, IInputMethodClient client, IBinder windowToken,
+ @StartInputFlags int startInputFlags,
+ @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode,
+ int windowFlags, @Nullable EditorInfo editorInfo,
+ IRemoteInputConnection inputConnection,
+ IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
+ int unverifiedTargetSdkVersion, @UserIdInt int userId,
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
+ return mCallback.startInputOrWindowGainedFocus(
+ startInputReason, client, windowToken, startInputFlags, softInputMode,
+ windowFlags, editorInfo, inputConnection, remoteAccessibilityInputConnection,
+ unverifiedTargetSdkVersion, userId, imeDispatcher);
+ }
+
+ @Override
+ public void startInputOrWindowGainedFocusAsync(@StartInputReason int startInputReason,
+ IInputMethodClient client, IBinder windowToken,
+ @StartInputFlags int startInputFlags,
+ @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode,
+ int windowFlags, @Nullable EditorInfo editorInfo,
+ IRemoteInputConnection inputConnection,
+ IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
+ int unverifiedTargetSdkVersion, @UserIdInt int userId,
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq) {
+ mCallback.startInputOrWindowGainedFocusAsync(
+ startInputReason, client, windowToken, startInputFlags, softInputMode,
+ windowFlags, editorInfo, inputConnection, remoteAccessibilityInputConnection,
+ unverifiedTargetSdkVersion, userId, imeDispatcher, startInputSeq);
+ }
+
+ @Override
+ public void showInputMethodPickerFromClient(IInputMethodClient client,
+ int auxiliarySubtypeMode) {
+ mCallback.showInputMethodPickerFromClient(client, auxiliarySubtypeMode);
+ }
+
+ @EnforcePermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ @Override
+ public void showInputMethodPickerFromSystem(int auxiliarySubtypeMode, int displayId) {
+ super.showInputMethodPickerFromSystem_enforcePermission();
+
+ mCallback.showInputMethodPickerFromSystem(auxiliarySubtypeMode, displayId);
+
+ }
+
+ @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
+ @Override
+ public boolean isInputMethodPickerShownForTest() {
+ super.isInputMethodPickerShownForTest_enforcePermission();
+
+ return mCallback.isInputMethodPickerShownForTest();
+ }
+
+ @Override
+ public InputMethodSubtype getCurrentInputMethodSubtype(@UserIdInt int userId) {
+ return mCallback.getCurrentInputMethodSubtype(userId);
+ }
+
+ @Override
+ public void setAdditionalInputMethodSubtypes(String id, InputMethodSubtype[] subtypes,
+ @UserIdInt int userId) {
+ mCallback.setAdditionalInputMethodSubtypes(id, subtypes, userId);
+ }
+
+ @Override
+ public void setExplicitlyEnabledInputMethodSubtypes(String imeId, int[] subtypeHashCodes,
+ @UserIdInt int userId) {
+ mCallback.setExplicitlyEnabledInputMethodSubtypes(imeId, subtypeHashCodes, userId);
+ }
+
+ @Override
+ public int getInputMethodWindowVisibleHeight(IInputMethodClient client) {
+ return mCallback.getInputMethodWindowVisibleHeight(client);
+ }
+
+ @Override
+ public void reportPerceptibleAsync(IBinder windowToken, boolean perceptible) {
+ mCallback.reportPerceptibleAsync(windowToken, perceptible);
+ }
+
+ @EnforcePermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW)
+ @Override
+ public void removeImeSurface() {
+ super.removeImeSurface_enforcePermission();
+
+ mCallback.removeImeSurface();
+ }
+
+ @Override
+ public void removeImeSurfaceFromWindowAsync(IBinder windowToken) {
+ mCallback.removeImeSurfaceFromWindowAsync(windowToken);
+ }
+
+ @Override
+ public void startProtoDump(byte[] protoDump, int source, String where) {
+ mCallback.startProtoDump(protoDump, source, where);
+ }
+
+ @Override
+ public boolean isImeTraceEnabled() {
+ return mCallback.isImeTraceEnabled();
+ }
+
+ @EnforcePermission(Manifest.permission.CONTROL_UI_TRACING)
+ @Override
+ public void startImeTrace() {
+ super.startImeTrace_enforcePermission();
+
+ mCallback.startImeTrace();
+ }
+
+ @EnforcePermission(Manifest.permission.CONTROL_UI_TRACING)
+ @Override
+ public void stopImeTrace() {
+ super.stopImeTrace_enforcePermission();
+
+ mCallback.stopImeTrace();
+ }
+
+ @Override
+ public void startStylusHandwriting(IInputMethodClient client) {
+ mCallback.startStylusHandwriting(client);
+ }
+
+ @Override
+ public void startConnectionlessStylusHandwriting(IInputMethodClient client,
+ @UserIdInt int userId, CursorAnchorInfo cursorAnchorInfo,
+ String delegatePackageName, String delegatorPackageName,
+ IConnectionlessHandwritingCallback callback) {
+ mCallback.startConnectionlessStylusHandwriting(client, userId, cursorAnchorInfo,
+ delegatePackageName, delegatorPackageName, callback);
+ }
+
+ @Override
+ public void prepareStylusHandwritingDelegation(IInputMethodClient client, @UserIdInt int userId,
+ String delegatePackageName, String delegatorPackageName) {
+ mCallback.prepareStylusHandwritingDelegation(client, userId,
+ delegatePackageName, delegatorPackageName);
+ }
+
+ @Override
+ public boolean acceptStylusHandwritingDelegation(IInputMethodClient client,
+ @UserIdInt int userId, String delegatePackageName, String delegatorPackageName,
+ @InputMethodManager.HandwritingDelegateFlags int flags) {
+ return mCallback.acceptStylusHandwritingDelegation(client, userId,
+ delegatePackageName, delegatorPackageName, flags);
+ }
+
+ @Override
+ public void acceptStylusHandwritingDelegationAsync(IInputMethodClient client,
+ @UserIdInt int userId, String delegatePackageName, String delegatorPackageName,
+ @InputMethodManager.HandwritingDelegateFlags int flags,
+ IBooleanListener callback) {
+ mCallback.acceptStylusHandwritingDelegationAsync(client, userId,
+ delegatePackageName, delegatorPackageName, flags, callback);
+ }
+
+ @Override
+ public boolean isStylusHandwritingAvailableAsUser(@UserIdInt int userId,
+ boolean connectionless) {
+ return mCallback.isStylusHandwritingAvailableAsUser(userId, connectionless);
+ }
+
+ @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
+ @Override
+ public void addVirtualStylusIdForTestSession(IInputMethodClient client) {
+ super.addVirtualStylusIdForTestSession_enforcePermission();
+
+ mCallback.addVirtualStylusIdForTestSession(client);
+ }
+
+ @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
+ @Override
+ public void setStylusWindowIdleTimeoutForTest(IInputMethodClient client, long timeout) {
+ super.setStylusWindowIdleTimeoutForTest_enforcePermission();
+
+ mCallback.setStylusWindowIdleTimeoutForTest(client, timeout);
+ }
+
+ @Override
+ public IImeTracker getImeTrackerService() {
+ return mCallback.getImeTrackerService();
+ }
+
+ @Override
+ public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+ @Nullable FileDescriptor err, @NonNull String[] args, @Nullable ShellCallback callback,
+ @NonNull ResultReceiver resultReceiver) {
+ mCallback.onShellCommand(in, out, err, args, callback, resultReceiver, this);
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ mCallback.dump(fd, pw, args);
+ }
+}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 1d07eee..03a85c4 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -61,7 +61,6 @@
import android.annotation.BinderThread;
import android.annotation.DrawableRes;
import android.annotation.DurationMillisLong;
-import android.annotation.EnforcePermission;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -172,7 +171,6 @@
import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
-import com.android.internal.view.IInputMethodManager;
import com.android.server.AccessibilityManagerInternal;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
@@ -211,8 +209,9 @@
/**
* This class provides a system service that manages input methods.
*/
-public final class InputMethodManagerService extends IInputMethodManager.Stub
- implements Handler.Callback {
+public final class InputMethodManagerService implements IInputMethodManagerImpl.Callback,
+ ZeroJankProxy.Callback, Handler.Callback {
+
// Virtual device id for test.
private static final Integer VIRTUAL_STYLUS_ID_FOR_TEST = 999999;
static final boolean DEBUG = false;
@@ -1236,21 +1235,14 @@
@Override
public void onStart() {
mService.publishLocalService();
- IInputMethodManager.Stub service;
+ IInputMethodManagerImpl.Callback service;
if (Flags.useZeroJankProxy()) {
- service =
- new ZeroJankProxy(
- mService.mHandler::post,
- mService,
- () -> {
- synchronized (ImfLock.class) {
- return mService.isInputShown();
- }
- });
+ service = new ZeroJankProxy(mService.mHandler::post, mService);
} else {
service = mService;
}
- publishBinderService(Context.INPUT_METHOD_SERVICE, service, false /*allowIsolated*/,
+ publishBinderService(Context.INPUT_METHOD_SERVICE,
+ IInputMethodManagerImpl.create(service), false /*allowIsolated*/,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PROTO);
}
@@ -1935,10 +1927,10 @@
}
@Nullable
- ClientState getClientState(IInputMethodClient client) {
- synchronized (ImfLock.class) {
- return mClientController.getClient(client.asBinder());
- }
+ @GuardedBy("ImfLock.class")
+ @Override
+ public ClientState getClientStateLocked(IInputMethodClient client) {
+ return mClientController.getClient(client.asBinder());
}
// TODO(b/314150112): Move this to ClientController.
@@ -2017,7 +2009,8 @@
}
@GuardedBy("ImfLock.class")
- private boolean isInputShown() {
+ @Override
+ public boolean isInputShownLocked() {
return mVisibilityStateComputer.isInputShown();
}
@@ -3133,11 +3126,16 @@
public void startConnectionlessStylusHandwriting(IInputMethodClient client, int userId,
@Nullable CursorAnchorInfo cursorAnchorInfo, @Nullable String delegatePackageName,
@Nullable String delegatorPackageName,
- @NonNull IConnectionlessHandwritingCallback callback) throws RemoteException {
+ @NonNull IConnectionlessHandwritingCallback callback) {
synchronized (ImfLock.class) {
if (!mBindingController.supportsConnectionlessStylusHandwriting()) {
Slog.w(TAG, "Connectionless stylus handwriting mode unsupported by IME.");
- callback.onError(CONNECTIONLESS_HANDWRITING_ERROR_UNSUPPORTED);
+ try {
+ callback.onError(CONNECTIONLESS_HANDWRITING_ERROR_UNSUPPORTED);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to report CONNECTIONLESS_HANDWRITING_ERROR_UNSUPPORTED", e);
+ e.rethrowAsRuntimeException();
+ }
return;
}
}
@@ -3148,7 +3146,12 @@
synchronized (ImfLock.class) {
if (!mClientController.verifyClientAndPackageMatch(client, delegatorPackageName)) {
Slog.w(TAG, "startConnectionlessStylusHandwriting() fail");
- callback.onError(CONNECTIONLESS_HANDWRITING_ERROR_OTHER);
+ try {
+ callback.onError(CONNECTIONLESS_HANDWRITING_ERROR_OTHER);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to report CONNECTIONLESS_HANDWRITING_ERROR_OTHER", e);
+ e.rethrowAsRuntimeException();
+ }
throw new IllegalArgumentException("Delegator doesn't match UID");
}
}
@@ -3172,7 +3175,12 @@
if (!startStylusHandwriting(
client, false, immsCallback, cursorAnchorInfo, isForDelegation)) {
- callback.onError(CONNECTIONLESS_HANDWRITING_ERROR_OTHER);
+ try {
+ callback.onError(CONNECTIONLESS_HANDWRITING_ERROR_OTHER);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to report CONNECTIONLESS_HANDWRITING_ERROR_OTHER", e);
+ e.rethrowAsRuntimeException();
+ }
}
}
@@ -3272,11 +3280,15 @@
@UserIdInt int userId,
@NonNull String delegatePackageName,
@NonNull String delegatorPackageName,
- @InputMethodManager.HandwritingDelegateFlags int flags, IBooleanListener callback)
- throws RemoteException {
+ @InputMethodManager.HandwritingDelegateFlags int flags, IBooleanListener callback) {
boolean result = acceptStylusHandwritingDelegation(
client, userId, delegatePackageName, delegatorPackageName, flags);
- callback.onResult(result);
+ try {
+ callback.onResult(result);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to report result=" + result, e);
+ e.rethrowAsRuntimeException();
+ }
}
@Override
@@ -3367,7 +3379,7 @@
@GuardedBy("ImfLock.class")
boolean showCurrentInputLocked(IBinder windowToken,
@NonNull ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
- int lastClickToolType, @Nullable ResultReceiver resultReceiver,
+ @MotionEvent.ToolType int lastClickToolType, @Nullable ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
if (!mVisibilityStateComputer.onImeShowFlags(statsToken, flags)) {
return false;
@@ -3413,7 +3425,7 @@
"InputMethodManagerService#hideSoftInput");
synchronized (ImfLock.class) {
if (!canInteractWithImeLocked(uid, client, "hideSoftInput", statsToken)) {
- if (isInputShown()) {
+ if (isInputShownLocked()) {
ImeTracker.forLogging().onFailed(
statsToken, ImeTracker.PHASE_SERVER_CLIENT_FOCUSED);
} else {
@@ -3436,10 +3448,8 @@
}
@Override
- @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
+ @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.TEST_INPUT_METHOD)
public void hideSoftInputFromServerForTest() {
- super.hideSoftInputFromServerForTest_enforcePermission();
-
synchronized (ImfLock.class) {
hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */,
SoftInputShowHideReason.HIDE_SOFT_INPUT);
@@ -3472,7 +3482,7 @@
// TODO(b/246309664): Clean up IMMS#mImeWindowVis
IInputMethodInvoker curMethod = getCurMethodLocked();
final boolean shouldHideSoftInput = curMethod != null
- && (isInputShown() || (mImeWindowVis & InputMethodService.IME_ACTIVE) != 0);
+ && (isInputShownLocked() || (mImeWindowVis & InputMethodService.IME_ACTIVE) != 0);
mVisibilityStateComputer.requestImeVisibility(windowToken, false);
if (shouldHideSoftInput) {
@@ -3845,13 +3855,11 @@
}
}
- @EnforcePermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.WRITE_SECURE_SETTINGS)
@Override
public void showInputMethodPickerFromSystem(int auxiliarySubtypeMode, int displayId) {
// Always call subtype picker, because subtype picker is a superset of input method
// picker.
- super.showInputMethodPickerFromSystem_enforcePermission();
-
mHandler.obtainMessage(MSG_SHOW_IM_SUBTYPE_PICKER, auxiliarySubtypeMode, displayId)
.sendToTarget();
}
@@ -3859,10 +3867,8 @@
/**
* A test API for CTS to make sure that the input method menu is showing.
*/
- @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
+ @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.TEST_INPUT_METHOD)
public boolean isInputMethodPickerShownForTest() {
- super.isInputMethodPickerShownForTest_enforcePermission();
-
synchronized (ImfLock.class) {
return mMenuController.isisInputMethodPickerShownForTestLocked();
}
@@ -4162,11 +4168,9 @@
});
}
- @EnforcePermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW)
+ @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.INTERNAL_SYSTEM_WINDOW)
@Override
public void removeImeSurface() {
- super.removeImeSurface_enforcePermission();
-
mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE).sendToTarget();
}
@@ -4275,11 +4279,9 @@
* a stylus deviceId is not already registered on device.
*/
@BinderThread
- @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
+ @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.TEST_INPUT_METHOD)
@Override
public void addVirtualStylusIdForTestSession(IInputMethodClient client) {
- super.addVirtualStylusIdForTestSession_enforcePermission();
-
int uid = Binder.getCallingUid();
synchronized (ImfLock.class) {
if (!canInteractWithImeLocked(uid, client, "addVirtualStylusIdForTestSession",
@@ -4302,12 +4304,10 @@
* @param timeout to set in milliseconds. To reset to default, use a value <= zero.
*/
@BinderThread
- @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
+ @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.TEST_INPUT_METHOD)
@Override
public void setStylusWindowIdleTimeoutForTest(
IInputMethodClient client, @DurationMillisLong long timeout) {
- super.setStylusWindowIdleTimeoutForTest_enforcePermission();
-
int uid = Binder.getCallingUid();
synchronized (ImfLock.class) {
if (!canInteractWithImeLocked(uid, client, "setStylusWindowIdleTimeoutForTest",
@@ -4403,10 +4403,9 @@
}
@BinderThread
- @EnforcePermission(Manifest.permission.CONTROL_UI_TRACING)
+ @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.CONTROL_UI_TRACING)
@Override
public void startImeTrace() {
- super.startImeTrace_enforcePermission();
ImeTracing.getInstance().startTrace(null /* printwriter */);
synchronized (ImfLock.class) {
mClientController.forAllClients(c -> c.mClient.setImeTraceEnabled(true /* enabled */));
@@ -4414,11 +4413,9 @@
}
@BinderThread
- @EnforcePermission(Manifest.permission.CONTROL_UI_TRACING)
+ @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.CONTROL_UI_TRACING)
@Override
public void stopImeTrace() {
- super.stopImeTrace_enforcePermission();
-
ImeTracing.getInstance().stopTrace(null /* printwriter */);
synchronized (ImfLock.class) {
mClientController.forAllClients(c -> c.mClient.setImeTraceEnabled(false /* enabled */));
@@ -4698,7 +4695,7 @@
// implemented so that auxiliary subtypes will be excluded when the soft
// keyboard is invisible.
synchronized (ImfLock.class) {
- showAuxSubtypes = isInputShown();
+ showAuxSubtypes = isInputShownLocked();
}
break;
case InputMethodManager.SHOW_IM_PICKER_MODE_INCLUDE_AUXILIARY_SUBTYPES:
@@ -5845,7 +5842,7 @@
@BinderThread
@Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
PriorityDump.dump(mPriorityDumper, fd, pw, args);
@@ -5975,7 +5972,7 @@
public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
@Nullable FileDescriptor err,
@NonNull String[] args, @Nullable ShellCallback callback,
- @NonNull ResultReceiver resultReceiver) throws RemoteException {
+ @NonNull ResultReceiver resultReceiver, @NonNull Binder self) {
final int callingUid = Binder.getCallingUid();
// Reject any incoming calls from non-shell users, including ones from the system user.
if (callingUid != Process.ROOT_UID && callingUid != Process.SHELL_UID) {
@@ -5996,7 +5993,7 @@
throw new SecurityException(errorMsg);
}
new ShellCommandImpl(this).exec(
- this, in, out, err, args, callback, resultReceiver);
+ self, in, out, err, args, callback, resultReceiver);
}
private static final class ShellCommandImpl extends ShellCommand {
diff --git a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
index ffc2319..1cd1ddc 100644
--- a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
+++ b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
@@ -36,17 +36,16 @@
import android.Manifest;
import android.annotation.BinderThread;
-import android.annotation.EnforcePermission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.UserIdInt;
import android.os.Binder;
import android.os.IBinder;
-import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.util.Slog;
+import android.view.MotionEvent;
import android.view.WindowManager;
import android.view.inputmethod.CursorAnchorInfo;
import android.view.inputmethod.EditorInfo;
@@ -56,6 +55,7 @@
import android.view.inputmethod.InputMethodSubtype;
import android.window.ImeOnBackInvokedDispatcher;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.DirectBootAwareness;
import com.android.internal.inputmethod.IBooleanListener;
import com.android.internal.inputmethod.IConnectionlessHandwritingCallback;
@@ -76,22 +76,25 @@
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
-import java.util.function.BooleanSupplier;
/**
* A proxy that processes all {@link IInputMethodManager} calls asynchronously.
- * @hide
*/
-public class ZeroJankProxy extends IInputMethodManager.Stub {
+final class ZeroJankProxy implements IInputMethodManagerImpl.Callback {
- private final IInputMethodManager mInner;
+ interface Callback extends IInputMethodManagerImpl.Callback {
+ @GuardedBy("ImfLock.class")
+ ClientState getClientStateLocked(IInputMethodClient client);
+ @GuardedBy("ImfLock.class")
+ boolean isInputShownLocked();
+ }
+
+ private final Callback mInner;
private final Executor mExecutor;
- private final BooleanSupplier mIsInputShown;
- ZeroJankProxy(Executor executor, IInputMethodManager inner, BooleanSupplier isInputShown) {
+ ZeroJankProxy(Executor executor, Callback inner) {
mInner = inner;
mExecutor = executor;
- mIsInputShown = isInputShown;
}
private void offload(ThrowingRunnable r) {
@@ -126,45 +129,43 @@
@Override
public void addClient(IInputMethodClient client, IRemoteInputConnection inputConnection,
- int selfReportedDisplayId) throws RemoteException {
+ int selfReportedDisplayId) {
offload(() -> mInner.addClient(client, inputConnection, selfReportedDisplayId));
}
@Override
- public InputMethodInfo getCurrentInputMethodInfoAsUser(int userId) throws RemoteException {
+ public InputMethodInfo getCurrentInputMethodInfoAsUser(int userId) {
return mInner.getCurrentInputMethodInfoAsUser(userId);
}
@Override
public List<InputMethodInfo> getInputMethodList(
- int userId, @DirectBootAwareness int directBootAwareness) throws RemoteException {
+ int userId, @DirectBootAwareness int directBootAwareness) {
return mInner.getInputMethodList(userId, directBootAwareness);
}
@Override
- public List<InputMethodInfo> getEnabledInputMethodList(int userId) throws RemoteException {
+ public List<InputMethodInfo> getEnabledInputMethodList(int userId) {
return mInner.getEnabledInputMethodList(userId);
}
@Override
public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String imiId,
- boolean allowsImplicitlyEnabledSubtypes, int userId)
- throws RemoteException {
+ boolean allowsImplicitlyEnabledSubtypes, int userId) {
return mInner.getEnabledInputMethodSubtypeList(imiId, allowsImplicitlyEnabledSubtypes,
userId);
}
@Override
- public InputMethodSubtype getLastInputMethodSubtype(int userId) throws RemoteException {
+ public InputMethodSubtype getLastInputMethodSubtype(int userId) {
return mInner.getLastInputMethodSubtype(userId);
}
@Override
public boolean showSoftInput(IInputMethodClient client, IBinder windowToken,
@Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
- int lastClickTooType, ResultReceiver resultReceiver,
- @SoftInputShowHideReason int reason)
- throws RemoteException {
+ @MotionEvent.ToolType int lastClickToolType, ResultReceiver resultReceiver,
+ @SoftInputShowHideReason int reason) {
offload(
() -> {
if (!mInner.showSoftInput(
@@ -172,7 +173,7 @@
windowToken,
statsToken,
flags,
- lastClickTooType,
+ lastClickToolType,
resultReceiver,
reason)) {
sendResultReceiverFailure(resultReceiver);
@@ -184,8 +185,7 @@
@Override
public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
@Nullable ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
- ResultReceiver resultReceiver, @SoftInputShowHideReason int reason)
- throws RemoteException {
+ ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
offload(
() -> {
if (!mInner.hideSoftInput(
@@ -200,17 +200,19 @@
if (resultReceiver == null) {
return;
}
- resultReceiver.send(
- mIsInputShown.getAsBoolean()
+ final boolean isInputShown;
+ synchronized (ImfLock.class) {
+ isInputShown = mInner.isInputShownLocked();
+ }
+ resultReceiver.send(isInputShown
? InputMethodManager.RESULT_UNCHANGED_SHOWN
: InputMethodManager.RESULT_UNCHANGED_HIDDEN,
null);
}
@Override
- @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
- public void hideSoftInputFromServerForTest() throws RemoteException {
- super.hideSoftInputFromServerForTest_enforcePermission();
+ @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.TEST_INPUT_METHOD)
+ public void hideSoftInputFromServerForTest() {
mInner.hideSoftInputFromServerForTest();
}
@@ -225,8 +227,7 @@
IRemoteInputConnection inputConnection,
IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion, @UserIdInt int userId,
- @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq)
- throws RemoteException {
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq) {
offload(() -> {
InputBindResult result = mInner.startInputOrWindowGainedFocus(startInputReason, client,
windowToken, startInputFlags, softInputMode, windowFlags,
@@ -249,99 +250,92 @@
IRemoteInputConnection inputConnection,
IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion, @UserIdInt int userId,
- @NonNull ImeOnBackInvokedDispatcher imeDispatcher)
- throws RemoteException {
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
// Should never be called when flag is enabled i.e. when this proxy is used.
return null;
}
@Override
public void showInputMethodPickerFromClient(IInputMethodClient client,
- int auxiliarySubtypeMode)
- throws RemoteException {
+ int auxiliarySubtypeMode) {
offload(() -> mInner.showInputMethodPickerFromClient(client, auxiliarySubtypeMode));
}
- @EnforcePermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.WRITE_SECURE_SETTINGS)
@Override
- public void showInputMethodPickerFromSystem(int auxiliarySubtypeMode, int displayId)
- throws RemoteException {
+ public void showInputMethodPickerFromSystem(int auxiliarySubtypeMode, int displayId) {
mInner.showInputMethodPickerFromSystem(auxiliarySubtypeMode, displayId);
}
- @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
+ @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.TEST_INPUT_METHOD)
@Override
- public boolean isInputMethodPickerShownForTest() throws RemoteException {
- super.isInputMethodPickerShownForTest_enforcePermission();
+ public boolean isInputMethodPickerShownForTest() {
return mInner.isInputMethodPickerShownForTest();
}
@Override
- public InputMethodSubtype getCurrentInputMethodSubtype(int userId) throws RemoteException {
+ public InputMethodSubtype getCurrentInputMethodSubtype(int userId) {
return mInner.getCurrentInputMethodSubtype(userId);
}
@Override
public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes,
- @UserIdInt int userId) throws RemoteException {
+ @UserIdInt int userId) {
mInner.setAdditionalInputMethodSubtypes(imiId, subtypes, userId);
}
@Override
public void setExplicitlyEnabledInputMethodSubtypes(String imeId,
- @NonNull int[] subtypeHashCodes, @UserIdInt int userId) throws RemoteException {
+ @NonNull int[] subtypeHashCodes, @UserIdInt int userId) {
mInner.setExplicitlyEnabledInputMethodSubtypes(imeId, subtypeHashCodes, userId);
}
@Override
- public int getInputMethodWindowVisibleHeight(IInputMethodClient client)
- throws RemoteException {
+ public int getInputMethodWindowVisibleHeight(IInputMethodClient client) {
return mInner.getInputMethodWindowVisibleHeight(client);
}
@Override
- public void reportPerceptibleAsync(IBinder windowToken, boolean perceptible)
- throws RemoteException {
+ public void reportPerceptibleAsync(IBinder windowToken, boolean perceptible) {
// Already async TODO(b/293640003): ordering issues?
mInner.reportPerceptibleAsync(windowToken, perceptible);
}
- @EnforcePermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW)
+ @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.INTERNAL_SYSTEM_WINDOW)
@Override
- public void removeImeSurface() throws RemoteException {
+ public void removeImeSurface() {
mInner.removeImeSurface();
}
@Override
- public void removeImeSurfaceFromWindowAsync(IBinder windowToken) throws RemoteException {
+ public void removeImeSurfaceFromWindowAsync(IBinder windowToken) {
mInner.removeImeSurfaceFromWindowAsync(windowToken);
}
@Override
- public void startProtoDump(byte[] bytes, int i, String s) throws RemoteException {
+ public void startProtoDump(byte[] bytes, int i, String s) {
mInner.startProtoDump(bytes, i, s);
}
@Override
- public boolean isImeTraceEnabled() throws RemoteException {
+ public boolean isImeTraceEnabled() {
return mInner.isImeTraceEnabled();
}
- @EnforcePermission(Manifest.permission.CONTROL_UI_TRACING)
+ @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.CONTROL_UI_TRACING)
@Override
- public void startImeTrace() throws RemoteException {
+ public void startImeTrace() {
mInner.startImeTrace();
}
- @EnforcePermission(Manifest.permission.CONTROL_UI_TRACING)
+ @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.CONTROL_UI_TRACING)
@Override
- public void stopImeTrace() throws RemoteException {
+ public void stopImeTrace() {
mInner.stopImeTrace();
}
@Override
- public void startStylusHandwriting(IInputMethodClient client)
- throws RemoteException {
+ public void startStylusHandwriting(IInputMethodClient client) {
offload(() -> mInner.startStylusHandwriting(client));
}
@@ -349,7 +343,7 @@
public void startConnectionlessStylusHandwriting(IInputMethodClient client, int userId,
@Nullable CursorAnchorInfo cursorAnchorInfo, @Nullable String delegatePackageName,
@Nullable String delegatorPackageName,
- @NonNull IConnectionlessHandwritingCallback callback) throws RemoteException {
+ @NonNull IConnectionlessHandwritingCallback callback) {
offload(() -> mInner.startConnectionlessStylusHandwriting(
client, userId, cursorAnchorInfo, delegatePackageName, delegatorPackageName,
callback));
@@ -363,14 +357,11 @@
@NonNull String delegatorPackageName,
@InputMethodManager.HandwritingDelegateFlags int flags) {
try {
- return CompletableFuture.supplyAsync(() -> {
- try {
- return mInner.acceptStylusHandwritingDelegation(
- client, userId, delegatePackageName, delegatorPackageName, flags);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }, this::offload).get();
+ return CompletableFuture.supplyAsync(() ->
+ mInner.acceptStylusHandwritingDelegation(
+ client, userId, delegatePackageName, delegatorPackageName,
+ flags),
+ this::offload).get();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
@@ -384,8 +375,7 @@
@UserIdInt int userId,
@NonNull String delegatePackageName,
@NonNull String delegatorPackageName,
- @InputMethodManager.HandwritingDelegateFlags int flags, IBooleanListener callback)
- throws RemoteException {
+ @InputMethodManager.HandwritingDelegateFlags int flags, IBooleanListener callback) {
offload(() -> mInner.acceptStylusHandwritingDelegationAsync(
client, userId, delegatePackageName, delegatorPackageName, flags, callback));
}
@@ -401,52 +391,45 @@
}
@Override
- public boolean isStylusHandwritingAvailableAsUser(int userId, boolean connectionless)
- throws RemoteException {
+ public boolean isStylusHandwritingAvailableAsUser(int userId, boolean connectionless) {
return mInner.isStylusHandwritingAvailableAsUser(userId, connectionless);
}
- @EnforcePermission("android.permission.TEST_INPUT_METHOD")
+ @IInputMethodManagerImpl.PermissionVerified("android.permission.TEST_INPUT_METHOD")
@Override
- public void addVirtualStylusIdForTestSession(IInputMethodClient client)
- throws RemoteException {
+ public void addVirtualStylusIdForTestSession(IInputMethodClient client) {
mInner.addVirtualStylusIdForTestSession(client);
}
- @EnforcePermission("android.permission.TEST_INPUT_METHOD")
+ @IInputMethodManagerImpl.PermissionVerified("android.permission.TEST_INPUT_METHOD")
@Override
- public void setStylusWindowIdleTimeoutForTest(IInputMethodClient client, long timeout)
- throws RemoteException {
+ public void setStylusWindowIdleTimeoutForTest(IInputMethodClient client, long timeout) {
mInner.setStylusWindowIdleTimeoutForTest(client, timeout);
}
@Override
- public IImeTracker getImeTrackerService() throws RemoteException {
+ public IImeTracker getImeTrackerService() {
return mInner.getImeTrackerService();
}
@BinderThread
@Override
public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
- @Nullable FileDescriptor err,
- @NonNull String[] args, @Nullable ShellCallback callback,
- @NonNull ResultReceiver resultReceiver) throws RemoteException {
- ((InputMethodManagerService) mInner).onShellCommand(
- in, out, err, args, callback, resultReceiver);
+ @Nullable FileDescriptor err, @NonNull String[] args, @Nullable ShellCallback callback,
+ @NonNull ResultReceiver resultReceiver, @NonNull Binder self) {
+ mInner.onShellCommand(in, out, err, args, callback, resultReceiver, self);
}
@Override
- protected void dump(@NonNull FileDescriptor fd,
- @NonNull PrintWriter fout,
+ public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
@Nullable String[] args) {
- ((InputMethodManagerService) mInner).dump(fd, fout, args);
+ mInner.dump(fd, fout, args);
}
private void sendOnStartInputResult(
IInputMethodClient client, InputBindResult res, int startInputSeq) {
synchronized (ImfLock.class) {
- InputMethodManagerService service = (InputMethodManagerService) mInner;
- final ClientState cs = service.getClientState(client);
+ final ClientState cs = mInner.getClientStateLocked(client);
if (cs != null && cs.mClient != null) {
cs.mClient.onStartInputResult(res, startInputSeq);
} else {
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 19562ef..dbdb155 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -950,13 +950,18 @@
&& android.multiuser.Flags.enablePrivateSpaceFeatures()
&& android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()) {
mHandler.post(() -> {
- UserProperties userProperties =
- mUserManager.getUserProperties(UserHandle.of(userId));
- if (userProperties != null
- && userProperties.getAllowStoppingUserWithDelayedLocking()) {
- int strongAuthRequired = LockPatternUtils.StrongAuthTracker
- .getDefaultFlags(mContext);
- requireStrongAuth(strongAuthRequired, userId);
+ try {
+ UserProperties userProperties =
+ mUserManager.getUserProperties(UserHandle.of(userId));
+ if (userProperties != null && userProperties
+ .getAllowStoppingUserWithDelayedLocking()) {
+ int strongAuthRequired = LockPatternUtils.StrongAuthTracker
+ .getDefaultFlags(mContext);
+ requireStrongAuth(strongAuthRequired, userId);
+ }
+ } catch (IllegalArgumentException e) {
+ Slogf.d(TAG, "User %d does not exist or has been removed",
+ userId);
}
});
}
diff --git a/services/core/java/com/android/server/media/MediaSession2Record.java b/services/core/java/com/android/server/media/MediaSession2Record.java
index 0cd7654..dfb2b0a 100644
--- a/services/core/java/com/android/server/media/MediaSession2Record.java
+++ b/services/core/java/com/android/server/media/MediaSession2Record.java
@@ -157,6 +157,11 @@
}
@Override
+ public void expireTempEngaged() {
+ // NA as MediaSession2 doesn't support UserEngagementStates for FGS.
+ }
+
+ @Override
public boolean sendMediaButton(String packageName, int pid, int uid, boolean asSystemService,
KeyEvent ke, int sequenceId, ResultReceiver cb) {
// TODO(jaewan): Implement.
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 93f68a8..194ab04 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -24,6 +24,7 @@
import static android.media.session.MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE;
import android.Manifest;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -85,6 +86,8 @@
import com.android.server.uri.UriGrantsManagerInternal;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -225,6 +228,49 @@
private int mPolicies;
+ private @UserEngagementState int mUserEngagementState = USER_DISENGAGED;
+
+ @IntDef({USER_PERMANENTLY_ENGAGED, USER_TEMPORARY_ENGAGED, USER_DISENGAGED})
+ @Retention(RetentionPolicy.SOURCE)
+ private @interface UserEngagementState {}
+
+ /**
+ * Indicates that the session is active and in one of the user engaged states.
+ *
+ * @see #updateUserEngagedStateIfNeededLocked(boolean) ()
+ */
+ private static final int USER_PERMANENTLY_ENGAGED = 0;
+
+ /**
+ * Indicates that the session is active and in {@link PlaybackState#STATE_PAUSED} state.
+ *
+ * @see #updateUserEngagedStateIfNeededLocked(boolean) ()
+ */
+ private static final int USER_TEMPORARY_ENGAGED = 1;
+
+ /**
+ * Indicates that the session is either not active or in one of the user disengaged states
+ *
+ * @see #updateUserEngagedStateIfNeededLocked(boolean) ()
+ */
+ private static final int USER_DISENGAGED = 2;
+
+ /**
+ * Indicates the duration of the temporary engaged states.
+ *
+ * <p>Some {@link MediaSession} states like {@link PlaybackState#STATE_PAUSED} are temporarily
+ * engaged, meaning the corresponding session is only considered in an engaged state for the
+ * duration of this timeout, and only if coming from an engaged state.
+ *
+ * <p>For example, if a session is transitioning from a user-engaged state {@link
+ * PlaybackState#STATE_PLAYING} to a temporary user-engaged state {@link
+ * PlaybackState#STATE_PAUSED}, then the session will be considered in a user-engaged state for
+ * the duration of this timeout, starting at the transition instant. However, a temporary
+ * user-engaged state is not considered user-engaged when transitioning from a non-user engaged
+ * state {@link PlaybackState#STATE_STOPPED}.
+ */
+ private static final int TEMP_USER_ENGAGED_TIMEOUT = 600000;
+
public MediaSessionRecord(
int ownerPid,
int ownerUid,
@@ -548,6 +594,7 @@
mSessionCb.mCb.asBinder().unlinkToDeath(this, 0);
mDestroyed = true;
mPlaybackState = null;
+ updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ true);
mHandler.post(MessageHandler.MSG_DESTROYED);
}
}
@@ -559,6 +606,12 @@
}
}
+ @Override
+ public void expireTempEngaged() {
+ mHandler.removeCallbacks(mHandleTempEngagedSessionTimeout);
+ updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ true);
+ }
+
/**
* Sends media button.
*
@@ -1129,6 +1182,11 @@
}
};
+ private final Runnable mHandleTempEngagedSessionTimeout =
+ () -> {
+ updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ true);
+ };
+
@RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
private static boolean componentNameExists(
@NonNull ComponentName componentName, @NonNull Context context, int userId) {
@@ -1145,6 +1203,40 @@
return !resolveInfos.isEmpty();
}
+ private void updateUserEngagedStateIfNeededLocked(boolean isTimeoutExpired) {
+ int oldUserEngagedState = mUserEngagementState;
+ int newUserEngagedState;
+ if (!isActive() || mPlaybackState == null) {
+ newUserEngagedState = USER_DISENGAGED;
+ } else if (isActive() && mPlaybackState.isActive()) {
+ newUserEngagedState = USER_PERMANENTLY_ENGAGED;
+ } else if (mPlaybackState.getState() == PlaybackState.STATE_PAUSED) {
+ newUserEngagedState =
+ oldUserEngagedState == USER_PERMANENTLY_ENGAGED || !isTimeoutExpired
+ ? USER_TEMPORARY_ENGAGED
+ : USER_DISENGAGED;
+ } else {
+ newUserEngagedState = USER_DISENGAGED;
+ }
+ if (oldUserEngagedState == newUserEngagedState) {
+ return;
+ }
+
+ if (newUserEngagedState == USER_TEMPORARY_ENGAGED) {
+ mHandler.postDelayed(mHandleTempEngagedSessionTimeout, TEMP_USER_ENGAGED_TIMEOUT);
+ } else if (oldUserEngagedState == USER_TEMPORARY_ENGAGED) {
+ mHandler.removeCallbacks(mHandleTempEngagedSessionTimeout);
+ }
+
+ boolean wasUserEngaged = oldUserEngagedState != USER_DISENGAGED;
+ boolean isNowUserEngaged = newUserEngagedState != USER_DISENGAGED;
+ mUserEngagementState = newUserEngagedState;
+ if (wasUserEngaged != isNowUserEngaged) {
+ mService.onSessionUserEngagementStateChange(
+ /* mediaSessionRecord= */ this, /* isUserEngaged= */ isNowUserEngaged);
+ }
+ }
+
private final class SessionStub extends ISession.Stub {
@Override
public void destroySession() throws RemoteException {
@@ -1182,8 +1274,10 @@
.logFgsApiEnd(ActivityManager.FOREGROUND_SERVICE_API_TYPE_MEDIA_PLAYBACK,
callingUid, callingPid);
}
-
- mIsActive = active;
+ synchronized (mLock) {
+ mIsActive = active;
+ updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ false);
+ }
long token = Binder.clearCallingIdentity();
try {
mService.onSessionActiveStateChanged(MediaSessionRecord.this, mPlaybackState);
@@ -1341,6 +1435,7 @@
&& TRANSITION_PRIORITY_STATES.contains(newState));
synchronized (mLock) {
mPlaybackState = state;
+ updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ false);
}
final long token = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
index 0999199..b57b148 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
@@ -196,6 +196,12 @@
*/
public abstract boolean isClosed();
+ /**
+ * Note: This method is only used for testing purposes If the session is temporary engaged, the
+ * timeout will expire and it will become disengaged.
+ */
+ public abstract void expireTempEngaged();
+
@Override
public final boolean equals(Object o) {
if (this == o) return true;
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 53c32cf..74adf5e 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -367,11 +367,13 @@
}
boolean isUserEngaged = isUserEngaged(record, playbackState);
- Log.d(TAG, "onSessionActiveStateChanged: "
- + "record=" + record
- + "playbackState=" + playbackState
- + "allowRunningInForeground=" + isUserEngaged);
- setForegroundServiceAllowance(record, /* allowRunningInForeground= */ isUserEngaged);
+ Log.d(
+ TAG,
+ "onSessionActiveStateChanged:"
+ + " record="
+ + record
+ + " playbackState="
+ + playbackState);
reportMediaInteractionEvent(record, isUserEngaged);
mHandler.postSessionsChanged(record);
}
@@ -479,11 +481,13 @@
}
user.mPriorityStack.onPlaybackStateChanged(record, shouldUpdatePriority);
boolean isUserEngaged = isUserEngaged(record, playbackState);
- Log.d(TAG, "onSessionPlaybackStateChanged: "
- + "record=" + record
- + "playbackState=" + playbackState
- + "allowRunningInForeground=" + isUserEngaged);
- setForegroundServiceAllowance(record, /* allowRunningInForeground= */ isUserEngaged);
+ Log.d(
+ TAG,
+ "onSessionPlaybackStateChanged:"
+ + " record="
+ + record
+ + " playbackState="
+ + playbackState);
reportMediaInteractionEvent(record, isUserEngaged);
}
}
@@ -650,68 +654,112 @@
session.close();
Log.d(TAG, "destroySessionLocked: record=" + session);
- setForegroundServiceAllowance(session, /* allowRunningInForeground= */ false);
+
reportMediaInteractionEvent(session, /* userEngaged= */ false);
mHandler.postSessionsChanged(session);
}
- private void setForegroundServiceAllowance(
- MediaSessionRecordImpl record, boolean allowRunningInForeground) {
- if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) {
- return;
- }
- ForegroundServiceDelegationOptions foregroundServiceDelegationOptions =
- record.getForegroundServiceDelegationOptions();
- if (foregroundServiceDelegationOptions == null) {
- return;
- }
- if (allowRunningInForeground) {
- onUserSessionEngaged(record);
+ void onSessionUserEngagementStateChange(
+ MediaSessionRecordImpl mediaSessionRecord, boolean isUserEngaged) {
+ if (isUserEngaged) {
+ addUserEngagedSession(mediaSessionRecord);
+ startFgsIfSessionIsLinkedToNotification(mediaSessionRecord);
} else {
- onUserDisengaged(record);
+ removeUserEngagedSession(mediaSessionRecord);
+ stopFgsIfNoSessionIsLinkedToNotification(mediaSessionRecord);
}
}
- private void onUserSessionEngaged(MediaSessionRecordImpl mediaSessionRecord) {
+ private void addUserEngagedSession(MediaSessionRecordImpl mediaSessionRecord) {
synchronized (mLock) {
int uid = mediaSessionRecord.getUid();
mUserEngagedSessionsForFgs.putIfAbsent(uid, new HashSet<>());
mUserEngagedSessionsForFgs.get(uid).add(mediaSessionRecord);
+ }
+ }
+
+ private void removeUserEngagedSession(MediaSessionRecordImpl mediaSessionRecord) {
+ synchronized (mLock) {
+ int uid = mediaSessionRecord.getUid();
+ Set<MediaSessionRecordImpl> mUidUserEngagedSessionsForFgs =
+ mUserEngagedSessionsForFgs.get(uid);
+ if (mUidUserEngagedSessionsForFgs == null) {
+ return;
+ }
+
+ mUidUserEngagedSessionsForFgs.remove(mediaSessionRecord);
+ if (mUidUserEngagedSessionsForFgs.isEmpty()) {
+ mUserEngagedSessionsForFgs.remove(uid);
+ }
+ }
+ }
+
+ private void startFgsIfSessionIsLinkedToNotification(
+ MediaSessionRecordImpl mediaSessionRecord) {
+ Log.d(TAG, "startFgsIfSessionIsLinkedToNotification: record=" + mediaSessionRecord);
+ if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) {
+ return;
+ }
+ synchronized (mLock) {
+ int uid = mediaSessionRecord.getUid();
for (Notification mediaNotification : mMediaNotifications.getOrDefault(uid, Set.of())) {
if (mediaSessionRecord.isLinkedToNotification(mediaNotification)) {
- mActivityManagerInternal.startForegroundServiceDelegate(
- mediaSessionRecord.getForegroundServiceDelegationOptions(),
- /* connection= */ null);
+ startFgsDelegate(mediaSessionRecord.getForegroundServiceDelegationOptions());
return;
}
}
}
}
- private void onUserDisengaged(MediaSessionRecordImpl mediaSessionRecord) {
+ private void startFgsDelegate(
+ ForegroundServiceDelegationOptions foregroundServiceDelegationOptions) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mActivityManagerInternal.startForegroundServiceDelegate(
+ foregroundServiceDelegationOptions, /* connection= */ null);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private void stopFgsIfNoSessionIsLinkedToNotification(
+ MediaSessionRecordImpl mediaSessionRecord) {
+ Log.d(TAG, "stopFgsIfNoSessionIsLinkedToNotification: record=" + mediaSessionRecord);
+ if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) {
+ return;
+ }
synchronized (mLock) {
int uid = mediaSessionRecord.getUid();
- if (mUserEngagedSessionsForFgs.containsKey(uid)) {
- mUserEngagedSessionsForFgs.get(uid).remove(mediaSessionRecord);
- if (mUserEngagedSessionsForFgs.get(uid).isEmpty()) {
- mUserEngagedSessionsForFgs.remove(uid);
- }
+ ForegroundServiceDelegationOptions foregroundServiceDelegationOptions =
+ mediaSessionRecord.getForegroundServiceDelegationOptions();
+ if (foregroundServiceDelegationOptions == null) {
+ return;
}
- boolean shouldStopFgs = true;
- for (MediaSessionRecordImpl sessionRecord :
+ for (MediaSessionRecordImpl record :
mUserEngagedSessionsForFgs.getOrDefault(uid, Set.of())) {
- for (Notification mediaNotification : mMediaNotifications.getOrDefault(uid,
- Set.of())) {
- if (sessionRecord.isLinkedToNotification(mediaNotification)) {
- shouldStopFgs = false;
+ for (Notification mediaNotification :
+ mMediaNotifications.getOrDefault(uid, Set.of())) {
+ if (record.isLinkedToNotification(mediaNotification)) {
+ // A user engaged session linked with a media notification is found.
+ // We shouldn't call stop FGS in this case.
+ return;
}
}
}
- if (shouldStopFgs) {
- mActivityManagerInternal.stopForegroundServiceDelegate(
- mediaSessionRecord.getForegroundServiceDelegationOptions());
- }
+
+ stopFgsDelegate(foregroundServiceDelegationOptions);
+ }
+ }
+
+ private void stopFgsDelegate(
+ ForegroundServiceDelegationOptions foregroundServiceDelegationOptions) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mActivityManagerInternal.stopForegroundServiceDelegate(
+ foregroundServiceDelegationOptions);
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
@@ -2502,7 +2550,6 @@
}
MediaSessionRecord session = null;
MediaButtonReceiverHolder mediaButtonReceiverHolder = null;
-
if (mCustomMediaKeyDispatcher != null) {
MediaSession.Token token = mCustomMediaKeyDispatcher.getMediaSession(
keyEvent, uid, asSystemService);
@@ -2630,6 +2677,18 @@
&& streamType <= AudioManager.STREAM_NOTIFICATION;
}
+ @Override
+ public void expireTempEngagedSessions() {
+ synchronized (mLock) {
+ for (Set<MediaSessionRecordImpl> uidSessions :
+ mUserEngagedSessionsForFgs.values()) {
+ for (MediaSessionRecordImpl sessionRecord : uidSessions) {
+ sessionRecord.expireTempEngaged();
+ }
+ }
+ }
+ }
+
private class MediaKeyListenerResultReceiver extends ResultReceiver implements Runnable {
private final String mPackageName;
private final int mPid;
@@ -3127,7 +3186,6 @@
super.onNotificationPosted(sbn);
Notification postedNotification = sbn.getNotification();
int uid = sbn.getUid();
-
if (!postedNotification.isMediaNotification()) {
return;
}
@@ -3138,11 +3196,9 @@
mUserEngagedSessionsForFgs.getOrDefault(uid, Set.of())) {
ForegroundServiceDelegationOptions foregroundServiceDelegationOptions =
mediaSessionRecord.getForegroundServiceDelegationOptions();
- if (mediaSessionRecord.isLinkedToNotification(postedNotification)
- && foregroundServiceDelegationOptions != null) {
- mActivityManagerInternal.startForegroundServiceDelegate(
- foregroundServiceDelegationOptions,
- /* connection= */ null);
+ if (foregroundServiceDelegationOptions != null
+ && mediaSessionRecord.isLinkedToNotification(postedNotification)) {
+ startFgsDelegate(foregroundServiceDelegationOptions);
return;
}
}
@@ -3173,21 +3229,7 @@
return;
}
- boolean shouldStopFgs = true;
- for (MediaSessionRecordImpl mediaSessionRecord :
- mUserEngagedSessionsForFgs.getOrDefault(uid, Set.of())) {
- for (Notification mediaNotification :
- mMediaNotifications.getOrDefault(uid, Set.of())) {
- if (mediaSessionRecord.isLinkedToNotification(mediaNotification)) {
- shouldStopFgs = false;
- }
- }
- }
- if (shouldStopFgs
- && notificationRecord.getForegroundServiceDelegationOptions() != null) {
- mActivityManagerInternal.stopForegroundServiceDelegate(
- notificationRecord.getForegroundServiceDelegationOptions());
- }
+ stopFgsIfNoSessionIsLinkedToNotification(notificationRecord);
}
}
diff --git a/services/core/java/com/android/server/media/MediaShellCommand.java b/services/core/java/com/android/server/media/MediaShellCommand.java
index a563808..a20de31 100644
--- a/services/core/java/com/android/server/media/MediaShellCommand.java
+++ b/services/core/java/com/android/server/media/MediaShellCommand.java
@@ -92,6 +92,8 @@
runMonitor();
} else if (cmd.equals("volume")) {
runVolume();
+ } else if (cmd.equals("expire-temp-engaged-sessions")) {
+ expireTempEngagedSessions();
} else {
showError("Error: unknown command '" + cmd + "'");
return -1;
@@ -367,4 +369,8 @@
private void runVolume() throws Exception {
VolumeCtrl.run(this);
}
+
+ private void expireTempEngagedSessions() throws Exception {
+ mSessionService.expireTempEngagedSessions();
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 88f86cc..a0902cd 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -8967,8 +8967,7 @@
: mDisplayContent.getDisplayInfo();
final Task task = getTask();
task.calculateInsetFrames(mTmpBounds /* outNonDecorBounds */,
- outStableBounds /* outStableBounds */, parentBounds /* bounds */, di,
- true /* useLegacyInsetsForStableBounds */);
+ outStableBounds /* outStableBounds */, parentBounds /* bounds */, di);
final int orientationWithInsets = outStableBounds.height() >= outStableBounds.width()
? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
// If orientation does not match the orientation with insets applied, then a
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index e157318..f8aa69b 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -912,7 +912,7 @@
@Override
public void grantInputChannel(int displayId, SurfaceControl surface,
IBinder clientToken, @Nullable InputTransferToken hostInputTransferToken, int flags,
- int privateFlags, int type, int inputFeatures, IBinder windowToken,
+ int privateFlags, int inputFeatures, int type, IBinder windowToken,
InputTransferToken inputTransferToken, String inputHandleName,
InputChannel outInputChannel) {
if (hostInputTransferToken == null && !mCanAddInternalSystemWindow) {
@@ -925,7 +925,7 @@
try {
mService.grantInputChannel(this, mUid, mPid, displayId, surface, clientToken,
hostInputTransferToken, flags, mCanAddInternalSystemWindow ? privateFlags : 0,
- type, inputFeatures, windowToken, inputTransferToken, inputHandleName,
+ inputFeatures, type, windowToken, inputTransferToken, inputHandleName,
outInputChannel);
} finally {
Binder.restoreCallingIdentity(identity);
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 129af90..218fb7f 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -2309,8 +2309,7 @@
// area, i.e. the screen area without the system bars.
// The non decor inset are areas that could never be removed in Honeycomb. See
// {@link WindowManagerPolicy#getNonDecorInsetsLw}.
- calculateInsetFrames(mTmpNonDecorBounds, mTmpStableBounds, mTmpFullBounds, di,
- false /* useLegacyInsetsForStableBounds */);
+ calculateInsetFrames(mTmpNonDecorBounds, mTmpStableBounds, mTmpFullBounds, di);
} else {
// Apply the given non-decor and stable insets to calculate the corresponding bounds
// for screen size of configuration.
@@ -2408,11 +2407,9 @@
* @param outNonDecorBounds where to place bounds with non-decor insets applied.
* @param outStableBounds where to place bounds with stable insets applied.
* @param bounds the bounds to inset.
- * @param useLegacyInsetsForStableBounds {@code true} if we need to use the legacy insets frame
- * for apps targeting U or before when calculating stable bounds.
*/
void calculateInsetFrames(Rect outNonDecorBounds, Rect outStableBounds, Rect bounds,
- DisplayInfo displayInfo, boolean useLegacyInsetsForStableBounds) {
+ DisplayInfo displayInfo) {
outNonDecorBounds.set(bounds);
outStableBounds.set(bounds);
if (mDisplayContent == null) {
@@ -2424,11 +2421,7 @@
final DisplayPolicy.DecorInsets.Info info = policy.getDecorInsetsInfo(
displayInfo.rotation, displayInfo.logicalWidth, displayInfo.logicalHeight);
intersectWithInsetsIfFits(outNonDecorBounds, mTmpBounds, info.mNonDecorInsets);
- if (!useLegacyInsetsForStableBounds) {
- intersectWithInsetsIfFits(outStableBounds, mTmpBounds, info.mConfigInsets);
- } else {
- intersectWithInsetsIfFits(outStableBounds, mTmpBounds, info.mOverrideConfigInsets);
- }
+ intersectWithInsetsIfFits(outStableBounds, mTmpBounds, info.mConfigInsets);
}
/**
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 222abc3..ce53290 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -565,6 +565,9 @@
if (isTransientCollect(ar)) {
return true;
}
+ for (int i = mWaitingTransitions.size() - 1; i >= 0; --i) {
+ if (mWaitingTransitions.get(i).isTransientLaunch(ar)) return true;
+ }
for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
if (mPlayingTransitions.get(i).isTransientLaunch(ar)) return true;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 0186006..42fe3a7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -1630,6 +1630,7 @@
assertTrue(controller.mWaitingTransitions.contains(transition));
assertTrue(controller.isTransientHide(appTask));
assertTrue(controller.isTransientVisible(appTask));
+ assertTrue(controller.isTransientLaunch(recent));
}
@Test