Merge "Return bounds when it's in fullscreen display."
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index fa1c05d..01cfae8 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1131,7 +1131,13 @@
*/
public void registerRtFrameCallback(FrameDrawingCallback callback) {
if (mAttachInfo.mThreadedRenderer != null) {
- mAttachInfo.mThreadedRenderer.registerRtFrameCallback(callback);
+ mAttachInfo.mThreadedRenderer.registerRtFrameCallback(frame -> {
+ try {
+ callback.onFrameDraw(frame);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception while executing onFrameDraw", e);
+ }
+ });
}
}
diff --git a/core/java/com/android/internal/infra/ServiceConnector.java b/core/java/com/android/internal/infra/ServiceConnector.java
index 283079a..8136cfc 100644
--- a/core/java/com/android/internal/infra/ServiceConnector.java
+++ b/core/java/com/android/internal/infra/ServiceConnector.java
@@ -558,8 +558,8 @@
} catch (RemoteException e) {
Log.e(LOG_TAG, "onServiceConnected " + name + ": ", e);
}
- processQueue();
onServiceConnectionStatusChanged(mService, true);
+ processQueue();
}
@Override
diff --git a/core/java/com/android/internal/policy/DecorContext.java b/core/java/com/android/internal/policy/DecorContext.java
index 56a40a3..da1b72f 100644
--- a/core/java/com/android/internal/policy/DecorContext.java
+++ b/core/java/com/android/internal/policy/DecorContext.java
@@ -16,6 +16,7 @@
package com.android.internal.policy;
+import android.content.AutofillOptions;
import android.content.ContentCaptureOptions;
import android.content.Context;
import android.content.res.AssetManager;
@@ -98,6 +99,15 @@
}
@Override
+ public AutofillOptions getAutofillOptions() {
+ Context activityContext = mActivityContext.get();
+ if (activityContext != null) {
+ return activityContext.getAutofillOptions();
+ }
+ return null;
+ }
+
+ @Override
public ContentCaptureOptions getContentCaptureOptions() {
Context activityContext = mActivityContext.get();
if (activityContext != null) {
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index d6f5c23a6..640b8f9 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -40,6 +40,8 @@
"android_graphics_Picture.cpp",
"android_nio_utils.cpp",
"android_util_PathParser.cpp",
+ "android_view_DisplayListCanvas.cpp",
+ "android_view_RenderNode.cpp",
"android/graphics/Bitmap.cpp",
"android/graphics/BitmapFactory.cpp",
"android/graphics/ByteBufferStreamAdaptor.cpp",
@@ -136,7 +138,6 @@
"android_database_SQLiteDebug.cpp",
"android_view_CompositionSamplingListener.cpp",
"android_view_DisplayEventReceiver.cpp",
- "android_view_DisplayListCanvas.cpp",
"android_view_TextureLayer.cpp",
"android_view_InputChannel.cpp",
"android_view_InputDevice.cpp",
@@ -147,7 +148,6 @@
"android_view_KeyEvent.cpp",
"android_view_MotionEvent.cpp",
"android_view_PointerIcon.cpp",
- "android_view_RenderNode.cpp",
"android_view_RenderNodeAnimator.cpp",
"android_view_Surface.cpp",
"android_view_SurfaceControl.cpp",
diff --git a/core/jni/LayoutlibLoader.cpp b/core/jni/LayoutlibLoader.cpp
index b172809..dd5113c 100644
--- a/core/jni/LayoutlibLoader.cpp
+++ b/core/jni/LayoutlibLoader.cpp
@@ -67,6 +67,8 @@
extern int register_android_graphics_text_LineBreaker(JNIEnv* env);
extern int register_android_graphics_text_MeasuredText(JNIEnv* env);
extern int register_android_util_PathParser(JNIEnv* env);
+extern int register_android_view_RenderNode(JNIEnv* env);
+extern int register_android_view_DisplayListCanvas(JNIEnv* env);
extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);
extern int register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper(JNIEnv *env);
@@ -83,6 +85,7 @@
{"android.graphics.BitmapFactory", REG_JNI(register_android_graphics_BitmapFactory)},
{"android.graphics.ByteBufferStreamAdaptor", REG_JNI(register_android_graphics_ByteBufferStreamAdaptor)},
{"android.graphics.Canvas", REG_JNI(register_android_graphics_Canvas)},
+ {"android.graphics.RenderNode", REG_JNI(register_android_view_RenderNode)},
{"android.graphics.ColorFilter", REG_JNI(register_android_graphics_ColorFilter)},
{"android.graphics.ColorSpace", REG_JNI(register_android_graphics_ColorSpace)},
{"android.graphics.CreateJavaOutputStreamAdaptor", REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor)},
@@ -98,6 +101,7 @@
{"android.graphics.PathEffect", REG_JNI(register_android_graphics_PathEffect)},
{"android.graphics.PathMeasure", REG_JNI(register_android_graphics_PathMeasure)},
{"android.graphics.Picture", REG_JNI(register_android_graphics_Picture)},
+ {"android.graphics.RecordingCanvas", REG_JNI(register_android_view_DisplayListCanvas)},
{"android.graphics.Region", REG_JNI(register_android_graphics_Region)},
{"android.graphics.Shader", REG_JNI(register_android_graphics_Shader)},
{"android.graphics.Typeface", REG_JNI(register_android_graphics_Typeface)},
diff --git a/core/jni/android_view_DisplayListCanvas.cpp b/core/jni/android_view_DisplayListCanvas.cpp
index 40a133b..9907da5 100644
--- a/core/jni/android_view_DisplayListCanvas.cpp
+++ b/core/jni/android_view_DisplayListCanvas.cpp
@@ -21,9 +21,9 @@
#include <nativehelper/JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
-
+#ifdef __ANDROID__ // Layoutlib does not support Looper and device properties
#include <utils/Looper.h>
-#include <cutils/properties.h>
+#endif
#include <SkBitmap.h>
#include <SkRegion.h>
@@ -34,7 +34,9 @@
#include <hwui/Canvas.h>
#include <hwui/Paint.h>
#include <minikin/Layout.h>
+#ifdef __ANDROID__ // Layoutlib does not support RenderThread
#include <renderthread/RenderProxy.h>
+#endif
#include "core_jni_helpers.h"
@@ -52,6 +54,7 @@
return env;
}
+#ifdef __ANDROID__ // Layoutlib does not support GL, Looper
class InvokeRunnableMessage : public MessageHandler {
public:
InvokeRunnableMessage(JNIEnv* env, jobject runnable) {
@@ -87,11 +90,13 @@
sp<Looper> mLooper;
sp<InvokeRunnableMessage> mMessage;
};
+#endif
// ---------------- @FastNative -----------------------------
static void android_view_DisplayListCanvas_callDrawGLFunction(JNIEnv* env, jobject clazz,
jlong canvasPtr, jlong functorPtr, jobject releasedCallback) {
+#ifdef __ANDROID__ // Layoutlib does not support GL
Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
Functor* functor = reinterpret_cast<Functor*>(functorPtr);
sp<GlFunctorReleasedCallbackBridge> bridge;
@@ -99,52 +104,57 @@
bridge = new GlFunctorReleasedCallbackBridge(env, releasedCallback);
}
canvas->callDrawGLFunction(functor, bridge.get());
+#endif
}
// ---------------- @CriticalNative -------------------------
-static jlong android_view_DisplayListCanvas_createDisplayListCanvas(jlong renderNodePtr,
+static jlong android_view_DisplayListCanvas_createDisplayListCanvas(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
jint width, jint height) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
return reinterpret_cast<jlong>(Canvas::create_recording_canvas(width, height, renderNode));
}
-static void android_view_DisplayListCanvas_resetDisplayListCanvas(jlong canvasPtr,
+static void android_view_DisplayListCanvas_resetDisplayListCanvas(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr,
jlong renderNodePtr, jint width, jint height) {
Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
canvas->resetRecording(width, height, renderNode);
}
-static jint android_view_DisplayListCanvas_getMaxTextureSize() {
+static jint android_view_DisplayListCanvas_getMaxTextureSize(CRITICAL_JNI_PARAMS) {
+#ifdef __ANDROID__ // Layoutlib does not support RenderProxy (RenderThread)
return android::uirenderer::renderthread::RenderProxy::maxTextureSize();
+#else
+ return 4096;
+#endif
}
-static void android_view_DisplayListCanvas_insertReorderBarrier(jlong canvasPtr,
+static void android_view_DisplayListCanvas_insertReorderBarrier(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr,
jboolean reorderEnable) {
Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
canvas->insertReorderBarrier(reorderEnable);
}
-static jlong android_view_DisplayListCanvas_finishRecording(jlong canvasPtr) {
+static jlong android_view_DisplayListCanvas_finishRecording(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr) {
Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
return reinterpret_cast<jlong>(canvas->finishRecording());
}
-static void android_view_DisplayListCanvas_drawRenderNode(jlong canvasPtr, jlong renderNodePtr) {
+static void android_view_DisplayListCanvas_drawRenderNode(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, jlong renderNodePtr) {
Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
canvas->drawRenderNode(renderNode);
}
-static void android_view_DisplayListCanvas_drawTextureLayer(jlong canvasPtr, jlong layerPtr) {
+static void android_view_DisplayListCanvas_drawTextureLayer(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, jlong layerPtr) {
Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr);
canvas->drawLayer(layer);
}
-static void android_view_DisplayListCanvas_drawRoundRectProps(jlong canvasPtr,
+static void android_view_DisplayListCanvas_drawRoundRectProps(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr,
jlong leftPropPtr, jlong topPropPtr, jlong rightPropPtr, jlong bottomPropPtr,
jlong rxPropPtr, jlong ryPropPtr, jlong paintPropPtr) {
Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
@@ -158,7 +168,7 @@
canvas->drawRoundRect(leftProp, topProp, rightProp, bottomProp, rxProp, ryProp, paintProp);
}
-static void android_view_DisplayListCanvas_drawCircleProps(jlong canvasPtr,
+static void android_view_DisplayListCanvas_drawCircleProps(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr,
jlong xPropPtr, jlong yPropPtr, jlong radiusPropPtr, jlong paintPropPtr) {
Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
CanvasPropertyPrimitive* xProp = reinterpret_cast<CanvasPropertyPrimitive*>(xPropPtr);
@@ -168,7 +178,7 @@
canvas->drawCircle(xProp, yProp, radiusProp, paintProp);
}
-static void android_view_DisplayListCanvas_drawWebViewFunctor(jlong canvasPtr, jint functor) {
+static void android_view_DisplayListCanvas_drawWebViewFunctor(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, jint functor) {
Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
canvas->drawWebViewFunctor(functor);
}
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 43c0bbe..9450bc9 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -16,9 +16,9 @@
#define LOG_TAG "OpenGLRenderer"
#define ATRACE_TAG ATRACE_TAG_VIEW
-
+#ifdef __ANDROID__ // Layoutlib does not support EGL
#include <EGL/egl.h>
-
+#endif
#include "jni.h"
#include "GraphicsJNI.h"
#include <nativehelper/JNIHelp.h>
@@ -28,7 +28,9 @@
#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 <hwui/Paint.h>
#include <utils/TraceUtils.h>
@@ -85,7 +87,7 @@
renderNode->setStagingDisplayList(newData);
}
-static jboolean android_view_RenderNode_isValid(jlong renderNodePtr) {
+static jboolean android_view_RenderNode_isValid(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
return reinterpret_cast<RenderNode*>(renderNodePtr)->isValid();
}
@@ -93,52 +95,52 @@
// RenderProperties - setters
// ----------------------------------------------------------------------------
-static jboolean android_view_RenderNode_setLayerType(jlong renderNodePtr, jint jlayerType) {
+static jboolean android_view_RenderNode_setLayerType(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jint jlayerType) {
LayerType layerType = static_cast<LayerType>(jlayerType);
return SET_AND_DIRTY(mutateLayerProperties().setType, layerType, RenderNode::GENERIC);
}
-static jboolean android_view_RenderNode_setLayerPaint(jlong renderNodePtr, jlong paintPtr) {
+static jboolean android_view_RenderNode_setLayerPaint(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jlong paintPtr) {
Paint* paint = reinterpret_cast<Paint*>(paintPtr);
return SET_AND_DIRTY(mutateLayerProperties().setFromPaint, paint, RenderNode::GENERIC);
}
-static jboolean android_view_RenderNode_setStaticMatrix(jlong renderNodePtr, jlong matrixPtr) {
+static jboolean android_view_RenderNode_setStaticMatrix(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jlong matrixPtr) {
SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr);
return SET_AND_DIRTY(setStaticMatrix, matrix, RenderNode::GENERIC);
}
-static jboolean android_view_RenderNode_setAnimationMatrix(jlong renderNodePtr, jlong matrixPtr) {
+static jboolean android_view_RenderNode_setAnimationMatrix(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jlong matrixPtr) {
SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr);
return SET_AND_DIRTY(setAnimationMatrix, matrix, RenderNode::GENERIC);
}
-static jboolean android_view_RenderNode_setClipToBounds(jlong renderNodePtr,
+static jboolean android_view_RenderNode_setClipToBounds(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
jboolean clipToBounds) {
return SET_AND_DIRTY(setClipToBounds, clipToBounds, RenderNode::GENERIC);
}
-static jboolean android_view_RenderNode_setClipBounds(jlong renderNodePtr,
+static jboolean android_view_RenderNode_setClipBounds(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
jint left, jint top, jint right, jint bottom) {
android::uirenderer::Rect clipBounds(left, top, right, bottom);
return SET_AND_DIRTY(setClipBounds, clipBounds, RenderNode::GENERIC);
}
-static jboolean android_view_RenderNode_setClipBoundsEmpty(jlong renderNodePtr) {
+static jboolean android_view_RenderNode_setClipBoundsEmpty(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
return SET_AND_DIRTY(setClipBoundsEmpty,, RenderNode::GENERIC);
}
-static jboolean android_view_RenderNode_setProjectBackwards(jlong renderNodePtr,
+static jboolean android_view_RenderNode_setProjectBackwards(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
jboolean shouldProject) {
return SET_AND_DIRTY(setProjectBackwards, shouldProject, RenderNode::GENERIC);
}
-static jboolean android_view_RenderNode_setProjectionReceiver(jlong renderNodePtr,
+static jboolean android_view_RenderNode_setProjectionReceiver(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
jboolean shouldRecieve) {
return SET_AND_DIRTY(setProjectionReceiver, shouldRecieve, RenderNode::GENERIC);
}
-static jboolean android_view_RenderNode_setOutlineRoundRect(jlong renderNodePtr,
+static jboolean android_view_RenderNode_setOutlineRoundRect(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
jint left, jint top, jint right, jint bottom, jfloat radius, jfloat alpha) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
renderNode->mutateStagingProperties().mutableOutline().setRoundRect(left, top, right, bottom,
@@ -147,7 +149,7 @@
return true;
}
-static jboolean android_view_RenderNode_setOutlineConvexPath(jlong renderNodePtr,
+static jboolean android_view_RenderNode_setOutlineConvexPath(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
jlong outlinePathPtr, jfloat alpha) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
SkPath* outlinePath = reinterpret_cast<SkPath*>(outlinePathPtr);
@@ -156,47 +158,47 @@
return true;
}
-static jboolean android_view_RenderNode_setOutlineEmpty(jlong renderNodePtr) {
+static jboolean android_view_RenderNode_setOutlineEmpty(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
renderNode->mutateStagingProperties().mutableOutline().setEmpty();
renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
return true;
}
-static jboolean android_view_RenderNode_setOutlineNone(jlong renderNodePtr) {
+static jboolean android_view_RenderNode_setOutlineNone(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
renderNode->mutateStagingProperties().mutableOutline().setNone();
renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
return true;
}
-static jboolean android_view_RenderNode_hasShadow(jlong renderNodePtr) {
+static jboolean android_view_RenderNode_hasShadow(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
return renderNode->stagingProperties().hasShadow();
}
-static jboolean android_view_RenderNode_setSpotShadowColor(jlong renderNodePtr, jint shadowColor) {
+static jboolean android_view_RenderNode_setSpotShadowColor(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jint shadowColor) {
return SET_AND_DIRTY(setSpotShadowColor,
static_cast<SkColor>(shadowColor), RenderNode::GENERIC);
}
-static jint android_view_RenderNode_getSpotShadowColor(jlong renderNodePtr) {
+static jint android_view_RenderNode_getSpotShadowColor(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
return renderNode->stagingProperties().getSpotShadowColor();
}
-static jboolean android_view_RenderNode_setAmbientShadowColor(jlong renderNodePtr,
+static jboolean android_view_RenderNode_setAmbientShadowColor(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
jint shadowColor) {
return SET_AND_DIRTY(setAmbientShadowColor,
static_cast<SkColor>(shadowColor), RenderNode::GENERIC);
}
-static jint android_view_RenderNode_getAmbientShadowColor(jlong renderNodePtr) {
+static jint android_view_RenderNode_getAmbientShadowColor(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
return renderNode->stagingProperties().getAmbientShadowColor();
}
-static jboolean android_view_RenderNode_setClipToOutline(jlong renderNodePtr,
+static jboolean android_view_RenderNode_setClipToOutline(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
jboolean clipToOutline) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
renderNode->mutateStagingProperties().mutableOutline().setShouldClip(clipToOutline);
@@ -204,7 +206,7 @@
return true;
}
-static jboolean android_view_RenderNode_setRevealClip(jlong renderNodePtr, jboolean shouldClip,
+static jboolean android_view_RenderNode_setRevealClip(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jboolean shouldClip,
jfloat x, jfloat y, jfloat radius) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
renderNode->mutateStagingProperties().mutableRevealClip().set(
@@ -213,106 +215,106 @@
return true;
}
-static jboolean android_view_RenderNode_setAlpha(jlong renderNodePtr, float alpha) {
+static jboolean android_view_RenderNode_setAlpha(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float alpha) {
return SET_AND_DIRTY(setAlpha, alpha, RenderNode::ALPHA);
}
-static jboolean android_view_RenderNode_setHasOverlappingRendering(jlong renderNodePtr,
+static jboolean android_view_RenderNode_setHasOverlappingRendering(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
bool hasOverlappingRendering) {
return SET_AND_DIRTY(setHasOverlappingRendering, hasOverlappingRendering,
RenderNode::GENERIC);
}
-static void android_view_RenderNode_setUsageHint(jlong renderNodePtr, jint usageHint) {
+static void android_view_RenderNode_setUsageHint(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jint usageHint) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
renderNode->setUsageHint(static_cast<UsageHint>(usageHint));
}
-static jboolean android_view_RenderNode_setElevation(jlong renderNodePtr, float elevation) {
+static jboolean android_view_RenderNode_setElevation(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float elevation) {
return SET_AND_DIRTY(setElevation, elevation, RenderNode::Z);
}
-static jboolean android_view_RenderNode_setTranslationX(jlong renderNodePtr, float tx) {
+static jboolean android_view_RenderNode_setTranslationX(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float tx) {
return SET_AND_DIRTY(setTranslationX, tx, RenderNode::TRANSLATION_X | RenderNode::X);
}
-static jboolean android_view_RenderNode_setTranslationY(jlong renderNodePtr, float ty) {
+static jboolean android_view_RenderNode_setTranslationY(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float ty) {
return SET_AND_DIRTY(setTranslationY, ty, RenderNode::TRANSLATION_Y | RenderNode::Y);
}
-static jboolean android_view_RenderNode_setTranslationZ(jlong renderNodePtr, float tz) {
+static jboolean android_view_RenderNode_setTranslationZ(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float tz) {
return SET_AND_DIRTY(setTranslationZ, tz, RenderNode::TRANSLATION_Z | RenderNode::Z);
}
-static jboolean android_view_RenderNode_setRotation(jlong renderNodePtr, float rotation) {
+static jboolean android_view_RenderNode_setRotation(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float rotation) {
return SET_AND_DIRTY(setRotation, rotation, RenderNode::ROTATION);
}
-static jboolean android_view_RenderNode_setRotationX(jlong renderNodePtr, float rx) {
+static jboolean android_view_RenderNode_setRotationX(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float rx) {
return SET_AND_DIRTY(setRotationX, rx, RenderNode::ROTATION_X);
}
-static jboolean android_view_RenderNode_setRotationY(jlong renderNodePtr, float ry) {
+static jboolean android_view_RenderNode_setRotationY(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float ry) {
return SET_AND_DIRTY(setRotationY, ry, RenderNode::ROTATION_Y);
}
-static jboolean android_view_RenderNode_setScaleX(jlong renderNodePtr, float sx) {
+static jboolean android_view_RenderNode_setScaleX(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float sx) {
return SET_AND_DIRTY(setScaleX, sx, RenderNode::SCALE_X);
}
-static jboolean android_view_RenderNode_setScaleY(jlong renderNodePtr, float sy) {
+static jboolean android_view_RenderNode_setScaleY(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float sy) {
return SET_AND_DIRTY(setScaleY, sy, RenderNode::SCALE_Y);
}
-static jboolean android_view_RenderNode_setPivotX(jlong renderNodePtr, float px) {
+static jboolean android_view_RenderNode_setPivotX(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float px) {
return SET_AND_DIRTY(setPivotX, px, RenderNode::GENERIC);
}
-static jboolean android_view_RenderNode_setPivotY(jlong renderNodePtr, float py) {
+static jboolean android_view_RenderNode_setPivotY(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float py) {
return SET_AND_DIRTY(setPivotY, py, RenderNode::GENERIC);
}
-static jboolean android_view_RenderNode_resetPivot(jlong renderNodePtr) {
+static jboolean android_view_RenderNode_resetPivot(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
return SET_AND_DIRTY(resetPivot, /* void */, RenderNode::GENERIC);
}
-static jboolean android_view_RenderNode_setCameraDistance(jlong renderNodePtr, float distance) {
+static jboolean android_view_RenderNode_setCameraDistance(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float distance) {
return SET_AND_DIRTY(setCameraDistance, distance, RenderNode::GENERIC);
}
-static jboolean android_view_RenderNode_setLeft(jlong renderNodePtr, int left) {
+static jboolean android_view_RenderNode_setLeft(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, int left) {
return SET_AND_DIRTY(setLeft, left, RenderNode::X);
}
-static jboolean android_view_RenderNode_setTop(jlong renderNodePtr, int top) {
+static jboolean android_view_RenderNode_setTop(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, int top) {
return SET_AND_DIRTY(setTop, top, RenderNode::Y);
}
-static jboolean android_view_RenderNode_setRight(jlong renderNodePtr, int right) {
+static jboolean android_view_RenderNode_setRight(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, int right) {
return SET_AND_DIRTY(setRight, right, RenderNode::X);
}
-static jboolean android_view_RenderNode_setBottom(jlong renderNodePtr, int bottom) {
+static jboolean android_view_RenderNode_setBottom(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, int bottom) {
return SET_AND_DIRTY(setBottom, bottom, RenderNode::Y);
}
-static jint android_view_RenderNode_getLeft(jlong renderNodePtr) {
+static jint android_view_RenderNode_getLeft(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getLeft();
}
-static jint android_view_RenderNode_getTop(jlong renderNodePtr) {
+static jint android_view_RenderNode_getTop(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getTop();
}
-static jint android_view_RenderNode_getRight(jlong renderNodePtr) {
+static jint android_view_RenderNode_getRight(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getRight();
}
-static jint android_view_RenderNode_getBottom(jlong renderNodePtr) {
+static jint android_view_RenderNode_getBottom(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getBottom();
}
-static jboolean android_view_RenderNode_setLeftTopRightBottom(jlong renderNodePtr,
+static jboolean android_view_RenderNode_setLeftTopRightBottom(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
int left, int top, int right, int bottom) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
if (renderNode->mutateStagingProperties().setLeftTopRightBottom(left, top, right, bottom)) {
@@ -322,11 +324,11 @@
return false;
}
-static jboolean android_view_RenderNode_offsetLeftAndRight(jlong renderNodePtr, jint offset) {
+static jboolean android_view_RenderNode_offsetLeftAndRight(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jint offset) {
return SET_AND_DIRTY(offsetLeftRight, offset, RenderNode::X);
}
-static jboolean android_view_RenderNode_offsetTopAndBottom(jlong renderNodePtr, jint offset) {
+static jboolean android_view_RenderNode_offsetTopAndBottom(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jint offset) {
return SET_AND_DIRTY(offsetTopBottom, offset, RenderNode::Y);
}
@@ -334,12 +336,12 @@
// RenderProperties - getters
// ----------------------------------------------------------------------------
-static jboolean android_view_RenderNode_hasOverlappingRendering(jlong renderNodePtr) {
+static jboolean android_view_RenderNode_hasOverlappingRendering(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
return renderNode->stagingProperties().hasOverlappingRendering();
}
-static jboolean android_view_RenderNode_getAnimationMatrix(jlong renderNodePtr, jlong outMatrixPtr) {
+static jboolean android_view_RenderNode_getAnimationMatrix(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jlong outMatrixPtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
SkMatrix* outMatrix = reinterpret_cast<SkMatrix*>(outMatrixPtr);
@@ -352,83 +354,83 @@
return JNI_FALSE;
}
-static jboolean android_view_RenderNode_getClipToBounds(jlong renderNodePtr) {
+static jboolean android_view_RenderNode_getClipToBounds(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
return renderNode->stagingProperties().getClipToBounds();
}
-static jboolean android_view_RenderNode_getClipToOutline(jlong renderNodePtr) {
+static jboolean android_view_RenderNode_getClipToOutline(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
return renderNode->stagingProperties().getOutline().getShouldClip();
}
-static jfloat android_view_RenderNode_getAlpha(jlong renderNodePtr) {
+static jfloat android_view_RenderNode_getAlpha(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
return renderNode->stagingProperties().getAlpha();
}
-static jfloat android_view_RenderNode_getCameraDistance(jlong renderNodePtr) {
+static jfloat android_view_RenderNode_getCameraDistance(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
return renderNode->stagingProperties().getCameraDistance();
}
-static jfloat android_view_RenderNode_getScaleX(jlong renderNodePtr) {
+static jfloat android_view_RenderNode_getScaleX(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
return renderNode->stagingProperties().getScaleX();
}
-static jfloat android_view_RenderNode_getScaleY(jlong renderNodePtr) {
+static jfloat android_view_RenderNode_getScaleY(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
return renderNode->stagingProperties().getScaleY();
}
-static jfloat android_view_RenderNode_getElevation(jlong renderNodePtr) {
+static jfloat android_view_RenderNode_getElevation(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
return renderNode->stagingProperties().getElevation();
}
-static jfloat android_view_RenderNode_getTranslationX(jlong renderNodePtr) {
+static jfloat android_view_RenderNode_getTranslationX(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
return renderNode->stagingProperties().getTranslationX();
}
-static jfloat android_view_RenderNode_getTranslationY(jlong renderNodePtr) {
+static jfloat android_view_RenderNode_getTranslationY(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
return renderNode->stagingProperties().getTranslationY();
}
-static jfloat android_view_RenderNode_getTranslationZ(jlong renderNodePtr) {
+static jfloat android_view_RenderNode_getTranslationZ(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
return renderNode->stagingProperties().getTranslationZ();
}
-static jfloat android_view_RenderNode_getRotation(jlong renderNodePtr) {
+static jfloat android_view_RenderNode_getRotation(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
return renderNode->stagingProperties().getRotation();
}
-static jfloat android_view_RenderNode_getRotationX(jlong renderNodePtr) {
+static jfloat android_view_RenderNode_getRotationX(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
return renderNode->stagingProperties().getRotationX();
}
-static jfloat android_view_RenderNode_getRotationY(jlong renderNodePtr) {
+static jfloat android_view_RenderNode_getRotationY(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
return renderNode->stagingProperties().getRotationY();
}
-static jboolean android_view_RenderNode_isPivotExplicitlySet(jlong renderNodePtr) {
+static jboolean android_view_RenderNode_isPivotExplicitlySet(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
return renderNode->stagingProperties().isPivotExplicitlySet();
}
-static jboolean android_view_RenderNode_hasIdentityMatrix(jlong renderNodePtr) {
+static jboolean android_view_RenderNode_hasIdentityMatrix(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
renderNode->mutateStagingProperties().updateMatrix();
return !renderNode->stagingProperties().hasTransformMatrix();
}
-static jint android_view_RenderNode_getLayerType(jlong renderNodePtr) {
+static jint android_view_RenderNode_getLayerType(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
return static_cast<int>(renderNode->stagingProperties().layerProperties().type());
}
@@ -437,7 +439,7 @@
// RenderProperties - computed getters
// ----------------------------------------------------------------------------
-static void android_view_RenderNode_getTransformMatrix(jlong renderNodePtr, jlong outMatrixPtr) {
+static void getTransformMatrix(jlong renderNodePtr, jlong outMatrixPtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
SkMatrix* outMatrix = reinterpret_cast<SkMatrix*>(outMatrixPtr);
@@ -451,10 +453,14 @@
}
}
-static void android_view_RenderNode_getInverseTransformMatrix(jlong renderNodePtr,
+static void android_view_RenderNode_getTransformMatrix(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jlong outMatrixPtr) {
+ getTransformMatrix(renderNodePtr, outMatrixPtr);
+}
+
+static void android_view_RenderNode_getInverseTransformMatrix(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
jlong outMatrixPtr) {
// load transform matrix
- android_view_RenderNode_getTransformMatrix(renderNodePtr, outMatrixPtr);
+ getTransformMatrix(renderNodePtr, outMatrixPtr);
SkMatrix* outMatrix = reinterpret_cast<SkMatrix*>(outMatrixPtr);
// return it inverted
@@ -464,35 +470,35 @@
}
}
-static jfloat android_view_RenderNode_getPivotX(jlong renderNodePtr) {
+static jfloat android_view_RenderNode_getPivotX(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
renderNode->mutateStagingProperties().updateMatrix();
return renderNode->stagingProperties().getPivotX();
}
-static jfloat android_view_RenderNode_getPivotY(jlong renderNodePtr) {
+static jfloat android_view_RenderNode_getPivotY(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
renderNode->mutateStagingProperties().updateMatrix();
return renderNode->stagingProperties().getPivotY();
}
-static jint android_view_RenderNode_getWidth(jlong renderNodePtr) {
+static jint android_view_RenderNode_getWidth(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getWidth();
}
-static jint android_view_RenderNode_getHeight(jlong renderNodePtr) {
+static jint android_view_RenderNode_getHeight(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getHeight();
}
-static jboolean android_view_RenderNode_setAllowForceDark(jlong renderNodePtr, jboolean allow) {
+static jboolean android_view_RenderNode_setAllowForceDark(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jboolean allow) {
return SET_AND_DIRTY(setAllowForceDark, allow, RenderNode::GENERIC);
}
-static jboolean android_view_RenderNode_getAllowForceDark(jlong renderNodePtr) {
+static jboolean android_view_RenderNode_getAllowForceDark(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getAllowForceDark();
}
-static jlong android_view_RenderNode_getUniqueId(jlong renderNodePtr) {
+static jlong android_view_RenderNode_getUniqueId(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
return reinterpret_cast<RenderNode*>(renderNodePtr)->uniqueId();
}
@@ -558,6 +564,7 @@
}
mPreviousPosition = bounds;
+#ifdef __ANDROID__ // Layoutlib does not support CanvasContext
incStrong(0);
auto functor = std::bind(
std::mem_fn(&PositionListenerTrampoline::doUpdatePositionAsync), this,
@@ -566,6 +573,7 @@
(jint) bounds.right, (jint) bounds.bottom);
info.canvasContext.enqueueFrameWork(std::move(functor));
+#endif
}
virtual void onPositionLost(RenderNode& node, const TreeInfo* info) override {
@@ -584,10 +592,11 @@
mWeakRef = nullptr;
return;
}
-
+#ifdef __ANDROID__ // Layoutlib does not support CanvasContext
// TODO: Remember why this is synchronous and then make a comment
env->CallVoidMethod(localref, gPositionListener_PositionLostMethod,
info ? info->canvasContext.getFrameNumber() : 0);
+#endif
env->DeleteLocalRef(localref);
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 3ae4f29..62351d0 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -824,6 +824,11 @@
grants your app this permission. If you don't need this permission, be sure your <a
href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
targetSdkVersion}</a> is 4 or higher.
+ <p>Is this permission is not whitelisted for an app that targets an API level before
+ {@link android.os.Build.VERSION_CODES#Q} this permission cannot be granted to apps.</p>
+ <p>Is this permission is not whitelisted for an app that targets an API level
+ {@link android.os.Build.VERSION_CODES#Q} or later the app will be forced into isolated storage.
+ </p>
-->
<permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:permissionGroup="android.permission-group.UNDEFINED"
@@ -845,6 +850,8 @@
read/write files in your application-specific directories returned by
{@link android.content.Context#getExternalFilesDir} and
{@link android.content.Context#getExternalCacheDir}.
+ <p>Is this permission is not whitelisted for an app that targets an API level before
+ {@link android.os.Build.VERSION_CODES#Q} this permission cannot be granted to apps.</p>
-->
<permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:permissionGroup="android.permission-group.UNDEFINED"
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 8dfb969..77fca8f 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -326,6 +326,8 @@
grantable in its full form to apps that meet special criteria
per platform policy. Otherwise, a weaker form of the permission
would be granted. The weak grant depends on the permission.
+ <p>What weak grant means is described in the documentation of
+ the permissions.
-->
<flag name="softRestricted" value="0x8" />
<!-- This permission is restricted immutably which means that its
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 354a4b3..5814bb7 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -56,6 +56,8 @@
host: {
include_dirs: [
"external/vulkan-headers/include",
+ "frameworks/native/libs/math/include",
+ "frameworks/native/libs/ui/include",
],
cflags: [
"-Wno-unused-variable",
@@ -159,6 +161,10 @@
whole_static_libs: ["libskia"],
srcs: [
+ "pipeline/skia/SkiaDisplayList.cpp",
+ "pipeline/skia/SkiaRecordingCanvas.cpp",
+ "pipeline/skia/RenderNodeDrawable.cpp",
+ "pipeline/skia/ReorderBarrierDrawables.cpp",
"hwui/AnimatedImageDrawable.cpp",
"hwui/AnimatedImageThread.cpp",
"hwui/Bitmap.cpp",
@@ -168,15 +174,24 @@
"hwui/PaintImpl.cpp",
"hwui/Typeface.cpp",
"utils/Blur.cpp",
+ "utils/Color.cpp",
"utils/LinearAllocator.cpp",
"utils/VectorDrawableUtils.cpp",
+ "AnimationContext.cpp",
"Animator.cpp",
+ "AnimatorManager.cpp",
+ "CanvasTransform.cpp",
+ "DamageAccumulator.cpp",
"Interpolator.cpp",
+ "LightingInfo.cpp",
"Matrix.cpp",
"PathParser.cpp",
"Properties.cpp",
"PropertyValuesAnimatorSet.cpp",
"PropertyValuesHolder.cpp",
+ "RecordingCanvas.cpp",
+ "RenderNode.cpp",
+ "RenderProperties.cpp",
"SkiaCanvas.cpp",
"VectorDrawable.cpp",
],
@@ -193,15 +208,11 @@
srcs: [
"pipeline/skia/GLFunctorDrawable.cpp",
"pipeline/skia/LayerDrawable.cpp",
- "pipeline/skia/RenderNodeDrawable.cpp",
- "pipeline/skia/ReorderBarrierDrawables.cpp",
"pipeline/skia/ShaderCache.cpp",
- "pipeline/skia/SkiaDisplayList.cpp",
"pipeline/skia/SkiaMemoryTracer.cpp",
"pipeline/skia/SkiaOpenGLPipeline.cpp",
"pipeline/skia/SkiaPipeline.cpp",
"pipeline/skia/SkiaProfileRenderer.cpp",
- "pipeline/skia/SkiaRecordingCanvas.cpp",
"pipeline/skia/SkiaVulkanPipeline.cpp",
"pipeline/skia/VectorDrawableAtlas.cpp",
"pipeline/skia/VkFunctorDrawable.cpp",
@@ -224,13 +235,8 @@
"surfacetexture/ImageConsumer.cpp",
"surfacetexture/SurfaceTexture.cpp",
"thread/CommonPool.cpp",
- "utils/Color.cpp",
"utils/GLUtils.cpp",
"utils/StringUtils.cpp",
- "AnimationContext.cpp",
- "AnimatorManager.cpp",
- "CanvasTransform.cpp",
- "DamageAccumulator.cpp",
"DeferredLayerUpdater.cpp",
"DeviceInfo.cpp",
"FrameInfo.cpp",
@@ -241,13 +247,9 @@
"JankTracker.cpp",
"Layer.cpp",
"LayerUpdateQueue.cpp",
- "LightingInfo.cpp",
"ProfileData.cpp",
"ProfileDataContainer.cpp",
"Readback.cpp",
- "RecordingCanvas.cpp",
- "RenderNode.cpp",
- "RenderProperties.cpp",
"TreeInfo.cpp",
"WebViewFunctorManager.cpp",
"protos/graphicsstats.proto",
@@ -257,6 +259,9 @@
cflags: ["-Wno-implicit-fallthrough"],
},
host: {
+ srcs: [
+ "utils/HostColorSpace.cpp",
+ ],
export_static_lib_headers: [
"libarect",
],
diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp
index cca0032..2d2df52 100644
--- a/libs/hwui/DamageAccumulator.cpp
+++ b/libs/hwui/DamageAccumulator.cpp
@@ -137,20 +137,28 @@
mapRect(frame->matrix4, frame->pendingDirty, &mHead->pendingDirty);
}
-static inline void mapRect(const RenderProperties& props, const SkRect& in, SkRect* out) {
- if (in.isEmpty()) return;
- const SkMatrix* transform = props.getTransformMatrix();
- SkRect temp(in);
+static inline void applyMatrix(const SkMatrix* transform, SkRect* rect) {
if (transform && !transform->isIdentity()) {
if (CC_LIKELY(!transform->hasPerspective())) {
- transform->mapRect(&temp);
+ transform->mapRect(rect);
} else {
// Don't attempt to calculate damage for a perspective transform
// as the numbers this works with can break the perspective
// calculations. Just give up and expand to DIRTY_MIN/DIRTY_MAX
- temp.set(DIRTY_MIN, DIRTY_MIN, DIRTY_MAX, DIRTY_MAX);
+ rect->set(DIRTY_MIN, DIRTY_MIN, DIRTY_MAX, DIRTY_MAX);
}
}
+}
+
+static inline void mapRect(const RenderProperties& props, const SkRect& in, SkRect* out) {
+ if (in.isEmpty()) return;
+ SkRect temp(in);
+ applyMatrix(props.getTransformMatrix(), &temp);
+ if (props.getStaticMatrix()) {
+ applyMatrix(props.getStaticMatrix(), &temp);
+ } else if (props.getAnimationMatrix()) {
+ applyMatrix(props.getAnimationMatrix(), &temp);
+ }
temp.offset(props.getLeft(), props.getTop());
out->join(temp);
}
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index a833f7e..8aee8f5 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -20,7 +20,12 @@
#include "Debug.h"
#include "TreeInfo.h"
#include "VectorDrawable.h"
+#ifdef __ANDROID__
#include "renderthread/CanvasContext.h"
+#else
+#include "DamageAccumulator.h"
+#include "pipeline/skia/SkiaDisplayList.h"
+#endif
#include "utils/FatVector.h"
#include "utils/MathUtils.h"
#include "utils/StringUtils.h"
@@ -160,6 +165,7 @@
}
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
@@ -188,6 +194,7 @@
// 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/RenderProperties.h b/libs/hwui/RenderProperties.h
index e6710cc..e979448 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -526,9 +526,13 @@
}
bool fitsOnLayer() const {
+#ifdef __ANDROID__ // Layoutlib does not support device info
const DeviceInfo* deviceInfo = DeviceInfo::get();
return mPrimitiveFields.mWidth <= deviceInfo->maxTextureSize() &&
mPrimitiveFields.mHeight <= deviceInfo->maxTextureSize();
+#else
+ return mPrimitiveFields.mWidth <= 4096 && mPrimitiveFields.mHeight <= 4096;
+#endif
}
bool promotedToLayer() const {
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index e2ddb91..a48c860 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -30,11 +30,7 @@
namespace android {
Canvas* Canvas::create_recording_canvas(int width, int height, uirenderer::RenderNode* renderNode) {
-#ifdef __ANDROID__ // Layoutlib does not support recording canvas
return new uirenderer::skiapipeline::SkiaRecordingCanvas(renderNode, width, height);
-#else
- return NULL;
-#endif
}
static inline void drawStroke(SkScalar left, SkScalar right, SkScalar top, SkScalar thickness,
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index 780ac20..c7d5f31 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -17,9 +17,15 @@
#include "SkiaDisplayList.h"
#include "DumpOpsCanvas.h"
+#ifdef __ANDROID__ // Layoutlib does not support SkiaPipeline
#include "SkiaPipeline.h"
+#else
+#include "DamageAccumulator.h"
+#endif
#include "VectorDrawable.h"
+#ifdef __ANDROID__
#include "renderthread/CanvasContext.h"
+#endif
#include <SkImagePriv.h>
#include <SkPathOps.h>
@@ -81,6 +87,7 @@
// 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
@@ -88,6 +95,7 @@
info.prepareTextures = false;
info.canvasContext.unpinImages();
}
+#endif
bool hasBackwardProjectedNodesHere = false;
bool hasBackwardProjectedNodesSubtree = false;
@@ -141,10 +149,12 @@
const SkRect& bounds = vectorDrawable->properties().getBounds();
if (intersects(info.screenSize, totalMatrix, bounds)) {
isDirty = true;
+#ifdef __ANDROID__ // Layoutlib does not support CanvasContext
static_cast<SkiaPipeline*>(info.canvasContext.getRenderPipeline())
->getVectorDrawables()
->push_back(vectorDrawable);
vectorDrawable->setPropertyChangeWillBeConsumed(true);
+#endif
}
}
}
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 16c8b89..38ef131 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -18,14 +18,18 @@
#include <SkImagePriv.h>
#include "CanvasTransform.h"
+#ifdef __ANDROID__ // Layoutlib does not support Layers
#include "Layer.h"
#include "LayerDrawable.h"
+#endif
#include "NinePatchUtils.h"
#include "RenderNode.h"
#include "pipeline/skia/AnimatedDrawables.h"
+#ifdef __ANDROID__ // Layoutlib does not support GL, Vulcan etc.
#include "pipeline/skia/GLFunctorDrawable.h"
#include "pipeline/skia/VkFunctorDrawable.h"
#include "pipeline/skia/VkInteropFunctorDrawable.h"
+#endif
namespace android {
namespace uirenderer {
@@ -102,13 +106,16 @@
}
void SkiaRecordingCanvas::drawLayer(uirenderer::DeferredLayerUpdater* layerUpdater) {
+#ifdef __ANDROID__ // Layoutlib does not support Layers
if (layerUpdater != nullptr) {
// Create a ref-counted drawable, which is kept alive by sk_sp in SkLiteDL.
sk_sp<SkDrawable> drawable(new LayerDrawable(layerUpdater));
drawDrawable(drawable.get());
}
+#endif
}
+
void SkiaRecordingCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) {
// Record the child node. Drawable dtor will be invoked when mChildNodes deque is cleared.
mDisplayList->mChildNodes.emplace_back(renderNode, asSkCanvas(), true, mCurrentBarrier);
@@ -125,8 +132,10 @@
}
}
+
void SkiaRecordingCanvas::callDrawGLFunction(Functor* functor,
uirenderer::GlFunctorLifecycleListener* listener) {
+#ifdef __ANDROID__ // Layoutlib does not support GL, Vulcan etc.
FunctorDrawable* functorDrawable;
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
functorDrawable = mDisplayList->allocateDrawable<VkInteropFunctorDrawable>(
@@ -137,9 +146,11 @@
}
mDisplayList->mChildFunctors.push_back(functorDrawable);
drawDrawable(functorDrawable);
+#endif
}
void SkiaRecordingCanvas::drawWebViewFunctor(int functor) {
+#ifdef __ANDROID__ // Layoutlib does not support GL, Vulcan etc.
FunctorDrawable* functorDrawable;
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
functorDrawable = mDisplayList->allocateDrawable<VkFunctorDrawable>(functor, asSkCanvas());
@@ -148,6 +159,7 @@
}
mDisplayList->mChildFunctors.push_back(functorDrawable);
drawDrawable(functorDrawable);
+#endif
}
void SkiaRecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 46c3f2f..280f7d3 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -145,12 +145,6 @@
GET_INST_PROC(GetPhysicalDeviceImageFormatProperties2);
GET_INST_PROC(CreateDevice);
GET_INST_PROC(EnumerateDeviceExtensionProperties);
- GET_INST_PROC(CreateAndroidSurfaceKHR);
- GET_INST_PROC(DestroySurfaceKHR);
- GET_INST_PROC(GetPhysicalDeviceSurfaceSupportKHR);
- GET_INST_PROC(GetPhysicalDeviceSurfaceCapabilitiesKHR);
- GET_INST_PROC(GetPhysicalDeviceSurfaceFormatsKHR);
- GET_INST_PROC(GetPhysicalDeviceSurfacePresentModesKHR);
uint32_t gpuCount;
LOG_ALWAYS_FATAL_IF(mEnumeratePhysicalDevices(mInstance, &gpuCount, nullptr));
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index dd3c6d0..ec217c0 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -107,14 +107,6 @@
FNPTR_TYPE fPtr;
};
- // WSI interface functions
- VkPtr<PFN_vkCreateAndroidSurfaceKHR> mCreateAndroidSurfaceKHR;
- VkPtr<PFN_vkDestroySurfaceKHR> mDestroySurfaceKHR;
- VkPtr<PFN_vkGetPhysicalDeviceSurfaceSupportKHR> mGetPhysicalDeviceSurfaceSupportKHR;
- VkPtr<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR> mGetPhysicalDeviceSurfaceCapabilitiesKHR;
- VkPtr<PFN_vkGetPhysicalDeviceSurfaceFormatsKHR> mGetPhysicalDeviceSurfaceFormatsKHR;
- VkPtr<PFN_vkGetPhysicalDeviceSurfacePresentModesKHR> mGetPhysicalDeviceSurfacePresentModesKHR;
-
// Instance Functions
VkPtr<PFN_vkEnumerateInstanceVersion> mEnumerateInstanceVersion;
VkPtr<PFN_vkEnumerateInstanceExtensionProperties> mEnumerateInstanceExtensionProperties;
diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp
index b2cc23e..a667660 100644
--- a/libs/hwui/renderthread/VulkanSurface.cpp
+++ b/libs/hwui/renderthread/VulkanSurface.cpp
@@ -49,21 +49,6 @@
}
}
-static int ConvertVkTransformToNative(VkSurfaceTransformFlagsKHR transform) {
- switch (transform) {
- case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
- return NATIVE_WINDOW_TRANSFORM_ROT_270;
- case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR:
- return NATIVE_WINDOW_TRANSFORM_ROT_180;
- case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
- return NATIVE_WINDOW_TRANSFORM_ROT_90;
- case VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR:
- case VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR:
- default:
- return 0;
- }
-}
-
static SkMatrix GetPreTransformMatrix(SkISize windowSize, int transform) {
const int width = windowSize.width();
const int height = windowSize.height();
@@ -140,105 +125,59 @@
return true;
}
-class VkSurfaceAutoDeleter {
-public:
- VkSurfaceAutoDeleter(VkInstance instance, VkSurfaceKHR surface,
- PFN_vkDestroySurfaceKHR destroySurfaceKHR)
- : mInstance(instance), mSurface(surface), mDestroySurfaceKHR(destroySurfaceKHR) {}
- ~VkSurfaceAutoDeleter() { destroy(); }
-
- void destroy() {
- if (mSurface != VK_NULL_HANDLE) {
- mDestroySurfaceKHR(mInstance, mSurface, nullptr);
- mSurface = VK_NULL_HANDLE;
- }
- }
-
-private:
- VkInstance mInstance;
- VkSurfaceKHR mSurface;
- PFN_vkDestroySurfaceKHR mDestroySurfaceKHR;
-};
-
VulkanSurface* VulkanSurface::Create(ANativeWindow* window, ColorMode colorMode,
SkColorType colorType, sk_sp<SkColorSpace> colorSpace,
GrContext* grContext, const VulkanManager& vkManager,
uint32_t extraBuffers) {
- VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo;
- memset(&surfaceCreateInfo, 0, sizeof(VkAndroidSurfaceCreateInfoKHR));
- surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
- surfaceCreateInfo.pNext = nullptr;
- surfaceCreateInfo.flags = 0;
- surfaceCreateInfo.window = window;
-
- VkSurfaceKHR vkSurface = VK_NULL_HANDLE;
- VkResult res = vkManager.mCreateAndroidSurfaceKHR(vkManager.mInstance, &surfaceCreateInfo,
- nullptr, &vkSurface);
- if (VK_SUCCESS != res) {
- ALOGE("VulkanSurface::Create() vkCreateAndroidSurfaceKHR failed (%d)", res);
- return nullptr;
- }
-
- VkSurfaceAutoDeleter vkSurfaceDeleter(vkManager.mInstance, vkSurface,
- vkManager.mDestroySurfaceKHR);
-
- SkDEBUGCODE(VkBool32 supported; res = vkManager.mGetPhysicalDeviceSurfaceSupportKHR(
- vkManager.mPhysicalDevice, vkManager.mPresentQueueIndex,
- vkSurface, &supported);
- // All physical devices and queue families on Android must be capable of
- // presentation with any native window.
- SkASSERT(VK_SUCCESS == res && supported););
-
- // check for capabilities
- VkSurfaceCapabilitiesKHR caps;
- res = vkManager.mGetPhysicalDeviceSurfaceCapabilitiesKHR(vkManager.mPhysicalDevice, vkSurface,
- &caps);
- if (VK_SUCCESS != res) {
- ALOGE("VulkanSurface::Create() vkGetPhysicalDeviceSurfaceCapabilitiesKHR failed (%d)", res);
- return nullptr;
- }
-
- LOG_ALWAYS_FATAL_IF(0 == (caps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR));
-
- /*
- * We must destroy the VK Surface before attempting to update the window as doing so after
- * will cause the native window to be modified in unexpected ways.
- */
- vkSurfaceDeleter.destroy();
+ // TODO(http://b/134182502)
+ const SkISize minSize = SkISize::Make(1, 1);
+ const SkISize maxSize = SkISize::Make(4096, 4096);
/*
* Populate Window Info struct
*/
WindowInfo windowInfo;
- windowInfo.transform = ConvertVkTransformToNative(caps.supportedTransforms);
- windowInfo.size = SkISize::Make(caps.currentExtent.width, caps.currentExtent.height);
-
- const SkISize minSize = SkISize::Make(caps.minImageExtent.width, caps.minImageExtent.height);
- const SkISize maxSize = SkISize::Make(caps.maxImageExtent.width, caps.maxImageExtent.height);
- ComputeWindowSizeAndTransform(&windowInfo, minSize, maxSize);
+ int err;
+ int width, height;
+ err = window->query(window, NATIVE_WINDOW_DEFAULT_WIDTH, &width);
+ if (err != 0 || width < 0) {
+ ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, width);
+ return nullptr;
+ }
+ err = window->query(window, NATIVE_WINDOW_DEFAULT_HEIGHT, &height);
+ if (err != 0 || height < 0) {
+ ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, height);
+ return nullptr;
+ }
+ windowInfo.size = SkISize::Make(width, height);
int query_value;
- int err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value);
+ err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT, &query_value);
if (err != 0 || query_value < 0) {
ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, query_value);
return nullptr;
}
- auto min_undequeued_buffers = static_cast<uint32_t>(query_value);
+ windowInfo.transform = query_value;
- windowInfo.bufferCount = min_undequeued_buffers +
- std::max(sTargetBufferCount + extraBuffers, caps.minImageCount);
- if (caps.maxImageCount > 0 && windowInfo.bufferCount > caps.maxImageCount) {
- // Application must settle for fewer images than desired:
- windowInfo.bufferCount = caps.maxImageCount;
+ ComputeWindowSizeAndTransform(&windowInfo, minSize, maxSize);
+
+ err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value);
+ if (err != 0 || query_value < 0) {
+ ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, query_value);
+ return nullptr;
}
+ windowInfo.bufferCount = static_cast<uint32_t>(query_value) + sTargetBufferCount + extraBuffers;
- // Currently Skia requires the images to be color attachments and support all transfer
- // operations.
- VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
- VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
- VK_IMAGE_USAGE_TRANSFER_DST_BIT;
- LOG_ALWAYS_FATAL_IF((caps.supportedUsageFlags & usageFlags) != usageFlags);
+ err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &query_value);
+ if (err != 0 || query_value < 0) {
+ ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, query_value);
+ return nullptr;
+ }
+ if (windowInfo.bufferCount > static_cast<uint32_t>(query_value)) {
+ // Application must settle for fewer images than desired:
+ windowInfo.bufferCount = static_cast<uint32_t>(query_value);
+ }
windowInfo.dataspace = HAL_DATASPACE_V0_SRGB;
if (colorMode == ColorMode::WideColorGamut) {
@@ -275,7 +214,10 @@
imageFormatInfo.format = vkPixelFormat;
imageFormatInfo.type = VK_IMAGE_TYPE_2D;
imageFormatInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
- imageFormatInfo.usage = usageFlags;
+ // Currently Skia requires the images to be color attachments and support all transfer
+ // operations.
+ imageFormatInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
+ VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
imageFormatInfo.flags = 0;
VkAndroidHardwareBufferUsageANDROID hwbUsage;
@@ -286,8 +228,8 @@
imgFormProps.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2;
imgFormProps.pNext = &hwbUsage;
- res = vkManager.mGetPhysicalDeviceImageFormatProperties2(vkManager.mPhysicalDevice,
- &imageFormatInfo, &imgFormProps);
+ VkResult res = vkManager.mGetPhysicalDeviceImageFormatProperties2(
+ vkManager.mPhysicalDevice, &imageFormatInfo, &imgFormProps);
if (VK_SUCCESS != res) {
ALOGE("Failed to query GetPhysicalDeviceImageFormatProperties2");
return nullptr;
diff --git a/libs/hwui/utils/HostColorSpace.cpp b/libs/hwui/utils/HostColorSpace.cpp
new file mode 100644
index 0000000..77a6820
--- /dev/null
+++ b/libs/hwui/utils/HostColorSpace.cpp
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+// This is copied from framework/native/libs/ui in order not to include libui in host build
+
+#include <ui/ColorSpace.h>
+
+using namespace std::placeholders;
+
+namespace android {
+
+static constexpr float linearResponse(float v) {
+ return v;
+}
+
+static constexpr float rcpResponse(float x, const ColorSpace::TransferParameters& p) {
+ return x >= p.d * p.c ? (std::pow(x, 1.0f / p.g) - p.b) / p.a : x / p.c;
+}
+
+static constexpr float response(float x, const ColorSpace::TransferParameters& p) {
+ return x >= p.d ? std::pow(p.a * x + p.b, p.g) : p.c * x;
+}
+
+static constexpr float rcpFullResponse(float x, const ColorSpace::TransferParameters& p) {
+ return x >= p.d * p.c ? (std::pow(x - p.e, 1.0f / p.g) - p.b) / p.a : (x - p.f) / p.c;
+}
+
+static constexpr float fullResponse(float x, const ColorSpace::TransferParameters& p) {
+ return x >= p.d ? std::pow(p.a * x + p.b, p.g) + p.e : p.c * x + p.f;
+}
+
+static float absRcpResponse(float x, float g,float a, float b, float c, float d) {
+ float xx = std::abs(x);
+ return std::copysign(xx >= d * c ? (std::pow(xx, 1.0f / g) - b) / a : xx / c, x);
+}
+
+static float absResponse(float x, float g, float a, float b, float c, float d) {
+ float xx = std::abs(x);
+ return std::copysign(xx >= d ? std::pow(a * xx + b, g) : c * xx, x);
+}
+
+static float safePow(float x, float e) {
+ return powf(x < 0.0f ? 0.0f : x, e);
+}
+
+static ColorSpace::transfer_function toOETF(const ColorSpace::TransferParameters& parameters) {
+ if (parameters.e == 0.0f && parameters.f == 0.0f) {
+ return std::bind(rcpResponse, _1, parameters);
+ }
+ return std::bind(rcpFullResponse, _1, parameters);
+}
+
+static ColorSpace::transfer_function toEOTF( const ColorSpace::TransferParameters& parameters) {
+ if (parameters.e == 0.0f && parameters.f == 0.0f) {
+ return std::bind(response, _1, parameters);
+ }
+ return std::bind(fullResponse, _1, parameters);
+}
+
+static ColorSpace::transfer_function toOETF(float gamma) {
+ if (gamma == 1.0f) {
+ return linearResponse;
+ }
+ return std::bind(safePow, _1, 1.0f / gamma);
+}
+
+static ColorSpace::transfer_function toEOTF(float gamma) {
+ if (gamma == 1.0f) {
+ return linearResponse;
+ }
+ return std::bind(safePow, _1, gamma);
+}
+
+static constexpr std::array<float2, 3> computePrimaries(const mat3& rgbToXYZ) {
+ float3 r(rgbToXYZ * float3{1, 0, 0});
+ float3 g(rgbToXYZ * float3{0, 1, 0});
+ float3 b(rgbToXYZ * float3{0, 0, 1});
+
+ return {{r.xy / dot(r, float3{1}),
+ g.xy / dot(g, float3{1}),
+ b.xy / dot(b, float3{1})}};
+}
+
+static constexpr float2 computeWhitePoint(const mat3& rgbToXYZ) {
+ float3 w(rgbToXYZ * float3{1});
+ return w.xy / dot(w, float3{1});
+}
+
+ColorSpace::ColorSpace(
+ const std::string& name,
+ const mat3& rgbToXYZ,
+ transfer_function OETF,
+ transfer_function EOTF,
+ clamping_function clamper) noexcept
+ : mName(name)
+ , mRGBtoXYZ(rgbToXYZ)
+ , mXYZtoRGB(inverse(rgbToXYZ))
+ , mOETF(std::move(OETF))
+ , mEOTF(std::move(EOTF))
+ , mClamper(std::move(clamper))
+ , mPrimaries(computePrimaries(rgbToXYZ))
+ , mWhitePoint(computeWhitePoint(rgbToXYZ)) {
+}
+
+ColorSpace::ColorSpace(
+ const std::string& name,
+ const mat3& rgbToXYZ,
+ const TransferParameters parameters,
+ clamping_function clamper) noexcept
+ : mName(name)
+ , mRGBtoXYZ(rgbToXYZ)
+ , mXYZtoRGB(inverse(rgbToXYZ))
+ , mParameters(parameters)
+ , mOETF(toOETF(mParameters))
+ , mEOTF(toEOTF(mParameters))
+ , mClamper(std::move(clamper))
+ , mPrimaries(computePrimaries(rgbToXYZ))
+ , mWhitePoint(computeWhitePoint(rgbToXYZ)) {
+}
+
+ColorSpace::ColorSpace(
+ const std::string& name,
+ const mat3& rgbToXYZ,
+ float gamma,
+ clamping_function clamper) noexcept
+ : mName(name)
+ , mRGBtoXYZ(rgbToXYZ)
+ , mXYZtoRGB(inverse(rgbToXYZ))
+ , mParameters({gamma, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f})
+ , mOETF(toOETF(gamma))
+ , mEOTF(toEOTF(gamma))
+ , mClamper(std::move(clamper))
+ , mPrimaries(computePrimaries(rgbToXYZ))
+ , mWhitePoint(computeWhitePoint(rgbToXYZ)) {
+}
+
+ColorSpace::ColorSpace(
+ const std::string& name,
+ const std::array<float2, 3>& primaries,
+ const float2& whitePoint,
+ transfer_function OETF,
+ transfer_function EOTF,
+ clamping_function clamper) noexcept
+ : mName(name)
+ , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint))
+ , mXYZtoRGB(inverse(mRGBtoXYZ))
+ , mOETF(std::move(OETF))
+ , mEOTF(std::move(EOTF))
+ , mClamper(std::move(clamper))
+ , mPrimaries(primaries)
+ , mWhitePoint(whitePoint) {
+}
+
+ColorSpace::ColorSpace(
+ const std::string& name,
+ const std::array<float2, 3>& primaries,
+ const float2& whitePoint,
+ const TransferParameters parameters,
+ clamping_function clamper) noexcept
+ : mName(name)
+ , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint))
+ , mXYZtoRGB(inverse(mRGBtoXYZ))
+ , mParameters(parameters)
+ , mOETF(toOETF(mParameters))
+ , mEOTF(toEOTF(mParameters))
+ , mClamper(std::move(clamper))
+ , mPrimaries(primaries)
+ , mWhitePoint(whitePoint) {
+}
+
+ColorSpace::ColorSpace(
+ const std::string& name,
+ const std::array<float2, 3>& primaries,
+ const float2& whitePoint,
+ float gamma,
+ clamping_function clamper) noexcept
+ : mName(name)
+ , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint))
+ , mXYZtoRGB(inverse(mRGBtoXYZ))
+ , mParameters({gamma, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f})
+ , mOETF(toOETF(gamma))
+ , mEOTF(toEOTF(gamma))
+ , mClamper(std::move(clamper))
+ , mPrimaries(primaries)
+ , mWhitePoint(whitePoint) {
+}
+
+constexpr mat3 ColorSpace::computeXYZMatrix(
+ const std::array<float2, 3>& primaries, const float2& whitePoint) {
+ const float2& R = primaries[0];
+ const float2& G = primaries[1];
+ const float2& B = primaries[2];
+ const float2& W = whitePoint;
+
+ float oneRxRy = (1 - R.x) / R.y;
+ float oneGxGy = (1 - G.x) / G.y;
+ float oneBxBy = (1 - B.x) / B.y;
+ float oneWxWy = (1 - W.x) / W.y;
+
+ float RxRy = R.x / R.y;
+ float GxGy = G.x / G.y;
+ float BxBy = B.x / B.y;
+ float WxWy = W.x / W.y;
+
+ float BY =
+ ((oneWxWy - oneRxRy) * (GxGy - RxRy) - (WxWy - RxRy) * (oneGxGy - oneRxRy)) /
+ ((oneBxBy - oneRxRy) * (GxGy - RxRy) - (BxBy - RxRy) * (oneGxGy - oneRxRy));
+ float GY = (WxWy - RxRy - BY * (BxBy - RxRy)) / (GxGy - RxRy);
+ float RY = 1 - GY - BY;
+
+ float RYRy = RY / R.y;
+ float GYGy = GY / G.y;
+ float BYBy = BY / B.y;
+
+ return {
+ float3{RYRy * R.x, RY, RYRy * (1 - R.x - R.y)},
+ float3{GYGy * G.x, GY, GYGy * (1 - G.x - G.y)},
+ float3{BYBy * B.x, BY, BYBy * (1 - B.x - B.y)}
+ };
+}
+
+const ColorSpace ColorSpace::sRGB() {
+ return {
+ "sRGB IEC61966-2.1",
+ {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
+ {0.3127f, 0.3290f},
+ {2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f, 0.0f, 0.0f}
+ };
+}
+
+const ColorSpace ColorSpace::linearSRGB() {
+ return {
+ "sRGB IEC61966-2.1 (Linear)",
+ {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
+ {0.3127f, 0.3290f}
+ };
+}
+
+const ColorSpace ColorSpace::extendedSRGB() {
+ return {
+ "scRGB-nl IEC 61966-2-2:2003",
+ {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
+ {0.3127f, 0.3290f},
+ std::bind(absRcpResponse, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f),
+ std::bind(absResponse, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f),
+ std::bind(clamp<float>, _1, -0.799f, 2.399f)
+ };
+}
+
+const ColorSpace ColorSpace::linearExtendedSRGB() {
+ return {
+ "scRGB IEC 61966-2-2:2003",
+ {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
+ {0.3127f, 0.3290f},
+ 1.0f,
+ std::bind(clamp<float>, _1, -0.5f, 7.499f)
+ };
+}
+
+const ColorSpace ColorSpace::NTSC() {
+ return {
+ "NTSC (1953)",
+ {{float2{0.67f, 0.33f}, {0.21f, 0.71f}, {0.14f, 0.08f}}},
+ {0.310f, 0.316f},
+ {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f}
+ };
+}
+
+const ColorSpace ColorSpace::BT709() {
+ return {
+ "Rec. ITU-R BT.709-5",
+ {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
+ {0.3127f, 0.3290f},
+ {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f}
+ };
+}
+
+const ColorSpace ColorSpace::BT2020() {
+ return {
+ "Rec. ITU-R BT.2020-1",
+ {{float2{0.708f, 0.292f}, {0.170f, 0.797f}, {0.131f, 0.046f}}},
+ {0.3127f, 0.3290f},
+ {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f}
+ };
+}
+
+const ColorSpace ColorSpace::AdobeRGB() {
+ return {
+ "Adobe RGB (1998)",
+ {{float2{0.64f, 0.33f}, {0.21f, 0.71f}, {0.15f, 0.06f}}},
+ {0.3127f, 0.3290f},
+ 2.2f
+ };
+}
+
+const ColorSpace ColorSpace::ProPhotoRGB() {
+ return {
+ "ROMM RGB ISO 22028-2:2013",
+ {{float2{0.7347f, 0.2653f}, {0.1596f, 0.8404f}, {0.0366f, 0.0001f}}},
+ {0.34567f, 0.35850f},
+ {1.8f, 1.0f, 0.0f, 1 / 16.0f, 0.031248f, 0.0f, 0.0f}
+ };
+}
+
+const ColorSpace ColorSpace::DisplayP3() {
+ return {
+ "Display P3",
+ {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}},
+ {0.3127f, 0.3290f},
+ {2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.039f, 0.0f, 0.0f}
+ };
+}
+
+const ColorSpace ColorSpace::DCIP3() {
+ return {
+ "SMPTE RP 431-2-2007 DCI (P3)",
+ {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}},
+ {0.314f, 0.351f},
+ 2.6f
+ };
+}
+
+const ColorSpace ColorSpace::ACES() {
+ return {
+ "SMPTE ST 2065-1:2012 ACES",
+ {{float2{0.73470f, 0.26530f}, {0.0f, 1.0f}, {0.00010f, -0.0770f}}},
+ {0.32168f, 0.33767f},
+ 1.0f,
+ std::bind(clamp<float>, _1, -65504.0f, 65504.0f)
+ };
+}
+
+const ColorSpace ColorSpace::ACEScg() {
+ return {
+ "Academy S-2014-004 ACEScg",
+ {{float2{0.713f, 0.293f}, {0.165f, 0.830f}, {0.128f, 0.044f}}},
+ {0.32168f, 0.33767f},
+ 1.0f,
+ std::bind(clamp<float>, _1, -65504.0f, 65504.0f)
+ };
+}
+
+std::unique_ptr<float3[]> ColorSpace::createLUT(uint32_t size, const ColorSpace& src,
+ const ColorSpace& dst) {
+ size = clamp(size, 2u, 256u);
+ float m = 1.0f / float(size - 1);
+
+ std::unique_ptr<float3[]> lut(new float3[size * size * size]);
+ float3* data = lut.get();
+
+ ColorSpaceConnector connector(src, dst);
+
+ for (uint32_t z = 0; z < size; z++) {
+ for (int32_t y = int32_t(size - 1); y >= 0; y--) {
+ for (uint32_t x = 0; x < size; x++) {
+ *data++ = connector.transform({x * m, y * m, z * m});
+ }
+ }
+ }
+
+ return lut;
+}
+
+static const float2 ILLUMINANT_D50_XY = {0.34567f, 0.35850f};
+static const float3 ILLUMINANT_D50_XYZ = {0.964212f, 1.0f, 0.825188f};
+static const mat3 BRADFORD = mat3{
+ float3{ 0.8951f, -0.7502f, 0.0389f},
+ float3{ 0.2664f, 1.7135f, -0.0685f},
+ float3{-0.1614f, 0.0367f, 1.0296f}
+};
+
+static mat3 adaptation(const mat3& matrix, const float3& srcWhitePoint, const float3& dstWhitePoint) {
+ float3 srcLMS = matrix * srcWhitePoint;
+ float3 dstLMS = matrix * dstWhitePoint;
+ return inverse(matrix) * mat3{dstLMS / srcLMS} * matrix;
+}
+
+ColorSpaceConnector::ColorSpaceConnector(
+ const ColorSpace& src,
+ const ColorSpace& dst) noexcept
+ : mSource(src)
+ , mDestination(dst) {
+
+ if (all(lessThan(abs(src.getWhitePoint() - dst.getWhitePoint()), float2{1e-3f}))) {
+ mTransform = dst.getXYZtoRGB() * src.getRGBtoXYZ();
+ } else {
+ mat3 rgbToXYZ(src.getRGBtoXYZ());
+ mat3 xyzToRGB(dst.getXYZtoRGB());
+
+ float3 srcXYZ = ColorSpace::XYZ(float3{src.getWhitePoint(), 1});
+ float3 dstXYZ = ColorSpace::XYZ(float3{dst.getWhitePoint(), 1});
+
+ if (any(greaterThan(abs(src.getWhitePoint() - ILLUMINANT_D50_XY), float2{1e-3f}))) {
+ rgbToXYZ = adaptation(BRADFORD, srcXYZ, ILLUMINANT_D50_XYZ) * src.getRGBtoXYZ();
+ }
+
+ if (any(greaterThan(abs(dst.getWhitePoint() - ILLUMINANT_D50_XY), float2{1e-3f}))) {
+ xyzToRGB = inverse(adaptation(BRADFORD, dstXYZ, ILLUMINANT_D50_XYZ) * dst.getRGBtoXYZ());
+ }
+
+ mTransform = xyzToRGB * rgbToXYZ;
+ }
+}
+
+}; // namespace android
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index e97055f0..2e2666e 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -94,6 +94,10 @@
<item type="id" name="top_roundess_animator_start_tag"/>
<item type="id" name="top_roundess_animator_end_tag"/>
+ <item type="id" name="keyguard_hun_animator_tag"/>
+ <item type="id" name="keyguard_hun_animator_start_tag"/>
+ <item type="id" name="keyguard_hun_animator_end_tag"/>
+
<!-- Accessibility actions for the notification menu -->
<item type="id" name="action_snooze_undo"/>
<item type="id" name="action_snooze_shorter"/>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
index 3f1ff33..4597b16 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
@@ -43,7 +43,6 @@
private static final String HEADS_UP_STATUS_BAR_VIEW_SUPER_PARCELABLE =
"heads_up_status_bar_view_super_parcelable";
private static final String FIRST_LAYOUT = "first_layout";
- private static final String PUBLIC_MODE = "public_mode";
private static final String VISIBILITY = "visibility";
private static final String ALPHA = "alpha";
private int mAbsoluteStartPadding;
@@ -54,7 +53,6 @@
private Rect mLayoutedIconRect = new Rect();
private int[] mTmpPosition = new int[2];
private boolean mFirstLayout = true;
- private boolean mPublicMode;
private int mMaxWidth;
private View mRootView;
private int mSysWinInset;
@@ -121,7 +119,6 @@
bundle.putParcelable(HEADS_UP_STATUS_BAR_VIEW_SUPER_PARCELABLE,
super.onSaveInstanceState());
bundle.putBoolean(FIRST_LAYOUT, mFirstLayout);
- bundle.putBoolean(PUBLIC_MODE, mPublicMode);
bundle.putInt(VISIBILITY, getVisibility());
bundle.putFloat(ALPHA, getAlpha());
@@ -139,7 +136,6 @@
Parcelable superState = bundle.getParcelable(HEADS_UP_STATUS_BAR_VIEW_SUPER_PARCELABLE);
super.onRestoreInstanceState(superState);
mFirstLayout = bundle.getBoolean(FIRST_LAYOUT, true);
- mPublicMode = bundle.getBoolean(PUBLIC_MODE, false);
if (bundle.containsKey(VISIBILITY)) {
setVisibility(bundle.getInt(VISIBILITY));
}
@@ -166,11 +162,13 @@
if (entry != null) {
mShowingEntry = entry;
CharSequence text = entry.headsUpStatusBarText;
- if (mPublicMode) {
+ if (entry.isSensitive()) {
text = entry.headsUpStatusBarTextPublic;
}
mTextView.setText(text);
- } else {
+ mShowingEntry.setOnSensitiveChangedListener(() -> setEntry(entry));
+ } else if (mShowingEntry != null){
+ mShowingEntry.setOnSensitiveChangedListener(null);
mShowingEntry = null;
}
}
@@ -273,10 +271,6 @@
mTextView.setTextColor(DarkIconDispatcher.getTint(area, this, tint));
}
- public void setPublicMode(boolean publicMode) {
- mPublicMode = publicMode;
- }
-
public void setOnDrawingRectChangedListener(Runnable onDrawingRectChangedListener) {
mOnDrawingRectChangedListener = onDrawingRectChangedListener;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 0b3c6a5..787cc97 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -161,7 +161,7 @@
boolean deviceSensitive = devicePublic
&& !mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(
currentUserId);
- ent.getRow().setSensitive(sensitive, deviceSensitive);
+ ent.setSensitive(sensitive, deviceSensitive);
ent.getRow().setNeedsRedaction(needsRedaction);
if (mGroupManager.isChildInGroupWithSummary(ent.notification)) {
NotificationEntry summary = mGroupManager.getGroupSummary(ent.notification);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index bdc4d2a..986486a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -36,9 +36,12 @@
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
+import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.ShadeController
+import com.android.systemui.statusbar.policy.HeadsUpManager
import javax.inject.Inject
import javax.inject.Singleton
@@ -51,7 +54,9 @@
class PulseExpansionHandler @Inject
constructor(context: Context,
private val wakeUpCoordinator: NotificationWakeUpCoordinator,
- private val bypassController: KeyguardBypassController) : Gefingerpoken {
+ private val bypassController: KeyguardBypassController,
+ private val headsUpManager: HeadsUpManagerPhone,
+ private val roundnessManager: NotificationRoundnessManager) : Gefingerpoken {
companion object {
private val RUBBERBAND_FACTOR_STATIC = 0.25f
private val SPRING_BACK_ANIMATION_LENGTH_MS = 375
@@ -67,9 +72,20 @@
val changed = field != value
field = value
bypassController.isPulseExpanding = value
- if (changed && !value && !leavingLockscreen) {
- bypassController.maybePerformPendingUnlock()
- pulseExpandAbortListener?.run()
+ if (changed) {
+ if (value) {
+ val topEntry = headsUpManager.topEntry
+ topEntry?.let {
+ roundnessManager.setTrackingHeadsUp(it.row)
+ }
+ } else {
+ roundnessManager.setTrackingHeadsUp(null)
+ if (!leavingLockscreen) {
+ bypassController.maybePerformPendingUnlock()
+ pulseExpandAbortListener?.run()
+ }
+ }
+ headsUpManager.unpinAll(true /* userUnPinned */)
}
}
var leavingLockscreen: Boolean = false
@@ -231,7 +247,7 @@
}
private fun captureStartingChild(x: Float, y: Float) {
- if (mStartingChild == null) {
+ if (mStartingChild == null && !bypassController.bypassEnabled) {
mStartingChild = findView(x, y)
if (mStartingChild != null) {
setUserLocked(mStartingChild!!, true)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index 95af9fd..6a3816c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -38,7 +38,7 @@
class NotificationWakeUpCoordinator @Inject constructor(
private val mContext: Context,
private val mHeadsUpManagerPhone: HeadsUpManagerPhone,
- private val mStatusBarStateController: StatusBarStateController,
+ private val statusBarStateController: StatusBarStateController,
private val bypassController: KeyguardBypassController)
: OnHeadsUpChangedListener, StatusBarStateController.StateListener {
@@ -69,6 +69,8 @@
private val mDozeParameters: DozeParameters
private var pulseExpanding: Boolean = false
private val wakeUpListeners = arrayListOf<WakeUpListener>()
+ private var state: Int = StatusBarState.KEYGUARD
+
var fullyAwake: Boolean = false
var willWakeUp = false
@@ -111,14 +113,14 @@
if (bypassController.bypassEnabled) {
// We also allow pulsing on the lock screen!
canShow = canShow || (mWakingUp || willWakeUp || fullyAwake)
- && mStatusBarStateController.state == StatusBarState.KEYGUARD
+ && statusBarStateController.state == StatusBarState.KEYGUARD
}
return canShow
}
init {
mHeadsUpManagerPhone.addListener(this)
- mStatusBarStateController.addCallback(this)
+ statusBarStateController.addCallback(this)
mDozeParameters = DozeParameters.getInstance(mContext)
}
@@ -217,13 +219,21 @@
override fun onStateChanged(newState: Int) {
updateDozeAmountIfBypass();
+ if (bypassController.bypassEnabled &&
+ newState == StatusBarState.KEYGUARD && state == StatusBarState.SHADE_LOCKED
+ && (!statusBarStateController.isDozing || shouldAnimateVisibility())) {
+ // We're leaving shade locked. Let's animate the notifications away
+ setNotificationsVisible(visible = true, increaseSpeed = false, animate = false)
+ setNotificationsVisible(visible = false, increaseSpeed = false, animate = true)
+ }
+ this.state = newState
}
private fun updateDozeAmountIfBypass(): Boolean {
if (bypassController.bypassEnabled) {
var amount = 1.0f;
- if (mStatusBarStateController.state == StatusBarState.SHADE
- || mStatusBarStateController.state == StatusBarState.SHADE_LOCKED) {
+ if (statusBarStateController.state == StatusBarState.SHADE
+ || statusBarStateController.state == StatusBarState.SHADE_LOCKED) {
amount = 0.0f;
}
setDozeAmount(amount, amount)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
index aacb22d..fe56552 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
@@ -115,7 +115,20 @@
view.setTag(animationEndTag, newEndValue);
}
- public static <T extends View> boolean isAnimating(T view, AnimatableProperty property) {
- return view.getTag(property.getAnimatorTag()) != null;
+ public static <T extends View> void applyImmediately(T view, AnimatableProperty property,
+ float newValue) {
+ cancelAnimation(view, property);
+ property.getProperty().set(view, newValue);
+ }
+
+ public static void cancelAnimation(View view, AnimatableProperty property) {
+ ValueAnimator animator = (ValueAnimator) view.getTag(property.getAnimatorTag());
+ if (animator != null) {
+ animator.cancel();
+ }
+ }
+
+ public static boolean isAnimating(View view, AnimatableProperty property) {
+ return view.getTag(property.getAnimatorTag()) != null;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 9184dec..9db715d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -173,6 +173,8 @@
* the lock screen/status bar and in the top section in the shade.
*/
private boolean mHighPriority;
+ private boolean mSensitive = true;
+ private Runnable mOnSensitiveChangedListener;
public NotificationEntry(StatusBarNotification n) {
this(n, null);
@@ -859,6 +861,30 @@
return Objects.equals(n.category, category);
}
+ /**
+ * Set this notification to be sensitive.
+ *
+ * @param sensitive true if the content of this notification is sensitive right now
+ * @param deviceSensitive true if the device in general is sensitive right now
+ */
+ public void setSensitive(boolean sensitive, boolean deviceSensitive) {
+ getRow().setSensitive(sensitive, deviceSensitive);
+ if (sensitive != mSensitive) {
+ mSensitive = sensitive;
+ if (mOnSensitiveChangedListener != null) {
+ mOnSensitiveChangedListener.run();
+ }
+ }
+ }
+
+ public boolean isSensitive() {
+ return mSensitive;
+ }
+
+ public void setOnSensitiveChangedListener(Runnable listener) {
+ mOnSensitiveChangedListener = listener;
+ }
+
/** Information about a suggestion that is being edited. */
public static class EditedSuggestionInfo {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 6d9d5ec..dca152f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -1040,6 +1040,10 @@
return false;
}
+ public int getHeadsUpHeightWithoutHeader() {
+ return getHeight();
+ }
+
public interface OnActivatedListener {
void onActivated(ActivatableNotificationView view);
void onActivationReset(ActivatableNotificationView view);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 0ca8814..ae534d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -2637,7 +2637,7 @@
private int getHeadsUpHeight() {
- return getShowingLayout().getHeadsUpHeight();
+ return getShowingLayout().getHeadsUpHeight(false /* forceNoHeader */);
}
public boolean areGutsExposed() {
@@ -2775,6 +2775,17 @@
}
@Override
+ public int getHeadsUpHeightWithoutHeader() {
+ if (!canShowHeadsUp() || !mIsHeadsUp) {
+ return getCollapsedHeight();
+ }
+ if (mIsSummaryWithChildren && !shouldShowPublic()) {
+ return mChildrenContainer.getCollapsedHeightWithoutHeader();
+ }
+ return getShowingLayout().getHeadsUpHeight(true /* forceNoHeader */);
+ }
+
+ @Override
public void setClipTopAmount(int clipTopAmount) {
super.setClipTopAmount(clipTopAmount);
for (NotificationContentView l : mLayouts) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index d6b7cf3..90f6324 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -713,11 +713,15 @@
}
private int getViewHeight(int visibleType) {
+ return getViewHeight(visibleType, false /* forceNoHeader */);
+ }
+
+ private int getViewHeight(int visibleType, boolean forceNoHeader) {
View view = getViewForVisibleType(visibleType);
int height = view.getHeight();
NotificationViewWrapper viewWrapper = getWrapperForView(view);
if (viewWrapper != null) {
- height += viewWrapper.getHeaderTranslation();
+ height += viewWrapper.getHeaderTranslation(forceNoHeader);
}
return height;
}
@@ -1748,14 +1752,15 @@
return getViewHeight(viewType) + getExtraRemoteInputHeight(mExpandedRemoteInput);
}
- public int getHeadsUpHeight() {
+ public int getHeadsUpHeight(boolean forceNoHeader) {
int viewType = VISIBLE_TYPE_HEADSUP;
if (mHeadsUpChild == null) {
viewType = VISIBLE_TYPE_CONTRACTED;
}
// The headsUp remote input quickly switches to the expanded one, so lets also include that
// one
- return getViewHeight(viewType) + getExtraRemoteInputHeight(mHeadsUpRemoteInput)
+ return getViewHeight(viewType, forceNoHeader)
+ + getExtraRemoteInputHeight(mHeadsUpRemoteInput)
+ getExtraRemoteInputHeight(mExpandedRemoteInput);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
index 7ebdb93..97d8443 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
@@ -51,7 +51,7 @@
*/
public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapper {
- private final int mTranslationForHeader;
+ private final int mFullHeaderTranslation;
protected ImageView mPicture;
private ProgressBar mProgressBar;
private TextView mTitle;
@@ -135,7 +135,7 @@
}
}, TRANSFORMING_VIEW_TEXT);
- mTranslationForHeader = ctx.getResources().getDimensionPixelSize(
+ mFullHeaderTranslation = ctx.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.notification_content_margin)
- ctx.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.notification_content_margin_top);
@@ -340,20 +340,20 @@
// We also need to compensate for any header translation, since we're always at the end.
mActionsContainer.setTranslationY(constrainedContentHeight - mView.getHeight()
- - getHeaderTranslation());
+ - getHeaderTranslation(false /* forceNoHeader */));
}
}
@Override
- public int getHeaderTranslation() {
- return (int) mHeaderTranslation;
+ public int getHeaderTranslation(boolean forceNoHeader) {
+ return forceNoHeader ? mFullHeaderTranslation : (int) mHeaderTranslation;
}
@Override
public void setHeaderVisibleAmount(float headerVisibleAmount) {
super.setHeaderVisibleAmount(headerVisibleAmount);
mNotificationHeader.setAlpha(headerVisibleAmount);
- mHeaderTranslation = (1.0f - headerVisibleAmount) * mTranslationForHeader;
+ mHeaderTranslation = (1.0f - headerVisibleAmount) * mFullHeaderTranslation;
mView.setTranslationY(mHeaderTranslation);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 3808702..47906a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -224,7 +224,7 @@
return null;
}
- public int getHeaderTranslation() {
+ public int getHeaderTranslation(boolean forceNoHeader) {
return 0;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 1242431..45f7b3a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -1067,6 +1067,11 @@
false /* likeHighPriority */);
}
+ public int getCollapsedHeightWithoutHeader() {
+ return getMinHeight(getMaxAllowedVisibleChildren(true /* forceCollapsed */),
+ false /* likeHighPriority */, 0);
+ }
+
/**
* Get the minimum Height for this group.
*
@@ -1074,10 +1079,22 @@
* @param likeHighPriority if the height should be calculated as if it were not low priority
*/
private int getMinHeight(int maxAllowedVisibleChildren, boolean likeHighPriority) {
+ return getMinHeight(maxAllowedVisibleChildren, likeHighPriority, mCurrentHeaderTranslation);
+ }
+
+ /**
+ * Get the minimum Height for this group.
+ *
+ * @param maxAllowedVisibleChildren the number of children that should be visible
+ * @param likeHighPriority if the height should be calculated as if it were not low priority
+ * @param headerTranslation the translation amount of the header
+ */
+ private int getMinHeight(int maxAllowedVisibleChildren, boolean likeHighPriority,
+ int headerTranslation) {
if (!likeHighPriority && showingAsLowPriority()) {
return mNotificationHeaderLowPriority.getHeight();
}
- int minExpandHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation;
+ int minExpandHeight = mNotificationHeaderMargin + headerTranslation;
int visibleChildren = 0;
boolean firstChild = true;
int childCount = mChildren.size();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
index d8ccd3a..4221846 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
@@ -18,10 +18,14 @@
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.NUM_SECTIONS;
+
+import android.util.MathUtils;
+
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import java.util.HashSet;
@@ -33,12 +37,13 @@
* A class that manages the roundness for notification views
*/
@Singleton
-class NotificationRoundnessManager implements OnHeadsUpChangedListener {
+public class NotificationRoundnessManager implements OnHeadsUpChangedListener {
private final ActivatableNotificationView[] mFirstInSectionViews;
private final ActivatableNotificationView[] mLastInSectionViews;
private final ActivatableNotificationView[] mTmpFirstInSectionViews;
private final ActivatableNotificationView[] mTmpLastInSectionViews;
+ private final KeyguardBypassController mBypassController;
private boolean mExpanded;
private HashSet<ExpandableView> mAnimatedChildren;
private Runnable mRoundingChangedCallback;
@@ -46,11 +51,12 @@
private float mAppearFraction;
@Inject
- NotificationRoundnessManager() {
+ NotificationRoundnessManager(KeyguardBypassController keyguardBypassController) {
mFirstInSectionViews = new ActivatableNotificationView[NUM_SECTIONS];
mLastInSectionViews = new ActivatableNotificationView[NUM_SECTIONS];
mTmpFirstInSectionViews = new ActivatableNotificationView[NUM_SECTIONS];
mTmpLastInSectionViews = new ActivatableNotificationView[NUM_SECTIONS];
+ mBypassController = keyguardBypassController;
}
@Override
@@ -124,18 +130,18 @@
if ((view.isPinned() || view.isHeadsUpAnimatingAway()) && !mExpanded) {
return 1.0f;
}
- if (view.showingPulsing()) {
- return 1.0f;
- }
if (isFirstInSection(view, true /* include first section */) && top) {
return 1.0f;
}
if (isLastInSection(view, true /* include last section */) && !top) {
return 1.0f;
}
- if (view == mTrackedHeadsUp && mAppearFraction <= 0.0f) {
+ if (view == mTrackedHeadsUp) {
// If we're pushing up on a headsup the appear fraction is < 0 and it needs to still be
// rounded.
+ return MathUtils.saturate(1.0f - mAppearFraction);
+ }
+ if (view.showingPulsing() && !mBypassController.getBypassEnabled()) {
return 1.0f;
}
return 0.0f;
@@ -232,6 +238,10 @@
}
public void setTrackingHeadsUp(ExpandableNotificationRow row) {
+ ExpandableNotificationRow previous = mTrackedHeadsUp;
mTrackedHeadsUp = row;
+ if (previous != null) {
+ updateView(previous, true /* animate */);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 44e5aef..097f92c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -466,12 +466,10 @@
private int mCornerRadius;
private int mSidePaddings;
private final Rect mBackgroundAnimationRect = new Rect();
- private int mAntiBurnInOffsetX;
private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>();
private int mHeadsUpInset;
private HeadsUpAppearanceController mHeadsUpAppearanceController;
private NotificationIconAreaController mIconAreaController;
- private float mHorizontalPanelTranslation;
private final NotificationLockscreenUserManager mLockscreenUserManager =
Dependency.get(NotificationLockscreenUserManager.class);
private final Rect mTmpRect = new Rect();
@@ -500,6 +498,8 @@
mNotificationGutsManager = Dependency.get(NotificationGutsManager.class);
private final NotificationSectionsManager mSectionsManager;
private boolean mAnimateBottomOnLayout;
+ private float mLastSentAppear;
+ private float mLastSentExpandedHeight;
@Inject
public NotificationStackScrollLayout(
@@ -561,13 +561,13 @@
res.getBoolean(R.bool.config_fadeNotificationsOnDismiss);
mRoundnessManager.setAnimatedChildren(mChildrenToAddAnimated);
mRoundnessManager.setOnRoundingChangedCallback(this::invalidate);
- addOnExpandedHeightListener(mRoundnessManager::setExpanded);
+ addOnExpandedHeightChangedListener(mRoundnessManager::setExpanded);
setOutlineProvider(mOutlineProvider);
// Blocking helper manager wants to know the expanded state, update as well.
NotificationBlockingHelperManager blockingHelperManager =
Dependency.get(NotificationBlockingHelperManager.class);
- addOnExpandedHeightListener((height, unused) -> {
+ addOnExpandedHeightChangedListener((height, unused) -> {
blockingHelperManager.setNotificationShadeExpanded(height);
});
@@ -634,7 +634,11 @@
public float getWakeUpHeight() {
ActivatableNotificationView firstChild = getFirstChildWithBackground();
if (firstChild != null) {
- return firstChild.getCollapsedHeight();
+ if (mKeyguardBypassController.getBypassEnabled()) {
+ return firstChild.getHeadsUpHeightWithoutHeader();
+ } else {
+ return firstChild.getCollapsedHeight();
+ }
}
return 0f;
}
@@ -1306,7 +1310,7 @@
stackHeight = (int) height;
}
} else {
- appearFraction = getAppearFraction(height);
+ appearFraction = calculateAppearFraction(height);
if (appearFraction >= 0) {
translationY = NotificationUtils.interpolate(getExpandTranslationStart(), 0,
appearFraction);
@@ -1329,9 +1333,26 @@
requestChildrenUpdate();
}
setStackTranslation(translationY);
- for (int i = 0; i < mExpandedHeightListeners.size(); i++) {
- BiConsumer<Float, Float> listener = mExpandedHeightListeners.get(i);
- listener.accept(mExpandedHeight, appearFraction);
+ notifyAppearChangedListeners();
+ }
+
+ private void notifyAppearChangedListeners() {
+ float appear;
+ float expandAmount;
+ if (mKeyguardBypassController.getBypassEnabled() && onKeyguard()) {
+ appear = calculateAppearFractionBypass();
+ expandAmount = getPulseHeight();
+ } else {
+ appear = MathUtils.saturate(calculateAppearFraction(mExpandedHeight));
+ expandAmount = mExpandedHeight;
+ }
+ if (appear != mLastSentAppear || expandAmount != mLastSentExpandedHeight) {
+ mLastSentAppear = appear;
+ mLastSentExpandedHeight = expandAmount;
+ for (int i = 0; i < mExpandedHeightListeners.size(); i++) {
+ BiConsumer<Float, Float> listener = mExpandedHeightListeners.get(i);
+ listener.accept(expandAmount, appear);
+ }
}
}
@@ -1453,7 +1474,7 @@
* @return the fraction of the appear animation that has been performed
*/
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
- public float getAppearFraction(float height) {
+ public float calculateAppearFraction(float height) {
float appearEndPosition = getAppearEndPosition();
float appearStartPosition = getAppearStartPosition();
return (height - appearStartPosition)
@@ -4707,17 +4728,6 @@
notifyHeightChangeListener(mShelf);
}
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- private void updatePanelTranslation() {
- setTranslationX(mHorizontalPanelTranslation + mAntiBurnInOffsetX * mInterpolatedHideAmount);
- }
-
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void setHorizontalPanelTranslation(float verticalPanelTranslation) {
- mHorizontalPanelTranslation = verticalPanelTranslation;
- updatePanelTranslation();
- }
-
/**
* Sets the current hide amount.
*
@@ -4747,7 +4757,6 @@
}
updateAlgorithmHeightAndPadding();
updateBackgroundDimming();
- updatePanelTranslation();
requestChildrenUpdate();
}
@@ -5241,7 +5250,7 @@
boolean publicMode = mLockscreenUserManager.isAnyProfilePublicMode();
if (mHeadsUpAppearanceController != null) {
- mHeadsUpAppearanceController.setPublicMode(publicMode);
+ mHeadsUpAppearanceController.onStateChanged();
}
SysuiStatusBarStateController state = (SysuiStatusBarStateController)
@@ -5297,12 +5306,6 @@
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void setAntiBurnInOffsetX(int antiBurnInOffsetX) {
- mAntiBurnInOffsetX = antiBurnInOffsetX;
- updatePanelTranslation();
- }
-
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s"
+ " alpha:%f scrollY:%d maxTopPadding:%d showShelfOnly=%s"
@@ -5360,13 +5363,13 @@
}
/**
- * Add a listener whenever the expanded height changes. The first value passed as an argument
- * is the expanded height and the second one is the appearFraction.
+ * Add a listener whenever the expanded height changes. The first value passed as an
+ * argument is the expanded height and the second one is the appearFraction.
*
* @param listener the listener to notify.
*/
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void addOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
+ public void addOnExpandedHeightChangedListener(BiConsumer<Float, Float> listener) {
mExpandedHeightListeners.add(listener);
}
@@ -5374,7 +5377,7 @@
* Stop a listener from listening to the expandedHeight.
*/
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void removeOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
+ public void removeOnExpandedHeightChangedListener(BiConsumer<Float, Float> listener) {
mExpandedHeightListeners.remove(listener);
}
@@ -5595,6 +5598,9 @@
*/
public float setPulseHeight(float height) {
mAmbientState.setPulseHeight(height);
+ if (mKeyguardBypassController.getBypassEnabled()) {
+ notifyAppearChangedListeners();
+ }
requestChildrenUpdate();
return Math.max(0, height - mAmbientState.getInnerHeight(true /* ignorePulseHeight */));
}
@@ -5654,6 +5660,16 @@
mAmbientState.setOnPulseHeightChangedListener(listener);
}
+ public float calculateAppearFractionBypass() {
+ float pulseHeight = getPulseHeight();
+ float wakeUpHeight = getWakeUpHeight();
+ float dragDownAmount = pulseHeight - wakeUpHeight;
+
+ // The total distance required to fully reveal the header
+ float totalDistance = getIntrinsicPadding();
+ return MathUtils.smoothStep(0, totalDistance, dragDownAmount);
+ }
+
/**
* A listener that is notified when the empty space below the notifications is clicked on
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 58f457e..d655b2f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -35,6 +35,7 @@
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
import com.android.systemui.statusbar.policy.EncryptionHelper;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
@@ -201,20 +202,21 @@
}
protected int adjustDisableFlags(int state) {
+ boolean headsUpVisible = mStatusBarComponent.headsUpShouldBeVisible();
+ if (headsUpVisible) {
+ state |= DISABLE_CLOCK;
+ }
+
if (!mKeyguardMonitor.isLaunchTransitionFadingAway()
&& !mKeyguardMonitor.isKeyguardFadingAway()
- && shouldHideNotificationIcons()) {
+ && shouldHideNotificationIcons()
+ && !(mStatusBarStateController.getState() == StatusBarState.KEYGUARD
+ && headsUpVisible)) {
state |= DISABLE_NOTIFICATION_ICONS;
state |= DISABLE_SYSTEM_INFO;
state |= DISABLE_CLOCK;
}
- // In landscape, the heads up show but shouldHideNotificationIcons() return false
- // because the visual icon is in notification icon area rather than heads up's space.
- // whether the notification icon show or not, clock should hide when heads up show.
- if (mStatusBarComponent.isHeadsUpShouldBeVisible()) {
- state |= DISABLE_CLOCK;
- }
if (mNetworkController != null && EncryptionHelper.IS_DATA_ENCRYPTED) {
if (mNetworkController.hasEmergencyCryptKeeperText()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 66903fa..46dd5e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.SysUiServiceProvider.getComponent;
+
import android.graphics.Point;
import android.graphics.Rect;
import android.view.DisplayCutout;
@@ -27,11 +29,16 @@
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.HeadsUpStatusBarView;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import java.util.function.BiConsumer;
@@ -56,13 +63,16 @@
private final Consumer<ExpandableNotificationRow>
mSetTrackingHeadsUp = this::setTrackingHeadsUp;
private final Runnable mUpdatePanelTranslation = this::updatePanelTranslation;
- private final BiConsumer<Float, Float> mSetExpandedHeight = this::setExpandedHeight;
+ private final BiConsumer<Float, Float> mSetExpandedHeight = this::setAppearFraction;
+ private final KeyguardBypassController mBypassController;
+ private final StatusBarStateController mStatusBarStateController;
+ private final CommandQueue mCommandQueue;
@VisibleForTesting
float mExpandedHeight;
@VisibleForTesting
boolean mIsExpanded;
@VisibleForTesting
- float mExpandFraction;
+ float mAppearFraction;
private ExpandableNotificationRow mTrackedChild;
private boolean mShown;
private final View.OnLayoutChangeListener mStackScrollLayoutChangeListener =
@@ -77,13 +87,17 @@
};
private boolean mAnimationsEnabled = true;
Point mPoint;
+ private KeyguardMonitor mKeyguardMonitor;
public HeadsUpAppearanceController(
NotificationIconAreaController notificationIconAreaController,
HeadsUpManagerPhone headsUpManager,
- View statusbarView) {
- this(notificationIconAreaController, headsUpManager,
+ View statusbarView,
+ SysuiStatusBarStateController statusBarStateController,
+ KeyguardBypassController keyguardBypassController) {
+ this(notificationIconAreaController, headsUpManager, statusBarStateController,
+ keyguardBypassController,
statusbarView.findViewById(R.id.heads_up_status_bar_view),
statusbarView.findViewById(R.id.notification_stack_scroller),
statusbarView.findViewById(R.id.notification_panel),
@@ -96,6 +110,8 @@
public HeadsUpAppearanceController(
NotificationIconAreaController notificationIconAreaController,
HeadsUpManagerPhone headsUpManager,
+ StatusBarStateController stateController,
+ KeyguardBypassController bypassController,
HeadsUpStatusBarView headsUpStatusBarView,
NotificationStackScrollLayout stackScroller,
NotificationPanelView panelView,
@@ -114,7 +130,7 @@
panelView.addTrackingHeadsUpListener(mSetTrackingHeadsUp);
panelView.addVerticalTranslationListener(mUpdatePanelTranslation);
panelView.setHeadsUpAppearanceController(this);
- mStackScroller.addOnExpandedHeightListener(mSetExpandedHeight);
+ mStackScroller.addOnExpandedHeightChangedListener(mSetExpandedHeight);
mStackScroller.addOnLayoutChangeListener(mStackScrollLayoutChangeListener);
mStackScroller.setHeadsUpAppearanceController(this);
mClockView = clockView;
@@ -135,6 +151,10 @@
mHeadsUpStatusBarView.removeOnLayoutChangeListener(this);
}
});
+ mBypassController = bypassController;
+ mStatusBarStateController = stateController;
+ mCommandQueue = getComponent(headsUpStatusBarView.getContext(), CommandQueue.class);
+ mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
}
@@ -144,7 +164,7 @@
mPanelView.removeTrackingHeadsUpListener(mSetTrackingHeadsUp);
mPanelView.removeVerticalTranslationListener(mUpdatePanelTranslation);
mPanelView.setHeadsUpAppearanceController(null);
- mStackScroller.removeOnExpandedHeightListener(mSetExpandedHeight);
+ mStackScroller.removeOnExpandedHeightChangedListener(mSetExpandedHeight);
mStackScroller.removeOnLayoutChangeListener(mStackScrollLayoutChangeListener);
mDarkIconDispatcher.removeDarkReceiver(this);
}
@@ -219,7 +239,7 @@
private void updateTopEntry() {
NotificationEntry newEntry = null;
- if (!mIsExpanded && mHeadsUpManager.hasPinnedHeadsUp()) {
+ if (shouldBeVisible()) {
newEntry = mHeadsUpManager.getTopEntry();
}
NotificationEntry previousEntry = mHeadsUpStatusBarView.getShowingEntry();
@@ -342,7 +362,13 @@
* @return if the heads up status bar view should be shown
*/
public boolean shouldBeVisible() {
- return !mIsExpanded && mHeadsUpManager.hasPinnedHeadsUp();
+ boolean canShow = !mIsExpanded;
+ if (mBypassController.getBypassEnabled() &&
+ (mStatusBarStateController.getState() == StatusBarState.KEYGUARD
+ || mKeyguardMonitor.isKeyguardGoingAway())) {
+ canShow = true;
+ }
+ return canShow && mHeadsUpManager.hasPinnedHeadsUp();
}
@Override
@@ -351,12 +377,24 @@
updateHeader(entry);
}
- public void setExpandedHeight(float expandedHeight, float appearFraction) {
- boolean changedHeight = expandedHeight != mExpandedHeight;
+ @Override
+ public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) {
+ if (mStatusBarStateController.getState() != StatusBarState.SHADE) {
+ // Show the status bar icons when the pinned mode changes
+ mCommandQueue.recomputeDisableFlags(
+ mHeadsUpStatusBarView.getContext().getDisplayId(), false);
+ }
+ }
+
+ public void setAppearFraction(float expandedHeight, float appearFraction) {
+ boolean changed = expandedHeight != mExpandedHeight;
mExpandedHeight = expandedHeight;
- mExpandFraction = appearFraction;
+ mAppearFraction = appearFraction;
boolean isExpanded = expandedHeight > 0;
- if (changedHeight) {
+ // We only notify if the expandedHeight changed and not on the appearFraction, since
+ // otherwise we may run into an infinite loop where the panel and this are constantly
+ // updating themselves over just a small fraction
+ if (changed) {
updateHeadsUpHeaders();
}
if (isExpanded != mIsExpanded) {
@@ -389,8 +427,9 @@
public void updateHeader(NotificationEntry entry) {
ExpandableNotificationRow row = entry.getRow();
float headerVisibleAmount = 1.0f;
- if (row.isPinned() || row.isHeadsUpAnimatingAway() || row == mTrackedChild) {
- headerVisibleAmount = mExpandFraction;
+ if (row.isPinned() || row.isHeadsUpAnimatingAway() || row == mTrackedChild
+ || row.showingPulsing()) {
+ headerVisibleAmount = mAppearFraction;
}
row.setHeaderVisibleAmount(headerVisibleAmount);
}
@@ -400,8 +439,7 @@
mHeadsUpStatusBarView.onDarkChanged(area, darkIntensity, tint);
}
- public void setPublicMode(boolean publicMode) {
- mHeadsUpStatusBarView.setPublicMode(publicMode);
+ public void onStateChanged() {
updateTopEntry();
}
@@ -410,7 +448,7 @@
mTrackedChild = oldController.mTrackedChild;
mExpandedHeight = oldController.mExpandedHeight;
mIsExpanded = oldController.mIsExpanded;
- mExpandFraction = oldController.mExpandFraction;
+ mAppearFraction = oldController.mAppearFraction;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index bec655c..ade855e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -66,6 +66,7 @@
@VisibleForTesting
final int mExtensionTime;
private final StatusBarStateController mStatusBarStateController;
+ private final KeyguardBypassController mBypassController;
private View mStatusBarWindowView;
private NotificationGroupManager mGroupManager;
private VisualStabilityManager mVisualStabilityManager;
@@ -113,7 +114,8 @@
@Inject
public HeadsUpManagerPhone(@NonNull final Context context,
- StatusBarStateController statusBarStateController) {
+ StatusBarStateController statusBarStateController,
+ KeyguardBypassController bypassController) {
super(context);
Resources resources = mContext.getResources();
mAutoDismissNotificationDecayDozing = resources.getInteger(
@@ -121,6 +123,7 @@
mExtensionTime = resources.getInteger(R.integer.ambient_notification_extension_time);
mStatusBarStateController = statusBarStateController;
mStatusBarStateController.addCallback(this);
+ mBypassController = bypassController;
initResources();
}
@@ -231,6 +234,17 @@
mStatusBarState = newState;
}
+ @Override
+ public void onDozingChanged(boolean isDozing) {
+ if (!isDozing) {
+ // Let's make sure all huns we got while dozing time out within the normal timeout
+ // duration. Otherwise they could get stuck for a very long time
+ for (AlertEntry entry : mAlertEntries.values()) {
+ entry.updateEntry(true /* updatePostTime */);
+ }
+ }
+ }
+
/**
* Set that we are exiting the headsUp pinned mode, but some notifications might still be
* animating out. This is used to keep the touchable regions in a sane state.
@@ -412,8 +426,11 @@
@Override
protected boolean shouldHeadsUpBecomePinned(NotificationEntry entry) {
- return mStatusBarState != StatusBarState.KEYGUARD && !mIsExpanded
- || super.shouldHeadsUpBecomePinned(entry);
+ boolean pin = mStatusBarState == StatusBarState.SHADE && !mIsExpanded;
+ if (mBypassController.getBypassEnabled()) {
+ pin |= mStatusBarState == StatusBarState.KEYGUARD;
+ }
+ return pin || super.shouldHeadsUpBecomePinned(entry);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 7b1d1c6..0bbfbef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -435,18 +435,6 @@
}
@Override
- public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) {
- }
-
- @Override
- public void onHeadsUpPinned(NotificationEntry entry) {
- }
-
- @Override
- public void onHeadsUpUnPinned(NotificationEntry entry) {
- }
-
- @Override
public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
onAlertStateChanged(entry, isHeadsUp);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 6f9a6ec..cd97722 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -13,7 +13,6 @@
import androidx.annotation.NonNull;
import androidx.collection.ArrayMap;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.ContrastColorUtil;
import com.android.settingslib.Utils;
@@ -24,7 +23,6 @@
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CrossFadeHelper;
-import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarIconView;
@@ -242,11 +240,15 @@
protected boolean shouldShowNotificationIcon(NotificationEntry entry,
boolean showAmbient, boolean showLowPriority, boolean hideDismissed,
boolean hideRepliedMessages, boolean hideCurrentMedia, boolean hideCenteredIcon,
- boolean hidePulsing) {
+ boolean hidePulsing, boolean onlyShowCenteredIcon) {
- final boolean isCenteredNotificationIcon = entry.centeredIcon != null
+ final boolean isCenteredNotificationIcon = mCenteredIconView != null
+ && entry.centeredIcon != null
&& Objects.equals(entry.centeredIcon, mCenteredIconView);
- if (hideCenteredIcon == isCenteredNotificationIcon) {
+ if (onlyShowCenteredIcon) {
+ return isCenteredNotificationIcon;
+ }
+ if (hideCenteredIcon && isCenteredNotificationIcon) {
return false;
}
if (mEntryManager.getNotificationData().isAmbient(entry.key) && !showAmbient) {
@@ -299,8 +301,9 @@
false /* hideDismissed */,
false /* hideRepliedMessages */,
false /* hideCurrentMedia */,
- true /* hide centered icon */,
- false /* hidePulsing */);
+ false /* hide centered icon */,
+ false /* hidePulsing */,
+ false /* onlyShowCenteredIcon */);
}
public void updateStatusBarIcons() {
@@ -311,7 +314,8 @@
true /* hideRepliedMessages */,
false /* hideCurrentMedia */,
true /* hide centered icon */,
- false /* hidePulsing */);
+ false /* hidePulsing */,
+ false /* onlyShowCenteredIcon */);
}
private void updateCenterIcon() {
@@ -322,7 +326,8 @@
false /* hideRepliedMessages */,
false /* hideCurrentMedia */,
false /* hide centered icon */,
- false /* hidePulsing */);
+ false /* hidePulsing */,
+ true/* onlyShowCenteredIcon */);
}
public void updateAodNotificationIcons() {
@@ -333,7 +338,8 @@
true /* hideRepliedMessages */,
true /* hideCurrentMedia */,
true /* hide centered icon */,
- mBypassController.getBypassEnabled() /* hidePulsing */);
+ mBypassController.getBypassEnabled() /* hidePulsing */,
+ false /* onlyShowCenteredIcon */);
}
/**
@@ -349,7 +355,7 @@
private void updateIconsForLayout(Function<NotificationEntry, StatusBarIconView> function,
NotificationIconContainer hostLayout, boolean showAmbient, boolean showLowPriority,
boolean hideDismissed, boolean hideRepliedMessages, boolean hideCurrentMedia,
- boolean hideCenteredIcon, boolean hidePulsing) {
+ boolean hideCenteredIcon, boolean hidePulsing, boolean onlyShowCenteredIcon) {
ArrayList<StatusBarIconView> toShow = new ArrayList<>(
mNotificationScrollLayout.getChildCount());
@@ -359,7 +365,8 @@
if (view instanceof ExpandableNotificationRow) {
NotificationEntry ent = ((ExpandableNotificationRow) view).getEntry();
if (shouldShowNotificationIcon(ent, showAmbient, showLowPriority, hideDismissed,
- hideRepliedMessages, hideCurrentMedia, hideCenteredIcon, hidePulsing)) {
+ hideRepliedMessages, hideCurrentMedia, hideCenteredIcon, hidePulsing,
+ onlyShowCenteredIcon)) {
StatusBarIconView iconView = function.apply(ent);
if (iconView != null) {
toShow.add(iconView);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 4acf7b2..a53ce9b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -320,7 +320,7 @@
if (child instanceof StatusBarIconView) {
boolean isReplacingIcon = isReplacingIcon(child);
final StatusBarIconView icon = (StatusBarIconView) child;
- if (mAnimationsEnabled && icon.getVisibleState() != StatusBarIconView.STATE_HIDDEN
+ if (areAnimationsEnabled(icon) && icon.getVisibleState() != StatusBarIconView.STATE_HIDDEN
&& child.getVisibility() == VISIBLE && isReplacingIcon) {
int animationStartIndex = findFirstViewIndexAfter(icon.getTranslationX());
if (mAddAnimationStartIndex < 0) {
@@ -331,7 +331,7 @@
}
if (!mChangingViewPositions) {
mIconStates.remove(child);
- if (mAnimationsEnabled && !isReplacingIcon) {
+ if (areAnimationsEnabled(icon) && !isReplacingIcon) {
addTransientView(icon, 0);
boolean isIsolatedIcon = child == mIsolatedIcon;
icon.setVisibleState(StatusBarIconView.STATE_HIDDEN, true /* animate */,
@@ -342,6 +342,10 @@
}
}
+ private boolean areAnimationsEnabled(StatusBarIconView icon) {
+ return mAnimationsEnabled || icon == mIsolatedIcon;
+ }
+
/**
* Finds the first view with a translation bigger then a given value
*/
@@ -695,7 +699,7 @@
StatusBarIconView icon = (StatusBarIconView) view;
boolean animate = false;
AnimationProperties animationProperties = null;
- boolean animationsAllowed = mAnimationsEnabled && !mDisallowNextAnimation
+ boolean animationsAllowed = areAnimationsEnabled(icon) && !mDisallowNextAnimation
&& !noAnimations;
if (animationsAllowed) {
if (justAdded || justReplaced) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 64a8ce0..32dc96d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -148,6 +148,15 @@
private static final AnimationProperties CLOCK_ANIMATION_PROPERTIES = new AnimationProperties()
.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ private static final AnimatableProperty KEYGUARD_HEADS_UP_SHOWING_AMOUNT
+ = AnimatableProperty.from("KEYGUARD_HEADS_UP_SHOWING_AMOUNT",
+ NotificationPanelView::setKeyguardHeadsUpShowingAmount,
+ NotificationPanelView::getKeyguardHeadsUpShowingAmount,
+ R.id.keyguard_hun_animator_tag,
+ R.id.keyguard_hun_animator_end_tag,
+ R.id.keyguard_hun_animator_start_tag);
+ private static final AnimationProperties KEYGUARD_HUN_PROPERTIES =
+ new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
private final InjectionInflationController mInjectionInflationController;
private final PowerManager mPowerManager;
@@ -356,6 +365,9 @@
private Runnable mOnReinflationListener;
private int mDarkIconSize;
private int mHeadsUpInset;
+ private boolean mHeadsUpPinnedMode;
+ private float mKeyguardHeadsUpShowingAmount = 0.0f;
+ private boolean mShowingKeyguardHeadsUp;
@Inject
public NotificationPanelView(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
@@ -741,7 +753,6 @@
stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded;
}
mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding);
- mNotificationStackScroller.setAntiBurnInOffsetX(mClockPositionResult.clockX);
mKeyguardBottomArea.setAntiBurnInOffsetX(mClockPositionResult.clockX);
mStackScrollerMeasuringPass++;
@@ -1415,6 +1426,7 @@
} else if (oldState == StatusBarState.SHADE_LOCKED
&& statusBarState == StatusBarState.KEYGUARD) {
animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ mNotificationStackScroller.resetScrollPosition();
// Only animate header if the header is visible. If not, it will partially animate out
// the top of QS
if (!mQsExpanded) {
@@ -1429,6 +1441,7 @@
}
}
}
+ updateKeyguardStatusBarForHeadsUp();
if (keyguardShowing) {
updateDozingVisibilities(false /* animate */);
}
@@ -1721,21 +1734,11 @@
} else {
int expandedPosition = mClockPositionResult.stackScrollerPadding;
return (int) MathUtils.lerp(collapsedPosition, expandedPosition,
- calculateHeaderAppearAmountBypass());
+ mNotificationStackScroller.calculateAppearFractionBypass());
}
}
- private float calculateHeaderAppearAmountBypass() {
- float pulseHeight = mNotificationStackScroller.getPulseHeight();
- float wakeUpHeight = mNotificationStackScroller.getWakeUpHeight();
- float dragDownAmount = pulseHeight - wakeUpHeight;
-
- // The total distance required to fully reveal the header
- float totalDistance = mClockPositionResult.stackScrollerPadding;
- return MathUtils.smoothStep(0, totalDistance, dragDownAmount);
- }
-
protected void requestScrollerTopPaddingUpdate(boolean animate) {
mNotificationStackScroller.updateTopPadding(calculateQsTopPadding(), animate);
if (mKeyguardShowing && mKeyguardBypassController.getBypassEnabled()) {
@@ -2029,7 +2032,8 @@
!mHeadsUpManager.hasPinnedHeadsUp()) {
alpha = getFadeoutAlpha();
}
- if (mBarState == StatusBarState.KEYGUARD && !mHintAnimationRunning) {
+ if (mBarState == StatusBarState.KEYGUARD && !mHintAnimationRunning
+ && !mKeyguardBypassController.getBypassEnabled()) {
alpha *= mClockPositionResult.clockAlpha;
}
mNotificationStackScroller.setAlpha(alpha);
@@ -2067,7 +2071,7 @@
if (mBarState == StatusBarState.KEYGUARD && !mKeyguardBypassController.getBypassEnabled()) {
return -mQs.getQsMinExpansionHeight();
}
- float appearAmount = mNotificationStackScroller.getAppearFraction(mExpandedHeight);
+ float appearAmount = mNotificationStackScroller.calculateAppearFraction(mExpandedHeight);
float startHeight = -mQsExpansionHeight;
if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()
&& mNotificationStackScroller.isPulseExpanding()) {
@@ -2077,7 +2081,7 @@
// again after the header has animated away
appearAmount = 0;
} else {
- appearAmount = calculateHeaderAppearAmountBypass();
+ appearAmount = mNotificationStackScroller.calculateAppearFractionBypass();
}
startHeight = -mQs.getQsMinExpansionHeight();
}
@@ -2118,6 +2122,7 @@
float alphaQsExpansion = 1 - Math.min(1, getQsExpansionFraction() * 2);
float newAlpha = Math.min(getKeyguardContentsAlpha(), alphaQsExpansion)
* mKeyguardStatusBarAnimateAlpha;
+ newAlpha *= 1.0f - mKeyguardHeadsUpShowingAmount;
mKeyguardStatusBar.setAlpha(newAlpha);
mKeyguardStatusBar.setVisibility(newAlpha != 0f && !mDozing ? VISIBLE : INVISIBLE);
}
@@ -2750,16 +2755,51 @@
mHeadsUpExistenceChangedRunnable);
}
updateGestureExclusionRect();
+ mHeadsUpPinnedMode = inPinnedMode;
+ updateHeadsUpVisibility();
+ updateKeyguardStatusBarForHeadsUp();
+ }
+
+ private void updateKeyguardStatusBarForHeadsUp() {
+ boolean showingKeyguardHeadsUp = mKeyguardShowing
+ && mHeadsUpAppearanceController.shouldBeVisible();
+ if (mShowingKeyguardHeadsUp != showingKeyguardHeadsUp) {
+ mShowingKeyguardHeadsUp = showingKeyguardHeadsUp;
+ if (mKeyguardShowing) {
+ PropertyAnimator.setProperty(this, KEYGUARD_HEADS_UP_SHOWING_AMOUNT,
+ showingKeyguardHeadsUp ? 1.0f : 0.0f, KEYGUARD_HUN_PROPERTIES,
+ true /* animate */);
+ } else {
+ PropertyAnimator.applyImmediately(this, KEYGUARD_HEADS_UP_SHOWING_AMOUNT, 0.0f);
+ }
+ }
+ }
+
+ private void setKeyguardHeadsUpShowingAmount(float amount) {
+ mKeyguardHeadsUpShowingAmount = amount;
+ updateHeaderKeyguardAlpha();
+ }
+
+ private float getKeyguardHeadsUpShowingAmount() {
+ return mKeyguardHeadsUpShowingAmount;
}
public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
mHeadsUpAnimatingAway = headsUpAnimatingAway;
mNotificationStackScroller.setHeadsUpAnimatingAway(headsUpAnimatingAway);
+ updateHeadsUpVisibility();
+ }
+
+ private void updateHeadsUpVisibility() {
+ ((PhoneStatusBarView) mBar).setHeadsUpVisible(mHeadsUpAnimatingAway || mHeadsUpPinnedMode);
}
@Override
public void onHeadsUpPinned(NotificationEntry entry) {
- mNotificationStackScroller.generateHeadsUpAnimation(entry.getHeadsUpAnimationView(), true);
+ if (!isOnKeyguard()) {
+ mNotificationStackScroller.generateHeadsUpAnimation(entry.getHeadsUpAnimationView(),
+ true);
+ }
}
@Override
@@ -2768,7 +2808,7 @@
// When we're unpinning the notification via active edge they remain heads-upped,
// we need to make sure that an animation happens in this case, otherwise the notification
// will stick to the top without any interaction.
- if (isFullyCollapsed() && entry.isRowHeadsUp()) {
+ if (isFullyCollapsed() && entry.isRowHeadsUp() && !isOnKeyguard()) {
mNotificationStackScroller.generateHeadsUpAnimation(
entry.getHeadsUpAnimationView(), false);
entry.setHeadsUpIsVisible();
@@ -2835,7 +2875,7 @@
}
protected void setHorizontalPanelTranslation(float translation) {
- mNotificationStackScroller.setHorizontalPanelTranslation(translation);
+ mNotificationStackScroller.setTranslationX(translation);
mQsFrame.setTranslationX(translation);
int size = mVerticalTranslationListener.size();
for (int i = 0; i < size; i++) {
@@ -2891,7 +2931,8 @@
@Override
protected boolean isPanelVisibleBecauseOfHeadsUp() {
- return mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway;
+ return (mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway)
+ && mBarState == StatusBarState.SHADE;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index 65b0ecc..063d00b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -107,8 +107,12 @@
return mExpanded;
}
- private void updateVisibility() {
- mPanel.setVisibility(mExpanded || mBouncerShowing ? VISIBLE : INVISIBLE);
+ protected void updateVisibility() {
+ mPanel.setVisibility(shouldPanelBeVisible() ? VISIBLE : INVISIBLE);
+ }
+
+ protected boolean shouldPanelBeVisible() {
+ return mExpanded || mBouncerShowing;
}
public boolean panelEnabled() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 68eba50..660810f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -46,6 +46,7 @@
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import java.util.Objects;
@@ -82,6 +83,7 @@
* Draw this many pixels into the left/right side of the cutout to optimally use the space
*/
private int mCutoutSideNudge = 0;
+ private boolean mHeadsUpVisible;
public PhoneStatusBarView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -379,4 +381,14 @@
}
return null;
}
+
+ public void setHeadsUpVisible(boolean headsUpVisible) {
+ mHeadsUpVisible = headsUpVisible;
+ updateVisibility();
+ }
+
+ @Override
+ protected boolean shouldPanelBeVisible() {
+ return mHeadsUpVisible || super.shouldPanelBeVisible();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 213475f..2a14c51 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -847,7 +847,8 @@
mHeadsUpAppearanceController.destroy();
}
mHeadsUpAppearanceController = new HeadsUpAppearanceController(
- mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow);
+ mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow,
+ mStatusBarStateController, mKeyguardBypassController);
mHeadsUpAppearanceController.readFrom(oldController);
mStatusBarWindow.setStatusBarView(mStatusBarView);
updateAreThereNotifications();
@@ -1537,10 +1538,16 @@
});
}
} else {
- if (!mNotificationPanel.isFullyCollapsed() || mNotificationPanel.isTracking()) {
+ boolean bypassKeyguard = mKeyguardBypassController.getBypassEnabled()
+ && mState == StatusBarState.KEYGUARD;
+ if (!mNotificationPanel.isFullyCollapsed() || mNotificationPanel.isTracking()
+ || bypassKeyguard) {
// We are currently tracking or is open and the shade doesn't need to be kept
// open artificially.
mStatusBarWindowController.setHeadsUpShowing(false);
+ if (bypassKeyguard) {
+ mStatusBarWindowController.setForceStatusBarVisible(false);
+ }
} else {
// we need to keep the panel open artificially, let's wait until the animation
// is finished.
@@ -1557,15 +1564,6 @@
}
@Override
- public void onHeadsUpPinned(NotificationEntry entry) {
- dismissVolumeDialog();
- }
-
- @Override
- public void onHeadsUpUnPinned(NotificationEntry entry) {
- }
-
- @Override
public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
mEntryManager.updateNotifications();
if (isDozing()) {
@@ -1671,7 +1669,7 @@
}
}
- public boolean isHeadsUpShouldBeVisible() {
+ public boolean headsUpShouldBeVisible() {
return mHeadsUpAppearanceController.shouldBeVisible();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index de8dcfe..7063ddf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -48,6 +48,7 @@
import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
import com.android.systemui.statusbar.notification.row.NotificationContentInflaterTest;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -78,7 +79,8 @@
mInstrumentation = InstrumentationRegistry.getInstrumentation();
StatusBarStateController stateController = mock(StatusBarStateController.class);
mGroupManager = new NotificationGroupManager(stateController);
- mHeadsUpManager = new HeadsUpManagerPhone(mContext, stateController);
+ mHeadsUpManager = new HeadsUpManagerPhone(mContext, stateController,
+ mock(KeyguardBypassController.class));
mHeadsUpManager.setUp(null, mGroupManager, null, null);
mGroupManager.setHeadsUpManager(mHeadsUpManager);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
index 7e6335d..524ad85 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
@@ -35,6 +35,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
import org.junit.Assert;
import org.junit.Before;
@@ -57,11 +58,13 @@
private ExpandableNotificationRow mSecond;
@Mock
private StatusBarStateController mStatusBarStateController;
+ @Mock
+ private KeyguardBypassController mBypassController;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mRoundnessManager = new NotificationRoundnessManager();
+ mRoundnessManager = new NotificationRoundnessManager(mBypassController);
com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
NotificationTestHelper testHelper = new NotificationTestHelper(getContext());
mFirst = testHelper.createRow();
@@ -260,15 +263,15 @@
}
@Test
- public void testTrackingHeadsUpNotRoundedIfPushingDown() {
+ public void testTrackingHeadsUpPartiallyRoundedIfPushingDown() {
mRoundnessManager.setExpanded(1.0f /* expandedHeight */, 0.5f /* appearFraction */);
mRoundnessManager.setTrackingHeadsUp(mFirst);
mRoundnessManager.updateRoundedChildren(new NotificationSection[]{
createSection(mSecond, mSecond),
createSection(null, null)
});
- Assert.assertEquals(0.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
- Assert.assertEquals(0.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+ Assert.assertEquals(0.5f, mFirst.getCurrentBottomRoundness(), 0.0f);
+ Assert.assertEquals(0.5f, mFirst.getCurrentTopRoundness(), 0.0f);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 66255dd..92173cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -200,17 +200,6 @@
}
@Test
- public void testAntiBurnInOffset() {
- final int burnInOffset = 30;
- mStackScroller.setAntiBurnInOffsetX(burnInOffset);
- mStackScroller.setHideAmount(0.0f, 0.0f);
- Assert.assertEquals(0 /* expected */, mStackScroller.getTranslationX(), 0.01 /* delta */);
- mStackScroller.setHideAmount(1.0f, 1.0f);
- Assert.assertEquals(burnInOffset /* expected */, mStackScroller.getTranslationX(),
- 0.01 /* delta */);
- }
-
- @Test
public void updateEmptyView_dndSuppressing() {
when(mEmptyShadeView.willBeGone()).thenReturn(true);
when(mBar.areNotificationsHidden()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index 0479b4a..b45707e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -32,6 +32,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.HeadsUpStatusBarView;
import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -56,6 +57,8 @@
private HeadsUpStatusBarView mHeadsUpStatusBarView;
private HeadsUpManagerPhone mHeadsUpManager;
private View mOperatorNameView;
+ private StatusBarStateController mStatusbarStateController;
+ private KeyguardBypassController mBypassController;
@Before
public void setUp() throws Exception {
@@ -67,16 +70,20 @@
mock(TextView.class));
mHeadsUpManager = mock(HeadsUpManagerPhone.class);
mOperatorNameView = new View(mContext);
+ mStatusbarStateController = mock(StatusBarStateController.class);
+ mBypassController = mock(KeyguardBypassController.class);
mHeadsUpAppearanceController = new HeadsUpAppearanceController(
mock(NotificationIconAreaController.class),
mHeadsUpManager,
+ mStatusbarStateController,
+ mBypassController,
mHeadsUpStatusBarView,
mStackScroller,
mPanelView,
new View(mContext),
mOperatorNameView,
new View(mContext));
- mHeadsUpAppearanceController.setExpandedHeight(0.0f, 0.0f);
+ mHeadsUpAppearanceController.setAppearFraction(0.0f, 0.0f);
}
@Test
@@ -139,11 +146,13 @@
@Test
public void testHeaderReadFromOldController() {
- mHeadsUpAppearanceController.setExpandedHeight(1.0f, 1.0f);
+ mHeadsUpAppearanceController.setAppearFraction(1.0f, 1.0f);
HeadsUpAppearanceController newController = new HeadsUpAppearanceController(
mock(NotificationIconAreaController.class),
mHeadsUpManager,
+ mStatusbarStateController,
+ mBypassController,
mHeadsUpStatusBarView,
mStackScroller,
mPanelView,
@@ -154,8 +163,8 @@
Assert.assertEquals(mHeadsUpAppearanceController.mExpandedHeight,
newController.mExpandedHeight, 0.0f);
- Assert.assertEquals(mHeadsUpAppearanceController.mExpandFraction,
- newController.mExpandFraction, 0.0f);
+ Assert.assertEquals(mHeadsUpAppearanceController.mAppearFraction,
+ newController.mAppearFraction, 0.0f);
Assert.assertEquals(mHeadsUpAppearanceController.mIsExpanded,
newController.mIsExpanded);
}
@@ -172,7 +181,7 @@
verify(mPanelView).removeVerticalTranslationListener(any());
verify(mPanelView).removeTrackingHeadsUpListener(any());
verify(mPanelView).setHeadsUpAppearanceController(any());
- verify(mStackScroller).removeOnExpandedHeightListener(any());
+ verify(mStackScroller).removeOnExpandedHeightChangedListener(any());
verify(mStackScroller).removeOnLayoutChangeListener(any());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index a66345b..f8b9e68 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -57,14 +57,16 @@
@Mock private VisualStabilityManager mVSManager;
@Mock private StatusBar mBar;
@Mock private StatusBarStateController mStatusBarStateController;
+ @Mock private KeyguardBypassController mBypassController;
private boolean mLivesPastNormalTime;
private final class TestableHeadsUpManagerPhone extends HeadsUpManagerPhone {
TestableHeadsUpManagerPhone(Context context, View statusBarWindowView,
NotificationGroupManager groupManager, StatusBar bar,
VisualStabilityManager vsManager,
- StatusBarStateController statusBarStateController) {
- super(context, statusBarStateController);
+ StatusBarStateController statusBarStateController,
+ KeyguardBypassController keyguardBypassController) {
+ super(context, statusBarStateController, keyguardBypassController);
setUp(statusBarWindowView, groupManager, bar, vsManager);
mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME;
@@ -84,7 +86,7 @@
.thenReturn(TEST_AUTO_DISMISS_TIME);
when(mVSManager.isReorderingAllowed()).thenReturn(true);
mHeadsUpManager = new TestableHeadsUpManagerPhone(mContext, mStatusBarWindowView,
- mGroupManager, mBar, mVSManager, mStatusBarStateController);
+ mGroupManager, mBar, mVSManager, mStatusBarStateController, mBypassController);
super.setUp();
mHeadsUpManager.mHandler = mTestHandler;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index 4b5e1dd..d14b460 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -45,6 +45,7 @@
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -120,7 +121,7 @@
new StatusBarStateControllerImpl(),
bypassController);
PulseExpansionHandler expansionHandler = new PulseExpansionHandler(mContext, coordinator,
- bypassController);
+ bypassController, mHeadsUpManager, mock(NotificationRoundnessManager.class));
mNotificationPanelView = new TestableNotificationPanelView(coordinator, expansionHandler,
bypassController);
mNotificationPanelView.setHeadsUpManager(mHeadsUpManager);
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 1a6faec..e66e596 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1255,23 +1255,21 @@
}
}
- public void notifyPhysicalChannelConfiguration(List<PhysicalChannelConfig> configs) {
- notifyPhysicalChannelConfigurationForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
- configs);
- }
-
- public void notifyPhysicalChannelConfigurationForSubscriber(int subId,
+ /**
+ * Notify physical channel configuration according to subscripton ID and phone ID
+ */
+ public void notifyPhysicalChannelConfigurationForSubscriber(int phoneId, int subId,
List<PhysicalChannelConfig> configs) {
if (!checkNotifyPermission("notifyPhysicalChannelConfiguration()")) {
return;
}
if (VDBG) {
- log("notifyPhysicalChannelConfiguration: subId=" + subId + " configs=" + configs);
+ log("notifyPhysicalChannelConfiguration: subId=" + subId + " phoneId=" + phoneId
+ + " configs=" + configs);
}
synchronized (mRecords) {
- int phoneId = SubscriptionManager.getPhoneId(subId);
if (validatePhoneId(phoneId)) {
mPhysicalChannelConfigs.set(phoneId, configs);
for (Record r : mRecords) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4222fc1..e6379f8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7653,7 +7653,25 @@
holder = getContentProviderExternalUnchecked(name, null, callingUid,
"*getmimetype*", userId);
if (holder != null) {
- return holder.provider.getType(uri);
+ final IBinder providerConnection = holder.connection;
+ final ComponentName providerName = holder.info.getComponentName();
+ // Note: creating a new Runnable instead of using a lambda here since lambdas in
+ // java provide no guarantee that there will be a new instance returned every call.
+ // Hence, it's possible that a cached copy is returned and the ANR is executed on
+ // the incorrect provider.
+ final Runnable providerNotResponding = new Runnable() {
+ @Override
+ public void run() {
+ Log.w(TAG, "Provider " + providerName + " didn't return from getType().");
+ appNotRespondingViaProvider(providerConnection);
+ }
+ };
+ mHandler.postDelayed(providerNotResponding, 1000);
+ try {
+ return holder.provider.getType(uri);
+ } finally {
+ mHandler.removeCallbacks(providerNotResponding);
+ }
}
} catch (RemoteException e) {
Log.w(TAG, "Content provider dead retrieving " + uri, e);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index eff35c0c7..972f30b 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -127,6 +127,7 @@
import android.util.IntArray;
import android.util.Log;
import android.util.MathUtils;
+import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseIntArray;
import android.view.KeyEvent;
@@ -6379,6 +6380,12 @@
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+ if (mAudioHandler != null) {
+ pw.println("\nMessage handler (watch for unhandled messages):");
+ mAudioHandler.dump(new PrintWriterPrinter(pw), " ");
+ } else {
+ pw.println("\nMessage handler is null");
+ }
mMediaFocusControl.dump(pw);
dumpStreamStates(pw);
dumpRingerMode(pw);
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index 90edcb5..def7f75 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -54,7 +54,6 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -503,8 +502,6 @@
try {
if (args.length > 1 && "--hal".equals(args[0])) {
dumpHal(fd, Arrays.copyOfRange(args, 1, args.length, args.getClass()));
- } else if (args.length > 0 && "--proto".equals(args[0])) {
- dumpProto(fd);
} else {
dumpInternal(pw);
}
@@ -1296,49 +1293,6 @@
mUsageStats.print(pw);
}
- private void dumpProto(FileDescriptor fd) {
- final ProtoOutputStream proto = new ProtoOutputStream(fd);
- for (UserInfo user : UserManager.get(getContext()).getUsers()) {
- final int userId = user.getUserHandle().getIdentifier();
-
- final long userToken = proto.start(FaceServiceDumpProto.USERS);
-
- proto.write(FaceUserStatsProto.USER_ID, userId);
- proto.write(FaceUserStatsProto.NUM_FACES,
- getBiometricUtils().getBiometricsForUser(getContext(), userId).size());
-
- // Normal face authentications (e.g. lockscreen)
- final PerformanceStats normal = mPerformanceMap.get(userId);
- if (normal != null) {
- final long countsToken = proto.start(FaceUserStatsProto.NORMAL);
- proto.write(FaceActionStatsProto.ACCEPT, normal.accept);
- proto.write(FaceActionStatsProto.REJECT, normal.reject);
- proto.write(FaceActionStatsProto.ACQUIRE, normal.acquire);
- proto.write(FaceActionStatsProto.LOCKOUT, normal.lockout);
- proto.write(FaceActionStatsProto.LOCKOUT_PERMANENT, normal.lockout);
- proto.end(countsToken);
- }
-
- // Statistics about secure face transactions (e.g. to unlock password
- // storage, make secure purchases, etc.)
- final PerformanceStats crypto = mCryptoPerformanceMap.get(userId);
- if (crypto != null) {
- final long countsToken = proto.start(FaceUserStatsProto.CRYPTO);
- proto.write(FaceActionStatsProto.ACCEPT, crypto.accept);
- proto.write(FaceActionStatsProto.REJECT, crypto.reject);
- proto.write(FaceActionStatsProto.ACQUIRE, crypto.acquire);
- proto.write(FaceActionStatsProto.LOCKOUT, crypto.lockout);
- proto.write(FaceActionStatsProto.LOCKOUT_PERMANENT, crypto.lockout);
- proto.end(countsToken);
- }
-
- proto.end(userToken);
- }
- proto.flush();
- mPerformanceMap.clear();
- mCryptoPerformanceMap.clear();
- }
-
private void dumpHal(FileDescriptor fd, String[] args) {
// WARNING: CDD restricts image data from leaving TEE unencrypted on
// production devices:
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index fa8c48b..64a05c9 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -1533,7 +1533,13 @@
}
}
if (duplicatesCount > 1) {
- Slog.e(TAG, "FATAL ERROR! File a bug if you see this.");
+ Slog.wtf(TAG, "duplicates found when scheduling a sync operation: "
+ + "owningUid=" + syncOperation.owningUid
+ + "; owningPackage=" + syncOperation.owningPackage
+ + "; source=" + syncOperation.syncSource
+ + "; adapter=" + (syncOperation.target != null
+ ? syncOperation.target.provider
+ : "unknown"));
}
if (syncOperation != syncToRun) {
diff --git a/services/core/java/com/android/server/notification/NotificationShellCmd.java b/services/core/java/com/android/server/notification/NotificationShellCmd.java
index a9834ca..979015d 100644
--- a/services/core/java/com/android/server/notification/NotificationShellCmd.java
+++ b/services/core/java/com/android/server/notification/NotificationShellCmd.java
@@ -46,6 +46,8 @@
import android.text.TextUtils;
import android.util.Slog;
+import com.android.internal.util.FunctionalUtils;
+
import java.io.PrintWriter;
import java.net.URISyntaxException;
import java.util.Collections;
@@ -136,8 +138,9 @@
case "off":
interruptionFilter = INTERRUPTION_FILTER_ALL;
}
- mBinderService.setInterruptionFilter(
- mDirectService.getContext().getPackageName(), interruptionFilter);
+ final int filter = interruptionFilter;
+ Binder.withCleanCallingIdentity(() -> mBinderService.setInterruptionFilter(
+ mDirectService.getContext().getPackageName(), filter));
}
break;
case "allow_dnd": {
diff --git a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
index 1658833..d53f685 100644
--- a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
+++ b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
@@ -91,8 +91,7 @@
// Storage uses a special app op to decide the mount state and supports soft restriction
// where the restricted state allows the permission but only for accessing the medial
// collections.
- case READ_EXTERNAL_STORAGE:
- case WRITE_EXTERNAL_STORAGE: {
+ case READ_EXTERNAL_STORAGE: {
final int flags;
final boolean applyRestriction;
final boolean isWhiteListed;
@@ -148,6 +147,42 @@
}
};
}
+ case WRITE_EXTERNAL_STORAGE: {
+ final boolean isWhiteListed;
+ final int targetSDK;
+
+ if (appInfo != null) {
+ final int flags = context.getPackageManager().getPermissionFlags(permission,
+ appInfo.packageName, user);
+ isWhiteListed = (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
+ targetSDK = appInfo.targetSdkVersion;
+ } else {
+ isWhiteListed = false;
+ targetSDK = 0;
+ }
+
+ return new SoftRestrictedPermissionPolicy() {
+ @Override
+ public int resolveAppOp() {
+ return OP_NONE;
+ }
+
+ @Override
+ public int getDesiredOpMode() {
+ return MODE_DEFAULT;
+ }
+
+ @Override
+ public boolean shouldSetAppOpIfNotDefault() {
+ return false;
+ }
+
+ @Override
+ public boolean canBeGranted() {
+ return isWhiteListed || targetSDK >= Build.VERSION_CODES.Q;
+ }
+ };
+ }
default:
return DUMMY_POLICY;
}
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index d6a9f427..7408dd4 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -199,6 +199,7 @@
private final Uri LOCK_SCREEN_WHEN_TRUST_LOST =
Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_WHEN_TRUST_LOST);
+ private final boolean mIsAutomotive;
private final ContentResolver mContentResolver;
private boolean mTrustAgentsExtendUnlock;
private boolean mLockWhenTrustLost;
@@ -210,6 +211,10 @@
*/
SettingsObserver(Handler handler) {
super(handler);
+
+ PackageManager packageManager = getContext().getPackageManager();
+ mIsAutomotive = packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+
mContentResolver = getContext().getContentResolver();
updateContentObserver();
}
@@ -233,11 +238,15 @@
@Override
public void onChange(boolean selfChange, Uri uri) {
if (TRUST_AGENTS_EXTEND_UNLOCK.equals(uri)) {
+ // Smart lock should only extend unlock. The only exception is for automotive,
+ // where it can actively unlock the head unit.
+ int defaultValue = mIsAutomotive ? 0 : 1;
+
mTrustAgentsExtendUnlock =
Settings.Secure.getIntForUser(
mContentResolver,
Settings.Secure.TRUST_AGENTS_EXTEND_UNLOCK,
- 1 /* default */,
+ defaultValue,
mCurrentUser) != 0;
} else if (LOCK_SCREEN_WHEN_TRUST_LOST.equals(uri)) {
mLockWhenTrustLost =
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 5c00694..cf87203 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1498,8 +1498,6 @@
}
sTmpRect.setEmpty();
- sTmpDockedFrame.set(displayFrames.mDock);
-
final int displayId = displayFrames.mDisplayId;
final Rect dockFrame = displayFrames.mDock;
final int displayHeight = displayFrames.mDisplayHeight;
@@ -1512,11 +1510,13 @@
continue;
}
- w.getWindowFrames().setFrames(sTmpDockedFrame /* parentFrame */,
- sTmpDockedFrame /* displayFrame */, sTmpDockedFrame /* overscanFrame */,
- sTmpDockedFrame /* contentFrame */, sTmpDockedFrame /* visibleFrame */,
- sTmpRect /* decorFrame */, sTmpDockedFrame /* stableFrame */,
- sTmpDockedFrame /* outsetFrame */);
+ w.getWindowFrames().setFrames(displayFrames.mUnrestricted /* parentFrame */,
+ displayFrames.mUnrestricted /* displayFrame */,
+ displayFrames.mUnrestricted /* overscanFrame */,
+ displayFrames.mUnrestricted /* contentFrame */,
+ displayFrames.mUnrestricted /* visibleFrame */, sTmpRect /* decorFrame */,
+ displayFrames.mUnrestricted /* stableFrame */,
+ displayFrames.mUnrestricted /* outsetFrame */);
w.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);
w.computeFrameLw();
final Rect frame = w.getFrameLw();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 4a87aa4..de184b8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -16,6 +16,10 @@
package com.android.server.wm;
+import static android.view.Gravity.BOTTOM;
+import static android.view.Gravity.LEFT;
+import static android.view.Gravity.RIGHT;
+import static android.view.Gravity.TOP;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
@@ -26,9 +30,11 @@
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -66,6 +72,7 @@
private WindowState mWindow;
private int mRotation = ROTATION_0;
private boolean mHasDisplayCutout;
+ private static final int DECOR_WINDOW_INSET = 50;
@Before
public void setUp() throws Exception {
@@ -520,6 +527,58 @@
}
}
+ @Test
+ public void testScreenDecorWindows() {
+ synchronized (mWm.mGlobalLock) {
+ final WindowState decorWindow = createWindow(null, TYPE_APPLICATION_OVERLAY,
+ "decorWindow");
+ decorWindow.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
+ decorWindow.mAttrs.privateFlags |= PRIVATE_FLAG_IS_SCREEN_DECOR;
+ addWindow(decorWindow);
+ addWindow(mWindow);
+
+ // Decor on top
+ updateDecorWindow(decorWindow, MATCH_PARENT, DECOR_WINDOW_INSET, TOP);
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), DECOR_WINDOW_INSET, NAV_BAR_HEIGHT);
+
+ // Decor on bottom
+ updateDecorWindow(decorWindow, MATCH_PARENT, DECOR_WINDOW_INSET, BOTTOM);
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT,
+ DECOR_WINDOW_INSET);
+
+ // Decor on the left
+ updateDecorWindow(decorWindow, DECOR_WINDOW_INSET, MATCH_PARENT, LEFT);
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+ assertInsetBy(mWindow.getContentFrameLw(), DECOR_WINDOW_INSET, STATUS_BAR_HEIGHT, 0,
+ NAV_BAR_HEIGHT);
+
+ // Decor on the right
+ updateDecorWindow(decorWindow, DECOR_WINDOW_INSET, MATCH_PARENT, RIGHT);
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+ assertInsetBy(mWindow.getContentFrameLw(), 0, STATUS_BAR_HEIGHT, DECOR_WINDOW_INSET,
+ NAV_BAR_HEIGHT);
+
+ // Decor not allowed as inset
+ updateDecorWindow(decorWindow, DECOR_WINDOW_INSET, DECOR_WINDOW_INSET, TOP);
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ }
+ }
+
+ private void updateDecorWindow(WindowState decorWindow, int width, int height, int gravity) {
+ decorWindow.mAttrs.width = width;
+ decorWindow.mAttrs.height = height;
+ decorWindow.mAttrs.gravity = gravity;
+ decorWindow.setRequestedSize(width, height);
+ }
+
/**
* Asserts that {@code actual} is inset by the given amounts from the full display rect.
*
diff --git a/startop/scripts/iorap/compiler.py b/startop/scripts/iorap/compiler.py
new file mode 100755
index 0000000..7914960
--- /dev/null
+++ b/startop/scripts/iorap/compiler.py
@@ -0,0 +1,244 @@
+#!/usr/bin/env python3
+
+#
+# Copyright (C) 2019 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.
+#
+
+#
+# Dependencies:
+#
+# $> sudo apt-get install python3-pip
+# $> pip3 install --user protobuf sqlalchemy sqlite3
+#
+
+import collections
+import optparse
+import os
+import re
+import sys
+
+from typing import Iterable
+
+from lib.inode2filename import Inode2Filename
+from generated.TraceFile_pb2 import *
+
+parent_dir_name = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
+sys.path.append(parent_dir_name + "/trace_analyzer")
+from lib.trace2db import Trace2Db, MmFilemapAddToPageCache
+
+_PAGE_SIZE = 4096 # adb shell getconf PAGESIZE ## size of a memory page in bytes.
+
+class PageRun:
+ """
+ Intermediate representation for a run of one or more pages.
+ """
+ def __init__(self, device_number: int, inode: int, offset: int, length: int):
+ self.device_number = device_number
+ self.inode = inode
+ self.offset = offset
+ self.length = length
+
+ def __str__(self):
+ return "PageRun(device_number=%d, inode=%d, offset=%d, length=%d)" \
+ %(self.device_number, self.inode, self.offset, self.length)
+
+def debug_print(msg):
+ #print(msg)
+ pass
+
+UNDER_LAUNCH = False
+
+def page_cache_entries_to_runs(page_cache_entries: Iterable[MmFilemapAddToPageCache]):
+ global _PAGE_SIZE
+
+ runs = [
+ PageRun(device_number=pg_entry.dev, inode=pg_entry.ino, offset=pg_entry.ofs,
+ length=_PAGE_SIZE)
+ for pg_entry in page_cache_entries
+ ]
+
+ for r in runs:
+ debug_print(r)
+
+ print("Stats: Page runs totaling byte length: %d" %(len(runs) * _PAGE_SIZE))
+
+ return runs
+
+def optimize_page_runs(page_runs):
+ new_entries = []
+ last_entry = None
+ for pg_entry in page_runs:
+ if last_entry:
+ if pg_entry.device_number == last_entry.device_number and pg_entry.inode == last_entry.inode:
+ # we are dealing with a run for the same exact file as a previous run.
+ if pg_entry.offset == last_entry.offset + last_entry.length:
+ # trivially contiguous entries. merge them together.
+ last_entry.length += pg_entry.length
+ continue
+ # Default: Add the run without merging it to a previous run.
+ last_entry = pg_entry
+ new_entries.append(pg_entry)
+ return new_entries
+
+def is_filename_matching_filter(file_name, filters=[]):
+ """
+ Blacklist-style regular expression filters.
+
+ :return: True iff file_name has an RE match in one of the filters.
+ """
+ for filt in filters:
+ res = re.search(filt, file_name)
+ if res:
+ return True
+
+ return False
+
+def build_protobuf(page_runs, inode2filename, filters=[]):
+ trace_file = TraceFile()
+ trace_file_index = trace_file.index
+
+ file_id_counter = 0
+ file_id_map = {} # filename -> id
+
+ stats_length_total = 0
+ filename_stats = {} # filename -> total size
+
+ skipped_inode_map = {}
+ filtered_entry_map = {} # filename -> count
+
+ for pg_entry in page_runs:
+ fn = inode2filename.resolve(pg_entry.device_number, pg_entry.inode)
+ if not fn:
+ skipped_inode_map[pg_entry.inode] = skipped_inode_map.get(pg_entry.inode, 0) + 1
+ continue
+
+ filename = fn
+
+ if filters and not is_filename_matching_filter(filename, filters):
+ filtered_entry_map[filename] = filtered_entry_map.get(filename, 0) + 1
+ continue
+
+ file_id = file_id_map.get(filename)
+ if not file_id:
+ file_id = file_id_counter
+ file_id_map[filename] = file_id_counter
+ file_id_counter = file_id_counter + 1
+
+ file_index_entry = trace_file_index.entries.add()
+ file_index_entry.id = file_id
+ file_index_entry.file_name = filename
+
+ # already in the file index, add the file entry.
+ file_entry = trace_file.list.entries.add()
+ file_entry.index_id = file_id
+ file_entry.file_length = pg_entry.length
+ stats_length_total += file_entry.file_length
+ file_entry.file_offset = pg_entry.offset
+
+ filename_stats[filename] = filename_stats.get(filename, 0) + file_entry.file_length
+
+ for inode, count in skipped_inode_map.items():
+ print("WARNING: Skip inode %s because it's not in inode map (%d entries)" %(inode, count))
+
+ print("Stats: Sum of lengths %d" %(stats_length_total))
+
+ if filters:
+ print("Filter: %d total files removed." %(len(filtered_entry_map)))
+
+ for fn, count in filtered_entry_map.items():
+ print("Filter: File '%s' removed '%d' entries." %(fn, count))
+
+ for filename, file_size in filename_stats.items():
+ print("%s,%s" %(filename, file_size))
+
+ return trace_file
+
+def query_add_to_page_cache(trace2db: Trace2Db):
+ # SELECT * FROM tbl ORDER BY id;
+ return trace2db.session.query(MmFilemapAddToPageCache).order_by(MmFilemapAddToPageCache.id).all()
+
+def main(argv):
+ parser = optparse.OptionParser(usage="Usage: %prog [options]", description="Compile systrace file into TraceFile.pb")
+ parser.add_option('-i', dest='inode_data_file', metavar='FILE',
+ help='Read cached inode data from a file saved earlier with pagecache.py -d')
+ parser.add_option('-t', dest='trace_file', metavar='FILE',
+ help='Path to systrace file (trace.html) that will be parsed')
+
+ parser.add_option('--db', dest='sql_db', metavar='FILE',
+ help='Path to intermediate sqlite3 database [default: in-memory].')
+
+ parser.add_option('-f', dest='filter', action="append", default=[],
+ help="Add file filter. All file entries not matching one of the filters are discarded.")
+
+ parser.add_option('-l', dest='launch_lock', action="store_true", default=False,
+ help="Exclude all events not inside launch_lock")
+
+ parser.add_option('-o', dest='output_file', metavar='FILE',
+ help='Output protobuf file')
+
+ options, categories = parser.parse_args(argv[1:])
+
+ # TODO: OptionParser should have some flags to make these mandatory.
+ if not options.inode_data_file:
+ parser.error("-i is required")
+ if not options.trace_file:
+ parser.error("-t is required")
+ if not options.output_file:
+ parser.error("-o is required")
+
+ if options.launch_lock:
+ print("INFO: Launch lock flag (-l) enabled; filtering all events not inside launch_lock.")
+
+
+ inode_table = Inode2Filename.new_from_filename(options.inode_data_file)
+
+ trace_file = open(options.trace_file)
+
+ sql_db_path = ":memory:"
+ if options.sql_db:
+ sql_db_path = options.sql_db
+
+ trace2db = Trace2Db(sql_db_path)
+ # Speed optimization: Skip any entries that aren't mm_filemap_add_to_pagecache.
+ trace2db.set_raw_ftrace_entry_filter(\
+ lambda entry: entry['function'] == 'mm_filemap_add_to_page_cache')
+ # TODO: parse multiple trace files here.
+ parse_count = trace2db.parse_file_into_db(options.trace_file)
+
+ mm_filemap_add_to_page_cache_rows = query_add_to_page_cache(trace2db)
+ print("DONE. Parsed %d entries into sql db." %(len(mm_filemap_add_to_page_cache_rows)))
+
+ page_runs = page_cache_entries_to_runs(mm_filemap_add_to_page_cache_rows)
+ print("DONE. Converted %d entries" %(len(page_runs)))
+
+ # TODO: flags to select optimizations.
+ optimized_page_runs = optimize_page_runs(page_runs)
+ print("DONE. Optimized down to %d entries" %(len(optimized_page_runs)))
+
+ print("Build protobuf...")
+ trace_file = build_protobuf(optimized_page_runs, inode_table, options.filter)
+
+ print("Write protobuf to file...")
+ output_file = open(options.output_file, 'wb')
+ output_file.write(trace_file.SerializeToString())
+ output_file.close()
+
+ print("DONE")
+
+ # TODO: Silent running mode [no output except on error] for build runs.
+
+ return 0
+
+sys.exit(main(sys.argv))
diff --git a/startop/scripts/iorap/generated/TraceFile_pb2.py b/startop/scripts/iorap/generated/TraceFile_pb2.py
new file mode 100644
index 0000000..f005bed
--- /dev/null
+++ b/startop/scripts/iorap/generated/TraceFile_pb2.py
@@ -0,0 +1,259 @@
+# Generated by the protocol buffer compiler. DO NOT EDIT!
+# source: TraceFile.proto
+
+import sys
+_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import message as _message
+from google.protobuf import reflection as _reflection
+from google.protobuf import symbol_database as _symbol_database
+from google.protobuf import descriptor_pb2
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+ name='TraceFile.proto',
+ package='iorap.serialize.proto',
+ syntax='proto2',
+ serialized_pb=_b('\n\x0fTraceFile.proto\x12\x15iorap.serialize.proto\"u\n\tTraceFile\x12\x34\n\x05index\x18\x01 \x02(\x0b\x32%.iorap.serialize.proto.TraceFileIndex\x12\x32\n\x04list\x18\x02 \x02(\x0b\x32$.iorap.serialize.proto.TraceFileList\"M\n\x0eTraceFileIndex\x12;\n\x07\x65ntries\x18\x01 \x03(\x0b\x32*.iorap.serialize.proto.TraceFileIndexEntry\"4\n\x13TraceFileIndexEntry\x12\n\n\x02id\x18\x01 \x02(\x03\x12\x11\n\tfile_name\x18\x02 \x02(\t\"G\n\rTraceFileList\x12\x36\n\x07\x65ntries\x18\x01 \x03(\x0b\x32%.iorap.serialize.proto.TraceFileEntry\"L\n\x0eTraceFileEntry\x12\x10\n\x08index_id\x18\x01 \x02(\x03\x12\x13\n\x0b\x66ile_offset\x18\x02 \x02(\x03\x12\x13\n\x0b\x66ile_length\x18\x03 \x02(\x03\x42\x1c\n\x18\x63om.google.android.iorapH\x03')
+)
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+
+
+
+_TRACEFILE = _descriptor.Descriptor(
+ name='TraceFile',
+ full_name='iorap.serialize.proto.TraceFile',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='index', full_name='iorap.serialize.proto.TraceFile.index', index=0,
+ number=1, type=11, cpp_type=10, label=2,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='list', full_name='iorap.serialize.proto.TraceFile.list', index=1,
+ number=2, type=11, cpp_type=10, label=2,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto2',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=42,
+ serialized_end=159,
+)
+
+
+_TRACEFILEINDEX = _descriptor.Descriptor(
+ name='TraceFileIndex',
+ full_name='iorap.serialize.proto.TraceFileIndex',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='entries', full_name='iorap.serialize.proto.TraceFileIndex.entries', index=0,
+ number=1, type=11, cpp_type=10, label=3,
+ has_default_value=False, default_value=[],
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto2',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=161,
+ serialized_end=238,
+)
+
+
+_TRACEFILEINDEXENTRY = _descriptor.Descriptor(
+ name='TraceFileIndexEntry',
+ full_name='iorap.serialize.proto.TraceFileIndexEntry',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='id', full_name='iorap.serialize.proto.TraceFileIndexEntry.id', index=0,
+ number=1, type=3, cpp_type=2, label=2,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='file_name', full_name='iorap.serialize.proto.TraceFileIndexEntry.file_name', index=1,
+ number=2, type=9, cpp_type=9, label=2,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto2',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=240,
+ serialized_end=292,
+)
+
+
+_TRACEFILELIST = _descriptor.Descriptor(
+ name='TraceFileList',
+ full_name='iorap.serialize.proto.TraceFileList',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='entries', full_name='iorap.serialize.proto.TraceFileList.entries', index=0,
+ number=1, type=11, cpp_type=10, label=3,
+ has_default_value=False, default_value=[],
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto2',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=294,
+ serialized_end=365,
+)
+
+
+_TRACEFILEENTRY = _descriptor.Descriptor(
+ name='TraceFileEntry',
+ full_name='iorap.serialize.proto.TraceFileEntry',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='index_id', full_name='iorap.serialize.proto.TraceFileEntry.index_id', index=0,
+ number=1, type=3, cpp_type=2, label=2,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='file_offset', full_name='iorap.serialize.proto.TraceFileEntry.file_offset', index=1,
+ number=2, type=3, cpp_type=2, label=2,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='file_length', full_name='iorap.serialize.proto.TraceFileEntry.file_length', index=2,
+ number=3, type=3, cpp_type=2, label=2,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto2',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=367,
+ serialized_end=443,
+)
+
+_TRACEFILE.fields_by_name['index'].message_type = _TRACEFILEINDEX
+_TRACEFILE.fields_by_name['list'].message_type = _TRACEFILELIST
+_TRACEFILEINDEX.fields_by_name['entries'].message_type = _TRACEFILEINDEXENTRY
+_TRACEFILELIST.fields_by_name['entries'].message_type = _TRACEFILEENTRY
+DESCRIPTOR.message_types_by_name['TraceFile'] = _TRACEFILE
+DESCRIPTOR.message_types_by_name['TraceFileIndex'] = _TRACEFILEINDEX
+DESCRIPTOR.message_types_by_name['TraceFileIndexEntry'] = _TRACEFILEINDEXENTRY
+DESCRIPTOR.message_types_by_name['TraceFileList'] = _TRACEFILELIST
+DESCRIPTOR.message_types_by_name['TraceFileEntry'] = _TRACEFILEENTRY
+
+TraceFile = _reflection.GeneratedProtocolMessageType('TraceFile', (_message.Message,), dict(
+ DESCRIPTOR = _TRACEFILE,
+ __module__ = 'TraceFile_pb2'
+ # @@protoc_insertion_point(class_scope:iorap.serialize.proto.TraceFile)
+ ))
+_sym_db.RegisterMessage(TraceFile)
+
+TraceFileIndex = _reflection.GeneratedProtocolMessageType('TraceFileIndex', (_message.Message,), dict(
+ DESCRIPTOR = _TRACEFILEINDEX,
+ __module__ = 'TraceFile_pb2'
+ # @@protoc_insertion_point(class_scope:iorap.serialize.proto.TraceFileIndex)
+ ))
+_sym_db.RegisterMessage(TraceFileIndex)
+
+TraceFileIndexEntry = _reflection.GeneratedProtocolMessageType('TraceFileIndexEntry', (_message.Message,), dict(
+ DESCRIPTOR = _TRACEFILEINDEXENTRY,
+ __module__ = 'TraceFile_pb2'
+ # @@protoc_insertion_point(class_scope:iorap.serialize.proto.TraceFileIndexEntry)
+ ))
+_sym_db.RegisterMessage(TraceFileIndexEntry)
+
+TraceFileList = _reflection.GeneratedProtocolMessageType('TraceFileList', (_message.Message,), dict(
+ DESCRIPTOR = _TRACEFILELIST,
+ __module__ = 'TraceFile_pb2'
+ # @@protoc_insertion_point(class_scope:iorap.serialize.proto.TraceFileList)
+ ))
+_sym_db.RegisterMessage(TraceFileList)
+
+TraceFileEntry = _reflection.GeneratedProtocolMessageType('TraceFileEntry', (_message.Message,), dict(
+ DESCRIPTOR = _TRACEFILEENTRY,
+ __module__ = 'TraceFile_pb2'
+ # @@protoc_insertion_point(class_scope:iorap.serialize.proto.TraceFileEntry)
+ ))
+_sym_db.RegisterMessage(TraceFileEntry)
+
+
+DESCRIPTOR.has_options = True
+DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('\n\030com.google.android.iorapH\003'))
+# @@protoc_insertion_point(module_scope)
diff --git a/startop/scripts/iorap/generated/codegen_protos b/startop/scripts/iorap/generated/codegen_protos
new file mode 100755
index 0000000..5688711
--- /dev/null
+++ b/startop/scripts/iorap/generated/codegen_protos
@@ -0,0 +1,35 @@
+#!/bin/bash
+#
+# Copyright 2019, 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.
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+APROTOC="$(which aprotoc)"
+
+IORAP_SERIALIZE_DIR="${DIR}/../../../../../../system/iorap/src/serialize"
+IORAP_PROTOS=($IORAP_SERIALIZE_DIR/*.proto)
+
+if [[ $? -ne 0 ]]; then
+ echo "Fatal: Missing aprotoc. Set APROTOC=... or lunch build/envsetup.sh?" >&2
+ exit 1
+fi
+
+if ! [[ -d $IORAP_SERIALIZE_DIR ]]; then
+ echo "Fatal: Directory '$IORAP_SERIALIZE_DIR' does not exist." >&2
+ exit 1
+fi
+
+# codegen the .py files into the same directory as this script.
+echo "$APROTOC" --proto_path="$IORAP_SERIALIZE_DIR" --python_out="$DIR" "${IORAP_PROTOS[@]}"
+"$APROTOC" --proto_path="$IORAP_SERIALIZE_DIR" --python_out="$DIR" "${IORAP_PROTOS[@]}"
diff --git a/startop/scripts/iorap/lib/inode2filename.py b/startop/scripts/iorap/lib/inode2filename.py
new file mode 100644
index 0000000..2e71393
--- /dev/null
+++ b/startop/scripts/iorap/lib/inode2filename.py
@@ -0,0 +1,94 @@
+#!/usr/bin/env python3
+
+#
+# Copyright (C) 2019 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.
+#
+
+from typing import Any, Callable, Dict, Generic, Iterable, List, NamedTuple, TextIO, Tuple, TypeVar, Optional, Union, TextIO
+
+import re
+
+class Inode2Filename:
+ """
+ Parses a text file of the format
+ "uint(dev_t) uint(ino_t) int(file_size) string(filepath)\\n"*
+
+ Lines not matching this format are ignored.
+ """
+
+ def __init__(self, inode_data_file: TextIO):
+ """
+ Create an Inode2Filename that reads cached inode from a file saved earlier
+ (e.g. with pagecache.py -d or with inode2filename --format=textcache)
+
+ :param inode_data_file: a file object (e.g. created with open or StringIO).
+
+ Lifetime: inode_data_file is only used during the construction of the object.
+ """
+ self._inode_table = Inode2Filename.build_inode_lookup_table(inode_data_file)
+
+ @classmethod
+ def new_from_filename(cls, textcache_filename: str) -> 'Inode2Filename':
+ """
+ Create an Inode2Filename that reads cached inode from a file saved earlier
+ (e.g. with pagecache.py -d or with inode2filename --format=textcache)
+
+ :param textcache_filename: path to textcache
+ """
+ with open(textcache_filename) as inode_data_file:
+ return cls(inode_data_file)
+
+ @staticmethod
+ def build_inode_lookup_table(inode_data_file: TextIO) -> Dict[Tuple[int, int], Tuple[str, str]]:
+ """
+ :return: map { (device_int, inode_int) -> (filename_str, size_str) }
+ """
+ inode2filename = {}
+ for line in inode_data_file:
+ # stat -c "%d %i %s %n
+ # device number, inode number, total size in bytes, file name
+ result = re.match('([0-9]+)d? ([0-9]+) -?([0-9]+) (.*)', line)
+ if result:
+ inode2filename[(int(result.group(1)), int(result.group(2)))] = \
+ (result.group(4), result.group(3))
+
+ return inode2filename
+
+ def resolve(self, dev_t: int, ino_t: int) -> Optional[str]:
+ """
+ Return a filename (str) from a (dev_t, ino_t) inode pair.
+
+ Returns None if the lookup fails.
+ """
+ maybe_result = self._inode_table.get((dev_t, ino_t))
+
+ if not maybe_result:
+ return None
+
+ return maybe_result[0] # filename str
+
+ def __len__(self) -> int:
+ """
+ :return: the number of inode entries parsed from the file.
+ """
+ return len(self._inode_table)
+
+ def __repr__(self) -> str:
+ """
+ :return: string representation for debugging/test failures.
+ """
+ return "Inode2Filename%s" %(repr(self._inode_table))
+
+ # end of class.
diff --git a/startop/scripts/iorap/lib/inode2filename_test.py b/startop/scripts/iorap/lib/inode2filename_test.py
new file mode 100755
index 0000000..1224c61
--- /dev/null
+++ b/startop/scripts/iorap/lib/inode2filename_test.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019, 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.
+#
+
+"""
+Unit tests for inode2filename module.
+
+Install:
+ $> sudo apt-get install python3-pytest ## OR
+ $> pip install -U pytest
+See also https://docs.pytest.org/en/latest/getting-started.html
+
+Usage:
+ $> ./inode2filename_test.py
+ $> pytest inode2filename_test.py
+ $> python -m pytest inode2filename_test.py
+
+See also https://docs.pytest.org/en/latest/usage.html
+"""
+
+# global imports
+from contextlib import contextmanager
+import io
+import shlex
+import sys
+import typing
+
+# pip imports
+import pytest
+
+# local imports
+from inode2filename import *
+
+def create_inode2filename(*contents):
+ buf = io.StringIO()
+
+ for c in contents:
+ buf.write(c)
+ buf.write("\n")
+
+ buf.seek(0)
+
+ i2f = Inode2Filename(buf)
+
+ buf.close()
+
+ return i2f
+
+def test_inode2filename():
+ a = create_inode2filename("")
+ assert len(a) == 0
+ assert a.resolve(1, 2) == None
+
+ a = create_inode2filename("1 2 3 foo.bar")
+ assert len(a) == 1
+ assert a.resolve(1, 2) == "foo.bar"
+ assert a.resolve(4, 5) == None
+
+ a = create_inode2filename("1 2 3 foo.bar", "4 5 6 bar.baz")
+ assert len(a) == 2
+ assert a.resolve(1, 2) == "foo.bar"
+ assert a.resolve(4, 5) == "bar.baz"
+
+ a = create_inode2filename("1567d 8910 -1 /a/b/c/", "4 5 6 bar.baz")
+ assert len(a) == 2
+ assert a.resolve(1567, 8910) == "/a/b/c/"
+ assert a.resolve(4, 5) == "bar.baz"
+
+if __name__ == '__main__':
+ pytest.main()
diff --git a/startop/scripts/trace_analyzer/lib/trace2db.py b/startop/scripts/trace_analyzer/lib/trace2db.py
new file mode 100644
index 0000000..f60d6ab
--- /dev/null
+++ b/startop/scripts/trace_analyzer/lib/trace2db.py
@@ -0,0 +1,343 @@
+#!/usr/bin/python3
+# Copyright (C) 2019 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.
+
+import re
+import sys
+
+from sqlalchemy import create_engine
+from sqlalchemy import Column, Date, Integer, Float, String, ForeignKey
+from sqlalchemy.ext.declarative import declarative_base
+
+from sqlalchemy.orm import sessionmaker
+
+import sqlalchemy
+
+from typing import Optional, Tuple
+
+_DEBUG = False # print sql commands to console
+_FLUSH_LIMIT = 10000 # how many entries are parsed before flushing to DB from memory
+
+Base = declarative_base()
+
+class RawFtraceEntry(Base):
+ __tablename__ = 'raw_ftrace_entries'
+
+ id = Column(Integer, primary_key=True)
+ task_name = Column(String, nullable=True) # <...> -> None.
+ task_pid = Column(String, nullable=False)
+ tgid = Column(Integer, nullable=True) # ----- -> None.
+ cpu = Column(Integer, nullable=False)
+ timestamp = Column(Float, nullable=False)
+ function = Column(String, nullable=False)
+ function_args = Column(String, nullable=False)
+
+ @staticmethod
+ def parse_dict(line):
+ # ' <...>-5521 (-----) [003] ...1 17148.446877: tracing_mark_write: trace_event_clock_sync: parent_ts=17148.447266'
+ m = re.match('\s*(.*)-(\d+)\s+\(([^\)]+)\)\s+\[(\d+)\]\s+([\w.]{4})\s+(\d+[.]\d+):\s+(\w+):\s+(.*)', line)
+ if not m:
+ return None
+
+ groups = m.groups()
+ # groups example:
+ # ('<...>',
+ # '5521',
+ # '-----',
+ # '003',
+ # '...1',
+ # '17148.446877',
+ # 'tracing_mark_write',
+ # 'trace_event_clock_sync: parent_ts=17148.447266')
+ task_name = groups[0]
+ if task_name == '<...>':
+ task_name = None
+
+ task_pid = int(groups[1])
+ tgid = groups[2]
+ if tgid == '-----':
+ tgid = None
+
+ cpu = int(groups[3])
+ # irq_flags = groups[4]
+ timestamp = float(groups[5])
+ function = groups[6]
+ function_args = groups[7]
+
+ return {'task_name': task_name, 'task_pid': task_pid, 'tgid': tgid, 'cpu': cpu, 'timestamp': timestamp, 'function': function, 'function_args': function_args}
+
+class SchedSwitch(Base):
+ __tablename__ = 'sched_switches'
+
+ id = Column(Integer, ForeignKey('raw_ftrace_entries.id'), primary_key=True)
+
+ prev_comm = Column(String, nullable=False)
+ prev_pid = Column(Integer, nullable=False)
+ prev_prio = Column(Integer, nullable=False)
+ prev_state = Column(String, nullable=False)
+
+ next_comm = Column(String, nullable=False)
+ next_pid = Column(Integer, nullable=False)
+ next_prio = Column(Integer, nullable=False)
+
+ @staticmethod
+ def parse_dict(function_args, id = None):
+ # 'prev_comm=kworker/u16:5 prev_pid=13971 prev_prio=120 prev_state=S ==> next_comm=swapper/4 next_pid=0 next_prio=120'
+ m = re.match("prev_comm=(.*) prev_pid=(\d+) prev_prio=(\d+) prev_state=(.*) ==> next_comm=(.*) next_pid=(\d+) next_prio=(\d+) ?", function_args)
+ if not m:
+ return None
+
+ groups = m.groups()
+ # ('kworker/u16:5', '13971', '120', 'S', 'swapper/4', '0', '120')
+ d = {}
+ if id is not None:
+ d['id'] = id
+ d['prev_comm'] = groups[0]
+ d['prev_pid'] = int(groups[1])
+ d['prev_prio'] = int(groups[2])
+ d['prev_state'] = groups[3]
+ d['next_comm'] = groups[4]
+ d['next_pid'] = int(groups[5])
+ d['next_prio'] = int(groups[6])
+
+ return d
+
+class SchedBlockedReason(Base):
+ __tablename__ = 'sched_blocked_reasons'
+
+ id = Column(Integer, ForeignKey('raw_ftrace_entries.id'), primary_key=True)
+
+ pid = Column(Integer, nullable=False)
+ iowait = Column(Integer, nullable=False)
+ caller = Column(String, nullable=False)
+
+ @staticmethod
+ def parse_dict(function_args, id = None):
+ # 'pid=2289 iowait=1 caller=wait_on_page_bit_common+0x2a8/0x5f'
+ m = re.match("pid=(\d+) iowait=(\d+) caller=(.*) ?", function_args)
+ if not m:
+ return None
+
+ groups = m.groups()
+ # ('2289', '1', 'wait_on_page_bit_common+0x2a8/0x5f8')
+ d = {}
+ if id is not None:
+ d['id'] = id
+ d['pid'] = int(groups[0])
+ d['iowait'] = int(groups[1])
+ d['caller'] = groups[2]
+
+ return d
+
+class MmFilemapAddToPageCache(Base):
+ __tablename__ = 'mm_filemap_add_to_page_caches'
+
+ id = Column(Integer, ForeignKey('raw_ftrace_entries.id'), primary_key=True)
+
+ dev = Column(Integer, nullable=False) # decoded from ${major}:${minor} syntax.
+ dev_major = Column(Integer, nullable=False) # original ${major} value.
+ dev_minor = Column(Integer, nullable=False) # original ${minor} value.
+
+ ino = Column(Integer, nullable=False) # decoded from hex to base 10
+ page = Column(Integer, nullable=False) # decoded from hex to base 10
+
+ pfn = Column(Integer, nullable=False)
+ ofs = Column(Integer, nullable=False)
+
+ @staticmethod
+ def parse_dict(function_args, id = None):
+ # dev 253:6 ino b2c7 page=00000000ec787cd9 pfn=1478539 ofs=4096
+ m = re.match("dev (\d+):(\d+) ino ([0-9a-fA-F]+) page=([0-9a-fA-F]+) pfn=(\d+) ofs=(\d+)", function_args)
+ if not m:
+ return None
+
+ groups = m.groups()
+ # ('253', '6', 'b2c7', '00000000ec787cd9', '1478539', '4096')
+ d = {}
+ if id is not None:
+ d['id'] = id
+
+ device_major = d['dev_major'] = int(groups[0])
+ device_minor = d['dev_minor'] = int(groups[1])
+ d['dev'] = device_major << 8 | device_minor
+ d['ino'] = int(groups[2], 16)
+ d['page'] = int(groups[3], 16)
+ d['pfn'] = int(groups[4])
+ d['ofs'] = int(groups[5])
+
+ return d
+
+class Trace2Db:
+ def __init__(self, db_filename: str):
+ (s, e) = self._init_sqlalchemy(db_filename)
+ self._session = s
+ self._engine = e
+ self._raw_ftrace_entry_filter = lambda x: True
+
+ def set_raw_ftrace_entry_filter(self, flt):
+ """
+ Install a function dict(RawFtraceEntry) -> bool
+
+ If this returns 'false', then we skip adding the RawFtraceEntry to the database.
+ """
+ self._raw_ftrace_entry_filter = flt
+
+ @staticmethod
+ def _init_sqlalchemy(db_filename: str) -> Tuple[object, object]:
+ global _DEBUG
+ engine = create_engine('sqlite:///' + db_filename, echo=_DEBUG)
+
+ # CREATE ... (tables)
+ Base.metadata.create_all(engine)
+
+ Session = sessionmaker(bind=engine)
+ session = Session()
+ return (session, engine)
+
+ def parse_file_into_db(self, filename: str, limit: Optional[int] = None):
+ """
+ Parse the ftrace/systrace at 'filename',
+ inserting the values into the current sqlite database.
+
+ :return: number of RawFtraceEntry inserted.
+ """
+ return parse_file(filename, self._session, self._engine, self._raw_ftrace_entry_filter, limit)
+
+ def parse_file_buf_into_db(self, file_buf, limit: Optional[int] = None):
+ """
+ Parse the ftrace/systrace at 'filename',
+ inserting the values into the current sqlite database.
+
+ :return: number of RawFtraceEntry inserted.
+ """
+ return parse_file_buf(file_buf, self._session, self._engine, self._raw_ftrace_entry_filter, limit)
+
+
+ @property
+ def session(self):
+ return self._session
+
+def insert_pending_entries(engine, kls, lst):
+ if len(lst) > 0:
+ # for some reason, it tries to generate an empty INSERT statement with len=0,
+ # which of course violates the first non-null constraint.
+ try:
+ # Performance-sensitive parsing according to:
+ # https://docs.sqlalchemy.org/en/13/faq/performance.html#i-m-inserting-400-000-rows-with-the-orm-and-it-s-really-slow
+ engine.execute(kls.__table__.insert(), lst)
+ lst.clear()
+ except sqlalchemy.exc.IntegrityError as err:
+ # possibly violating some SQL constraint, print data here.
+ print(err)
+ print(lst)
+ raise
+
+def parse_file(filename: str, *args, **kwargs) -> int:
+ # use explicit encoding to avoid UnicodeDecodeError.
+ with open(filename, encoding="ISO-8859-1") as f:
+ return parse_file_buf(f, *args, **kwargs)
+
+def parse_file_buf(filebuf, session, engine, raw_ftrace_entry_filter, limit=None) -> int:
+ global _FLUSH_LIMIT
+ count = 0
+
+ pending_entries = []
+ pending_sched_switch = []
+ pending_sched_blocked_reasons = []
+ pending_mm_filemap_add_to_pagecaches = []
+
+ def insert_all_pending_entries():
+ insert_pending_entries(engine, RawFtraceEntry, pending_entries)
+ insert_pending_entries(engine, SchedSwitch, pending_sched_switch)
+ insert_pending_entries(engine, SchedBlockedReason, pending_sched_blocked_reasons)
+ insert_pending_entries(engine, MmFilemapAddToPageCache, pending_mm_filemap_add_to_pagecaches)
+
+ # for trace.html files produced by systrace,
+ # the actual ftrace is in the 'second' trace-data script class.
+ parsing_trace_data = 0
+ parsing_systrace_file = False
+
+ f = filebuf
+ for l in f:
+ if parsing_trace_data == 0 and l == "<!DOCTYPE html>\n":
+ parsing_systrace_file = True
+ continue
+ if parsing_trace_data != 2 and parsing_systrace_file:
+ if l == ' <script class="trace-data" type="application/text">\n':
+ parsing_trace_data = parsing_trace_data + 1
+ continue
+
+ if parsing_systrace_file and parsing_trace_data != 2:
+ continue
+ elif parsing_systrace_file and parsing_trace_data == 2 and l == " </script>\n":
+ # the rest of this file is just random html
+ break
+
+ # now parsing the ftrace data.
+ if len(l) > 1 and l[0] == '#':
+ continue
+
+ count = count + 1
+
+ if limit and count >= limit:
+ break
+
+ raw_ftrace_entry = RawFtraceEntry.parse_dict(l)
+ if not raw_ftrace_entry:
+ print("WARNING: Failed to parse raw ftrace entry: " + l)
+ continue
+
+ if not raw_ftrace_entry_filter(raw_ftrace_entry):
+ # Skip processing raw ftrace entries that don't match a filter.
+ # This is an optimization for when Trace2Db is used programatically
+ # to avoid having an overly large database.
+ continue
+
+ pending_entries.append(raw_ftrace_entry)
+
+ if raw_ftrace_entry['function'] == 'sched_switch':
+ sched_switch = SchedSwitch.parse_dict(raw_ftrace_entry['function_args'], count)
+
+ if not sched_switch:
+ print("WARNING: Failed to parse sched_switch: " + l)
+ else:
+ pending_sched_switch.append(sched_switch)
+
+ elif raw_ftrace_entry['function'] == 'sched_blocked_reason':
+ sbr = SchedBlockedReason.parse_dict(raw_ftrace_entry['function_args'], count)
+
+ if not sbr:
+ print("WARNING: Failed to parse sched_blocked_reason: " + l)
+ else:
+ pending_sched_blocked_reasons.append(sbr)
+
+ elif raw_ftrace_entry['function'] == 'mm_filemap_add_to_page_cache':
+ d = MmFilemapAddToPageCache.parse_dict(raw_ftrace_entry['function_args'], count)
+ if not d:
+ print("WARNING: Failed to parse mm_filemap_add_to_page_cache: " + l)
+ else:
+ pending_mm_filemap_add_to_pagecaches.append(d)
+
+ # Objects are cached in python memory, not yet sent to SQL database.
+
+ # Send INSERT/UPDATE/etc statements to the underlying SQL database.
+ if count % _FLUSH_LIMIT == 0:
+ insert_all_pending_entries()
+
+ insert_all_pending_entries()
+
+ # Ensure underlying database commits changes from memory to disk.
+ session.commit()
+
+ return count
diff --git a/startop/scripts/trace_analyzer/lib/trace2db_test.py b/startop/scripts/trace_analyzer/lib/trace2db_test.py
new file mode 100755
index 0000000..b67cffa
--- /dev/null
+++ b/startop/scripts/trace_analyzer/lib/trace2db_test.py
@@ -0,0 +1,202 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019, 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.
+#
+
+"""
+Unit tests for inode2filename module.
+
+Install:
+ $> sudo apt-get install python3-pytest ## OR
+ $> pip install -U pytest
+See also https://docs.pytest.org/en/latest/getting-started.html
+
+Usage:
+ $> ./inode2filename_test.py
+ $> pytest inode2filename_test.py
+ $> python -m pytest inode2filename_test.py
+
+See also https://docs.pytest.org/en/latest/usage.html
+"""
+
+# global imports
+from contextlib import contextmanager
+import io
+import shlex
+import sys
+import typing
+
+from copy import deepcopy
+
+# pip imports
+import pytest
+
+# local imports
+from trace2db import *
+
+# This pretty-prints the raw dictionary of the sqlalchemy object if it fails.
+class EqualsSqlAlchemyObject:
+ # For convenience to write shorter tests, we also add 'ignore_fields' which allow us to specify
+ # which fields to ignore when doing the comparison.
+ def __init__(self_, self, ignore_fields=[]):
+ self_.self = self
+ self_.ignore_fields = ignore_fields
+
+ # Do field-by-field comparison.
+ # It seems that SQLAlchemy does not implement __eq__ itself so we have to do it ourselves.
+ def __eq__(self_, other):
+ if isinstance(other, EqualsSqlAlchemyObject):
+ other = other.self
+
+ self = self_.self
+
+ classes_match = isinstance(other, self.__class__)
+ a, b = deepcopy(self.__dict__), deepcopy(other.__dict__)
+
+ #compare based on equality our attributes, ignoring SQLAlchemy internal stuff
+
+ a.pop('_sa_instance_state', None)
+ b.pop('_sa_instance_state', None)
+
+ for f in self_.ignore_fields:
+ a.pop(f, None)
+ b.pop(f, None)
+
+ attrs_match = (a == b)
+ return classes_match and attrs_match
+
+ def __repr__(self):
+ return repr(self.self.__dict__)
+
+
+def assert_eq_ignore_id(left, right):
+ # This pretty-prints the raw dictionary of the sqlalchemy object if it fails.
+ # It does field-by-field comparison, but ignores the 'id' field.
+ assert EqualsSqlAlchemyObject(left, ignore_fields=['id']) == EqualsSqlAlchemyObject(right)
+
+def parse_trace_file_to_db(*contents):
+ """
+ Make temporary in-memory sqlite3 database by parsing the string contents as a trace.
+
+ :return: Trace2Db instance
+ """
+ buf = io.StringIO()
+
+ for c in contents:
+ buf.write(c)
+ buf.write("\n")
+
+ buf.seek(0)
+
+ t2d = Trace2Db(":memory:")
+ t2d.parse_file_buf_into_db(buf)
+
+ buf.close()
+
+ return t2d
+
+def test_ftrace_mm_filemap_add_to_pagecache():
+ test_contents = """
+MediaStoreImpor-27212 (27176) [000] .... 16136.595194: mm_filemap_add_to_page_cache: dev 253:6 ino 7580 page=0000000060e990c7 pfn=677646 ofs=159744
+MediaStoreImpor-27212 (27176) [000] .... 16136.595920: mm_filemap_add_to_page_cache: dev 253:6 ino 7580 page=0000000048e2e156 pfn=677645 ofs=126976
+MediaStoreImpor-27212 (27176) [000] .... 16136.597793: mm_filemap_add_to_page_cache: dev 253:6 ino 7580 page=0000000051eabfb2 pfn=677644 ofs=122880
+MediaStoreImpor-27212 (27176) [000] .... 16136.597815: mm_filemap_add_to_page_cache: dev 253:6 ino 7580 page=00000000ce7cd606 pfn=677643 ofs=131072
+MediaStoreImpor-27212 (27176) [000] .... 16136.603732: mm_filemap_add_to_page_cache: dev 253:6 ino 1 page=000000008ffd3030 pfn=730119 ofs=186482688
+MediaStoreImpor-27212 (27176) [000] .... 16136.604126: mm_filemap_add_to_page_cache: dev 253:6 ino b1d8 page=0000000098d4d2e2 pfn=829676 ofs=0
+ <...>-27197 (-----) [002] .... 16136.613471: mm_filemap_add_to_page_cache: dev 253:6 ino 7580 page=00000000aca88a97 pfn=743346 ofs=241664
+ <...>-27197 (-----) [002] .... 16136.615979: mm_filemap_add_to_page_cache: dev 253:6 ino 7580 page=00000000351f2bc1 pfn=777799 ofs=106496
+ <...>-27224 (-----) [006] .... 16137.400090: mm_filemap_add_to_page_cache: dev 253:6 ino 712d page=000000006ff7ffdb pfn=754861 ofs=0
+ <...>-1396 (-----) [000] .... 16137.451660: mm_filemap_add_to_page_cache: dev 253:6 ino 1 page=00000000ba0cbb34 pfn=769173 ofs=187191296
+ <...>-1396 (-----) [000] .... 16137.453020: mm_filemap_add_to_page_cache: dev 253:6 ino b285 page=00000000f6ef038e pfn=820291 ofs=0
+ <...>-1396 (-----) [000] .... 16137.453067: mm_filemap_add_to_page_cache: dev 253:6 ino b285 page=0000000083ebc446 pfn=956463 ofs=4096
+ <...>-1396 (-----) [000] .... 16137.453101: mm_filemap_add_to_page_cache: dev 253:6 ino b285 page=000000009dc2cd25 pfn=822813 ofs=8192
+ <...>-1396 (-----) [000] .... 16137.453113: mm_filemap_add_to_page_cache: dev 253:6 ino b285 page=00000000a11167fb pfn=928650 ofs=12288
+ <...>-1396 (-----) [000] .... 16137.453126: mm_filemap_add_to_page_cache: dev 253:6 ino b285 page=00000000c1c3311b pfn=621110 ofs=16384
+ <...>-1396 (-----) [000] .... 16137.453139: mm_filemap_add_to_page_cache: dev 253:6 ino b285 page=000000009aa78342 pfn=689370 ofs=20480
+ <...>-1396 (-----) [000] .... 16137.453151: mm_filemap_add_to_page_cache: dev 253:6 ino b285 page=0000000082cddcd6 pfn=755584 ofs=24576
+ <...>-1396 (-----) [000] .... 16137.453162: mm_filemap_add_to_page_cache: dev 253:6 ino b285 page=00000000b0249bc7 pfn=691431 ofs=28672
+ <...>-1396 (-----) [000] .... 16137.453183: mm_filemap_add_to_page_cache: dev 253:6 ino b285 page=000000006a776ff0 pfn=795084 ofs=32768
+ <...>-1396 (-----) [000] .... 16137.453203: mm_filemap_add_to_page_cache: dev 253:6 ino b285 page=000000001a4918a7 pfn=806998 ofs=36864
+ <...>-2578 (-----) [002] .... 16137.561871: mm_filemap_add_to_page_cache: dev 253:6 ino 1 page=00000000d65af9d2 pfn=719246 ofs=187015168
+ <...>-2578 (-----) [002] .... 16137.562846: mm_filemap_add_to_page_cache: dev 253:6 ino b25a page=000000002f6ba74f pfn=864982 ofs=0
+ <...>-2578 (-----) [000] .... 16138.104500: mm_filemap_add_to_page_cache: dev 253:6 ino 1 page=00000000f888d0f6 pfn=805812 ofs=192794624
+ <...>-2578 (-----) [000] .... 16138.105836: mm_filemap_add_to_page_cache: dev 253:6 ino b7dd page=000000003749523b pfn=977196 ofs=0
+ <...>-27215 (-----) [001] .... 16138.256881: mm_filemap_add_to_page_cache: dev 253:6 ino 758f page=000000001b375de1 pfn=755928 ofs=0
+ <...>-27215 (-----) [001] .... 16138.257526: mm_filemap_add_to_page_cache: dev 253:6 ino 7591 page=000000004e039481 pfn=841534 ofs=0
+ NonUserFacing6-5246 ( 1322) [005] .... 16138.356491: mm_filemap_add_to_page_cache: dev 253:6 ino 1 page=00000000d65af9d2 pfn=719246 ofs=161890304
+ NonUserFacing6-5246 ( 1322) [005] .... 16138.357538: mm_filemap_add_to_page_cache: dev 253:6 ino 9a64 page=000000002f6ba74f pfn=864982 ofs=0
+ NonUserFacing6-5246 ( 1322) [005] .... 16138.357581: mm_filemap_add_to_page_cache: dev 253:6 ino 9a64 page=000000006e0f8322 pfn=797894 ofs=4096
+ <...>-27197 (-----) [005] .... 16140.143224: mm_filemap_add_to_page_cache: dev 253:6 ino 7580 page=00000000a42527c6 pfn=1076669 ofs=32768
+ """
+
+ t2d = parse_trace_file_to_db(test_contents)
+ session = t2d.session
+
+ first_row = session.query(MmFilemapAddToPageCache).order_by(MmFilemapAddToPageCache.id).first()
+
+ #dev 253:6 ino 7580 page=0000000060e990c7 pfn=677646 ofs=159744
+ assert_eq_ignore_id(MmFilemapAddToPageCache(dev=64774, dev_major=253, dev_minor=6,
+ ino=0x7580, page=0x0000000060e990c7, pfn=677646, ofs=159744), first_row)
+
+ second_to_last_row = session.query(MmFilemapAddToPageCache).filter(MmFilemapAddToPageCache.page.in_([0x000000006e0f8322])).first()
+
+ # dev 253:6 ino 9a64 page=000000006e0f8322 pfn=797894 ofs=4096
+ assert_eq_ignore_id(MmFilemapAddToPageCache(dev=64774, dev_major=253, dev_minor=6,
+ ino=0x9a64, page=0x000000006e0f8322, pfn=797894, ofs=4096), second_to_last_row)
+
+def test_systrace_mm_filemap_add_to_pagecache():
+ test_contents = """
+<!DOCTYPE html>
+<html>
+<head i18n-values="dir:textdirection;">
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<meta charset="utf-8"/>
+<title>Android System Trace</title>
+ <script class="trace-data" type="application/text">
+PROCESS DUMP
+USER PID PPID VSZ RSS WCHAN PC S NAME COMM
+root 1 0 62148 5976 0 0 S init [init]
+root 2 0 0 0 0 0 S [kthreadd] [kthreadd]
+ </script>
+
+ <script class="trace-data" type="application/text">
+MediaStoreImpor-27212 (27176) [000] .... 16136.595194: mm_filemap_add_to_page_cache: dev 253:6 ino 7580 page=0000000060e990c7 pfn=677646 ofs=159744
+NonUserFacing6-5246 ( 1322) [005] .... 16138.357581: mm_filemap_add_to_page_cache: dev 253:6 ino 9a64 page=000000006e0f8322 pfn=797894 ofs=4096
+ </script>
+
+ <script class="trace-data" type="application/text">
+{"traceEvents": [{"category": "process_argv", "name": "process_argv", "args": {"argv": ["/mnt/ssd3/workspace/master/external/chromium-trace/systrace.py", "-t", "5", "pagecache"]}, "pid": 160383, "ts": 1037300940509.7991, "tid": 139628672526080, "ph": "M"}, {"category": "python", "name": "clock_sync", "args": {"issue_ts": 1037307346185.212, "sync_id": "9a7e4fe3-89ad-441f-8226-8fe533fe973e"}, "pid": 160383, "ts": 1037307351643.906, "tid": 139628726089536, "ph": "c"}], "metadata": {"clock-domain": "SYSTRACE"}}
+ </script>
+<!-- END TRACE -->
+ """
+
+ t2d = parse_trace_file_to_db(test_contents)
+ session = t2d.session
+
+ first_row = session.query(MmFilemapAddToPageCache).order_by(MmFilemapAddToPageCache.id).first()
+
+ #dev 253:6 ino 7580 page=0000000060e990c7 pfn=677646 ofs=159744
+ assert_eq_ignore_id(MmFilemapAddToPageCache(dev=64774, dev_major=253, dev_minor=6,
+ ino=0x7580, page=0x0000000060e990c7, pfn=677646, ofs=159744), first_row)
+
+ second_to_last_row = session.query(MmFilemapAddToPageCache).filter(MmFilemapAddToPageCache.page.in_([0x000000006e0f8322])).first()
+
+ # dev 253:6 ino 9a64 page=000000006e0f8322 pfn=797894 ofs=4096
+ assert_eq_ignore_id(MmFilemapAddToPageCache(dev=64774, dev_major=253, dev_minor=6,
+ ino=0x9a64, page=0x000000006e0f8322, pfn=797894, ofs=4096), second_to_last_row)
+
+
+if __name__ == '__main__':
+ pytest.main()
diff --git a/startop/scripts/trace_analyzer/trace_analyzer.py b/startop/scripts/trace_analyzer/trace_analyzer.py
index 4542933..62ae018 100755
--- a/startop/scripts/trace_analyzer/trace_analyzer.py
+++ b/startop/scripts/trace_analyzer/trace_analyzer.py
@@ -15,335 +15,36 @@
import re
import sys
+import argparse
-from sqlalchemy import create_engine
-from sqlalchemy import Column, Date, Integer, Float, String, ForeignKey
-from sqlalchemy.ext.declarative import declarative_base
+from lib.trace2db import Trace2Db
-from sqlalchemy.orm import sessionmaker
-
-import sqlalchemy
-
-_DEBUG = False
-#_LIMIT = 100000
-_LIMIT = None
-_FLUSH_LIMIT = 10000
-
-Base = declarative_base()
-
-class RawFtraceEntry(Base):
- __tablename__ = 'raw_ftrace_entries'
-
- id = Column(Integer, primary_key=True)
- task_name = Column(String, nullable=True) # <...> -> None.
- task_pid = Column(String, nullable=False)
- tgid = Column(Integer, nullable=True) # ----- -> None.
- cpu = Column(Integer, nullable=False)
- timestamp = Column(Float, nullable=False)
- function = Column(String, nullable=False)
- function_args = Column(String, nullable=False)
-
-# __mapper_args__ = {
-# 'polymorphic_identity':'raw_ftrace_entry',
-# 'polymorphic_on':function
-# }
-
- @staticmethod
- def parse(line):
- # ' <...>-5521 (-----) [003] ...1 17148.446877: tracing_mark_write: trace_event_clock_sync: parent_ts=17148.447266'
- m = re.match('\s*(.*)-(\d+)\s+\(([^\)]+)\)\s+\[(\d+)\]\s+([\w.]{4})\s+(\d+[.]\d+):\s+(\w+):\s+(.*)', line)
- if not m:
- return None
-
- groups = m.groups()
- # groups example:
- # ('<...>',
- # '5521',
- # '-----',
- # '003',
- # '...1',
- # '17148.446877',
- # 'tracing_mark_write',
- # 'trace_event_clock_sync: parent_ts=17148.447266')
- task_name = groups[0]
- if task_name == '<...>':
- task_name = None
-
- task_pid = int(groups[1])
- tgid = groups[2]
- if tgid == '-----':
- tgid = None
-
- cpu = int(groups[3])
- # irq_flags = groups[4]
- timestamp = float(groups[5])
- function = groups[6]
- function_args = groups[7]
-
- return RawFtraceEntry(task_name=task_name, task_pid=task_pid, tgid=tgid, cpu=cpu,
- timestamp=timestamp, function=function, function_args=function_args)
-
- @staticmethod
- def parse_dict(line):
- # ' <...>-5521 (-----) [003] ...1 17148.446877: tracing_mark_write: trace_event_clock_sync: parent_ts=17148.447266'
- m = re.match('\s*(.*)-(\d+)\s+\(([^\)]+)\)\s+\[(\d+)\]\s+([\w.]{4})\s+(\d+[.]\d+):\s+(\w+):\s+(.*)', line)
- if not m:
- return None
-
- groups = m.groups()
- # groups example:
- # ('<...>',
- # '5521',
- # '-----',
- # '003',
- # '...1',
- # '17148.446877',
- # 'tracing_mark_write',
- # 'trace_event_clock_sync: parent_ts=17148.447266')
- task_name = groups[0]
- if task_name == '<...>':
- task_name = None
-
- task_pid = int(groups[1])
- tgid = groups[2]
- if tgid == '-----':
- tgid = None
-
- cpu = int(groups[3])
- # irq_flags = groups[4]
- timestamp = float(groups[5])
- function = groups[6]
- function_args = groups[7]
-
- return {'task_name': task_name, 'task_pid': task_pid, 'tgid': tgid, 'cpu': cpu, 'timestamp': timestamp, 'function': function, 'function_args': function_args}
-
-#class TracingMarkWriteFtraceEntry(RawFtraceEntry):
-# __tablename__ = 'tracing_mark_write_ftrace_entries'
+# This script requires 'sqlalchemy' to access the sqlite3 database.
#
-# id = Column(Integer, ForeignKey('raw_ftrace_entries.id'), primary_key=True)
-# mark_type = Column(String(1), nullable=False)
-# mark_id = Column(Integer, nullable=False)
-# message = Column(String)
+# $> sudo apt-get install python3-pip
+# $> pip3 install --user sqlalchemy
#
-## __mapper_args__ = {
-## 'polymorphic_identity':'tracing_mark_write',
-## }
-#
-# @staticmethod
-# def decode(raw_ftrace_entry):
-# if raw_ftrace_entry.function != 'tracing_mark_write':
-# raise ValueError("raw_ftrace_entry must be function 'tracing_mark_write':" + raw_ftrace_entry)
-#
-# #"B|2446|(Paused)ClearCards|Foo"
-# match = re.match("([^|]*)\|([^|]*)\|(.*)", raw_ftrace_entry.function_args)
-#
-# if not match:
-# return None
-#
-# # ('B', '2446', '(Paused)ClearCards|Foo')
-# groups = match.groups()
-#
-# mark_type = groups[0]
-# mark_id = int(groups[1])
-# message = groups[2]
-#
-# return TracingMarkWriteFtraceEntry(id = raw_ftrace_entry.id,
-# mark_type = mark_type,
-# mark_id = mark_id,
-# message = message)
-
-class SchedSwitch(Base):
- __tablename__ = 'sched_switches'
-
- id = Column(Integer, ForeignKey('raw_ftrace_entries.id'), primary_key=True)
-
- prev_comm = Column(String, nullable=False)
- prev_pid = Column(Integer, nullable=False)
- prev_prio = Column(Integer, nullable=False)
- prev_state = Column(String, nullable=False)
-
- next_comm = Column(String, nullable=False)
- next_pid = Column(Integer, nullable=False)
- next_prio = Column(Integer, nullable=False)
-
-# __mapper_args__ = {
-# 'polymorphic_identity':'raw_ftrace_entry',
-# 'polymorphic_on':function
-# }
-
- @staticmethod
- def parse_dict(function_args, id = None):
- # 'prev_comm=kworker/u16:5 prev_pid=13971 prev_prio=120 prev_state=S ==> next_comm=swapper/4 next_pid=0 next_prio=120'
- m = re.match("prev_comm=(.*) prev_pid=(\d+) prev_prio=(\d+) prev_state=(.*) ==> next_comm=(.*) next_pid=(\d+) next_prio=(\d+) ?", function_args)
- if not m:
- return None
-
- groups = m.groups()
- # ('kworker/u16:5', '13971', '120', 'S', 'swapper/4', '0', '120')
- d = {}
- if id is not None:
- d['id'] = id
- d['prev_comm'] = groups[0]
- d['prev_pid'] = int(groups[1])
- d['prev_prio'] = int(groups[2])
- d['prev_state'] = groups[3]
- d['next_comm'] = groups[4]
- d['next_pid'] = int(groups[5])
- d['next_prio'] = int(groups[6])
-
- return d
-
-class SchedBlockedReason(Base):
- __tablename__ = 'sched_blocked_reasons'
-
- id = Column(Integer, ForeignKey('raw_ftrace_entries.id'), primary_key=True)
-
- pid = Column(Integer, nullable=False)
- iowait = Column(Integer, nullable=False)
- caller = Column(String, nullable=False)
-
- @staticmethod
- def parse_dict(function_args, id = None):
- # 'pid=2289 iowait=1 caller=wait_on_page_bit_common+0x2a8/0x5f'
- m = re.match("pid=(\d+) iowait=(\d+) caller=(.*) ?", function_args)
- if not m:
- return None
-
- groups = m.groups()
- # ('2289', '1', 'wait_on_page_bit_common+0x2a8/0x5f8')
- d = {}
- if id is not None:
- d['id'] = id
- d['pid'] = int(groups[0])
- d['iowait'] = int(groups[1])
- d['caller'] = groups[2]
-
- return d
-
-def init_sqlalchemy(db_filename):
- global _DEBUG
- engine = create_engine('sqlite:///' + db_filename, echo=_DEBUG)
-
- # DROP TABLES
-# Base.metadata.drop_all(engine)
- # CREATE ... (tables)
- Base.metadata.create_all(engine)
-
- Session = sessionmaker(bind=engine)
- session = Session()
- return (session, engine)
-
-def insert_pending_entries(engine, kls, lst):
- if len(lst) > 0:
- # for some reason, it tries to generate an empty INSERT statement with len=0,
- # which of course violates the first non-null constraint.
- try:
- # Performance-sensitive parsing according to:
- # https://docs.sqlalchemy.org/en/13/faq/performance.html#i-m-inserting-400-000-rows-with-the-orm-and-it-s-really-slow
- engine.execute(kls.__table__.insert(), lst)
- lst.clear()
- except sqlalchemy.exc.IntegrityError as err:
- # possibly violating some SQL constraint, print data here.
- print(err)
- print(lst)
- raise
-
-def parse_file(filename, session, engine):
- global _LIMIT
- global _FLUSH_LIMIT
- count = 0
-
- pending_entries = []
- pending_sched_switch = []
- pending_sched_blocked_reasons = []
-
- def insert_all_pending_entries():
- insert_pending_entries(engine, RawFtraceEntry, pending_entries)
- insert_pending_entries(engine, SchedSwitch, pending_sched_switch)
- insert_pending_entries(engine, SchedBlockedReason, pending_sched_blocked_reasons)
-
- # use explicit encoding to avoid UnicodeDecodeError.
- with open(filename, encoding="ISO-8859-1") as f:
- for l in f:
-
- if len(l) > 1 and l[0] == '#':
- continue
-
- count = count + 1
-
- if _LIMIT and count >= _LIMIT:
- break
-
- raw_ftrace_entry = RawFtraceEntry.parse_dict(l)
- if not raw_ftrace_entry:
- print("WARNING: Failed to parse raw ftrace entry: " + l)
- continue
-
- pending_entries.append(raw_ftrace_entry)
-
- if raw_ftrace_entry['function'] == 'sched_switch':
- sched_switch = SchedSwitch.parse_dict(raw_ftrace_entry['function_args'], count)
-
- if not sched_switch:
- print("WARNING: Failed to parse sched_switch: " + l)
- else:
- pending_sched_switch.append(sched_switch)
-
- elif raw_ftrace_entry['function'] == 'sched_blocked_reason':
- sbr = SchedBlockedReason.parse_dict(raw_ftrace_entry['function_args'], count)
-
- if not sbr:
- print("WARNING: Failed to parse sched_blocked_reason: " + l)
- else:
- pending_sched_blocked_reasons.append(sbr)
-
- # Objects are cached in python memory, not yet sent to SQL database.
- #session.add(raw_ftrace_entry)
-
- # Send INSERT/UPDATE/etc statements to the underlying SQL database.
- if count % _FLUSH_LIMIT == 0:
- # session.flush()
- #session.bulk_save_objects(pending_entries)
- #session.bulk_insert_mappings(RawFtraceEntry, pending_entries)
- insert_all_pending_entries()
-
- insert_all_pending_entries()
-
- # Ensure underlying database commits changes from memory to disk.
- session.commit()
-
- return count
-
-#def decode_raw_traces(session, engine):
-# count = 0
-# global _FLUSH_LIMIT
-#
-# for tmw in session.query(RawFtraceEntry).filter_by(function = 'tracing_mark_write'):
-# print(tmw)
-# decoded = TracingMarkWriteFtraceEntry.decode(tmw)
-# session.add(decoded)
-#
-# if count % _FLUSH_LIMIT == 0:
-# session.flush()
-#
-# session.commit()
-#
-# return count
def main(argv):
- db_filename = sys.argv[1]
- trace_filename = sys.argv[2]
+ parser = argparse.ArgumentParser(description='Convert ftrace/systrace file into sqlite3 db.')
+ parser.add_argument('db_filename', metavar='sql_filename.db', type=str,
+ help='path to sqlite3 db filename')
+ parser.add_argument('trace_filename', metavar='systrace.ftrace', type=str,
+ help='path to ftrace/systrace filename')
+ parser.add_argument('--limit', type=int, help='limit the number of entries parsed [for debugging]')
- session, engine = init_sqlalchemy(db_filename)
+ args = parser.parse_args()
+
+ db_filename = args.db_filename
+ trace_filename = args.trace_filename
+
+ trace2db = Trace2Db(db_filename)
print("SQL Alchemy db initialized")
# parse 'raw_ftrace_entries' table
- count = parse_file(trace_filename, session, engine)
+ count = trace2db.parse_file_into_db(trace_filename, limit=args.limit)
print("Count was ", count)
- # create other tables
-# count = decode_raw_traces(session, engine)
-
return 0
if __name__ == '__main__':
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index f2f3c2d..dc026d4 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -68,8 +68,7 @@
void notifyOtaspChanged(in int subId, in int otaspMode);
@UnsupportedAppUsage
void notifyCellInfo(in List<CellInfo> cellInfo);
- void notifyPhysicalChannelConfiguration(in List<PhysicalChannelConfig> configs);
- void notifyPhysicalChannelConfigurationForSubscriber(in int subId,
+ void notifyPhysicalChannelConfigurationForSubscriber(in int phoneId, in int subId,
in List<PhysicalChannelConfig> configs);
void notifyPreciseCallState(int phoneId, int subId, int ringingCallState,
int foregroundCallState, int backgroundCallState);
diff --git a/tests/FeatureSplit/feature1/Android.bp b/tests/FeatureSplit/feature1/Android.bp
index 1a93e84..706a4f5 100644
--- a/tests/FeatureSplit/feature1/Android.bp
+++ b/tests/FeatureSplit/feature1/Android.bp
@@ -18,7 +18,7 @@
name: "FeatureSplit1",
srcs: ["**/*.java"],
sdk_version: "current",
- libs: ["FeatureSplitBase"],
+ libs: ["FeatureSplitBase", "FeatureSplit2"],
aaptflags: [
"--package-id",
"0x80",
diff --git a/tests/FeatureSplit/feature1/AndroidManifest.xml b/tests/FeatureSplit/feature1/AndroidManifest.xml
index b87361f..086c2c3 100644
--- a/tests/FeatureSplit/feature1/AndroidManifest.xml
+++ b/tests/FeatureSplit/feature1/AndroidManifest.xml
@@ -19,6 +19,7 @@
featureSplit="feature1">
<uses-sdk android:minSdkVersion="21" />
+ <uses-split android:name="feature2" />
<application>
<activity android:name=".one.One" android:label="Feature One">
diff --git a/tests/FeatureSplit/feature1/res/layout/included.xml b/tests/FeatureSplit/feature1/res/layout/included.xml
index c64bdb7..f0c56f8 100644
--- a/tests/FeatureSplit/feature1/res/layout/included.xml
+++ b/tests/FeatureSplit/feature1/res/layout/included.xml
@@ -2,4 +2,5 @@
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/text"
android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
+ android:layout_height="wrap_content"
+ android:text="@string/feature2_string" />
diff --git a/tests/FeatureSplit/feature1/res/values/values.xml b/tests/FeatureSplit/feature1/res/values/values.xml
index 7d58865..6a840df 100644
--- a/tests/FeatureSplit/feature1/res/values/values.xml
+++ b/tests/FeatureSplit/feature1/res/values/values.xml
@@ -20,7 +20,8 @@
<integer name="test_integer2">200</integer>
<color name="test_color2">#00ff00</color>
<string-array name="string_array2">
- <item>@string/app_title</item>
+ <item>@string/app_title</item>
+ <item>@string/feature2_string</item>
</string-array>
</resources>
diff --git a/tests/FeatureSplit/feature2/res/values/values.xml b/tests/FeatureSplit/feature2/res/values/values.xml
index af5ed1b..70e772c 100644
--- a/tests/FeatureSplit/feature2/res/values/values.xml
+++ b/tests/FeatureSplit/feature2/res/values/values.xml
@@ -15,10 +15,11 @@
-->
<resources>
+ <string name="feature2_string">feature 2 string referenced from feature 1</string>
<integer name="test_integer3">300</integer>
<color name="test_color3">#0000ff</color>
<string-array name="string_array3">
- <item>@string/app_title</item>
+ <item>@string/app_title</item>
</string-array>
</resources>
diff --git a/tools/aapt2/AppInfo.h b/tools/aapt2/AppInfo.h
index 7512353..d3ca357 100644
--- a/tools/aapt2/AppInfo.h
+++ b/tools/aapt2/AppInfo.h
@@ -17,6 +17,7 @@
#ifndef AAPT_APP_INFO_H
#define AAPT_APP_INFO_H
+#include <set>
#include <string>
#include "util/Maybe.h"
@@ -42,6 +43,9 @@
// The app's split name, if it is a split.
Maybe<std::string> split_name;
+
+ // The split names that this split depends on.
+ std::set<std::string> split_name_dependencies;
};
} // namespace aapt
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 9b81369f..c7ac438 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -629,6 +629,12 @@
return 0;
}
+ const std::set<std::string>& GetSplitNameDependencies() override {
+ UNIMPLEMENTED(FATAL) << "No Split Name Dependencies be needed in compile phase";
+ static std::set<std::string> empty;
+ return empty;
+ }
+
private:
DISALLOW_COPY_AND_ASSIGN(CompileContext);
diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index 0cf86cc..22bcd85 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -243,6 +243,12 @@
return 0u;
}
+ const std::set<std::string>& GetSplitNameDependencies() override {
+ UNIMPLEMENTED(FATAL) << "Split Name Dependencies should not be necessary";
+ static std::set<std::string> empty;
+ return empty;
+ }
+
bool verbose_ = false;
std::string package_;
diff --git a/tools/aapt2/cmd/Diff.cpp b/tools/aapt2/cmd/Diff.cpp
index 262f4fc4e..d56994e 100644
--- a/tools/aapt2/cmd/Diff.cpp
+++ b/tools/aapt2/cmd/Diff.cpp
@@ -65,6 +65,12 @@
return 0;
}
+ const std::set<std::string>& GetSplitNameDependencies() override {
+ UNIMPLEMENTED(FATAL) << "Split Name Dependencies should not be necessary";
+ static std::set<std::string> empty;
+ return empty;
+ }
+
private:
std::string empty_;
StdErrDiagnostics diagnostics_;
diff --git a/tools/aapt2/cmd/Dump.cpp b/tools/aapt2/cmd/Dump.cpp
index a23a6a4..429aff1 100644
--- a/tools/aapt2/cmd/Dump.cpp
+++ b/tools/aapt2/cmd/Dump.cpp
@@ -118,6 +118,12 @@
return 0;
}
+ const std::set<std::string>& GetSplitNameDependencies() override {
+ UNIMPLEMENTED(FATAL) << "Split Name Dependencies should not be necessary";
+ static std::set<std::string> empty;
+ return empty;
+ }
+
private:
StdErrDiagnostics diagnostics_;
bool verbose_ = false;
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index b8be21c..bbf71e7 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -140,6 +140,14 @@
min_sdk_version_ = minSdk;
}
+ const std::set<std::string>& GetSplitNameDependencies() override {
+ return split_name_dependencies_;
+ }
+
+ void SetSplitNameDependencies(const std::set<std::string>& split_name_dependencies) {
+ split_name_dependencies_ = split_name_dependencies;
+ }
+
private:
DISALLOW_COPY_AND_ASSIGN(LinkContext);
@@ -151,6 +159,7 @@
SymbolTable symbols_;
bool verbose_ = false;
int min_sdk_version_ = 0;
+ std::set<std::string> split_name_dependencies_;
};
// A custom delegate that generates compatible pre-O IDs for use with feature splits.
@@ -269,6 +278,7 @@
bool keep_raw_values = false;
bool do_not_compress_anything = false;
bool update_proguard_spec = false;
+ bool do_not_fail_on_missing_resources = false;
OutputFormat output_format = OutputFormat::kApk;
std::unordered_set<std::string> extensions_to_not_compress;
Maybe<std::regex> regex_to_not_compress;
@@ -435,7 +445,7 @@
xml::StripAndroidStudioAttributes(doc->root.get());
XmlReferenceLinker xml_linker;
- if (!xml_linker.Consume(context_, doc)) {
+ if (!options_.do_not_fail_on_missing_resources && !xml_linker.Consume(context_, doc)) {
return {};
}
@@ -963,6 +973,17 @@
app_info.min_sdk_version = ResourceUtils::ParseSdkVersion(min_sdk->value);
}
}
+
+ for (const xml::Element* child_el : manifest_el->GetChildElements()) {
+ if (child_el->namespace_uri.empty() && child_el->name == "uses-split") {
+ if (const xml::Attribute* split_name =
+ child_el->FindAttribute(xml::kSchemaAndroid, "name")) {
+ if (!split_name->value.empty()) {
+ app_info.split_name_dependencies.insert(split_name->value);
+ }
+ }
+ }
+ }
return app_info;
}
@@ -1674,6 +1695,7 @@
file_flattener_options.update_proguard_spec =
static_cast<bool>(options_.generate_proguard_rules_path);
file_flattener_options.output_format = options_.output_format;
+ file_flattener_options.do_not_fail_on_missing_resources = options_.merge_only;
ResourceFileFlattener file_flattener(file_flattener_options, context_, keep_set);
if (!file_flattener.Flatten(table, writer)) {
@@ -1770,6 +1792,7 @@
context_->SetMinSdkVersion(app_info_.min_sdk_version.value_or_default(0));
context_->SetNameManglerPolicy(NameManglerPolicy{context_->GetCompilationPackage()});
+ context_->SetSplitNameDependencies(app_info_.split_name_dependencies);
// Override the package ID when it is "android".
if (context_->GetCompilationPackage() == "android") {
@@ -1901,7 +1924,7 @@
}
ReferenceLinker linker;
- if (!linker.Consume(context_, &final_table_)) {
+ if (!options_.merge_only && !linker.Consume(context_, &final_table_)) {
context_->GetDiagnostics()->Error(DiagMessage() << "failed linking references");
return 1;
}
@@ -2053,7 +2076,7 @@
manifest_xml->file.name.package = context_->GetCompilationPackage();
XmlReferenceLinker manifest_linker;
- if (manifest_linker.Consume(context_, manifest_xml.get())) {
+ if (options_.merge_only || manifest_linker.Consume(context_, manifest_xml.get())) {
if (options_.generate_proguard_rules_path &&
!proguard::CollectProguardRulesForManifest(manifest_xml.get(), &proguard_keep_set)) {
error = true;
@@ -2187,6 +2210,12 @@
return 1;
}
+ if (options_.merge_only && !static_lib_) {
+ context.GetDiagnostics()->Error(
+ DiagMessage() << "the --merge-only flag can be only used when building a static library");
+ return 1;
+ }
+
// The default build type.
context.SetPackageType(PackageType::kApp);
context.SetPackageId(kAppPackageId);
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
index 37765f6..324807c 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -71,6 +71,7 @@
// Static lib options.
bool no_static_lib_packages = false;
+ bool merge_only = false;
// AndroidManifest.xml massaging options.
ManifestFixerOptions manifest_fixer_options;
@@ -285,6 +286,10 @@
AddOptionalSwitch("-v", "Enables verbose logging.", &verbose_);
AddOptionalFlag("--trace-folder", "Generate systrace json trace fragment to specified folder.",
&trace_folder_);
+ AddOptionalSwitch("--merge-only",
+ "Only merge the resources, without verifying resource references. This flag\n"
+ "should only be used together with the --static-lib flag.",
+ &options_.merge_only);
}
int Action(const std::vector<std::string>& args) override;
diff --git a/tools/aapt2/cmd/Link_test.cpp b/tools/aapt2/cmd/Link_test.cpp
index bf8f043..062dd8e 100644
--- a/tools/aapt2/cmd/Link_test.cpp
+++ b/tools/aapt2/cmd/Link_test.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "AppInfo.h"
#include "Link.h"
#include "LoadedApk.h"
@@ -253,4 +254,67 @@
EXPECT_EQ(actual_style->entries[0].key.id, 0x010100d4); // android:background
}
+TEST_F(LinkTest, AppInfoWithUsesSplit) {
+ StdErrDiagnostics diag;
+ const std::string base_files_dir = GetTestPath("base");
+ ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
+ R"(<resources>
+ <string name="bar">bar</string>
+ </resources>)",
+ base_files_dir, &diag));
+ const std::string base_apk = GetTestPath("base.apk");
+ std::vector<std::string> link_args = {
+ "--manifest", GetDefaultManifest("com.aapt2.app"),
+ "-o", base_apk,
+ };
+ ASSERT_TRUE(Link(link_args, base_files_dir, &diag));
+
+ const std::string feature_manifest = GetTestPath("feature_manifest.xml");
+ WriteFile(feature_manifest, android::base::StringPrintf(R"(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.aapt2.app" split="feature1">
+ </manifest>)"));
+ const std::string feature_files_dir = GetTestPath("feature");
+ ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
+ R"(<resources>
+ <string name="foo">foo</string>
+ </resources>)",
+ feature_files_dir, &diag));
+ const std::string feature_apk = GetTestPath("feature.apk");
+ const std::string feature_package_id = "0x80";
+ link_args = {
+ "--manifest", feature_manifest,
+ "-I", base_apk,
+ "--package-id", feature_package_id,
+ "-o", feature_apk,
+ };
+ ASSERT_TRUE(Link(link_args, feature_files_dir, &diag));
+
+ const std::string feature2_manifest = GetTestPath("feature2_manifest.xml");
+ WriteFile(feature2_manifest, android::base::StringPrintf(R"(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.aapt2.app" split="feature2">
+ <uses-split android:name="feature1"/>
+ </manifest>)"));
+ const std::string feature2_files_dir = GetTestPath("feature2");
+ ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
+ R"(<resources>
+ <string-array name="string_array">
+ <item>@string/bar</item>
+ <item>@string/foo</item>
+ </string-array>
+ </resources>)",
+ feature2_files_dir, &diag));
+ const std::string feature2_apk = GetTestPath("feature2.apk");
+ const std::string feature2_package_id = "0x81";
+ link_args = {
+ "--manifest", feature2_manifest,
+ "-I", base_apk,
+ "-I", feature_apk,
+ "--package-id", feature2_package_id,
+ "-o", feature2_apk,
+ };
+ ASSERT_TRUE(Link(link_args, feature2_files_dir, &diag));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index 2e6af18..5e06818 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -108,6 +108,12 @@
return sdk_version_;
}
+ const std::set<std::string>& GetSplitNameDependencies() override {
+ UNIMPLEMENTED(FATAL) << "Split Name Dependencies should not be necessary";
+ static std::set<std::string> empty;
+ return empty;
+ }
+
private:
DISALLOW_COPY_AND_ASSIGN(OptimizeContext);
diff --git a/tools/aapt2/integration-tests/MergeOnlyTest/Android.mk b/tools/aapt2/integration-tests/MergeOnlyTest/Android.mk
new file mode 100644
index 0000000..6361f9b
--- /dev/null
+++ b/tools/aapt2/integration-tests/MergeOnlyTest/Android.mk
@@ -0,0 +1,2 @@
+LOCAL_PATH := $(call my-dir)
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tools/aapt2/integration-tests/MergeOnlyTest/App/Android.mk b/tools/aapt2/integration-tests/MergeOnlyTest/App/Android.mk
new file mode 100644
index 0000000..6bc2064
--- /dev/null
+++ b/tools/aapt2/integration-tests/MergeOnlyTest/App/Android.mk
@@ -0,0 +1,29 @@
+//
+// Copyright (C) 2019 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.
+//
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
+LOCAL_AAPT_NAMESPACES := true
+LOCAL_PACKAGE_NAME := AaptTestMergeOnly_App
+LOCAL_SDK_VERSION := current
+LOCAL_EXPORT_PACKAGE_RESOURCES := true
+LOCAL_MODULE_TAGS := tests
+LOCAL_STATIC_ANDROID_LIBRARIES := \
+ AaptTestMergeOnly_LeafLib \
+ AaptTestMergeOnly_LocalLib
+include $(BUILD_PACKAGE)
\ No newline at end of file
diff --git a/tools/aapt2/integration-tests/MergeOnlyTest/App/AndroidManifest.xml b/tools/aapt2/integration-tests/MergeOnlyTest/App/AndroidManifest.xml
new file mode 100644
index 0000000..bc3a7e5
--- /dev/null
+++ b/tools/aapt2/integration-tests/MergeOnlyTest/App/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.app">
+
+ <application
+ android:label="@*com.local.lib:string/lib_string"/>
+
+</manifest>
\ No newline at end of file
diff --git a/tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/Android.mk b/tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/Android.mk
new file mode 100644
index 0000000..7bf8cf8
--- /dev/null
+++ b/tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/Android.mk
@@ -0,0 +1,28 @@
+//
+// Copyright (C) 2019 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.
+//
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
+LOCAL_AAPT_NAMESPACES := true
+LOCAL_MODULE := AaptTestMergeOnly_LeafLib
+LOCAL_SDK_VERSION := current
+LOCAL_MODULE_TAGS := tests
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_MIN_SDK_VERSION := 21
+LOCAL_AAPT_FLAGS := --merge-only
+include $(BUILD_STATIC_JAVA_LIBRARY)
\ No newline at end of file
diff --git a/tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/AndroidManifest.xml b/tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/AndroidManifest.xml
new file mode 100644
index 0000000..9907bd9
--- /dev/null
+++ b/tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest package="com.leaf.lib" />
diff --git a/tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/res/layout/activity.xml b/tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/res/layout/activity.xml
new file mode 100644
index 0000000..07de87f
--- /dev/null
+++ b/tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/res/layout/activity.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:leaf="http://schemas.android.com/apk/res/com.leaf.lib">
+
+ <TextView android:text="@string/leaf_string"
+ leaf:leaf_attr="hello"
+ style="@style/LeafChildStyle"/>
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/res/values/values.xml b/tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/res/values/values.xml
new file mode 100644
index 0000000..7f94c26
--- /dev/null
+++ b/tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/res/values/values.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<resources>
+ <attr format="string" name="leaf_attr"/>
+ <attr format="string" name="leaf_attr2"/>
+
+ <string name="leaf_string">I am a leaf</string>
+
+ <style name="LeafParentStyle">
+ <item type="attr" name="leaf_attr"/>
+ <item type="attr" name="leaf_attr2"/>
+ </style>
+
+ <style name="LeafChildStyle" parent="LeafParentStyle">
+ <item type="attr" name="leaf_attr2">hello</item>
+ </style>
+
+ <style name="LeafParentStyle.DottedChild"/>
+
+ <declare-styleable name="leaf_ds">
+ <attr name="leaf_attr">hello</attr>
+ </declare-styleable>
+
+ <public type="attr" name="leaf_attr"/>
+ <public type="attr" name="leaf_attr2"/>
+ <public type="style" name="LeafParentStyle"/>
+ <public type="style" name="LeafChildStyle"/>
+ <public type="style" name="LeafParentStyle.DottedChild"/>
+</resources>
\ No newline at end of file
diff --git a/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/Android.mk b/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/Android.mk
new file mode 100644
index 0000000..ba781c5
--- /dev/null
+++ b/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/Android.mk
@@ -0,0 +1,28 @@
+//
+// Copyright (C) 2019 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.
+//
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
+LOCAL_AAPT_NAMESPACES := true
+LOCAL_MODULE := AaptTestMergeOnly_LocalLib
+LOCAL_SDK_VERSION := current
+LOCAL_MODULE_TAGS := tests
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_MIN_SDK_VERSION := 21
+LOCAL_AAPT_FLAGS := --merge-only
+include $(BUILD_STATIC_JAVA_LIBRARY)
\ No newline at end of file
diff --git a/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/AndroidManifest.xml b/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/AndroidManifest.xml
new file mode 100644
index 0000000..aa0ff5d
--- /dev/null
+++ b/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.local.lib">
+
+ <application>
+
+ <activity
+ android:name="com.myapp.MyActivity"
+ android:theme="@com.leaf.lib:style/LeafParentStyle.DottedChild"/>
+
+ </application>
+
+</manifest>
diff --git a/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/res/layout/activity.xml b/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/res/layout/activity.xml
new file mode 100644
index 0000000..80d2fd6
--- /dev/null
+++ b/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/res/layout/activity.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:leaf="http://schemas.android.com/apk/res/com.leaf.lib">
+
+ <TextView android:text="@*com.leaf.lib:string/leaf_string"
+ leaf:leaf_attr="hello"
+ style="@com.leaf.lib:style/LeafChildStyle"/>
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/res/layout/includer.xml b/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/res/layout/includer.xml
new file mode 100644
index 0000000..f063718
--- /dev/null
+++ b/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/res/layout/includer.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:leaf="http://schemas.android.com/apk/res/com.leaf.lib">
+
+ <include layout="@layout/activity"/>
+
+ <include layout="@*com.leaf.lib:layout/activity"/>
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/res/values/values.xml b/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/res/values/values.xml
new file mode 100644
index 0000000..2f9704d
--- /dev/null
+++ b/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/res/values/values.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<resources>
+ <string name="lib_string">@*com.leaf.lib:string/leaf_string</string>
+
+ <style name="lib_style" parent="@com.leaf.lib:style/LeafChildStyle">
+ <item name="com.leaf.lib:leaf_attr">hello</item>
+ </style>
+
+ <style name="LeafParentStyle.DottedChild.LocalLibStyle"
+ parent="@com.leaf.lib:style/LeafParentStyle.DottedChild"/>
+
+ <public type="style" name="LeafParentStyle.DottedChild.LocalLibStyle"/>
+
+</resources>
\ No newline at end of file
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 28f09aa..8e49fab 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -17,6 +17,7 @@
#include "link/ReferenceLinker.h"
#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
#include "androidfw/ResourceTypes.h"
#include "Diagnostics.h"
@@ -33,6 +34,7 @@
using ::aapt::ResourceUtils::StringBuilder;
using ::android::StringPiece;
+using ::android::base::StringPrintf;
namespace aapt {
@@ -81,7 +83,7 @@
// Find the attribute in the symbol table and check if it is visible from this callsite.
const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveAttributeCheckVisibility(
- transformed_reference, callsite_, symbols_, &err_str);
+ transformed_reference, callsite_, context_, symbols_, &err_str);
if (symbol) {
// Assign our style key the correct ID. The ID may not exist.
entry.key.id = symbol->id;
@@ -203,12 +205,35 @@
const SymbolTable::Symbol* ReferenceLinker::ResolveSymbol(const Reference& reference,
const CallSite& callsite,
+ IAaptContext* context,
SymbolTable* symbols) {
if (reference.name) {
const ResourceName& name = reference.name.value();
if (name.package.empty()) {
// Use the callsite's package name if no package name was defined.
- return symbols->FindByName(ResourceName(callsite.package, name.type, name.entry));
+ const SymbolTable::Symbol* symbol = symbols->FindByName(
+ ResourceName(callsite.package, name.type, name.entry));
+ if (symbol) {
+ return symbol;
+ }
+
+ // If the callsite package is the same as the current compilation package,
+ // check the feature split dependencies as well. Feature split resources
+ // can be referenced without a namespace, just like the base package.
+ // TODO: modify the package name of included splits instead of having the
+ // symbol table look up the resource in in every package. b/136105066
+ if (callsite.package == context->GetCompilationPackage()) {
+ const auto& split_name_dependencies = context->GetSplitNameDependencies();
+ for (const std::string& split_name : split_name_dependencies) {
+ std::string split_package =
+ StringPrintf("%s.%s", callsite.package.c_str(), split_name.c_str());
+ symbol = symbols->FindByName(ResourceName(split_package, name.type, name.entry));
+ if (symbol) {
+ return symbol;
+ }
+ }
+ }
+ return nullptr;
}
return symbols->FindByName(name);
} else if (reference.id) {
@@ -220,9 +245,10 @@
const SymbolTable::Symbol* ReferenceLinker::ResolveSymbolCheckVisibility(const Reference& reference,
const CallSite& callsite,
+ IAaptContext* context,
SymbolTable* symbols,
std::string* out_error) {
- const SymbolTable::Symbol* symbol = ResolveSymbol(reference, callsite, symbols);
+ const SymbolTable::Symbol* symbol = ResolveSymbol(reference, callsite, context, symbols);
if (!symbol) {
if (out_error) *out_error = "not found";
return nullptr;
@@ -236,10 +262,10 @@
}
const SymbolTable::Symbol* ReferenceLinker::ResolveAttributeCheckVisibility(
- const Reference& reference, const CallSite& callsite, SymbolTable* symbols,
- std::string* out_error) {
+ const Reference& reference, const CallSite& callsite, IAaptContext* context,
+ SymbolTable* symbols, std::string* out_error) {
const SymbolTable::Symbol* symbol =
- ResolveSymbolCheckVisibility(reference, callsite, symbols, out_error);
+ ResolveSymbolCheckVisibility(reference, callsite, context, symbols, out_error);
if (!symbol) {
return nullptr;
}
@@ -253,10 +279,11 @@
Maybe<xml::AaptAttribute> ReferenceLinker::CompileXmlAttribute(const Reference& reference,
const CallSite& callsite,
+ IAaptContext* context,
SymbolTable* symbols,
std::string* out_error) {
const SymbolTable::Symbol* symbol =
- ResolveAttributeCheckVisibility(reference, callsite, symbols, out_error);
+ ResolveAttributeCheckVisibility(reference, callsite, context, symbols, out_error);
if (!symbol) {
return {};
}
@@ -335,7 +362,7 @@
std::string err_str;
const SymbolTable::Symbol* s =
- ResolveSymbolCheckVisibility(transformed_reference, callsite, symbols, &err_str);
+ ResolveSymbolCheckVisibility(transformed_reference, callsite, context, symbols, &err_str);
if (s) {
// The ID may not exist. This is fine because of the possibility of building
// against libraries without assigned IDs.
diff --git a/tools/aapt2/link/ReferenceLinker.h b/tools/aapt2/link/ReferenceLinker.h
index b0b4945..1256709 100644
--- a/tools/aapt2/link/ReferenceLinker.h
+++ b/tools/aapt2/link/ReferenceLinker.h
@@ -39,13 +39,16 @@
// package if the reference has no package name defined (implicit).
// Returns nullptr if the symbol was not found.
static const SymbolTable::Symbol* ResolveSymbol(const Reference& reference,
- const CallSite& callsite, SymbolTable* symbols);
+ const CallSite& callsite,
+ IAaptContext* context,
+ SymbolTable* symbols);
// Performs name mangling and looks up the resource in the symbol table. If the symbol is not
// visible by the reference at the callsite, nullptr is returned.
// `out_error` holds the error message.
static const SymbolTable::Symbol* ResolveSymbolCheckVisibility(const Reference& reference,
const CallSite& callsite,
+ IAaptContext* context,
SymbolTable* symbols,
std::string* out_error);
@@ -53,6 +56,7 @@
// That is, the return value will have a non-null value for ISymbolTable::Symbol::attribute.
static const SymbolTable::Symbol* ResolveAttributeCheckVisibility(const Reference& reference,
const CallSite& callsite,
+ IAaptContext* context,
SymbolTable* symbols,
std::string* out_error);
@@ -60,6 +64,7 @@
// If resolution fails, outError holds the error message.
static Maybe<xml::AaptAttribute> CompileXmlAttribute(const Reference& reference,
const CallSite& callsite,
+ IAaptContext* context,
SymbolTable* symbols,
std::string* out_error);
diff --git a/tools/aapt2/link/ReferenceLinker_test.cpp b/tools/aapt2/link/ReferenceLinker_test.cpp
index be38b96..a31ce94 100644
--- a/tools/aapt2/link/ReferenceLinker_test.cpp
+++ b/tools/aapt2/link/ReferenceLinker_test.cpp
@@ -266,8 +266,13 @@
std::string error;
const CallSite call_site{"com.app.test"};
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .SetCompilationPackage("com.app.test")
+ .SetPackageId(0x7f)
+ .Build();
const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveSymbolCheckVisibility(
- *test::BuildReference("com.app.test:string/foo"), call_site, &table, &error);
+ *test::BuildReference("com.app.test:string/foo"), call_site, context.get(), &table, &error);
ASSERT_THAT(symbol, NotNull());
EXPECT_TRUE(error.empty());
}
@@ -281,17 +286,23 @@
.AddPublicSymbol("com.app.test:attr/public_foo", ResourceId(0x7f010001),
test::AttributeBuilder().Build())
.Build());
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .SetCompilationPackage("com.app.ext")
+ .SetPackageId(0x7f)
+ .Build();
std::string error;
const CallSite call_site{"com.app.ext"};
EXPECT_FALSE(ReferenceLinker::CompileXmlAttribute(
- *test::BuildReference("com.app.test:attr/foo"), call_site, &table, &error));
+ *test::BuildReference("com.app.test:attr/foo"), call_site, context.get(), &table, &error));
EXPECT_FALSE(error.empty());
error = "";
ASSERT_TRUE(ReferenceLinker::CompileXmlAttribute(
- *test::BuildReference("com.app.test:attr/public_foo"), call_site, &table, &error));
+ *test::BuildReference("com.app.test:attr/public_foo"), call_site, context.get(), &table,
+ &error));
EXPECT_TRUE(error.empty());
}
@@ -302,20 +313,62 @@
.AddSymbol("com.app.test:string/foo", ResourceId(0x7f010000))
.AddSymbol("com.app.lib:string/foo", ResourceId(0x7f010001))
.Build());
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .SetCompilationPackage("com.app.test")
+ .SetPackageId(0x7f)
+ .Build();
const SymbolTable::Symbol* s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"),
- CallSite{"com.app.test"}, &table);
+ CallSite{"com.app.test"},
+ context.get(), &table);
ASSERT_THAT(s, NotNull());
EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010000)));
s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"), CallSite{"com.app.lib"},
- &table);
+ context.get(), &table);
ASSERT_THAT(s, NotNull());
EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010001)));
EXPECT_THAT(ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"),
- CallSite{"com.app.bad"}, &table),
+ CallSite{"com.app.bad"}, context.get(), &table),
IsNull());
}
+TEST(ReferenceLinkerTest, ReferenceSymbolFromOtherSplit) {
+ NameMangler mangler(NameManglerPolicy{"com.app.test"});
+ SymbolTable table(&mangler);
+ table.AppendSource(test::StaticSymbolSourceBuilder()
+ .AddSymbol("com.app.test.feature:string/bar", ResourceId(0x80010000))
+ .Build());
+ std::set<std::string> split_name_dependencies;
+ split_name_dependencies.insert("feature");
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .SetCompilationPackage("com.app.test")
+ .SetPackageId(0x81)
+ .SetSplitNameDependencies(split_name_dependencies)
+ .Build();
+
+ const SymbolTable::Symbol* s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/bar"),
+ CallSite{"com.app.test"},
+ context.get(), &table);
+ ASSERT_THAT(s, NotNull());
+ EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x80010000)));
+
+ s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"), CallSite{"com.app.lib"},
+ context.get(), &table);
+ EXPECT_THAT(s, IsNull());
+
+ context =
+ test::ContextBuilder()
+ .SetCompilationPackage("com.app.test")
+ .SetPackageId(0x81)
+ .Build();
+ s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/bar"),CallSite{"com.app.test"},
+ context.get(), &table);
+
+ EXPECT_THAT(s, IsNull());
+}
+
} // namespace aapt
diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp
index d68f7dd..f3be483 100644
--- a/tools/aapt2/link/XmlReferenceLinker.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker.cpp
@@ -99,7 +99,7 @@
std::string err_str;
attr.compiled_attribute =
- ReferenceLinker::CompileXmlAttribute(attr_ref, callsite_, symbols_, &err_str);
+ ReferenceLinker::CompileXmlAttribute(attr_ref, callsite_, context_, symbols_, &err_str);
if (!attr.compiled_attribute) {
DiagMessage error_msg(source);
diff --git a/tools/aapt2/optimize/MultiApkGenerator.cpp b/tools/aapt2/optimize/MultiApkGenerator.cpp
index 8c9c434..c686a10 100644
--- a/tools/aapt2/optimize/MultiApkGenerator.cpp
+++ b/tools/aapt2/optimize/MultiApkGenerator.cpp
@@ -101,6 +101,10 @@
util::make_unique<SourcePathDiagnostics>(Source{source}, context_->GetDiagnostics());
}
+ const std::set<std::string>& GetSplitNameDependencies() override {
+ return context_->GetSplitNameDependencies();
+ }
+
private:
IAaptContext* context_;
std::unique_ptr<SourcePathDiagnostics> source_diag_;
diff --git a/tools/aapt2/process/IResourceTableConsumer.h b/tools/aapt2/process/IResourceTableConsumer.h
index 30dad802..9c4b323 100644
--- a/tools/aapt2/process/IResourceTableConsumer.h
+++ b/tools/aapt2/process/IResourceTableConsumer.h
@@ -19,6 +19,7 @@
#include <iostream>
#include <list>
+#include <set>
#include <sstream>
#include "Diagnostics.h"
@@ -50,6 +51,7 @@
virtual NameMangler* GetNameMangler() = 0;
virtual bool IsVerbose() = 0;
virtual int GetMinSdkVersion() = 0;
+ virtual const std::set<std::string>& GetSplitNameDependencies() = 0;
};
struct IResourceTableConsumer {
diff --git a/tools/aapt2/test/Context.h b/tools/aapt2/test/Context.h
index 0564db0..7e10a59 100644
--- a/tools/aapt2/test/Context.h
+++ b/tools/aapt2/test/Context.h
@@ -81,6 +81,10 @@
return min_sdk_version_;
}
+ const std::set<std::string>& GetSplitNameDependencies() override {
+ return split_name_dependencies_;
+ }
+
private:
DISALLOW_COPY_AND_ASSIGN(Context);
@@ -93,6 +97,7 @@
NameMangler name_mangler_;
SymbolTable symbols_;
int min_sdk_version_;
+ std::set<std::string> split_name_dependencies_;
};
class ContextBuilder {
@@ -127,6 +132,11 @@
return *this;
}
+ ContextBuilder& SetSplitNameDependencies(const std::set<std::string>& split_name_dependencies) {
+ context_->split_name_dependencies_ = split_name_dependencies;
+ return *this;
+ }
+
std::unique_ptr<Context> Build() { return std::move(context_); }
private: